J'essaie de réaliser un Class Based ListView
qui affiche une sélection d'un ensemble de tables. Si le site est demandé pour la première fois, le jeu de données devrait être affiché. Je préférerais une soumission POST, mais GET convient également.
C’est un problème facile à manipuler avec function based views
, mais avec des vues basées sur les classes, j’ai du mal à comprendre.
Mon problème est que je reçois un certain nombre d’erreurs, dues à ma compréhension limitée des vues classées. J'ai lu diverses documentations et je comprends les vues pour les requêtes directes, mais dès que je souhaite 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
.
Quelle serait la procédure de travail recommandée pour un ListView basé sur une classe en fonction des entrées de formulaire (sinon, sélection de la base de données entière)?
Ceci est mon exemple de 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é en CBV? Cela semble 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))
Je pense que votre objectif est d'essayer de filtrer le groupe de requêtes en fonction de la soumission du formulaire, le cas échéant, à l'aide de 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
Ensuite, tout ce que vous avez à faire est d’écrire une méthode get
pour restituer 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 sa 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 le plus simple de connaître la méthode disponible, démarrez un shell et tapez:
from Django.views.generic import ListView
si vous voulez en savoir plus sur 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 ensemble de requêtes. Pourquoi ne pas simplement le définir comme ça, ça marche 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 pouvons faire du filtrage dynamique, en utilisant n'importe quelle donnée/valeur/information dont nous disposons, cela signifie que nous pouvons aussi utiliser le paramètre name
envoyé par GET
, et qu'il est disponible sur kwargs
, ou dans ce cas sur self.kwargs["some_key"]
où some_key
est quelconque paramètre que vous avez spécifié
Eh bien, je pense que laisser la validation pour former est une bonne idée. Cela ne vaut peut-être pas la peine dans ce cas particulier, car il s’agit d’un formulaire très simple - mais c’est certain, avec un formulaire plus compliqué (et peut-être que le vôtre grandira également), je ferais donc 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()
Ceci a été bien expliqué sur le sujet des vues génériques ici Filtrage dynamique .
Vous pouvez filtrer à travers GET
, je ne pense pas que vous puissiez utiliser la méthode POST
pour cela, car ListView
n'est pas hérité des mélanges d'édition.
Ce que vous pouvez faire c'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 Nom. Ainsi, bien que le formulaire soit valide, les données nettoyées pour votre champ name
sont vides.
Celles-ci 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 de ligne:
self.profiles = Profile.objects.filter(name__icontains=form.cleaned_data['name'])
pour ça:
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 rencontrez est ce que j'ai déjà dit: nom du champ non requis.
Faites-nous savoir si cela ne fonctionne pas!
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 les données get pour les renvoyer 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
Rechercher dans 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
Cette approche est similaire à celle de @jasisz, mais plus simple.
class ProfileList(ListView):
template_name = 'your_template.html'
model = Profile
def get_queryset(self):
query = self.request.GET.get('q')
if query:
object_list = self.model.objects.filter(name__icontains=query)
else:
object_list = self.model.objects.none()
return object_list
Ensuite, tout ce que vous avez à faire sur le modèle html est:
<form method='GET'>
<input type='text' name='q' value='{{ request.GET.q }}'>
<input class="button" type='submit' value="Search Profile">
</form>