Django Admin Общий тип контента несколько моделей встроенная форма

Я начинаю с Django, и я немного застрял в области мультимоделей, AKA Generic Relation (Content Type)

У меня есть общий тип контента "student_solution", который может принадлежать либо:

  • Org модель
  • Institution модель
  • Campus модель

Таким образом, в каждой из этих трех моделей, у меня models.py:

# Reverse generic relation - XXX See https://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#reverse-generic-relations
student_solutions = GenericRelation('student_solution.StudentSolution')

Я не уверен, что это правильный подход, я так думаю, но подтверждение приветствуется:)


Он работает нормально, как и сейчас, но он не удобен для пользователя в интерфейсе администратора Django, посмотрите, как он отображается в администраторе django при создании решения для ученика (я бы ожидал, что в окне выбора отображается label поле вместо ввода идентификатора типа содержимого вручную):

При создании Org, Institution или Campus поле вообще не отображается в Django Admin (поэтому я, вероятно, что-то неправильно настроил)

Я попробовал следующее Как заменить поля content_type и object_id полем с реальным объектом в админ-инлайн? улучшить пользовательский интерфейс, позволяя выбрать правильный тип контента и "объект", используя метку объекта. Но это не работает в настоящее время.


student_solution/models.py:

from django.contrib.contenttypes import fields
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models import Q
from jsonfield import JSONField

from tfp_backoffice.apps.institution.models import Institution

CONTENT_TYPE_CHOICES = (
  Q(app_label='org', model='org') |
  Q(app_label='institution', model='institution') |
  Q(app_label='campus', model='campus')
)

class StudentSolution(models.Model):
  # Dynamic relationship to either Org, Institution or Campus entities
  # XXX https://simpleisbetterthancomplex.com/tutorial/2016/10/13/how-to-use-generic-relations.html
  content_type = models.ForeignKey(
    ContentType,
    on_delete=models.CASCADE,  # TODO check if good thing
    limit_choices_to=CONTENT_TYPE_CHOICES,
  )
  object_id = models.PositiveIntegerField()
  content_object = fields.GenericForeignKey(
    'content_type',
    'object_id'
  )

student_solution/admin.py:

from django.contrib import admin
from modeltranslation.admin import TranslationAdmin

from tfp_backoffice.apps.org.models import Org
from tfp_backoffice.apps.student_solution.forms import StudentSolutionAdminForm, GenericStudentSolutionOwnerChoicesFieldForm
from tfp_backoffice.apps.student_solution.models import StudentSolution


class StudentSolutionInlineAdmin(admin.TabularInline):
  form = GenericStudentSolutionOwnerChoicesFieldForm
  model = Org  # TODO not sure at all about that, should be either of 3 related ContentTypes (Org | Institution | Campus)
  # This throw error "<class 'tfp_backoffice.apps.student_solution.admin.StudentSolutionInlineAdmin'>: (admin.E202) 'org.Org' has no ForeignKey to 'student_solution.StudentSolution'."


class StudentSolutionAdmin(TranslationAdmin):
  form = StudentSolutionAdminForm
  inlines = [
    StudentSolutionInlineAdmin,
  ]


admin.site.register(StudentSolution, StudentSolutionAdmin)

student_solution/forms.py:

from django import forms
from django.contrib.contenttypes.models import ContentType

from tfp_backoffice.apps.org.models import Org
from tfp_backoffice.apps.student_solution.models import CONTENT_TYPE_CHOICES, StudentSolution


class StudentSolutionAdminForm(forms.ModelForm):
  class Meta:
    model = StudentSolution
    fields = '__all__'  # Keep all fields    

class GenericStudentSolutionOwnerChoicesFieldForm(forms.ModelForm):
  ct_place_type = ContentType.objects.get_for_model(Org)  # TODO not sure at all about that, should be either of 3 related ContentTypes (Org | Institution | Campus)

  object_id = forms.ModelChoiceField(
    Org.objects.all(),
    limit_choices_to=CONTENT_TYPE_CHOICES,
    label='Student solution'
  )
  content_type = forms.ModelChoiceField(
    ContentType.objects.all(),
    initial=ct_place_type,  
    limit_choices_to=CONTENT_TYPE_CHOICES,  # should I use this here?
    widget=forms.HiddenInput()
  )

  def clean_object_id(self):
    return self.cleaned_data['object_id'].pk

  def clean_content_type(self):
    return self.ct_place_type

Но этот код не работает и выдает эту ошибку при запуске сервера

django.core.management.base.SystemCheckError: SystemCheckError: Проверка системы выявила некоторые проблемы:

<class 'tfp_backoffice.apps.student_solution.admin.StudentSolutionInlineAdmin'>: (admin.E202) 'org.Org' has no ForeignKey to 'student_solution.StudentSolution'.

0 ответов

Если я понимаю, что вы хотели бы сделать правильно, у вас должен быть StudentSolutionInlineAdmin в каждом из Org, Institution, а также Campus админы, и это должно быть GenericTabularInline(https://docs.djangoproject.com/en/2.2/ref/contrib/contenttypes/).

Таким образом, у вас будет что-то подобное в (например) вашем org/admin.py:

from django.contrib import admin
from django.contrib.contenttypes.admin import GenericTabularInline
from django import forms

from .models import Org
from student_solution.models import StudentSolution

class StudentSolutionInlineAdmin(GenericTabularInline):
    model = StudentSolution
    extra = 1

class StudentSolutionAdminForm(forms.ModelForm):
    class Meta:
        model = StudentSolution
        fields = '__all__'  # Keep all fields 

@admin.register(Org)
class OrgAdmin(admin.ModelAdmin):
    form = StudentSolutionAdminForm
    inlines = [StudentSolutionInlineAdmin]

Это позволит вам добавить StudentSolution, связанный с Org изнутри Org админ.

Вы можете посмотреть на это: https://docs.djangoproject.com/en/2.2/ref/contrib/contenttypes/

У них есть специальные встроенные типы, которые можно использовать, если вы используете фреймворк для типов контента

Другие вопросы по тегам