Django interface d'administration: utilisation d'un filtre horizontal avec le champ ManyToMany en ligne

J'ai un champ modèle Django que j'aimerais aligner. Le domaine est une relation de plusieurs à plusieurs. Il y a donc des "Projets" et "profil Utilisateur". Chaque profil d'utilisateur peut sélectionner n'importe quel nombre de projets.

actuellement, j'ai la vue" tabulaire " en ligne qui fonctionne. Y a-t-il un moyen d'avoir un "filtre horizontal" pour que je puisse facilement ajouter et supprimer des projets d'un profil d'utilisateur?

voir l'image ci-jointe pour un exemple.enter image description here

Voici le Code modèle pour le profil de L'utilisateur:

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

et le code modèle pour un projet:

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()

Et l'admin code pour l'afficher:

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)
18
demandé sur Dylan Klomparens 2012-07-26 00:00:57

2 réponses

Le problème n'est pas d'avoir des inlines; c'est à partir de la voie ModelForms du travail en général. Ils construisent seulement des champs de forme pour les champs réels sur le modèle, pas des attributs de gestionnaire liés. Cependant, vous pouvez ajouter cette fonctionnalité au formulaire:

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

une petite révision s'impose probablement. Tout d'abord, nous définissons un userprofiles champ de formulaire. Il va utiliser un ModelMultipleChoiceField, qui, par défaut, aboutira à une boîte de sélection multiple. Puisqu'il ne s'agit pas d'un champ réel sur le modèle, nous ne pouvons pas simplement l'ajouter filter_horizontal, donc on lui dit d'utiliser simplement le même widget,FilteredSelectMultiple, qu'elle utiliserait si elle était listée dans filter_horizontal.

nous avons d'abord placé le queryset comme UserProfile set, vous ne pouvez pas encore le filtrer ici, car à ce stade de la définition de la classe, la forme n'a pas été instanciée et n'a donc pas de instance encore fixé. En conséquence, nous outrepassons __init__ de sorte que nous puissions définir la valeur initiale du champ queryset filtré.

Enfin, nous remplacer le save méthode, de sorte que nous pouvons définir le contenu du gestionnaire lié à la même chose que ce qui était dans les données POST du formulaire, et vous avez terminé.

36
répondu Chris Pratt 2014-05-01 16:54:42

un petit ajout quand il s'agit d'une relation de plusieurs à plusieurs avec lui-même. On pourrait vouloir s'exclure des choix:

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"""
1
répondu Shishir Biyyala 2014-05-01 15:06:14