Django 管理界面:使用带中间表的 ManyToMany 字段的 Horizo​​ntal_filter

Posted

技术标签:

【中文标题】Django 管理界面:使用带中间表的 ManyToMany 字段的 Horizo​​ntal_filter【英文标题】:Django admin interface: using horizontal_filter with ManyToMany field with intermediate table 【发布时间】:2017-11-26 14:52:09 【问题描述】:

我正在尝试增强 django 管理界面,类似于在接受的this SO post 答案中所做的。我在User 表和Project 表之间存在多对多关系。在 django 管理员中,我希望能够将用户分配给项目,如下图所示:

简单的ManyToManyField 可以正常工作,但问题是我的模型使用ManyToManyFieldthrough 参数来使用中间表。我不能使用save_m2m()set() 函数,而且我对如何调整下面的代码使其工作一无所知。

型号:

class UserProfile(models.Model):
    user = models.OneToOneField(User, unique=True)
    projects = models.ManyToManyField(Project, through='Membership')

class Project(models.Model):
    name = models.CharField(max_length=100, unique=True)
    application_identifier = models.CharField(max_length=100)
    type = models.IntegerField(choices=ProjectType)
    ...

class Membership(models.Model):
    project = models.ForeignKey(Project,on_delete=models.CASCADE)
    user = models.ForeignKey(UserProfile,on_delete=models.CASCADE)

    # extra fields
    rating = models.IntegerField(choices=ProjectType)
    ...

admin.py 中用于小部件的代码:

from django.contrib.admin.widgets import FilteredSelectMultiple

class ProjectAdminForm(forms.ModelForm):
    class Meta:
        model = Project
        fields = "__all__" # not in original SO post

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

注意:中间模型中的所有额外字段都不需要在项目管理视图中更改(它们是自动计算的)并且它们都有一个默认值。

感谢您的帮助!

【问题讨论】:

【参考方案1】:

我可以找到解决此问题的方法。这个想法是:

    当且仅当它们是新条目时才在 Membership 表中创建新条目(否则它会删除 Membership 表中其他字段的现有数据) 删除从成员资格表中取消选择的条目

为此,我替换了:

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

作者:

if project.pk:
    # Get the existing relationships
    current_project_selections = Membership.objects.filter(project=project)
    current_selections = [o.userprofile for o in current_project_selections]

    # Get the submitted relationships
    submitted_selections = self.cleaned_data['userprofiles']

    # Create new relation in Membership table if they do not exist
    for userprofile in submitted_selections :
        if userprofile not in current_selections:
            Membership(project=project,userprofile=userprofile).save()

    # Remove the relations that were deselected from the Membership table
    for project_userprofile in current_project_selections:
        if project_userprofile.userprofile not in submitted_selections :
            project_userprofile.delete()

【讨论】:

为了让它工作,Membershipuser 字段应该被称为userprofile,对吧?否则我不会理解[o.userprofile for o in current_project_selections] 如何工作。

以上是关于Django 管理界面:使用带中间表的 ManyToMany 字段的 Horizo​​ntal_filter的主要内容,如果未能解决你的问题,请参考以下文章

Django框架之admin管理后台

django 图书管理系统

django多对多中间表详解

多对多中间表详解 -- Django从入门到精通系列教程

Django:在管理界面中使用 TinyMCE 4

如何使用 Django 中同一个表的多个外键来管理删除?