23

Sto cercando di realizzare un Class Based ListView che visualizza una selezione di un set di tabella. Se il sito viene richiesto per la prima volta, dovrebbe essere visualizzato il set di dati. Preferirei un invio POST, ma anche GET va bene.Django: modulo di ricerca in ListView basato su classi

Questo è un problema, che è stato facile da gestire con function based views, tuttavia con le visualizzazioni basate sulla classe ho difficoltà a capirlo.

Il mio problema è che ottengo un numero diverso di errori, causati dalla mia comprensione limitata delle viste basate su classi. Ho letto varie documentazioni e ho compreso le visualizzazioni per richieste di query dirette, ma non appena desidero aggiungere un modulo all'istruzione della query, mi imbatto in un errore diverso. Per il codice qui sotto, ricevo un ValueError: Cannot use None as a query value.

Quale sarebbe il flusso di lavoro delle best practice per un ListView basato su classi in base alle voci del modulo (altrimenti selezionando l'intero database)?

Questo è il mio codice di esempio:

models.py

class Profile(models.Model): 
    name = models.CharField(_('Name'), max_length=255) 

    def __unicode__(self): 
     return '%name' % {'name': self.name} 

    @staticmethod 
    def get_queryset(params): 

     date_created = params.get('date_created') 
     keyword = params.get('keyword') 
     qset = Q(pk__gt = 0) 
     if keyword: 
      qset &= Q(title__icontains = keyword) 
     if date_created: 
      qset &= Q(date_created__gte = date_created) 
     return qset 

forms.py

class ProfileSearchForm(forms.Form): 
    name = forms.CharField(required=False) 

views.py

class ProfileList(ListView): 
    model = Profile 
    form_class = ProfileSearchForm 
    context_object_name = 'profiles' 
    template_name = 'pages/profile/list_profiles.html' 
    profiles = [] 


    def post(self, request, *args, **kwargs): 
     self.show_results = False 
     self.object_list = self.get_queryset() 
     form = form_class(self.request.POST or None) 
     if form.is_valid(): 
      self.show_results = True 
      self.profiles = Profile.objects.filter(name__icontains=form.cleaned_data['name']) 
     else: 
      self.profiles = Profile.objects.all() 
     return self.render_to_response(self.get_context_data(object_list=self.object_list, form=form)) 

    def get_context_data(self, **kwargs): 
     context = super(ProfileList, self).get_context_data(**kwargs) 
     if not self.profiles: 
      self.profiles = Profile.objects.all() 
     context.update({ 
      'profiles': self.profiles 
     }) 
     return context 

Di seguito ho aggiunto l'FBV che esegue il lavoro. Come posso convertire questa funzionalità in CBV? Sembra essere così semplice nelle viste basate sulla funzione, ma non nelle viste basate sulla classe.

def list_profiles(request): 
    form_class = ProfileSearchForm 
    model = Profile 
    template_name = 'pages/profile/list_profiles.html' 
    paginate_by = 10 

    form = form_class(request.POST or None) 
    if form.is_valid(): 
     profile_list = model.objects.filter(name__icontains=form.cleaned_data['name']) 
    else: 
     profile_list = model.objects.all() 

    paginator = Paginator(profile_list, 10) # Show 10 contacts per page 
    page = request.GET.get('page') 
    try: 
     profiles = paginator.page(page) 
    except PageNotAnInteger: 
     profiles = paginator.page(1) 
    except EmptyPage: 
     profiles = paginator.page(paginator.num_pages) 

    return render_to_response(template_name, 
      {'form': form, 'profiles': suppliers,}, 
      context_instance=RequestContext(request)) 
+1

Domanda, stai tentando di visualizzare un valore da un set di query basato sull'invio del modulo? –

+0

sì, quello era l'obiettivo. – neurix

+0

Dai un'occhiata a questo approccio generico di mixin: http://stackoverflow.com/questions/7011773/how-to-create-a-filter-form-for-a-class-based-generic-object- list-in-django –

risposta

35

Credo che il vostro obiettivo sta cercando di filtrare set di query basata su modulo di presentazione, in caso affermativo, utilizzando GET:

class ProfileSearchView(ListView) 
    template_name = '/your/template.html' 
    model = Person 

    def get_queryset(self): 
     try: 
      name = self.kwargs['name'] 
     except: 
      name = '' 
     if (name != ''): 
      object_list = self.model.objects.filter(name__icontains = name) 
     else: 
      object_list = self.model.objects.all() 
     return object_list 

Poi tutto quello che dovete fare è scrivere un metodo get per rendere modello e contesto.

Forse non l'approccio migliore. Usando il codice sopra, non è necessario definire un modulo django.

Ecco come funziona: le viste basate su classi separano il modo di renderizzare il modello, elaborare la forma e così via. Ad esempio, get gestisce la risposta GET, post gestisce la risposta POST, get_queryset e get_object è autoesplicativo e così via. Il modo più semplice per sapere che cosa è il metodo a disposizione, avviare una shell e digitare:

from django.views.generic import ListView se si desidera conoscere ListView

e quindi digitare dir(ListView). Lì puoi vedere tutto il metodo definito e andare a visitare il codice sorgente per capirlo. Il metodo get_queryset utilizzato per ottenere un set di query.Perché non basta definirlo in questo modo, funziona anche:

class FooView(ListView): 
    template_name = 'foo.html' 
    queryset = Photo.objects.all() # or anything 

Possiamo farlo come sopra, ma non possiamo fare il filtraggio dinamico utilizzando questo approccio. Usando get_queryset possiamo fare il filtraggio dinamico, usando qualsiasi dato/valore/informazione che abbiamo, significa che possiamo anche usare il parametro name che viene inviato da GET, ed è disponibile su kwargs, o in questo caso, su dove è some_key parametro specificato

+2

invece di usare la prova che potresti usare get con un valore predefinito, penso che aiuti con la leggibilità. 'name = self.kargs.get ('name', None)' e poi 'if name: # do some stuff' – agmezr

2

Questo è stato spiegato bene sull'argomento delle viste generiche qui Dynamic filtering.

È possibile eseguire il filtraggio tramite GET, non penso che sia possibile utilizzare il metodo POST per questo come ListView non ereditato dai mix di modifica.

Che cosa si può fare è:

urls.py

urlpatterns = patterns('', 
       (r'^search/(\w+)/$', ProfileSearchListView.as_view()), 
      ) 

views.py

class ProfileSearchListView(ListView): 
    model = Profile 
    context_object_name = 'profiles' 
    template_name = 'pages/profile/list_profiles.html' 
    profiles = [] 

    def get_queryset(self): 
     if len(self.args) > 0: 
       return Profile.objects.filter(name__icontains=self.args[0]) 
     else: 
       return Profile.objects.filter() 
9

Beh, penso che lasciare la convalida di forma è bella idea. Forse non vale la pena in questo caso particolare, perché è forma molto semplice - ma di sicuro con un più complicato (e forse anche la vostra crescerà anche), quindi vorrei fare qualcosa di simile:

class ProfileList(ListView): 
    model = Profile 
    form_class = ProfileSearchForm 
    context_object_name = 'profiles' 
    template_name = 'pages/profile/list_profiles.html' 
    profiles = [] 


    def get_queryset(self): 
     form = self.form_class(self.request.GET) 
     if form.is_valid(): 
      return Profile.objects.filter(name__icontains=form.cleaned_data['name']) 
     return Profile.objects.all() 
+0

ho un errore che form_class non è definito, come risolvere? –

+0

Vic Nicethemer, è possibile definirlo – meteor

+3

@VicNicethemer Dovrebbe essere form = self.form_class (self.request.GET). –

1

Penso che la l'errore che si ottiene è perché il modulo non richiede il campo del nome. Quindi, sebbene il modulo sia valido, il clean_data per il tuo campo name è vuoto.

Queste potrebbero essere le linee di problematiche:

if form.is_valid(): 
    self.show_results = True 
    self.profiles = Profile.objects.filter(name__icontains=form.cleaned_data['name']) 

Se fossi in te, vorrei provare a cambiare la linea:

self.profiles = Profile.objects.filter(name__icontains=form.cleaned_data['name']) 

a questo:

self.profiles = Profile.objects.none() 

Se ci si ferma ricevendo errori (e il tuo modello riceve un vuoto object_list), il problema che hai è quello che ho detto prima: campo del nome non richiesto.

Facci sapere se questo non funziona!

0

Ricerca su tutti i campi nel modello

class SearchListView(ItemsListView): 

# Display a Model List page filtered by the search query. 

def get_queryset(self): 
    fields = [m.name for m in super(SearchListView, self).model._meta.fields] 
    result = super(SearchListView, self).get_queryset() 
    query = self.request.GET.get('q') 
    if query: 
     result = result.filter(
      reduce(lambda x, y: x | Q(**{"{}__icontains".format(y): query}), fields, Q()) 
     ) 
    return result 
0

penso che sarebbe meglio fare questo tramite get_context_data. Crea manualmente il tuo modulo HTML e usa GET per recuperare questi dati. Di seguito un esempio tratto da qualcosa che ho scritto. Quando si invia il modulo, è possibile utilizzare i dati di acquisizione per ritrasferire tramite i dati di contesto. Questo esempio non è adatto alla tua richiesta, ma dovrebbe aiutare gli altri utenti.

def get_context_data(self, **kwargs): 
    context = super(Search, self).get_context_data(**kwargs) 
    filter_set = Gauges.objects.all() 
    if self.request.GET.get('gauge_id'): 
     gauge_id = self.request.GET.get('gauge_id') 
     filter_set = filter_set.filter(gauge_id=gauge_id) 

    if self.request.GET.get('type'): 
     type = self.request.GET.get('type') 
     filter_set = filter_set.filter(type=type) 

    if self.request.GET.get('location'): 
     location = self.request.GET.get('location') 
     filter_set = filter_set.filter(location=location) 

    if self.request.GET.get('calibrator'): 
     calibrator = self.request.GET.get('calibrator') 
     filter_set = filter_set.filter(calibrator=calibrator) 

    if self.request.GET.get('next_cal_date'): 
     next_cal_date = self.request.GET.get('next_cal_date') 
     filter_set = filter_set.filter(next_cal_date__lte=next_cal_date) 

    context['gauges'] = filter_set 
    context['title'] = "Gauges " 
    context['types'] = Gauge_Types.objects.all() 
    context['locations'] = Locations.objects.all() 
    context['calibrators'] = Calibrator.objects.all() 
    # And so on for more models 
    return context 
Problemi correlati