複雑な検索フォームを作る方法がわからない
ちょっと複雑な検索をDjangoでやってみたけどうまくいかない。
一応動作はするけどコードが大変なことになってる。
誰かが「勝手に添削」してくれることを期待して恥をさらしておく。 :p
モデルはこんな感じで
models.py
# -*- encoding: utf-8 -*- from django.db import models class Publisher(models.Model): name = models.CharField(maxlength=200) class Author(models.Model): name = models.CharField(maxlength=200) class Genre(models.Model): name = models.CharField(maxlength=200) class Book(models.Model): name = models.CharField(maxlength=200) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) genres = models.ManyToManyField(Genre)
'/search/' で検索フォームの表示とフォームの受け取りをやってる
urls.py
urlpatterns += patterns('myproj.book.views', (r'^search/$', 'search'), )
こんな感じ。
views.py
# -*- encoding: utf-8 -*- from django.views.generic.list_detail import object_list from myproj.book.models import Book from django.http import HttpResponse, Http404, HttpResponseNotAllowed from django.shortcuts import render_to_response from django import newforms as forms from django.db.models import Q def search(request): if request.method == 'POST': return HttpResponseNotAllowed(['GET']) # GET with query strings if len(request.GET) > 0: book_list = Book.objects.all() for f in request.GET: vlist = request.GET.getlist(f) vlist = [ v for v in vlist if v ] if not vlist: continue if f == 'name': # a field in the same model book_list = book_list.filter(**{f + '__icontains': vlist[0]}) else: criteria = [Q(**{f + '__id': v}) for v in vlist if v] criteria = reduce(lambda x, y: x|y, criteria) book_list = book_list.filter(criteria).distinct() return object_list(request, queryset=book_list, allow_empty=True) # No query strings. Just render search form. SearchBookForm = forms.form_for_model(Book) form = SearchBookForm() return render_to_response('book/search.html', {'form': form})
一応動作するけど、深めにネストしてて可読性もかなり低い。
Q object使い方がいまいちよくわかっていない気が。
検索フォームの表示にforms.form_for_model(Book)なフォームをそのまま表示してるのも変。
form_for_model使わないで検索フォームを自分で定義しておく方がいいのかな。