Django管理界面:使用horizontal_filter与内联ManyToMany字段 [英] Django admin interface: using horizontal_filter with inline ManyToMany field
问题描述
目前,我已经有了表格式的内联视图。有没有办法拥有一个横向过滤器,以便我可以轻松地从用户个人资料中添加和删除项目?
请参见附件的示例。 img src =https://i.stack.imgur.com/v9bQE.pngalt =在此输入图像描述>
以下是User Profile:
class UserProfile(models.Model):
user = models.OneToOneField(User,unique = True )
projects = models.ManyToManyField(Project,blank = True,help_text =选择此用户正在处理的项目。)
项目的模型代码:
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)
a ctive = 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)
问题不在于内联;一般来说,这是从 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)
如果提交:
project.save()
如果proje ct.pk:
project.userprofile_set = self.cleaned_data ['userprofiles']
self.save_m2m()
返回项目
class ProjectAdmin admin.ModelAdmin):
form = ProjectAdminForm
...
演练可能是有序的。首先,我们定义一个 userprofiles
表单字段。它将使用一个 ModelMultipleChoiceField
,默认情况下会导致一个多选框。由于这不是模型上的实际字段,所以我们不能将它添加到 filter_horizontal
中,所以我们可以直接使用相同的小部件, FilteredSelectMultiple
,如果它在 filter_horizontal
中列出,它将使用。
我们最初将查询集设置为整个 UserProfile
集合,您不能在此处过滤它,但是,因为在类定义的这个阶段,表单尚未实例化,因此没有设置实例
。因此,我们覆盖 __ init __
,以便我们可以将过滤的查询集设置为字段的初始值。
最后,我们覆盖 save
方法,以便我们可以将相关管理器的内容设置为与表单POST数据相同,并完成。
I have a Django model field that I'd like to inline. The field is a many-to-many relationship. So there are "Projects" and "User profiles". Each user profile can select any number of projects.
Currently, I've got the "tabular" inline view working. Is there a way to have a "horizontal filter" so that I can easily add and remove projects from a user profile?
Please see the attached picture for an example.
Here's the model code for the User Profile:
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.")
And the model code for a Project:
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()
And the admin code for the view:
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)
The problem isn't from having inlines; it's from the way ModelForm
s work, in general. They only build form fields for actual fields on the model, not related manager attributes. However, you can add this functionality to the form:
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
...
A little walkthrough is probably in order. First, we define a userprofiles
form field. It will use a ModelMultipleChoiceField
, which by default will result in a multiple select box. Since this isn't an actual field on the model, we can't just add it to filter_horizontal
, so we instead tell it to simply use the same widget, FilteredSelectMultiple
, that it would use if it were listed in filter_horizontal
.
We initially set the queryset as the entire UserProfile
set, you can't filter it here, yet, because at this stage of the class definition, the form hasn't been instantiated and thus doesn't have it's instance
set yet. As a result, we override __init__
so that we can set the filtered queryset as the field's initial value.
Finally, we override the save
method, so that we can set the related manager's contents to the same as what was in the form's POST data, and you're done.
这篇关于Django管理界面:使用horizontal_filter与内联ManyToMany字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!