【问题标题】:Django admin interface: using horizontal_filter with inline ManyToMany fieldDjango 管理界面:使用带有内联 ManyToMany 字段的 Horizo​​ntal_filter
【发布时间】:2012-07-24 08:29:46
【问题描述】:

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

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

示例请看附图。

这是用户个人资料的型号代码:

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)

【问题讨论】:

    标签: python django django-admin


    【解决方案1】:

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

    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,如果它在 @ 中列出,它将使用它987654327@.

    我们最初将查询集设置为整个UserProfile 集,你不能在这里过滤它,但是,因为在类定义的这个阶段,表单还没有被实例化,因此没有它的@ 987654329@ 已设置。结果,我们覆盖了__init__,以便我们可以将过滤后的查询集设置为字段的初始值。

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

    【讨论】:

    • 非常感谢克里斯!这是我第一次尝试时的魅力!
    • 当我将 userprofile_set 更改为 userprofile 时,代码正在为我工​​作。非常干净的代码。谢谢
    • 谢谢!这对我帮助很大!一个变化是,在 Django 1.9 中,您必须将字段 = [ ] 添加到元部分,否则会出现错误。
    • 漂亮的答案,我将尝试找出如何使其适应与自定义中介模型的多对多关系,因为它不适用于这些情况。
    • 这个答案太棒了~!但我只是想知道,我们可以在左侧进行过滤,是否也可以在右侧进行过滤输入?
    【解决方案2】:

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

    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"""
    

    【讨论】:

      【解决方案3】:

      有一个更简单的解决方案,只需添加filter_horizontal,解释为here

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

      之前:

      之后:

      【讨论】:

      • 这不适用于 InlineAdmin 对象——这是问题的主题。
      猜你喜欢
      • 2017-11-26
      • 2011-05-10
      • 1970-01-01
      • 2010-09-16
      • 1970-01-01
      • 1970-01-01
      • 2022-11-13
      • 2023-03-07
      • 2019-11-24
      相关资源
      最近更新 更多