Django管理界面:使用horizo​​ntal_filter和内联ManyToMany字段

时间:2012-07-25 20:00:58

标签: python django django-admin

我有一个Django模型字段,我想内联。该领域是一种多对多的关系。所以有“项目”和“用户档案”。每个用户配置文件都可以选择任意数量的项目。

目前,我已经在“表格”内联视图中工作了。有没有办法有一个“水平过滤器”,以便我可以轻松地添加和删除用户配置文件中的项目?

请参阅附图以获取示例。enter image description here

以下是用户档案的型号代码:

class UserProfile(models.Model):
    user = models.OneToOneField(User, unique=True)
    projects = models.ManyToManyField(Project, blank=True, help_text="Select the projects that this user is currently working on.")

项目的模型代码:

class Project(models.Model):
    name = models.CharField(max_length=100, unique=True)
    application_identifier = models.CharField(max_length=100)
    type = models.IntegerField(choices=ProjectType)
    account = models.ForeignKey(Account)
    principle_investigator = models.ForeignKey(User)
    active = models.BooleanField()

视图的管理代码:

class UserProfileInline(admin.TabularInline):
    model = UserProfile.projects.through
    extra = 0
    verbose_name = 'user'
    verbose_name_plural = 'users'

class ProjectAdmin(admin.ModelAdmin):
    list_display = ('name', 'application_identifier', 'type', 'account', 'active')
    search_fields = ('name', 'application_identifier', 'account__name')
    list_filter = ('type', 'active')
    inlines = [UserProfileInline,]
admin.site.register(Project, ProjectAdmin)

3 个答案:

答案 0 :(得分:40)

问题不在于内联;一般来说,它来自ModelForm的工作方式。它们仅为模型上的实际字段构建表单字段,而不是相关的管理器属性。但是,您可以将此功能添加到表单中:

from django.contrib.admin.widgets import FilteredSelectMultiple

class ProjectAdminForm(forms.ModelForm):
    class Meta:
        model = Project

    userprofiles = forms.ModelMultipleChoiceField(
        queryset=UserProfile.objects.all(),
        required=False,
        widget=FilteredSelectMultiple(
            verbose_name='User Profiles',
            is_stacked=False
        )
    )

    def __init__(self, *args, **kwargs):
        super(ProjectAdminForm, self).__init__(*args, **kwargs)
            if self.instance.pk:
                self.fields['userprofiles'].initial = self.instance.userprofile_set.all()

    def save(self, commit=True):
        project = super(ProjectAdminForm, self).save(commit=False)  
        if commit:
            project.save()

        if project.pk:
            project.userprofile_set = self.cleaned_data['userprofiles']
            self.save_m2m()

        return project

class ProjectAdmin(admin.ModelAdmin):
    form = ProjectAdminForm
    ...

可能需要一点点演练。首先,我们定义一个userprofiles表单字段。它将使用ModelMultipleChoiceField,默认情况下会生成多个选择框。由于这不是模型上的实际字段,我们不能只将其添加到filter_horizontal,因此我们告诉它只使用相同的小部件FilteredSelectMultiple,如果它使用它列在filter_horizontal

我们最初将查询集设置为整个UserProfile集合,您无法在此处对其进行过滤,因为在类定义的此阶段,表单尚未实例化,因此没有它已设置instance。因此,我们覆盖__init__,以便我们可以将过滤后的查询集设置为字段的初始值。

最后,我们覆盖save方法,这样我们就可以将相关管理器的内容设置为与表单的POST数据相同,然后就完成了。

答案 1 :(得分:1)

在处理与自身的多对多关系时的一个小的补充。人们可能希望将自己排除在选择之外:

if self.instance.pk:
        self.fields['field_being_added'].queryset = self.fields['field_being_added'].queryset.exclude(pk=self.instance.pk)
        self.fields['field_being_added'].initial = """Corresponding result queryset"""

答案 2 :(得分:0)

有一个更简单的解决方案,只需添加filter_horizontal,如here所述:

class YourAdmin(ModelAdmin)
    filter_horizontal = ('your_many_to_many_field',)

之前:enter image description here

之后:enter image description here