Django: formulaire de recherche dans ListView basé sur la classe
J'essaie de réaliser un {[4] } qui affiche une sélection d'un ensemble de table. Si le site est demandé la première fois, l'ensemble de données doit être affiché. Je préférerais une soumission de poste, mais GET est également très bien.
C'est un problème, qui était facile à gérer avec function based views
, mais avec les vues basées sur les classes, j'ai du mal à me débrouiller.
Mon problème est que j'obtiens un nombre différent d'erreurs, qui sont causées par ma compréhension limitée des vues basées sur les classes. J'ai lisez diverses documentations et je comprends les vues pour les requêtes de requête directes, mais dès que je voudrais ajouter un formulaire à l'instruction de requête, je rencontre une erreur différente. Pour le code ci-dessous, je reçois un ValueError: Cannot use None as a query value
.
Quel serait le flux de travail de meilleure pratique pour un ListView basé sur une classe en fonction des entrées de formulaire (sinon en sélectionnant l'ensemble de la base de données)?
C'est mon échantillon code:
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
Ci-dessous, j'ai ajouté le FBV qui fait le travail. Comment puis-je traduire cette fonctionnalité dans un CBV? Il semble être si simple dans les vues basées sur les fonctions, mais pas dans les vues basées sur les classes.
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))
6 réponses
Je pense que votre objectif est d'essayer de filtrer queryset basé sur la soumission de formulaire, si c'est le cas, en utilisant 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
Alors tout ce que vous devez faire est d'écrire une méthode get
pour rendre le modèle et le contexte.
Peut-être pas la meilleure approche. En utilisant le code ci-dessus, vous n'avez pas besoin de définir un formulaire django.
Voici comment cela fonctionne : les vues basées sur les classes séparent leur manière de rendre le modèle, de traiter le formulaire, etc. Comme, get
gère la réponse GET, post
gère la réponse POST, get_queryset
et get_object
est explicite, et ainsi de suite. Le moyen facile de savoir quelle est la méthode disponible, lancez un shell et tapez:
from django.views.generic import ListView
si vous voulez savoir à propos de ListView
, puis tapez dir(ListView)
. Là, vous pouvez voir toute la méthode définie et aller visiter le code source pour le comprendre. La méthode get_queryset
utilisée pour obtenir un queryset. Pourquoi ne pas simplement le Définir comme ceci, cela fonctionne aussi:
class FooView(ListView):
template_name = 'foo.html'
queryset = Photo.objects.all() # or anything
Nous pouvons le faire comme ci-dessus, mais nous ne pouvons pas faire de filtrage dynamique en utilisant cette approche. En utilisant get_queryset
nous peut faire un filtrage dynamique, en utilisant toutes les données / valeurs / informations que nous avons, cela signifie que nous pouvons également utiliser name
paramètre qui est envoyé par GET
, et son disponible sur kwargs
, ou dans ce cas, sur self.kwargs["some_key"]
où some_key
est un paramètre que vous avez spécifié
Eh bien, je pense que laisser la validation à la forme est une bonne idée. Peut-être pas la peine dans ce cas particulier, parce que c'est une forme très simple - mais à coup sûr avec un plus compliqué (et peut-être que le vôtre va grandir aussi), donc je ferais quelque chose comme:
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()
Cela a été bien expliqué sur le sujet des vues génériques ici filtrage dynamique .
Vous pouvez faire du filtrage via GET
, Je ne pense pas que vous puissiez utiliser la méthode POST
pour cela car ListView
n'est pas hérité des mixages d'édition.
Ce que vous pouvez faire est:
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()
Je pense que l'erreur que vous obtenez est parce que votre formulaire ne nécessite pas le champ name. Ainsi, bien que le formulaire soit valide, le cleaned_data de votre Champ name
est vide.
Ce pourraient être les lignes problématiques:
if form.is_valid():
self.show_results = True
self.profiles = Profile.objects.filter(name__icontains=form.cleaned_data['name'])
Si j'étais vous, j'essaierais de changer la ligne:
self.profiles = Profile.objects.filter(name__icontains=form.cleaned_data['name'])
À ceci:
self.profiles = Profile.objects.none()
Si vous arrêtez de recevoir des erreurs (et que votre modèle reçoit un object_list
vide), le problème que vous avez Est ce que j'ai dit auparavant: champ de nom non requis.
Laissez-nous savoir si cela ne fonctionne pas!
Recherche sur tous les champs du modèle
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
Je pense que vous feriez mieux de le faire via get_context_data. Créez manuellement votre formulaire HTML et utilisez GET pour récupérer ces données. Un exemple de quelque chose que j'ai écrit est ci-dessous. Lorsque vous soumettez le formulaire, vous pouvez utiliser le obtenir les données à transmettre via les données de contexte. Cet exemple n'est pas adapté à votre demande, mais il devrait aider les autres utilisateurs.
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