Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
555 views
in Technique[技术] by (71.8m points)

python - Django admin interface: using horizontal_filter with inline ManyToMany field

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.enter image description here

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)
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

The problem isn't from having inlines; it's from the way ModelForms 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...