【问题标题】:Django Admin Generic content type multiple models inline formDjango Admin 通用内容类型多模型内联表单
【发布时间】:2019-07-29 22:43:56
【问题描述】:

我刚开始使用 Django,但我有点卡在多模型领域,AKA Generic Relation(内容类型)

我有一个通用的内容类型“student_solution”,它可以属于:

  • Org 模型
  • Institution 模型
  • Campus 模型

因此,在这 3 个模型中,我在每个 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 管理 UI 中不是用户友好的,请查看它在 django admin 上的显示方式,创建学生解决方案时(我希望选择框显示 label 字段,而不是手动输入内容类型 ID):

在创建 Org、Institution 或 Campus 时,该字段根本不会显示在 Django Admin 中(所以我可能配置错误)

我尝试关注How to replace content_type and object_id fields by a field with actual object in admin inline?,通过允许使用对象的标签选择正确的内容类型和“对象”来改进 UI。但目前不起作用。


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: 系统检查发现一些问题:

&lt;class 'tfp_backoffice.apps.student_solution.admin.StudentSolutionInlineAdmin'&gt;: (admin.E202) 'org.Org' has no ForeignKey to 'student_solution.StudentSolution'.

【问题讨论】:

    标签: python django django-contenttypes django-generic-relations


    【解决方案1】:

    如果我理解您想要正确执行的操作,您应该在每个 OrgInstitutionCampus 管理员中都有您的 StudentSolutionInlineAdmin,它应该是 GenericTabularInline (https://docs.djangoproject.com/en/2.2/ref/contrib/contenttypes/#generic-relations-in-admin )。

    所以你会在(例如)你的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]
    

    这将允许您从 Org 管理员中添加与 Org 相关的 StudentSolution。

    【讨论】:

    • 感谢您的回复,我无法检查这是否可行,因为我已经更改了项目。另外,由于您是新成员,因此我会支持您的答案,因此假设您的解决方案是正确的。如果不是这样,我会让社区让我知道。 - 我这样做是因为您写了一个清晰而详细的答案,并且在撰写本文时只获得了 10 名声望。我知道在一开始就获得一些代表是多么困难,希望这会让你更容易。明智地使用您的新权利! :)
    【解决方案2】:

    你可以看看这个:https://docs.djangoproject.com/en/2.2/ref/contrib/contenttypes/#generic-relations-in-admin

    如果您使用的是内容类型功能框架,它们有特殊的内联类型可供使用

    【讨论】:

      猜你喜欢
      • 2017-10-26
      • 2012-04-19
      • 1970-01-01
      • 2019-04-23
      • 2018-10-11
      • 2011-07-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多