Как изменить атрибуты полевой модели при наследовании от абстрактных моделей

В своем коде я использую много абстрактных классов 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].

Вернуться на верх