Как изменить атрибуты полевой модели при наследовании от абстрактных моделей
В своем коде я использую много абстрактных классов Model. Когда я наследую от них, мне часто нужно изменить некоторые атрибуты определенных полей. Я пытаюсь реализовать решение, в котором эти атрибуты определяются как переменные класса, которые затем могут быть переопределены при инстанцировании.
Вот простой пример того, что я пытаюсь сделать:
Допустим, у нас есть класс абстрактной модели следующего вида:
class AbstractThing(models.Model):
MAX_LENGTH = 1000
IS_UNIQUE = True
name = models.CharField(max_length=MAX_LENGTH, unique=IS_UNIQUE)
class Meta:
abstract = True
Я надеюсь сделать следующее:
class RealThing(AbstractThing):
IS_UNIQUE = False
MAX_LENGTH = 200
Однако это не работает. Полученный файл миграции выглядит следующим образом:
migrations.CreateModel(
name='RealThing',
fields=[
('id', models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name='ID')),
('name', models.CharField(
max_length=1000,
unique=True)
),
],
options={
'abstract': False,
},
),
Неужели это просто невозможно или я делаю что-то неправильно? Любые идеи будут очень признательны.
models.CharField(max_length=MAX_LENGTH, unique=IS_UNIQUE)
вычисляется как во время определения класса, что означает, что MAX_LENGTH
и IS_UNIQUE
равны разрешен один раз и не будет динамически обновляться при создании подкласса.
Чтобы исправить это, просто переопределите поле в подклассе.
class RealThing(AbstractThing):
IS_UNIQUE = False
MAX_LENGTH = 200
name = models.CharField(max_length=MAX_LENGTH, unique=IS_UNIQUE) # override it
Вероятно, вы могли бы работать с метаклассом, например:
class AbstractMeta(ModelBase):
def __new__(cls, name, bases, attrs):
print((name, bases, attrs))
attrs['name'] = models.CharField(
max_length=attrs.get('MAX_LENGTH', 1000),
unique=attrs.get('IS_UNIQUE', True),
)
return super().__new__(cls, name, bases, attrs)
class AbstractThing(models.Model, metaclass=AbstractMeta):
class Meta:
abstract = True
class Foo(AbstractThing):
MAX_LENGTH = 100
class Bar(AbstractThing):
MAX_LENGTH = 200
Однако я не уверен, что это хорошая идея. Он содержит некоторые настройки, и, вероятно, логика должна выполняться перед .__init_subclass__(…)
метод [python-doc].