Skip to content
Browse files

Replaced most views with class-based generic views.

  • Loading branch information...
1 parent 42ca1dd commit 221491742caa7e79cda413c6cc52f0f713a98095 @jacobian jacobian committed May 10, 2011
View
29 faq/templates/faq/base.html
@@ -1,29 +1,14 @@
+<!DOCTYPE html>
<html>
<head>
- <title>{% block title %}Django FAQ {% endblock %}</title>
-
-<style>
-
-.answer
-{
- color: #CC0000;
-}
-
-</style>
-
+ <meta charset=utf-8 />
+ <title>{% block title %}Django FAQ {% endblock %}</title>
</head>
<body>
-{% if messages %}
-<ul id="messages">
- {% for message in messages %}
- <li>{{ message }}</li>
- {% endfor %}
-</ul>
-<hr/>
-{% endif %}
-
-{% block body %}{% endblock %}
+ {% for message in messages %}
+ <p class="message">{{ message }}</p>
+ {% endfor %}
+ {% block body %}{% endblock %}
</body>
-
</html>
View
24 faq/templates/faq/question_list.html
@@ -1,24 +0,0 @@
-{% extends "faq/base.html" %}
-
-{% block title %}{{ block.super }}: Example {% endblock %}
-
-{% block body %}
-<h1>{{ page_title }}</h1>
-<p>
-Last updated on: {{ updated_on|date:"M d, Y" }}
-</p>
-
-<h3>Questions</h3>
-<ol>
-{% for question in object_list %}
-
-<li>
-{{ question.text }}
-<br>
-<span class="answer">answer: {{ question.answer }}</span>
-</li>
-
-{% endfor %}
-</ol>
-
-{% endblock %}
View
4 faq/templates/faq/submit_thanks.html
@@ -0,0 +1,4 @@
+{% extends "faq/base.html" %}
+
+{% block title %}{{ block.super }}: Thanks{% endblock %}
+{% block body %}<h3>Thanks!</h3>{% endblock %}
View
14 faq/templates/faq/topic_detail.html
@@ -0,0 +1,14 @@
+{% extends "faq/base.html" %}
+
+{% block title %}{{ block.super }}: {{ topic }}{% endblock %}
+
+{% block body %}
+<h3>{{ topic }}</h3>
+<dl>
+{% for question in questions %}
+ <dt>{{ question.text }}</dt>
+ <dd class="answer">{{ question.answer }}</dd>
+{% endfor %}
+</dl>
+
+{% endblock %}
View
12 faq/templates/faq/topic_list.html
@@ -0,0 +1,12 @@
+{% extends "faq/base.html" %}
+
+{% block title %}{{ block.super }}: Topics{% endblock %}
+
+{% block body %}
+<h1>Topics</h1>
+<ul>
+ {% for topic in topics %}
+ <li><a href="{{ topic.get_absolute_url }}">{{ topic }}</a></li>
+ {% endfor %}
+</ul>
+{% endblock %}
View
55 faq/tests/test_views.py
@@ -1,6 +1,7 @@
from __future__ import absolute_import
import os
+import datetime
import django.test
from django.conf import settings
from ..models import Topic, Question
@@ -17,15 +18,65 @@ def setUp(self):
def tearDown(self):
settings.TEMPLATE_DIRS = self._oldtd
- def test_submit_faq_view(self):
+ def test_submit_faq_get(self):
+ response = self.client.get('/submit/')
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, "faq/submit_question.html")
+
+ def test_submit_faq_post(self):
data = {
'topic': '1',
'text': 'What is your favorite color?',
'answer': 'Blue. I mean red. I mean *AAAAHHHHH....*',
}
response = self.client.post('/submit/', data)
- self.assertRedirects(response, "/")
+ self.assertRedirects(response, "/submit/thanks/")
self.assert_(
Question.objects.filter(text=data['text']).exists(),
"Expected question object wasn't created."
)
+
+ def test_submit_thanks(self):
+ response = self.client.get('/submit/thanks/')
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, "faq/submit_thanks.html")
+
+ def test_faq_index(self):
+ response = self.client.get('/')
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, "faq/topic_list.html")
+ self.assertQuerysetEqual(
+ response.context["topics"],
+ ["<Topic: Silly questions>", "<Topic: Serious questions>"]
+ )
+ self.assertEqual(
+ response.context['last_updated'],
+ Question.objects.order_by('-updated_on')[0].updated_on
+ )
+
+ def test_topic_detail(self):
+ response = self.client.get('/silly-questions/')
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, "faq/topic_detail.html")
+ self.assertEqual(
+ response.context['topic'],
+ Topic.objects.get(slug="silly-questions")
+ )
+ self.assertEqual(
+ response.context['last_updated'],
+ Topic.objects.get(slug='silly-questions').questions.order_by('-updated_on')[0].updated_on
+ )
+ self.assertQuerysetEqual(
+ response.context["questions"],
+ ["<Question: What is your favorite color?>",
+ "<Question: What is your quest?>"]
+ )
+
+ def test_question_detail(self):
+ response = self.client.get('/silly-questions/your-quest/')
+ self.assertEqual(response.status_code, 200)
+ self.assertTemplateUsed(response, "faq/question_detail.html")
+ self.assertEqual(
+ response.context["question"],
+ Question.objects.get(slug="your-quest")
+ )
View
14 faq/urls.py
@@ -5,15 +5,23 @@
urlpatterns = patterns('',
url(regex = r'^$',
- view = faq_views.question_list,
- name = 'faq_question_list',
+ view = faq_views.TopicList.as_view(),
+ name = 'faq_topic_list',
),
url(regex = r'^submit/$',
view = faq_views.submit_faq,
name = 'faq_submit',
),
+ url(regex = r'^submit/thanks/$',
+ view = faq_views.SubmitFAQThanks.as_view(),
+ name = 'faq_submit_thanks',
+ ),
url(regex = r'^(?P<slug>[\w-]+)/$',
- view = faq_views.question_detail,
+ view = faq_views.TopicDetail.as_view(),
+ name = 'faq_topic_detail',
+ ),
+ url(regex = r'^(?P<topic_slug>[\w-]+)/(?P<slug>[\w-]+)/$',
+ view = faq_views.QuestionDetail.as_view(),
name = 'faq_question_detail',
),
)
View
105 faq/views.py
@@ -1,49 +1,79 @@
from __future__ import absolute_import
+from django.db.models import Max
from django.contrib import messages
from django.http import Http404
-from django.http import HttpResponseRedirect
-from django.shortcuts import redirect, render
-from django.shortcuts import render_to_response
-from django.template import RequestContext
+from django.shortcuts import redirect, render, get_object_or_404
from django.utils.translation import ugettext as _
from django.views.generic.list_detail import object_detail, object_list
-from .models import Question
+from django.views.generic import ListView, DetailView, TemplateView
+from .models import Question, Topic
from .forms import SubmitFAQForm
-def question_detail(request, slug, template_name='faq/question_detail.html', extra_context={}):
- """
- Displays an individual question.
- """
- return object_detail(
- request,
- template_name = template_name,
- extra_context = extra_context,
- slug = slug,
- slug_field = 'slug',
- queryset = Question.objects.active(user=request.user),
- )
+class TopicList(ListView):
+ model = Topic
+ template = "faq/topic_list.html"
+ allow_empty = True
+ context_object_name = "topics"
+
+ def get_context_data(self, **kwargs):
+ data = super(TopicList, self).get_context_data(**kwargs)
+
+ # This slightly magical queryset grabs the latest update date for
+ # topic's questions, then the latest date for that whole group.
+ # In other words, it's::
+ #
+ # max(max(q.updated_on for q in topic.questions) for topic in topics)
+ #
+ # Except performed in the DB, so quite a bit more efficiant.
+ #
+ # We can't just do Question.objects.all().aggregate(max('updated_on'))
+ # because that'd prevent a subclass from changing the view's queryset
+ # (or even model -- this view'll even work with a different model
+ # as long as that model has a many-to-one to something called "questions"
+ # with an "updated_on" field). So this magic is the price we pay for
+ # being generic.
+ last_updated = (data['object_list']
+ .annotate(updated=Max('questions__updated_on'))
+ .aggregate(Max('updated')))
-def question_list(request, template_name='faq/question_list.html',
- extra_context={}, group=False):
- '''
- Displays a list of all the questions.
- '''
- query_set = Question.objects.active(group=group,user=request.user)
+ data.update({'last_updated': last_updated['updated__max']})
+ return data
+
+class TopicDetail(DetailView):
+ model = Topic
+ template = "faq/topic_detail.html"
+ context_object_name = "topic"
- if len(query_set) == 0:
- raise Http404()
+ def get_context_data(self, **kwargs):
+ # Include a list of questions this user has access to. If the user is
+ # logged in, this includes protected questions. Otherwise, not.
+ qs = self.object.questions.active()
+ if self.request.user.is_anonymous():
+ qs = qs.exclude(protected=True)
+
+ data = super(TopicDetail, self).get_context_data(**kwargs)
+ data.update({
+ 'questions': qs,
+ 'last_updated': qs.aggregate(updated=Max('updated_on'))['updated'],
+ })
+ return data
- last_update = query_set.values('updated_on').order_by('-updated_on',)[0]
- extra = { 'updated_on': last_update['updated_on'] }
-
- extra.update( extra_context )
+class QuestionDetail(DetailView):
+ queryset = Question.objects.active()
+ template = "faq/question_detail.html"
- return object_list(
- request,
- template_name = template_name,
- extra_context = extra,
- queryset = query_set
- )
+ def get_queryset(self):
+ topic = get_object_or_404(Topic, slug=self.kwargs['topic_slug'])
+
+ # Careful here not to hardcode a base queryset. This lets
+ # subclassing users re-use this view on a subset of questions, or
+ # even on a new model.
+ # FIXME: similar logic as above. This should push down into managers.
+ qs = super(QuestionDetail, self).get_queryset().filter(topic=topic)
+ if self.request.user.is_anonymous():
+ qs = qs.exclude(protected=True)
+
+ return qs
def submit_faq(request, form_class=SubmitFAQForm,
template_name="faq/submit_question.html",
@@ -62,8 +92,11 @@ def submit_faq(request, form_class=SubmitFAQForm,
_("Your question was submitted and will be reviewed by for inclusion in the FAQ."),
fail_silently=True,
)
- return redirect(success_url if success_url else "faq_question_list")
+ return redirect(success_url if success_url else "faq_submit_thanks")
context = {'form': form}
context.update(extra_context)
return render(request, template_name, context)
+
+class SubmitFAQThanks(TemplateView):
+ template_name = "faq/submit_thanks.html"

0 comments on commit 2214917

Please sign in to comment.
Something went wrong with that request. Please try again.