Django: accéder à L'instance model depuis ModelAdmin?

j'ai un modèle pour les commandes dans une application de boutique en ligne, avec une clé primaire auto-incrémentée et une clé étrangère à elle-même, puisque les commandes peuvent être divisées en plusieurs commandes, mais la relation avec l'ordre original doit être maintenue.

class Order(models.Model):
    ordernumber = models.AutoField(primary_key=True)
    parent_order = models.ForeignKey('self', null=True, blank=True, related_name='child_orders')
    # .. other fields not relevant here

j'ai enregistré une classe OrderAdmin pour le site admin. Pour la vue détaillée, j'ai inclus parent_order dans l'attribut fieldsets . Bien sûr, par défaut, cette liste toutes les commandes dans une boîte de sélection, mais ce n'est pas le comportement souhaité. À la place, pour les commandes qui n'ont pas d'ordre parent (c'est-à-dire qui n'ont pas été séparées d'un autre ordre; parent_order est NULL/None), aucune commande ne doit être affichée. Pour les commandes qui ont été divisées, cela ne devrait afficher que la commande mère simple.

il y a une méthode ModelAdmin un peu Nouvelle disponible, formfield_for_foreignkey , qui semble parfaite pour cela, puisque le queryset peut être filtré à l'intérieur. Imaginez que nous regardions le détail vue de l'ordre N ° 11234, qui a été séparé de l'ordonnance n ° 11208. Le code est en dessous de

def formfield_for_foreignkey(self, db_field, request, **kwargs):
    if db_field.name == 'parent_order':
        # kwargs["queryset"] = Order.objects.filter(child_orders__ordernumber__exact=11234)
        return db_field.formfield(**kwargs)
    return super(OrderAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)

la ligne commentée fonctionne lorsqu'elle est exécutée dans un shell Python, en retournant un queryset à un seul élément contenant l'ordre #11208 pour #11234 et tous les autres ordres qui peuvent avoir été séparés de celui-ci.

bien sûr, on ne peut pas coder le numéro de commande là-bas. Nous avons besoin d'une référence au champ ordernumber de l'instance de commande dont nous regardons les détails. Comme ceci:

kwargs["queryset"] = Order.objects.filter(child_orders__ordernumber__exact=?????)

Je n'ai trouvé aucun moyen de remplacer ????? avec une référence à L'instance de L'ordre "courant", et j'ai creusé assez profond. self inside formfield_for_foreignkey se réfère à L'instance ModelAdmin, et bien que cela ait un attribut model , ce n'est pas l'instance order model (c'est une référence ModelBase; self.model() renvoie une instance, mais son ordernumber est None).

une solution pourrait être de retirer le numéro de commande de la demande.chemin (/admin/commande/commande/11234/), mais c'est vraiment laid. J'aimerais vraiment qu'il ya une meilleure façon.

42
demandé sur JK Laiho 2009-06-04 12:32:38

3 réponses

je pense que vous pourriez avoir besoin d'aborder cela d'une manière légèrement différente - en modifiant la ModelForm, plutôt que la classe admin. Quelque chose comme ceci:

class OrderForm(forms.ModelForm):

    def __init__(self, *args, **kwargs):
        super(OrderForm, self).__init__(*args, **kwargs)
        self.fields['parent_order'].queryset = Order.objects.filter(
            child_orders__ordernumber__exact=self.instance.pk)

class OrderAdmin(admin.ModelAdmin):
    form = OrderForm
55
répondu Daniel Roseman 2009-06-04 08:37:34

j'ai modelé ma classe en ligne de cette façon. Il est un peu moche sur la façon dont il obtient le formulaire parent id pour filtrer les données en ligne, mais il fonctionne. Il filtre les unités par entreprise à partir du formulaire de la société mère.

le concept original est expliqué ici http://www.stereoplex.com/blog/filtering-dropdown-lists-in-the-django-admin

class CompanyOccupationInline(admin.TabularInline):

    model = Occupation
    # max_num = 1
    extra = 0
    can_delete = False
    formset = RequiredInlineFormSet

    def formfield_for_dbfield(self, field, **kwargs):

        if field.name == 'unit':
            parent_company = self.get_object(kwargs['request'], Company)
            units = Unit.objects.filter(company=parent_company)
            return forms.ModelChoiceField(queryset=units)
        return super(CompanyOccupationInline, self).formfield_for_dbfield(field, **kwargs)

    def get_object(self, request, model):
        object_id = resolve(request.path).args[0]
        try:
            object_id = int(object_id)
        except ValueError:
            return None
        return model.objects.get(pk=object_id)
7
répondu Erwin Julius 2018-07-13 14:19:13

la réponse ci-dessus de Erwin Julius a fonctionné pour moi, sauf que j'ai trouvé que le nom" get_object "est en conflit avec une fonction Django alors nomme la fonction"my_get_object".

class CompanyOccupationInline(admin.TabularInline):

    model = Occupation
    # max_num = 1
    extra = 0
    can_delete = False
    formset = RequiredInlineFormSet

    def formfield_for_dbfield(self, field, **kwargs):

        if field.name == 'unit':
            parent_company = self.my_get_object(kwargs['request'], Company)
            units = Unit.objects.filter(company=parent_company)
            return forms.ModelChoiceField(queryset=units)
        return super(CompanyOccupationInline, self).formfield_for_dbfield(field, **kwargs)

    def my_get_object(self, request, model):
        object_id = request.META['PATH_INFO'].strip('/').split('/')[-1]
        try:
            object_id = int(object_id)
        except ValueError:
            return None
        return model.objects.get(pk=object_id)

il m'a dit de ne pas" répondre "aux réponses des autres, mais je ne suis pas encore autorisé à" répondre", et je cherche cela depuis un certain temps donc j'espère que cela sera utile aux autres. Je ne suis pas non plus autorisé à upvote encore ou je le ferais totalement!

3
répondu jenniwren 2013-10-29 21:21:37