Как преобразовать модель django в абстрактную модель, если у нее уже есть связанные классы

Допустим, у меня есть следующая базовая модель:

class human(models.Model):
   gender = models.BooleanField()
   age = models.IntegerField()
   name = models.CharField(max_length=200)

И две модели, наследующие его:

class superhero(human):
   can_fly = models.BooleanField()

class villain(human):
   fingerprint = models.ImageField()

В какой-то момент в процессе разработки я понял, что на самом деле мне не нужен человеческий класс напрямую. Мне нужно только, чтобы это был набор параметров шаблона для моделей супергероев и злодеев. Если теперь я перейду к человеческому классу Meta и установлю abstract=True и изменю свои модели следующим образом:

class human(models.Model):
   gender = models.BooleanField()
   age = models.IntegerField()
   name = models.CharField(max_length=200)

   class Meta:
       abstract = True

class superhero(human):
   can_fly = models.BooleanField()

class villain(human):
   fingerprint = models.ImageField()

попытка выполнить миграцию и миграцию вызовет следующую ошибку

Локальное поле u'gender' в классе 'супергерой' конфликтует с полем с таким же именем из базового класса 'человек'

Как я могу переключиться на абстрактный класс, сохраняя все мои миграции, не возясь напрямую с базой данных?


person George    schedule 15.11.2015    source источник


Ответы (2)


Итак, прочитав документы еще раз, я нашел решение:

Ошибка возникает из-за того, что Django сохраняет модели в базе данных. Все модели, которые унаследованы от базовой модели human, не имеют всех human полей в своих собственных таблицах. Вместо этого у них есть только свои собственные поля и внешний ключ, который связывает их с соответствующими строками в таблице human. Но когда вы наследуете от абстрактного класса, все поля сохраняются непосредственно в таблице вашей модели. Поэтому, когда я попытался изменить класс human на abstract=True и унаследовать его в классе superhero, Django попытался создать все поля из таблицы human в таблице superhero, которая все еще имеет внешний ключ для существующей записи человека с полями с точно такими же именами.

предупреждение

Следование этой инструкции даст желаемый результат, но, к сожалению, уничтожит все записи моделей human superhero и villain

  1. Закомментируйте модели superhero и villain, чтобы Джанго удалил их.
  2. Выполните миграцию и выполните миграцию, чтобы таблицы superhero и villain были удалены.
  3. Установить abstract=True в human классе
  4. Выполните миграцию и снова выполните миграцию. Это удалит таблицу human, потому что теперь это абстрактный класс.
  5. Раскомментировать модели superhero и villain
  6. Делайте миграции и мигрируйте. Это создаст таблицы villain и superhero со всеми полями из класса human.

Это оно.

P.S. Почему мне нужно было перейти к абстрактному классу? Потому что я хотел сделать все свои villains и superheroes уникальными, используя параметр unique_together, который накладывает некоторые ограничения на уровне БД. Чтобы это стало возможным, все superhero полей должны были находиться в одной таблице. Теперь это работает.

person George    schedule 15.11.2015
comment
Вы можете предотвратить удаление данных, подделав миграцию. docs.djangoproject.com/en/2.2/ref /джанго-админ/ - person Greg Schmit; 09.10.2019

Это старый вопрос, но ответ сэкономил мне много усилий. Я просто хочу добавить кое-что.

При создании класса модели для наследования от абстрактной модели миграция Django удалит эту модель.

MyModel(models.Model):
    # some fields to be inherited later from the abstract model
    author = models.ForeignKey('auth.User')
    # other fields specific to this model

Теперь, если вы создадите абстрактную модель:

MyAbstractModel(models.Model):
    # fields to be used by children classes

    class Meta:
        abstract = True

и пусть ваша модель наследуется от него:

MyModel(MyAbstractModel):
    author = models.ForeignKey('auth.User')
    # other fields specific to this model

Если вы запустите makemigrations и мигрируете в свое приложение, Django удалит эту модель и удалит соответствующую таблицу БД.

Вы можете перехитрить Джанго, закомментировав код этой модели. Django удалит его со всеми остальными последствиями. Теперь вы можете раскомментировать код и снова запустить миграцию, и Django снова создаст таблицу БД.

Однако у вас, вероятно, будет администратор, представления, формы и т. д. для импорта вашей модели. Это означает, что миграция вызовет ошибку, потому что этот класс не может быть найден. Вам придется закомментировать код во всех файлах, в которые импортируется ваша модель.

Проще было бы пропустить комментирование и написать нужную миграцию вручную:

from __future__ import unicode_literals

from django.db import migrations


class Migration(migrations.Migration):

    dependencies = [
        # app_label should be your app
        # and 000x_the_last_migration is the name of the migration
        # it depends on, usually the last one
        ('app_label', '000x_the_last_migration'),
    ]

    operations = [
        # do this for all ForeignKey, ManyToManyField, OneToOneField
        # where model_name is obviously the model name and name is the
        # field name
        migrations.RemoveField(
            model_name='mymodel',
            name='author',
        ),
        # finally delete the model
        migrations.DeleteModel(
            name='MyModel',
        ),
    ]

Теперь вы можете запустить миграцию:

python manage.py migrate app_label

позже наследовать от абстрактной модели (см. код выше, 3-й блок) и выполнить новую миграцию:

python manage.py makemigrations app_label

Это должно избавить вас от комментирования больших кусков кода. Если вы хотите сохранить данные в таблице БД, вы можете использовать dumpdata и loaddata.

TLDR

Это не было пошаговым руководством и требовало опыта работы с Django. Короче говоря, вы должны:

  1. Создать абстрактную модель
  2. Напишите миграцию вручную для модели, которая должна наследоваться от абстрактной модели.
  3. Запустите миграцию (это удалит таблицу БД со всеми записями!)
  4. Наследовать от абстрактной модели
  5. Запуск makemigrations и миграция
person cezar    schedule 25.08.2017