Django admin: отображение полей в зависимости от модели

Я разрабатываю бэкенд для сайта типа посадочной страницы, и в процессе разработки проекта я столкнулся с большим количеством моделей

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

АДМИН-ПАНЕЛЬ

В качестве решения я рассмотрел передачу url-параметров и использование get_fieldsets для фильтрации полей, но не смог добиться желаемого.

@admin.register(Body)
class BodyAdmin(admin.ModelAdmin):
    def get_fieldsets(self, request, obj):
        model = self.model
        related_model = self.opts.related_objects[0].related_model
        fieldsets = copy.deepcopy(super().get_fieldsets(request, obj))

        if related_model is PageHome:
             fieldsets[0][1]['fields'] = model.FieldSets.HOME.value
        if related_model is PageServices:
            fieldsets[0][1]['fields'] = model.FieldSets.SERVICES.value

        return fieldsets

Правилен ли мой подход к созданию бэкенда для посадочной страницы и если да, то можно ли добиться фильтрации полей в теле? заранее спасибо

Я пробовал передавать параметры запроса, чтобы решить эту проблему, я также искал зависимость в BodyAdmin от вызывающего Page...Admin, но эти два варианта оказались безуспешными

Я думаю, что эти fieldsets[0][1]['fields'] = model.FieldSets.HOME.value и fieldsets[0][1]['fields'] = model.FieldSets.SERVICES.value должны работать нормально, можете ли вы проверить, что related_model - это то, что вы ожидаете? Попробуйте напечатать model.FieldSets.HOME.value перед if related_model is PageHome: и/или внутри него, просто чтобы отладить, где проблема.

Я думаю, что BodyAdmin не знает, с какого типа страницы вы пришли, что означает, что вам может потребоваться передать параметр всплывающей странице, а затем в BodyAdmin.get_fieldsets() вы обрабатываете переданный параметр.

Я не уверен, но думаю, что вам нужно изменить параметры URL, переопределив ForeignKeyRawIdWidget.url_parameters() в django.contrib.admin.widgets

Что касается структуры ваших моделей, я думаю, ее можно улучшить.

Поскольку PageHome, PageService и PageCareer похожи и имеют одинаковые поля, то почему бы вам не использовать одну модель с дополнительным полем для различения типа страницы. Затем используйте функцию прокси с пользовательским менеджером для каждого из них, например, так:

Создайте своих менеджеров:

# Base manager
class PageManager(models.Manager):
    class PageType(models.TextChoices):
        HOME = 'H', _('Home')
        SERVICE = 'S', _('Service')
        CAREER = 'C', _('Career')

    # default page type
    PAGE_TYPE = PageType.SERVICE

    def get_queryset(self, *args, **kwargs):
        return (
            super().get_queryset(*args, **kwargs)
            .filter(type=self.PAGE_TYPE)
        )

# Page managers
class PageHomeManager(PageManager):
    PAGE_TYPE = PageManager.PageType.HOME

class PageServiceManager(PageManager):
    PAGE_TYPE = PageManager.PageType.SERVICE

class PageCareerManager(PageManager):
    PAGE_TYPE = PageManager.PageType.CAREER

и что касается ваших моделей:

# Models
class Page(models.Model):
    title = models.CharField(max_length=255)
    type = models.CharField(max_length=1, choices=PageManager.PageType.choices, default=PageManager.PAGE_TYPE)
    language = models.ForeignKey(to='Language', on_delete=models.SET_NULL, blank=True, null=True)
    head = models.OneToOneField(to='Head', on_delete=models.SET_NULL, blank=True, null=True)
    body = models.OneToOneField(to='Body', on_delete=models.SET_NULL, blank=True, null=True, related_name='page_home')

    objects = PageManager()
    objects_lang = LanguageManager()

    def save(self, *args, **kwargs):
        if not self.pk:
            self.type = self.default_type if self.default_type else self.type
        return super().save(*args, **kwargs)

    def __str__(self):
        return f'{self.title}'


class PageHome(Page):
    class Meta:
        proxy = True

    objects = PageHomeManager()
    default_type = PageHomeManager.PAGE_TYPE

class PageServices(Page):
    class Meta:
        proxy = True

    objects = PageServiceManager()
    default_type = PageServiceManager.PAGE_TYPE

class PageCareer(Page):
    class Meta:
        proxy = True

    objects = PageCareerManager()
    default_type = PageCareerManager.PAGE_TYPE

Тогда вы сможете использовать PageHome, PageService и PageCareer так, как раньше. Потому что они будут вести себя как отдельная модель с собственной таблицей и полями, но на самом деле это не так. Они используют модель Page с пользовательским фильтром.

Надеюсь, это поможет

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