Как вручную сгенерировать фикстуры для полиморфных моделей Django?
У меня есть несколько Django Polymorphic моделей:
import uuid
from django.db import models
from polymorphic.models import PolymorphicModel
class Fruit(PolymorphicModel):
class Meta:
abstract = True
class Apple(Fruit):
variety=models.CharField(max_length=30,primary_key=True)
class Grape(Fruit):
id=models.UUIDField(primary_key=True, default=uuid.uuid4)
colour=models.CharField(max_length=30)
Тогда я могу создать несколько приспособлений:
[
{"model": "test_polymorphic.apple", "pk": "bramley", "fields": {}},
{"model": "test_polymorphic.apple", "pk": "granny smith", "fields": {}},
{"model": "test_polymorphic.grape", "pk": "00000000-0000-4000-8000-000000000000", "fields": { "colour": "red"} },
{"model": "test_polymorphic.grape", "pk": "00000000-0000-4000-8000-000000000001", "fields": { "colour": "green"} }
]
и с помощью python -m django loaddata fixture_name
загрузите его в базу данных, что "кажется" успешным.
Тогда, если я использую:
from test_polymorphic import models
models.Apple.objects.all()
При этом возникает ошибка:
PolymorphicTypeUndefined: The model Apple#bramley does not have a `polymorphic_ctype_id` value defined. If you created models outside polymorphic, e.g. through an import or migration, make sure the `polymorphic_ctype_id` field points to the ContentType ID of the model subclass.
Использование loaddata
обходит метод save()
модели, поэтому типы содержимого по умолчанию не устанавливаются в моделях.
Я смог найти соответствующие типы содержимого, используя:
from django.contrib.contenttypes.models import ContentType
for model_name in ("apple", "grape"):
print(
model_name,
ContentType.objects.get(app_label="test_polymorphic", model=model_name).id,
)
Что выводит:
apple: 3 grape: 2
и затем измените крепление на:
[
{
"model": "test_polymorphic.apple",
"pk": "bramley",
"fields": { "polymorphic_ctype_id": 3 }
},
{
"model": "test_polymorphic.apple",
"pk": "granny smith",
"fields": { "polymorphic_ctype_id": 3 }
},
{
"model": "test_polymorphic.grape",
"pk": "00000000-0000-4000-8000-000000000000",
"fields": { "colour": "red", "polymorphic_ctype_id": 2 }
},
{
"model": "test_polymorphic.grape",
"pk": "00000000-0000-4000-8000-000000000001",
"fields": { "colour": "green", "polymorphic_ctype_id": 2 }
}
]
Включение полиморфного типа содержимого в поля, после чего приспособление работает. Однако это значение фактически является магическим числом, и я не уверен, что оно не изменится, если:
- Приложение установлено рядом с другими Django-приложениями, которые уже генерируют типы контента; или
- Если в приложение добавлены другие модели;
- и т.д.
Вопрос:
Могу ли я полагаться на то, что polymorphic_ctype_id
будет фиксированным значением, и если нет, то как мне установить полиморфный тип содержимого по умолчанию в модели при загрузке приспособлений (если использование loaddata
обходит метод save
в модели и я не могу быть уверен в том, какими будут значения id типов содержимого)?
В документации Django Polymorphic - "Migrating existing models to polymorphic" показано, как перенести неполиморфные модели в полиморфные:
Для заполнения значения модели можно использовать следующий код на языке Python:
from django.contrib.contenttypes.models import ContentType from myapp.models import MyModel new_ct = ContentType.objects.get_for_model(MyModel) MyModel.objects.filter(polymorphic_ctype__isnull=True).update(polymorphic_ctype=new_ct)
Они приводят пример включения этой функции в миграцию, но ее также можно запустить вручную после использования loaddata
для загрузки приспособления, в котором значения не установлены.
Если необходимо обновить несколько моделей, то можно написать вспомогательную функцию:
from django.contrib.contenttypes.models import ContentType
def set_polymorphic_ctype(model):
app_label = model._meta.app_label
name = model._meta.model_name
ctype = ContentType.objects.get(app_label=app_label, model=name)
return model.objects.filter(polymorphic_ctype__isnull=True).update(polymorphic_ctype=ctype)
и вызовите его для каждой из моделей:
set_polymorphic_ctype(models.Apple)
set_polymorphic_ctype(models.Grape)
Если вы загружаете приспособления в TestCase
, то я не нашел метода обратного вызова, который выполнялся бы после загрузки приспособлений, но вы можете вызвать вспомогательную функцию во время настройки теста.
class FruitTestCase(TestCase):
fixtures = [ "fixture_name" ]
def setUp(self):
set_polymorphic_ctype(models.Apple)
set_polymorphic_ctype(models.Grape)
def test_something(self):
...