Как создать двойную встроенную связь с полиморфизмом?

Как создать двухуровневый инлайн в админке, где первый уровень будет обычным, а второй уровень будет содержать выбор "типа блока" для модели Screens, аналогичный использованию полиморфизма?Я уже попробовал реализовать это, но внутри модели Screens не появляется выбор типа блока. Как можно это исправить?
Изображение к вопросу
  • 6 февраля 2024 г. 17:20
Ответы на вопрос 1
Чтобы реализовать двухуровневый инлайн в админке Django с использованием полиморфизма, можно использовать следующий подход, который основан на использовании абстрактных базовых классов и создании конкретных подклассов для каждого типа блока.

Предположим, у нас есть следующая структура моделей:

```python
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.fields import GenericForeignKey
from django.db import models

class Screen(models.Model):
    name = models.CharField(max_length=255)

class Block(models.Model):
    screen = models.ForeignKey(Screen, related_name='blocks', on_delete=models.CASCADE)
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    class Meta:
        abstract = True

class TextBlock(Block):
    text = models.TextField()

class ImageBlock(Block):
    image = models.ImageField(upload_to='images/')
```

В этом случае каждый `Block` может быть либо `TextBlock`, либо `ImageBlock`.

Вам нужно будет создать соответствующие административные классы для каждого вида блока и зарегистрировать их в системе администрирования Django. Также потребуется определить инлайны для связанных объектов:

```python
from django.contrib import admin
from django.contrib.contenttypes.admin import GenericTabularInline
from .models import Screen, TextBlock, ImageBlock

class TextBlockInline(GenericTabularInline):
    model = TextBlock

class ImageBlockInline(GenericTabularInline):
    model = ImageBlock

class ScreenAdmin(admin.ModelAdmin):
    inlines = [TextBlockInline, ImageBlockInline]

admin.site.register(Screen, ScreenAdmin)
```

При таком подходе, инлайны для `TextBlock` и `ImageBlock` будут отображаться в админке как часть `Screen`.

Однако, если вы хотите использовать более гибкий и динамичный подход, к которому можно динамически добавлять новые типы блоков без необходимости изменения админ-класса, потребуется сложная реализация через `GenericForeignKey` и возможно использование сторонних библиотек, таких как `django-polymorphic` для упрощения работы с полиморфными моделями.

При использовании `django-polymorphic`, модели и административная часть могли бы выглядеть следующим образом:

```python
from django.db import models
from polymorphic.models import PolymorphicModel
from polymorphic.admin import PolymorphicParentModelAdmin, PolymorphicChildModelAdmin

class Block(PolymorphicModel):
    screen = models.ForeignKey(Screen, related_name='blocks', on_delete=models.CASCADE)

class TextBlock(Block):
    text = models.TextField()

class ImageBlock(Block):
    image = models.ImageField(upload_to='images/')

class BlockChildAdmin(PolymorphicChildModelAdmin):
    base_model = Block

class TextBlockAdmin(BlockChildAdmin):
    base_model = TextBlock

class ImageBlockAdmin(BlockChildAdmin):
    base_model = ImageBlock

class BlockParentAdmin(PolymorphicParentModelAdmin):
    base_model = Block
    child_models = (TextBlock, ImageBlock)

admin.site.register(Block, BlockParentAdmin)
admin.site.register(TextBlock, TextBlockAdmin)
admin.site.register(ImageBlock, ImageBlockAdmin)
```

Данный пример использует полиморфные модели `django-polymorphic`, которые позволяют легко определять административную часть для каждого типа блока. Это делает возможным выбор типа блока прямо в админке, сохраняя при этом понятное и чистое отображение в административном интерфейсе.
Похожие вопросы