2013-02-19 11 views
15

Sto usando il formfield_for_manytomany dato in django documentation. Ma all'interno di quella funzione ho bisogno di ottenere l'oggetto genitore corrente in fase di modifica.Come ottenere l'istanza del modello corrente da inlineadmin in Django

def formfield_for_manytomany(self, db_field, request, **kwargs): 
    if db_field.name == "car": 
     kwargs["queryset"] = Cars.objects.filter(owner=person) 
    return super(myModel, self).formfield_for_manytomany(db_field, request, **kwargs) 

Come posso ottenere la persona che si modifica?

risposta

18

Se il person non può essere facilmente ottenuto da request, potrebbe essere necessario passare manualmente sovrascrivendo ModelAdmin.get_form() o InlineModelAdmin.get_formset():

from functools import partial 

class MyModelAdmin(admin.ModelAdmin): 
    def get_form(self, request, obj=None, **kwargs): 
     kwargs['formfield_callback'] = partial(self.formfield_for_dbfield, request=request, obj=obj) 
     return super(MyModelAdmin, self).get_form(request, obj, **kwargs) 

    def formfield_for_dbfield(self, db_field, **kwargs): 
     person = kwargs.pop('obj', None) 
     formfield = super(MyModelAdmin, self).formfield_for_dbfield(db_field, **kwargs) 
     if db_field.name == "car" and person: 
      formfield.queryset = Cars.objects.filter(owner=person) 
     return formfield 

# or its inline 
class MyInlineModelAdmin(admin.StackedInline): 
    def get_formset(self, request, obj=None, **kwargs): 
     kwargs['formfield_callback'] = partial(self.formfield_for_dbfield, request=request, obj=obj) 
     return super(MyInlineModelAdmin, self).get_formset(request, obj, **kwargs) 

    def formfield_for_dbfield(self, db_field, **kwargs): 
     person = kwargs.pop('obj', None) 
     formfield = super(MyInlineModelAdmin, self).formfield_for_dbfield(db_field, **kwargs) 
     if db_field.name == "car" and person: 
      formfield.queryset = Cars.objects.filter(owner=person) 
     return formfield 

O

class MyModelAdmin(admin.ModelAdmin): 
    def get_form(self, request, obj=None, **kwargs): 
     kwargs['formfield_callback'] = partial(self.formfield_for_dbfield, request=request, obj=obj) 
     return super(MyModelAdmin, self).get_form(request, obj, **kwargs) 

    def formfield_for_dbfield(self, db_field, **kwargs): 
     if db_field.name != "car": 
      kwargs.pop('obj', None) 
     return super(MyModelAdmin, self).formfield_for_dbfield(db_field, **kwargs) 

    def formfield_for_manytomany(self, db_field, request=None, **kwargs): 
     person = kwargs.pop('obj', None) 
     if db_field.name == "car" and person: 
      kwargs['queryset'] = Cars.objects.filter(owner=person) 
     return super(MyModelAdmin, self).formfield_for_manytomany(db_field, request, **kwargs) 
+1

non capisco. cosa è 'formfield_callback' e cosa è' partial' – user192082107

+0

Il callback è la magia dietro le quinte per invocare 'formfield_for_manytomany', dobbiamo passarlo manualmente' obj', per ottenere 'obj' da' kwargs' all'interno ' formfield_for_manytomany'. La parte 'partial' crea una funzione di ordine elevato che, quando viene chiamata' formfield_callback (some_more_arguments) ', si comporta come' self.formfield_for_dbfield (request, some_more_arguments) ', puoi controllare [il doc] (http: // docs .python.org/2/library/functools.html # functools.partial) e [il post] (http://stackoverflow.com/questions/3252228/python-why-is-functools-partial-necessario) – okm

+0

Tutto è andando sopra la mia testa. Ho due Theseosn 1. Da qui posso ottenere maggiori informazioni su 'formfield_callback'. è costruito in django o nella sua usanza 2. come cna so quali sono le cose o campi o funzioni disponibili all'interno di kwargs. come fai a sapere che obj è in kwargs – user192082107

3

per accedere all'istanza modello del InlineModelAdmin del genitore ModelAdmin, ho usato questo hack in passato:

class PersonAdmin(ModelAdmin): 
    def get_formsets(self, request, obj=None, *args, **kwargs): 
     for inline in self.inline_instances: 
      inline._parent_instance = obj 
      yield inline.get_formset(request, obj) 

class CarInline(TabularInline): 
    _parent_instance = None 

    def get_formset(self, *args, **kwargs): 
     def formfield_callback(field, **kwargs): 
      formfield = field.formfield(**kwargs) 
      if field.name == 'car': 
       formfield.queryset = Cars.objects.filter(owner=self._parent_instance) 
      return formfield 

     if self._parent_instance is not None: 
      kwargs['formfield_callback'] = formfield_callback 
     return super(CarInline, self).get_formset(*args, 
                   **kwargs) 
2

Ho scoperto un modo più pulito senza tutto l'hacking in get_formset()/get_formsets()/formfield_for_dbfield(). Usa l'oggetto Request di Django (a cui hai accesso) per recuperare lo request.path_info, quindi recupera il PK dagli args nella partita resolve. Esempio:

from django.contrib import admin 
from django.core.urlresolvers import resolve 
from app.models import YourParentModel, YourInlineModel 


class YourInlineModelInline(admin.StackedInline): 
    model = YourInlineModel 

    def get_parent_object_from_request(self, request): 
     """ 
     Returns the parent object from the request or None. 

     Note that this only works for Inlines, because the `parent_model` 
     is not available in the regular admin.ModelAdmin as an attribute. 
     """ 
     resolved = resolve(request.path_info) 
     if resolved.args: 
      return self.parent_model.objects.get(pk=resolved.args[0]) 
     return None 

    def has_add_permission(self, request): 
     parent = self.get_parent_object_from_request(request) 
     if parent and parent.is_active is True: 
      return False 
     return super(YourInlineModelInline, self).has_add_permission(request) 

    def get_formset(self, request, *args, **kwargs): 
     """ 
     Using the get_formset example from above to override the QuerySet. 
     """ 
     def formfield_callback(field, **kwargs): 
      formfield = field.formfield(**kwargs) 
      if field.name == 'car': 
       formfield.queryset = self.parent_model.objects.filter(
        owner=self.get_parent_object_from_request(request) 
       ) 
      return formfield 

     if self.get_parent_object_from_request(request) is not None: 
      kwargs['formfield_callback'] = formfield_callback 

     return super(YourInlineModelInline, self).get_formset(*args, **kwargs) 


@admin.register(YourParentModel) 
class YourParentModelAdmin(admin.ModelAdmin): 
    inlines = [YourInlineModelInline] 
+0

Non c'è abbastanza tempo per una corretta modifica, quindi lascerò qui alcune note per farlo funzionare sotto Django 2.0: 'da django.urls import resolve',' pk = resolved.kwargs ['object_id'] ', per ModelAdmin' self.parent_model' -> 'self.model' –

Problemi correlati