Наследование и factory функции в Python и Django

107
6

Я создаю приложение Django, которое использует некоторое наследование в модели, главным образом потому, что мне нужно назначить все UUID и ссылку, чтобы я знал, какой класс был. Здесь упрощенная версия базового класса:


class BaseElement(models.Model):
uuid = models.CharField(max_length=64, editable=False, blank=True, default=lambda:unicode(uuid4()))
objmodule = models.CharField(max_length=255, editable=False, blank=False)
objclass = models.CharField(max_length=255, editable=False, blank=False)

class ChildElement(BaseElement):
somefield = models.CharField(max_length=255)


Я хотел бы убедиться, что objmodule, objclass и uuid установлены автоматически. Я узнал из этого сообщения, что это плохая идея сделать это, написав мой собственный конструктор и что мне лучше написать функцию factory. Итак, теперь мой BaseElement и ChildElement выглядит так:


class BaseElement(models.Model):
uuid = models.CharField(max_length=64, editable=False, blank=True, default=lambda:unicode(uuid4()))
objmodule = models.CharField(max_length=255, editable=False, blank=False)
objclass = models.CharField(max_length=255, editable=False, blank=False)

def set_defaults(self):
self.objmodule = unicode(self.__class__.__module__)
self.objclass = unicode(self.__class__.__name__)
self.uuid = unicode(uuid4())

class ChildElement(BaseElement):
somefield = models.CharField(max_length=255)

@staticmethod
def create(*args, **kwargs):
ce = ChildElement(*args, **kwargs)
ce.set_defaults()
return ce


Это работает. Я могу вызвать ChildElement.create(somefield="foo"), и я получу соответствующий объект с полями uuid, objmodule и objclass. Тем не менее, по мере того, как я просматриваю и создаю больше таких классов, как ChildElement2 и ChildElement3, я обнаруживаю, что я вставляю ту же статическую функцию factory. Это решается на меня, потому что неправильное копирование кода.


С обычными методами я просто вставляю функцию create factory в BaseElement, однако я не могу этого сделать, потому что у меня нет дескриптора для себя (потому что он не был созданный еще), чтобы получить информацию о классе объекта, который вызвал метод.


Можно ли переносить этот factory в класс BaseElement, поэтому мне не нужно дублировать этот код всюду и все еще иметь его, чтобы он автоматически устанавливал значения uuid, objmodule, и objclass?

спросил(а) 2021-01-25T17:02:04+03:00 4 месяца, 4 недели назад
1
Решение
125

Если вы сделаете create() a @classmethod вместо @staticmethod, у вас будет доступ к объекту класса, который вы можете использовать вместо обращения к нему по имени:


@classmethod
def create(cls, *args, **kwargs):
obj = cls(*args, **kwargs)
obj.set_defaults()
return obj

Теперь это общее и может идти в базовом классе вместо каждого дочернего класса.

ответил(а) 2021-01-25T17:02:04+03:00 4 месяца, 4 недели назад
78

Я думаю, что вам, возможно, было бы лучше отказаться от сохранения в вашем BaseElement. Затем при сохранении вы можете установить эти поля. Это будет что-то вроде:


class MyBase(models.Model):
uuid = models.CharField(max_length=64, editable=False, blank=True,
default=lambda:unicode(uuid4()))
objmodule = models.CharField(max_length=255, editable=False, blank=False)
objclass = models.CharField(max_length=255, editable=False, blank=False)

def save(self):
if not self.id:
self.objmodule = unicode(self.__class__.__module__)
self.objclass = unicode(self.__class__.__name__)
self.uuid = unicode(uuid4())
super(self.__class__.__base__, self).save()

class InheritedFromBase(MyBase):
new_field = models.CharField(max_length=100)

Я тестировал это, и он, похоже, делал то, что вы ищете. Я смог создать объект InheritedFromBase, который имел нужные вам поля, без большого количества дублирования кода.

ответил(а) 2021-01-25T17:02:04+03:00 4 месяца, 4 недели назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема