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
с пользовательским фильтром.
Надеюсь, это поможет