2009-06-04 26 views
37

Mam model zamówień w aplikacji sklepu internetowego, z automatycznie zwiększającym się kluczem podstawowym i kluczem obcym dla siebie, ponieważ zamówienia można podzielić na wiele zamówień, ale związek z oryginalnym zamówieniem musi być zachowany.Django: uzyskiwanie dostępu do instancji modelu z poziomu programu 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 

Zarejestrowałem klasę OrderAdmin dla strony administracyjnej. W widoku szczegółów dodałem atrybut parent_order w atrybucie fieldsets. Oczywiście domyślnie wyświetla listę wszystkich zamówień w polu wyboru, ale nie jest to pożądane zachowanie. Zamiast tego dla zamówień, które nie mają zamówienia nadrzędnego (tj. Nie zostały podzielone z innego zamówienia, parent_order ma wartość NULL/None), nie powinny być wyświetlane żadne zamówienia. W przypadku zamówień, które zostały podzielone, powinno być wyświetlane tylko jednoosobowe zamówienie.

Dostępna jest całkiem nowa metoda ModelAdmin, formfield_for_foreignkey, która wydaje się idealna do tego, ponieważ zestaw zapytań można filtrować wewnątrz niego. Wyobraźmy sobie, że patrzymy na szczegółowy widok zamówienia nr 11234, który został podzielony na zamówienie nr 11208. Kod jest poniżej

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) 

komentowanym rząd działa, gdy uruchomić w powłoce Pythona, wracając do queryset pojedynczego elementu zawierającego zamówienie # 11208 # 11234 i dla wszystkich innych zleceń, które mogły zostać podzielone z niego.

Nie możemy oczywiście zakodować tam numeru zamówienia. Potrzebujemy odniesienia do pola ordernumber instancji zamówienia, której stronę szczegółów szukamy. W ten sposób:

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

Nie znalazłem działającego sposobu na zastąpienie ????? z odniesieniem do "aktualnej" instancji Orderu, a ja wykopałem dość głęboko. self wewnątrz formfield_for_foreignkey odnosi się do instancji ModelAdmin i chociaż ma atrybut model, nie jest to instancja modelu zamówienia (jest to odwołanie do ModelBase, self.model() zwraca instancję, ale jej numer porządkowy to Brak).

Jednym z rozwiązań może być pobranie numeru zamówienia z request.path (/ admin/orders/order/11234 /), ale to jest naprawdę brzydkie. Naprawdę chcę, żeby był lepszy sposób.

Odpowiedz

54

Myślę, że być może trzeba będzie podejść do tego w nieco inny sposób - modyfikując ModelForm, a nie klasę admin. Coś takiego:

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

To działa! Dziękuję bardzo!Jestem całkowicie nowy w całym tym modelu ModelForm/ModelAdmin i szukałem w niewłaściwym miejscu. –

+0

Zdaję sobie sprawę, że ten post jest stary, ale działało to również jako przyzwoite obejście dla mnie dla get_readonly_fields na InlineModelAdmin, ponieważ przekazany do niego parametr obj jest obecnie obiektem nadrzędnym, a nie obiektem instancji wbudowanej. Dla wszystkich intencji i celów uczyniło to mój obiekt tylko do odczytu, pozwalając mi tylko zwrócić pojedynczy obiekt do mojego klucza obcego. – dgraves

+0

Upewnij się, że najpierw wywołasz super .__ init__. To ustawia self.instance. – yellottyellott

5

W ten sposób zamodelowałem moją klasę wbudowaną. To trochę brzydkie, jak pobiera identyfikator formularza nadrzędnego do filtrowania danych wbudowanych, ale działa. Filtruje jednostki według firmy z formularza nadrzędnego.

Pierwotna koncepcja jest wyjaśnione tutaj 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

Nieco czystszym sposobem podejścia do tego problemu jest skorzystanie z narzędzia do rozpoznawania adresów URL: 'object_id = resolve (request.path) .args [0]' – philipk

3

Powyższa odpowiedź od Erwin Julius pracował dla mnie, chyba znalazłem, że nazwa „get_object” konflikty z funkcją Django tak nazwać funkcję „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) 

To mi się nie „odpowiada” na odpowiedzi innych, ale nie wolno mi «odpowiedz» jeszcze, a ja czekałem na to przez chwilę więc nadzieję, że będzie to pomocne dla innych . Nie wolno mi także przegłosować, bo inaczej bym to zrobił!

+0

Podczas gdy lepiej byłoby pozostawić te informacje jako komentarz, zrozum, że nie masz wystarczającej reputacji - znajomość 'get_object' powoduje, że kolizja jest cenna. – BlackVegetable

Powiązane problemy