Python >> Tutoriel Python >  >> Python

Comment filtrer les choix ForeignKey dans un Django ModelForm ?

ForeignKey est représenté par django.forms.ModelChoiceField, qui est un ChoiceField dont les choix sont un modèle QuerySet. Voir la référence pour ModelChoiceField.

Donc, fournissez un QuerySet au queryset du champ attribut. Cela dépend de la façon dont votre formulaire est construit. Si vous construisez un formulaire explicite, vous aurez des champs nommés directement.

form.rate.queryset = Rate.objects.filter(company_id=the_company.id)

Si vous prenez l'objet ModelForm par défaut, form.fields["rate"].queryset = ...

Ceci est fait explicitement dans la vue. Pas de piratage.


En plus de la réponse de S.Lott et comme devenirGuru l'a mentionné dans les commentaires, il est possible d'ajouter les filtres de l'ensemble de requêtes en remplaçant le ModelForm.__init__ fonction. (Cela pourrait facilement s'appliquer aux formulaires réguliers), cela peut aider à la réutilisation et garder la fonction d'affichage bien rangée.

class ClientForm(forms.ModelForm):
    def __init__(self,company,*args,**kwargs):
        super (ClientForm,self ).__init__(*args,**kwargs) # populates the post
        self.fields['rate'].queryset = Rate.objects.filter(company=company)
        self.fields['client'].queryset = Client.objects.filter(company=company)

    class Meta:
        model = Client

def addclient(request, company_id):
        the_company = get_object_or_404(Company, id=company_id)

        if request.POST:
            form = ClientForm(the_company,request.POST)  #<-- Note the extra arg
            if form.is_valid():
                form.save()
                return HttpResponseRedirect(the_company.get_clients_url())
        else:
            form = ClientForm(the_company)

        return render_to_response('addclient.html', 
                                  {'form': form, 'the_company':the_company})

Cela peut être utile pour la réutilisation, par exemple si vous avez besoin de filtres communs sur de nombreux modèles (normalement, je déclare une classe Form abstraite). Par exemple

class UberClientForm(ClientForm):
    class Meta:
        model = UberClient

def view(request):
    ...
    form = UberClientForm(company)
    ...

#or even extend the existing custom init
class PITAClient(ClientForm):
    def __init__(company, *args, **args):
        super (PITAClient,self ).__init__(company,*args,**kwargs)
        self.fields['support_staff'].queryset = User.objects.exclude(user='michael')

En dehors de cela, je ne fais que reformuler le matériel du blog Django dont il existe de nombreux bons.


C'est simple, et fonctionne avec Django 1.4 :

class ClientAdminForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super(ClientAdminForm, self).__init__(*args, **kwargs)
        # access object through self.instance...
        self.fields['base_rate'].queryset = Rate.objects.filter(company=self.instance.company)

class ClientAdmin(admin.ModelAdmin):
    form = ClientAdminForm
    ....

Vous n'avez pas besoin de le spécifier dans une classe de formulaire, mais vous pouvez le faire directement dans ModelAdmin, car Django inclut déjà cette méthode intégrée sur ModelAdmin (à partir de la documentation) :

ModelAdmin.formfield_for_foreignkey(self, db_field, request, **kwargs)¶
'''The formfield_for_foreignkey method on a ModelAdmin allows you to 
   override the default formfield for a foreign keys field. For example, 
   to return a subset of objects for this foreign key field based on the
   user:'''

class MyModelAdmin(admin.ModelAdmin):
    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if db_field.name == "car":
            kwargs["queryset"] = Car.objects.filter(owner=request.user)
        return super(MyModelAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

Une façon encore plus astucieuse de le faire (par exemple en créant une interface d'administration frontale à laquelle les utilisateurs peuvent accéder) consiste à sous-classer ModelAdmin, puis à modifier les méthodes ci-dessous. Le résultat net est une interface utilisateur qui leur montre UNIQUEMENT le contenu qui leur est lié, tout en vous permettant (un super-utilisateur) de tout voir.

J'ai remplacé quatre méthodes, les deux premières empêchent un utilisateur de supprimer quoi que ce soit, et cela supprime également les boutons de suppression du site d'administration.

Le troisième remplacement filtre toute requête contenant une référence à (dans l'exemple 'user' ou 'porcupine' (juste à titre d'illustration).

Le dernier remplacement filtre tout champ de clé étrangère dans le modèle pour filtrer les choix disponibles de la même manière que le jeu de requêtes de base.

De cette façon, vous pouvez présenter un site d'administration frontal facile à gérer qui permet aux utilisateurs de jouer avec leurs propres objets, et vous n'avez pas à vous rappeler de saisir les filtres ModelAdmin spécifiques dont nous avons parlé ci-dessus.

class FrontEndAdmin(models.ModelAdmin):
    def __init__(self, model, admin_site):
        self.model = model
        self.opts = model._meta
        self.admin_site = admin_site
        super(FrontEndAdmin, self).__init__(model, admin_site)

supprimer les boutons "supprimer" :

    def get_actions(self, request):
        actions = super(FrontEndAdmin, self).get_actions(request)
        if 'delete_selected' in actions:
            del actions['delete_selected']
        return actions

empêche la suppression de l'autorisation

    def has_delete_permission(self, request, obj=None):
        return False

filtre les objets visibles sur le site d'administration :

    def get_queryset(self, request):
        if request.user.is_superuser:
            try:
                qs = self.model.objects.all()
            except AttributeError:
                qs = self.model._default_manager.get_queryset()
            return qs

        else:
            try:
                qs = self.model.objects.all()
            except AttributeError:
                qs = self.model._default_manager.get_queryset()

            if hasattr(self.model, ‘user’):
                return qs.filter(user=request.user)
            if hasattr(self.model, ‘porcupine’):
                return qs.filter(porcupine=request.user.porcupine)
            else:
                return qs

filtre les choix pour tous les champs de clé étrangère sur le site d'administration :

    def formfield_for_foreignkey(self, db_field, request, **kwargs):
        if request.employee.is_superuser:
            return super(FrontEndAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

        else:
            if hasattr(db_field.rel.to, 'user'):
                kwargs["queryset"] = db_field.rel.to.objects.filter(user=request.user)
            if hasattr(db_field.rel.to, 'porcupine'):
                kwargs["queryset"] = db_field.rel.to.objects.filter(porcupine=request.user.porcupine)
            return super(ModelAdminFront, self).formfield_for_foreignkey(db_field, request, **kwargs)