Вопрос:

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

python django django-models abstract-class django-migrations

1366 просмотра

2 ответа

174 Репутация автора

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

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 'в классе' superhero 'конфликтует с полем с похожим именем из базового класса' human '

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

Автор: George Источник Размещён: 15.11.2015 04:21

Ответы (2)


4 плюса

174 Репутация автора

Так что после прочтения документов я снова нашел решение:

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

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

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

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

Это оно.

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

Автор: George Размещён: 15.11.2015 04:48

1 плюс

6520 Репутация автора

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

При создании класса модели для наследования от абстрактной модели, миграции 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

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

  1. Создать абстрактную модель
  2. Напишите миграцию вручную для модели, которая должна наследоваться от абстрактной модели.
  3. Запустите миграцию (это удалит таблицу БД со всеми записями!)
  4. Наследовать от абстрактной модели
  5. Запустите makemigrations и мигрируйте
Автор: cezar Размещён: 25.08.2017 09:27
Вопросы из категории :
32x32