2012-07-25 92 views
18

我有一個Django模型字段,我想內聯。該領域是多對多的關係。所以有「項目」和「用戶配置文件」。每個用戶配置文件可以選擇任意數量的項目。Django管理界面:使用horizo​​ntal_filter與內聯ManyToMany字段

目前,我已經有了「表格式」內聯視圖。有沒有辦法設置「水平過濾器」,以便我可以輕鬆地從用戶配置文件添加和刪除項目?

請參閱附圖中的示例。 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) 

回答

35

問題不在於內聯;一般來說,它的工作方式爲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,所以我們改爲告訴它只使用與filter_horizontal中列出的相同的窗口小部件FilteredSelectMultiple

我們最初設定的查詢集爲整個UserProfile集,你不能在這裏過濾它,但是,因爲在這個階段的類定義的,形式沒有被實例化,因此不會有它的一套instance然而。因此,我們覆蓋__init__,以便我們可以將篩選後的查詢集設置爲字段的初始值。

最後,我們重寫save方法,以便我們可以將相關管理器的內容設置爲與表單的POST數據中的內容相同,然後就完成了。

+0

非常感謝克里斯!這是第一次嘗試它時的魅力! – 2012-07-25 20:52:49

+0

謝謝你,你是男人。 – whooot 2013-03-19 18:24:57

+0

偉大的解決方案,對我很好。 – Blackeagle52 2015-11-10 12:05:23

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