Django Admin 通用内容类型多模型内联表单
Django Admin Generic content type multiple models inline form
我刚开始使用 Django,我有点卡在多模型领域,又名通用关系(内容类型)
我有一个通用内容类型“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')
I'm not sure whether this is the right approach, I think so, but a confirmation is welcome :)
它现在工作正常,但它在 Django 管理中不是用户友好的 UI,看看它在创建学生解决方案时如何在 Django 管理中显示(我希望 select 框显示 label
字段,而不是手动输入内容类型 ID):
创建组织、机构或校园时,该字段在 Django 管理中根本不显示(所以我可能配置错误)
我尝试按照 改进 UI,允许 select 使用对象标签的正确内容类型和“对象”。但是暂时不行。
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: System check identified some issues:
<class 'tfp_backoffice.apps.student_solution.admin.StudentSolutionInlineAdmin'>: (admin.E202) 'org.Org' has no ForeignKey to 'student_solution.StudentSolution'.
你可以看看这个:https://docs.djangoproject.com/en/2.2/ref/contrib/contenttypes/#generic-relations-in-admin
如果您使用内容类型功能框架,它们有特殊的内联类型可供使用
如果我理解您想正确地做什么,您应该在每个 Org
、Institution
和 Campus
管理员中拥有您的 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。
我刚开始使用 Django,我有点卡在多模型领域,又名通用关系(内容类型)
我有一个通用内容类型“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')
I'm not sure whether this is the right approach, I think so, but a confirmation is welcome :)
它现在工作正常,但它在 Django 管理中不是用户友好的 UI,看看它在创建学生解决方案时如何在 Django 管理中显示(我希望 select 框显示 label
字段,而不是手动输入内容类型 ID):
创建组织、机构或校园时,该字段在 Django 管理中根本不显示(所以我可能配置错误)
我尝试按照
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: System check identified some issues:
<class 'tfp_backoffice.apps.student_solution.admin.StudentSolutionInlineAdmin'>: (admin.E202) 'org.Org' has no ForeignKey to 'student_solution.StudentSolution'.
你可以看看这个:https://docs.djangoproject.com/en/2.2/ref/contrib/contenttypes/#generic-relations-in-admin
如果您使用内容类型功能框架,它们有特殊的内联类型可供使用
如果我理解您想正确地做什么,您应该在每个 Org
、Institution
和 Campus
管理员中拥有您的 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。