Как настроить отображение связанного поля (ForeignKey) в админ-панели Django, используя инлайн?

Как можно в админке Django настроить Inline для модели ScreenButton так, чтобы при выборе экрана в поле screen были доступны только те экраны, которые относятся к тому же объекту Practicum, что и текущий экран?
  • 24 января 2024 г. 7:38
Ответы на вопрос 1
Чтобы настроить отображение связанного поля (ForeignKey) в админ-панели Django с использованием Inline, необходимо создать класс Inline, наследующий `admin.TabularInline` или `admin.StackedInline`, и затем связать его с соответствующей моделью в админ-классе родительской модели.

Ниже приведен пример настройки Inline для модели `ScreenButton`, предполагая, что у вас есть также модели `Screen` и `Practicum`, причем `Screen` связан с `Practicum` через ForeignKey.

```python
from django.contrib import admin
from .models import Practicum, Screen, ScreenButton

class ScreenButtonInline(admin.TabularInline):  # или admin.StackedInline
    model = ScreenButton
    # Здесь можно добавить дополнительные настройки, 
    # например 'extra' для числа отображаемых пустых форм

class ScreenAdmin(admin.ModelAdmin):
    inlines = [ScreenButtonInline,]
    # Другие настройки админ-класса на ваше усмотрение

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

Теперь, чтобы ограничить выбор экранов в поле `screen` только теми, которые относятся к тому же объекту `Practicum`, что и текущий экран, можно использовать прием с фильтрацией полей формы в админке. Это можно сделать с помощью переопределения метода `formfield_for_foreignkey` в админ-классе Inline следующим образом:

```python
from django import forms

class ScreenButtonInline(admin.TabularInline):
    model = ScreenButton

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "screen":
            if request._obj_ is not None:
                kwargs["queryset"] = Screen.objects.filter(practicum=request._obj_.practicum)
        return super().formfield_for_foreignkey(db_field, request, **kwargs)

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

    def get_form(self, request, obj=None, **kwargs):
        # Сохраняем ссылку на текущий экран для использования в Inline
        request._obj_ = obj
        return super(ScreenAdmin, self).get_form(request, obj, **kwargs)

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

В этом подходе, когда вы открываете экран (объект `Screen`) в админ-панели, объект экрана передается в `get_form`, откуда он становится доступным через `request._obj_`. Затем метод `formfield_for_foreignkey` проверяет имя поля и в случае, если это поле `screen`, фильтрует QuerySet для этого поля так, чтобы в нём остались только записи, которые относятся к той же практике (объекту `Practicum`), что и редактируемый экран.

Учтите, что использование атрибута `_obj_` - это просто соглашение; вы можете использовать любое другое имя атрибута. Перед использованием этого кода, убедитесь, что у вас корректно установлены отношения ForeignKey в вашей модели и что названия полей и моделей соответствуют вашему случаю использования.
Похожие вопросы