2009-06-04 11 views
37

Ich habe ein Modell für Bestellungen in einer Webshop-Anwendung, mit einem automatisch inkrementierenden Primärschlüssel und einem Fremdschlüssel für sich selbst, da Bestellungen in mehrere Bestellungen aufgeteilt werden können, aber die Beziehung zur ursprünglichen Bestellung beibehalten werden muss.Django: Zugriff auf die Modellinstanz von ModelAdmin?

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 

Ich habe eine OrderAdmin-Klasse für die Verwaltungsseite registriert. Für die Detailansicht habe ich parent_order in das Attribut fieldsets aufgenommen. Natürlich listet dies standardmäßig alle Aufträge in einem Auswahlfeld auf, aber dies ist nicht das gewünschte Verhalten. Bei Aufträgen, die keinen übergeordneten Auftrag haben (d. H. Nicht von einem anderen Auftrag getrennt wurden; parent_order ist NULL/None), sollten keine Aufträge angezeigt werden. Bei geteilten Aufträgen sollte nur die einzelne übergeordnete Bestellung angezeigt werden.

Es gibt eine ziemlich neue ModelAdmin-Methode, formfield_for_foreignkey, die perfekt dafür scheint, da das Abfrage-Set darin gefiltert werden kann. Stellen Sie sich vor, wir sehen uns die Detailansicht der Bestellung # 11234 an, die von der Bestellnummer 11208 getrennt wurde. Der Code ist unter

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) 

Die kommentierte Zeile funktioniert, wenn in einem Python-Shell läuft, einen Single-Artikel queryset Rückkehr enthält, um # 11208 für # 11234 und alle anderen Aufträge, die von ihm aufgespalten wurden.

Natürlich können wir dort die Bestellnummer nicht fest codieren. Wir benötigen einen Verweis auf das Feld ordernumber der Bestellinstanz, dessen Detailseite wir betrachten. Gefällt mir:

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

Ich habe keinen funktionierenden Weg gefunden zu ersetzen ????? mit einem Verweis auf die "aktuelle" Order-Instanz, und ich habe ziemlich tief gegraben. self innerhalb formfield_for_foreignkey bezieht sich auf die ModelAdmin-Instanz, und während das hat ein model Attribut, es ist nicht die Auftragsmodellinstanz (es ist eine ModelBase-Referenz; self.model() gibt eine Instanz zurück, aber seine Bestellnummer ist None).

Eine Lösung könnte sein, die Bestellnummer aus request.path (/ admin/orders/order/11234 /) zu ziehen, aber das ist wirklich hässlich. Ich wünschte wirklich, es gäbe einen besseren Weg.

Antwort

54

Ich denke, Sie müssen das vielleicht etwas anders angehen - indem Sie die ModelForm anstelle der Admin-Klasse ändern. Etwas wie dieses:

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 
+1

Es funktioniert! Ich danke dir sehr!Ich bin völlig neu in all dem ModelForm/ModelAdmin-Geschäft und habe an der falschen Stelle gesucht. –

+0

Ich weiß, dass dieser Beitrag alt ist, aber dies funktionierte auch als eine gute Lösung für mich für get_readonly_fields auf einem InlineModelAdmin, da der übergebene obj-Parameter aktuell das übergeordnete Objekt und nicht das Objekt der Inline-Instanz ist. Für alle Absichten und Zwecke machte dies mein Objekt nur lesbar, indem ich nur ein einzelnes Objekt in meinen Fremdschlüssel zurückgeben konnte. – dgraves

+0

Stellen Sie sicher, dass Sie zuerst super .__ init__ aufrufen. Das setzt Selbstzerstörung. – yellottyellott

5

Ich habe meine Inline-Klasse auf diese Weise modelliert. Es ist ein bisschen hässlich, wie es die Elternformular-ID erhält, um Inline-Daten zu filtern, aber es funktioniert. Es filtert Einheiten nach Unternehmen aus dem übergeordneten Formular.

Das ursprüngliche Konzept hier erklärt 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 = request.META['PATH_INFO'].strip('/').split('/')[-1] 
     try: 
      object_id = int(object_id) 
     except ValueError: 
      return None 
     return model.objects.get(pk=object_id) 
+5

Eine etwas sauberere Möglichkeit, dies zu erreichen wäre, den URL-Resolver zu nutzen: 'object_id = auflösen (request.path) .args [0]' – philipk

3

Die obige Antwort von Erwin Julius für mich gearbeitet, außer ich, dass die Bezeichnung „get_object“ Konflikte gefunden, so mit einer Django-Funktion die Funktion „my_get_object“ nennen.

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) 

Es sagte mir nicht zu „antworten“ an anderen Antworten, aber ich bin nicht zu ‚antworten‘ noch erlaubt, und ich habe für eine Weile dafür gesucht, so hofft, dass dies für andere hilfreich sein . Ich darf auch noch nicht updaten, sonst würde ich es tun!

+0

Während es besser wäre, diese Informationen als Kommentar zu verlassen, ich Verstehe, dass du nicht genug Ansehen dafür hast - zu wissen, dass "get_object" eine Kollision verursacht, ist jedoch wertvoll. – BlackVegetable

Verwandte Themen