Django Admin根据另一个字段过滤ForeignKey下拉列表

时间:2014-05-14 20:29:39

标签: django django-models django-forms django-admin

我有3个型号:

class FileType(models.Model):
    name=models.CharField(max_length=128)

class ManagedFile(models.Model):
    type = models.ForeignKey(FileType)
    content = models.FileField(upload_to=path_maker)

class Tag(models.Model):
    type = models.ForeignKey(FileType)
    m_file = models.ForeignKey(ManagedFile)

    def clean(self):
        if self.m_file is None:
            return
        if self.type != self.m_file.type:
            raise ValidationError("File type does not match Tag type")

为标记选择m_file时,m_files类型必须与Tags类型匹配。这一切都很好,但Tag.m_file的管理下拉列表显示所有类型的文件,无论Tag的类型如何。这给用户带来了困惑。

在我看来,有很多方法可以静态过滤下拉列表。因此,如果我想说我们永远不会让用户在下拉列表中看到Type.pk = 1,我可以这样做。但似乎没有办法过滤m_file.Type == Self.Type

2 个答案:

答案 0 :(得分:5)

实际上,动态创建管理表单类非常容易。这样的事情应该有效:

def tagform_factory(filetype):
    class TagForm(forms.ModelForm):
        m_file = forms.ModelChoiceField(
            queryset=ManagedFile.objects.filter(type=filetype)
        )
    return TagForm


class TagAdmin(admin.ModelAdmin):

    def get_form(self, request, obj=None, **kwargs):
        if obj is not None and obj.type is not None:
            kwargs['form'] = tagform_factory(obj.type)
        return super(TagAdmin, self).get_form(request, obj, **kwargs)

请注意,get_form方法负责构建表单,而不是表单实例。绰号很差,恕我直言。

但是,您仍然需要决定如何处理用于添加新标记的表单,而不是编辑现有标记。在这种情况下,您还没有可以限制下拉列表的类型。也许潜伏在这里的数据建模问题?您真的需要type模型上的Tag字段吗?也许它应该被删除?

答案 1 :(得分:-1)

尝试在管理员中覆盖formfield_for_foreignkey()。我不是100%它会做你想做的事,但如果没有它应该让你开始:

class TagAdmin(admin.ModelAdmin):
    ...
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        kwargs['queryset'] = Tag.objects.filter(type=self.type)