Reputation: 431
Currently, I am at the end of Django and wanted to add a functionality to check whether the Question object being used actually does have any choices. Right now, it will not show off any boxes and give me a 'Vote'-button which is effectively useless because I cannot check off any boxes.
I have tried using the 'pk' keyword that is supposed to be passed on to DetailView when I enter a webpage, as evident here:
url(r'^specifics/(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'),
But when I try to use the pk variable in my DetailView, it complains that it does not know any keyword by the name of pk. I get this error in particular: NameError: name 'pk' is not defined
. In my details.html, I tried to print out {{ question.pk }}
and that is working flawlessly (however, I suspect it is due to the vote function in views.py).
Here are both DetailView and vote() in my web app:
class DetailView(generic.DetailView):
question = get_object_or_404(Question, pk=pk)
model = Question
template_name = 'polls/detail.html'
question_exist = Question.objects.exists()
empty_choice = question[pk].choice_query.exists()
def get_queryset(self):
""" Exclude any unpublished questions. """
return Question.objects.filter(pub_date__lte=timezone.now())
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = question.choice_set.get(pk=request.POST['choice'])
except(KeyError, Choice.DoesNotExist):
# Redisplay the question voting form.
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice!",
})
else:
selected_choice.votes += 1
selected_choice.save()
# Always return HttpResonseRedirect after POST success, prevents
# data from being posted twice if someone hits the back button!
return HttpResponseRedirect( reverse('polls:results', args=(question.id, )) )
And here is my detail.html:
<h1> {{ question.question_text }} </h1>
{% if error_message %}<p> <strong> {{ error_message }} </strong> </p> {% endif %}
{% if empty_choice %}
<p> There are no choices! </p>
{% else %}
<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
{% for choice in question.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}"
value="{{ choice.id }}"/>
<label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label>
<br />
{% endfor %}
<input type="submit" value="Vote" />
</form>
{% endif %}
{{ question.pk }}
question.pk returns the correct number in each website, but when I try to use it in DetailView, Django says that it doesn't understand what pk it. Why?
Upvotes: 2
Views: 3261
Reputation: 224
you can access the id,slug etc of an object in a generic view from your get_object()
example
class DetailView(generic.DetailView):
question = get_object_or_404(Question, pk=pk)
model = Question
template_name = 'polls/detail.html'
def get_queryset(self):
""" Exclude any unpublished questions. """
id = self.get_object().id
# put remaining code here
Upvotes: 1
Reputation: 309099
The problem is that you are trying to access pk
when you are defining the class.
class DetailView(generic.DetailView):
question = get_object_or_404(Question, pk=pk)
This won't work because you don't have access to the pk
yet. You can access the pk
in the class methods, when the request is handled.
class DetailView(generic.DetailView):
model = Question
template_name = 'polls/detail.html'
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
context['empty_choice'] = self.object.choice_query.exists()
return context
def get_queryset(self):
""" Exclude any unpublished questions. """
return Question.objects.filter(pub_date__lte=timezone.now())
Note that you don't have to get the question using get_object_or_404
- the get_object
method of the generic view will take care of that for you.
It would probably be better to name your view something else (e.g. QuestionDetailView
, so that it doesn't get confused with Django's DetailView
.
Upvotes: 3
Reputation: 3027
You cannot use the pk
during class construction, it is not there until a request comes in. Classes are constructed when django is loaded into memory much before any request can be taken. Basically you cannot use the following lines because django has no clue about a pk
when they're being executed:
class DetailView(generic.DetailView):
question = get_object_or_404(Question, pk=pk)
question_exist = Question.objects.exists()
empty_choice = question[pk].choice_query.exists()
Instead you shall use the get_queryset
function, which is only called once a request comes in. The pk
will be in self.kwargs
(because you did give it the a name in the URLconf regex):
class DetailView(generic.DetailView):
model = Question
template_name = 'polls/detail.html'
def get_queryset(self):
""" Exclude any unpublished questions. """
return get_object_or_404( Question,
pk=self.kwargs['pk'],
pub_date__lte=timezone.now() )
(This example might have some quirks, i haven't tested it. Apologies for that. I'll test it once i get home)
Upvotes: 1