Django管理界面:对带有中间表的ManyToMany字段使用horizontal_filter [英] Django admin interface: using horizontal_filter with ManyToMany field with intermediate table
问题描述
I am trying to enhance the django admin interface similar to what has been done in the accepted answer of this SO post. I have a many-to-many relationship between a User
table and a Project
table. In the django admin, I would like to be able to assign users to a project as in the image below:
使用简单的ManyToManyField
可以很好地工作,但是问题是我的模型使用ManyToManyField
的through
参数来使用中间表.我不能使用save_m2m()
和set()
函数,对于如何改编下面的代码以使其正常工作,我一无所知.
It works fine with a simple ManyToManyField
but the problem is that my model uses the through
parameter of the ManyToManyField
to use an intermediary table. I cannot use the save_m2m()
and set()
function and I am clueless on how to adapt the code below to make it work.
模型:
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
中用于窗口小部件的代码:
The code used for the widget in 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
...
注意:中介模型中的所有其他字段都不需要在Project Admin视图中进行更改(它们是自动计算的),它们都具有默认值.
Note: all the extra fields from the intermediary model do not need to be changed in the Project Admin view (they are automatically computed) and they all have a default value.
感谢您的帮助!
推荐答案
我可以找到一种解决此问题的方法.这个想法是:
I could find a way of solving this issue. The idea is:
- 在且仅当它们是新的时才在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()
这篇关于Django管理界面:对带有中间表的ManyToMany字段使用horizontal_filter的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!