我有一个数据模型如下:
class Candidate(models.Model):
name = models.CharField()
class Skill(models.Model):
name = models.CharField()
class CandidateSkill(models.Model):
candidate = models.ForeignKey(Candidate)
skill = models.ForeignKey(Skill, related_name='candidate_skills')
proficiency = models.CharField()
在管理员中我有:
class CandidateSkillInline(admin.TabularInline):
model = CandidateSkill
fields = ('skill', )
extra = 0
raw_id_fields = ('skill',)
class CandidateAdmin(admin.ModelAdmin):
model = Candidate
fields = ('name',)
inlines = [CandidateSkillInline]
每位候选人都有很多技能。这里的问题是,在每个内联的更改页面中,将使用一个查询来获取技能(SELECT ••• FROM "skill" WHERE "skill"."id" = <id>
)。如果我将skill
中的字段CandidateSkillInline
添加为read_only
,则不会有额外的查询。但是,我希望能够在内联中添加新项目。我试过了:
1)向CandidateSkillInline
添加了自定义formset:
class CandidateSkillInlineFormset(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super(CandidateSkillInlineFormset, self).__init__(*args, **kwargs)
self.queryset = self.queryset.select_related('skill')
2)覆盖内联的get_queryset
:
def get_queryset(self, request):
super(CandidateSkillInline, self).get_queryset(request).select_related('skill')
3)覆盖get_queryset
上的CandidateAdmin
:
def get_queryset(self, request):
return super(CandidateAdmin, self).get_queryset(request).prefetch_related('candidate_skills__skill')
但是,我仍然可以查询每项技能。查询未发送的唯一方法是我在CandidateSkillInilne中的skill
中设置read_only_fields
。问题是我如何在一个查询中选择或预取技能,而不是每个内联一个?
答案 0 :(得分:1)
这似乎是您尝试实现自己的ManyToManyField
。你可以使用ManyToManyField和内联吗?它在管理员中有一个很好的多选小部件。
https://docs.djangoproject.com/en/dev/ref/contrib/admin/#working-with-many-to-many-models
答案 1 :(得分:0)
嗯,ForeignKeyRawIdWidget
的设计不是那么优雅。 ForeignKeyRawIdWidget
不仅要以特定方式显示某些数据(这是小部件的基本职责),还需要进行额外的查询以在屏幕上显示更多相关信息(它显示str(obj)
的值方法)。
以label_and_url_for_value
方法执行查询。因此,您可以尝试使用自己的自定义窗口小部件来避免此查询,但是您必须考虑可视化中的权衡。
一种可能的解决方案:
from django.contrib.admin.widgets import ForeignKeyRawIdWidget
from django.urls import reverse
class OptimisedForeignKeyRawIdWidget(ForeignKeyRawIdWidget):
def label_and_url_for_value(self, value):
try:
url = reverse(
'%s:%s_%s_change' % (
self.admin_site.name,
self.rel.model._meta.app_label,
self.rel.model._meta.object_name.lower(),
),
args=(value,)
)
except NoReverseMatch:
url = '' # Admin not registered for target model.
return str(value), url
最后一步,您将必须在ModelForm
类中设置自定义窗口小部件。并且有很多方法可以做到这一点。