I decided to learn Django and started with the 'Django Polls App' which I figured would be the most simple, I am gripping the idea of how it works but I am stuck. To learn a bit better, I changed the variables and names from the original docs to my own.
Example : question_text = q_text, Choice = Choices, choice_text = choice.. etc.
Now I can't figure out what's wrong with my code as I can't make the polls work. There is no error code but it simply doesn't display the number of votes or show a success sign. I was also following Traversy Media's Django Crash Course (Polls App).
My code :
views.py
from django.http import Http404, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.urls import reverse
from django.template import loader
from .models import Question, Choices
def index(request):
latest_ques = Question.objects.order_by('-published')[:5]
context = {'latest_ques': latest_ques}
return render(request, 'polls/index.html', context)
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404("Question does not exist")
return render(request, 'polls/detail.html', {'question': question})
def results(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/results.html', {'question': question})
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
selected_choice = question.choices_set.get(pk=request.POST['choices'])
except (KeyError, Choices.DoesNotExist):
return render(request, 'polls/detail.html', {
'question': question,
'error_message': "You didn't select a choice.",
})
else:
selected_choice.votes += 1
selected_choice.save()
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
models.py
from django.db import models
class Question(models.Model):
q_text = models.CharField(max_length=200)
published = models.DateTimeField('date published')
def __str__(self):
return self.q_text
class Choices(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
def __str__(self):
return self.choice
details.html
{% extends 'base.html' %}
{% block content %}
<a class="btn btn-secondary btn-sm mb-3" href="{% url 'polls:index' %}">Back To Polls</a>
<h1 class="text-center mb-3">{{ question.q_text }}</h1>
{% if error_message %}
<p class="alert alert-danger">
<strong>{{ error_message }}</strong>
</p>
{% endif %}
<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
{% for choices in question.choices_set.all %}
<div class="form-check">
<input type="radio"
name="choices"
class="form-check-input"
id="choices{{ forloop.counter }}"
value="{{ choices.id }}">
<label for="choices{{ forloop.counter }}">{{ choices.choice }}</label>
</div>
{% endfor %}
<input type="submit" value="Vote" class="btn btn-success btn-lg btn-block mt-4" />
</form>
{% endblock %}
results.html
{% extends 'base.html' %}
{% block content %}
<h1 class="mb-5 text-center">{{ question.q_text }}</h1>
<ul class="list-group mb-5">
{% for choices in question.choices_set.all %}
<li class="list-group-item">
{{ choices.choice }} <span class="badge badge-success float-right">{{ choices.votes }} vote{{ choices.votes | pluralize }}</span>
</li>
{% endfor %}
</ul>
<a class="btn btn-secondary" href="{% url 'polls:index' %}">Back To Polls</a>
<a class="btn btn-dark" href="{% url 'polls:detail' question.id %}">Vote Again?</a>
{% endblock %}
urls.py
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.index, name='index'),
path('<int:question_id>/', views.detail, name='detail'),
path('<int:question_id>/results/', views.results, name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
I've been on the internet for hours and can't figure it out. Not getting a proper answer. Thought I'd try my luck here.
I've tried to change the names of multiple variables, I've had many errors throughout but this one I can't figure out. The solution would actually be quite simple I know it but can't really put a finger on it. Thanks.
Nevermind, I found out that there were some misspelled variables. It worked after I went line by line to find the error.
Related
I'm trying to finish my Django Polls Project from the Django Documentation but I ran into the "Reverse for 'polls.index' not found. 'polls.index' is not a valid view function or pattern name." error.
The full error details can be seen here
The following are my files.
base.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link
rel="stylesheet"
href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh"
crossorigin="anonymous"
/>
<title>Pollster {% block title %}{% endblock %}</title>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-6 m-auto">
{% block content %}{% endblock %}
</div>
</div>
</div>
</body>
</html>
index.html
{% extends 'base.html' %}
{% block content %}
<h1 class="text-center mb-3">Poll Questions</h1>
{% if latest_question_list %}
{% for question in latest_question_list %}
<div class="card mb-3">
<div class="card-body">
<p class="lead">{{ question.question_text}}</p>
Vote Now
Results
</div>
</div>
{% endfor %}
{% else %}
<p>No polls available</p>
{% endif %}
{% endblock %}
mysite.urls
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls),
]
polls.urls
from django.urls import path
from . import views #from ALL import views
app_name = 'polls'
urlpatterns = [
path('', views.index, name='index'),
path('<int:question_id>/', views.detail, name='detail'),
path('<int:question_id>/results/', views.results, name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
polls.views
from django.shortcuts import render
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from .models import Question, Choice
# Get questions and display them
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {'latest_question_list': latest_question_list}
return render(request, 'polls/index.html', context)
# Show specific question and choices
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404("Question does not exist")
return render(request, 'polls/detail.html', {'question': question})
# Get question and display results
def results(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/results.html', {'question': question})
# Vote for a question choice
def vote(request, question_id):
# print (request.POST['choice'])
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 an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
detail.html
{% extends 'base.html' %}
{% block content %}
Back To Polls
<h1 class="text-center mb-3">{{ question.question_text }}</h1>
{% if error_message %}
<p class="alert alert-danger">
<strong>{{ error_message }}</strong>
</p>
{% endif %}
<form action="{% url 'polls:vote' question.id %}" method ="post">
{% csrf_token %}
{% for choice in question.choice_set.all %}
<div class="form-check">
<input type="radio" name="choice" class="form-check-input" id="choice{{ forloop.counter }}" value = "{{ choice.id }}"/>
<label for="choice{{ forloop.counter }}">
{{ choice.choice_text }}
</label>
</div>
{% endfor %}
<input type="submit" value ="Vote" class ="btn btn-success btn-lg btn-block mt-4" />
</form>
{% endblock %}
I am new to Django and I would like to learn methods to find out where the errors could be originating from. I checked the code in reference with this finished project from the channel I'm watching but couldn't find the error I made.
Where could the error in my code be?
I researched and this stackoverflow question might be the same as mine but was not solved as well.
Here is my folder structure
I am also new to python for web development so any tips on how I could study this would be helpful. Thank you in advanced!
This is the polls app tutorial from the Django Docs.
When I go to the first question http://127.0.0.1:8000/polls/1/, select an option and click 'Vote', I get the error message.
error message
views.py:
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic
from .models import Choice, Question
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
"""Return the last five published questions."""
return Question.objects.order_by('-pub_date')[:5]
class DetailView(generic.DetailView):
model = Question
template_name = 'polls/detail.html'
class ResultsView(generic.DetailView):
model = Question
template_name = 'polls/results.html'
def vote(request, question_id):
question = get_object_or_404(Question, pk=question_id)
try:
# request.POST['choice'] returns ID of the selected choice as a string
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 a HttpResponseRedirect after successfully dealing with POST data.
# This prevents the data from being posted twice if a user hits the Back button.
return HttpResponseRedirect(reverse('polls:results', args=question_id, ))
polls/urls.py:
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
polls/templates/polls/index.html:
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li>{{ question.question_text }}</li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
polls/templates/polls/detail.html:
<h1>{{ question.question_text }}</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<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>
polls/templates/polls/results.html:
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>
Vote again?
Can anyone please help?
The problem is that you write:
return HttpResponseRedirect(reverse('polls:results', args=question_id, ))
Now args is used for positional arguments, and there can be (in theory) multiple. So it should be a collection of items. For example:
return HttpResponseRedirect(reverse('polls:results', args=[question_id], ))
Note: in the tutorial they write args=(question_id,). Now that is something different than args=question_id. In Python a (0, ) is not an integer, but a 1-tuple containing one element: 0. In short: the brackets matter.
But there is no need to do all this wrapping. Django has a shorcut redirect(..) [Django-doc], to build HttpResponseRedirects in a more convenient way:
return redirect('polls:results', question_id)
This takes *args and **kwargs itself as positional and named arguments. So you can write it as if you were calling the view directly as function (with the name of the view in front).
I'm following the Django tutorial process, currently located on part 6.
I get an error when I click on the list from my web page. I looked up where the problem occurred, but I could not find it.
This is the message from a web browser:
Page not found (404)
Request Method: GET
Request URL: http://localhost:8000/polls/1//
Using the URLconf defined in mysite.urls, Django tried these URL patterns, in this order:
polls/ [name='index']
polls/ <int:pk>/ [name='detail']
polls/ <int:pk>/results/ [name='results']
polls/ <int:question_id>/vote/ [name='vote']
admin/
The current path, polls/1//, didn't match any of these.
You're seeing this error because you have DEBUG = True in your Django settings file. Change that to False, and Django will display a standard 404 page.
traceback
"GET /polls/1// HTTP/1.1" 404 2703
mysite/polls/views.py
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import get_object_or_404, render
from django.urls import reverse
from django.views import generic
from django.utils import timezone
from .models import Choice, Question
# ...
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
"""
Return the last five published questions (not including those set to be
published in the future).
"""
return Question.objects.filter(
pub_date__lte=timezone.now()
).order_by('-pub_date')[:5]
class DetailView(generic.DetailView):
model = Question
template_name = 'polls/detail.html'
def get_queryset(self):
"""
Excludes any questions that aren't published yet.
"""
return Question.objects.filter(pub_date__lte=timezone.now())
class ResultsView(generic.DetailView):
model = Question
template_name = 'polls/results.html'
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 an HttpResponseRedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
mysite/polls/urls.py
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),
path('<int:question_id>/vote/', views.vote, name='vote'),
]
mysite/urls.py
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls),
]
mysite/polls/templates/polls/index.html
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'polls/style.css' %}" />
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li>{{ question.question_text }}</li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
mysite/polls/templates/polls/detail.html
<h1>{{ question.question_text }}</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<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>
mysite/polls/templates/polls/result.html
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
<li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>
Vote again?
You have two slashes ('//') at the end of your url, this should be just one:
http://localhost:8000/polls/1/
More specifically, in your polls/index.html template, you have the following line:
<li>{{ question.question_text }}</li>
Of that line, this part generates the url for you:
{% url 'polls:detail' question.id %}
that becomes:
http://localhost:8000/polls/1/
But you have added an extra slash behind it, look at the end of the below string:
"{% url 'polls:detail' question.id %}/"
Remove that, and I think your code should work.
I'm checking out django for the first time following the tutorial
[https://docs.djangoproject.com/en/2.0/intro/tutorial03/]
When I change the hardcoded url from:
<li>{{ question.question_text }}</li>
to:
<li>{{ poll.question }}</li>
I get a error as follow:
My urls.py files are:
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.index, name='index'),
# ex:/polls/5/
path('<int:question_id>/', views.detail, name='datail'),
# ex: /polls/5/results/
path('<int:question_id>/vote/', views.vote, name='vote'),
# ex: /polls/vote/
path('<int:question_id>/results/', views.results, name='results'),
]
My views.py files are:
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from .models import Question,Choice
def detail(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/detail.html', {'question':question})
def results(request, question_id):
question = get_object_or_404(Question, pk=question_id)
return render(request, 'polls/results.html', {'question':question})
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 an HttpRespinsedirect after successfully dealing
# with POST data. This prevents data from being posted twice if a
# user hits the Back button.
return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))
def index(request):
latest_question_list = Question.objects.order_by('-pub_date')[:5]
context = {'latest_question_list':latest_question_list}
return render(request, 'polls/index.html', context)
My index.html fils are:
{% if latest_question_list %}
<ul>
{% for question in latest_question_list %}
<li>{{ question.question_text }}</li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
my details.html fils are:
<h1>{{ question.question_text }}</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<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>
So, I am confused of Removing hardcoded URLs in templates does not work.
Could someone help me? Thanks a lot!
You should be using name spacing in your urls. That is why you set the app_name to polls. Your url should look like this,
<li>{{ question.question_text }}</li>
Right now your url conf doesn't know what detail your talking about.
Here is the url conf docs and name spacing is talked about at the bottom.
https://docs.djangoproject.com/en/2.0/topics/http/urls/
so I started on the generic views section of the tutorial, until which point everything was smooth sailing, working perfectly and then I get this error:
Reverse for 'vote' with arguments '('',)' and keyword arguments '{}' not found. 1 pattern(s) tried: [u'polls/(?P[0-9]+)/vote/$']
Here is my urls.py:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'),
url(r'^(?P<pk>[0-9]+)/results/$', views.ResultsView.as_view(), name='results'),
url(r'^(?P<question_id>[0-9]+)/vote/$', views.vote, name ='vote'),
]
And here is my views.py:
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, HttpResponseRedirect
from django.template import RequestContext, loader
from django.http import Http404
from .models import Choice, Quesion
from django.core.urlresolvers import reverse
from django.views import generic
# Create your views here.
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list'
def get_queryset(self):
return Quesion.objects.order_by('-pub_date')[:5]
class DetailView(generic.DetailView):
model = Quesion
template_name = 'polls/detail.html'
class ResultsView(generic.DetailView):
model = Quesion
template_name = 'polls/results.html'
def vote(request, question_id):
p = get_object_or_404(Quesion, pk=question_id)
try:
selected_choice = p.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
return render(request, 'polls/detail.html', {
'question': p,
'error_message':"No choice selected",
})
else:
selected_choice.votes += 1
selected_choice.save()
return HttpResponseRedirect(reverse('polls:results',args=(p.id,)))
And here is my detail.html
<h1>{{ question.question_text }}</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<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_txt }} </label><br />
{% endfor %}
<input type="submit" value="vote" />
</form>
You have two problems. The first is that DetailView doesn't provide a question variable in the template, but provides one called object instead. So all instances of question in the template needs to be changed to object.
Secondly, the URL is expecting a keyword argument question_id, but you're passing a non-keyword argument to the {% url %} tag. You need to change that to say question_id=object.id. Your detail.html should look like this:
<h1>{{ object.question_text }}</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="{% url 'polls:vote' question_id=object.id %}" method="post">
{% csrf_token %}
{% for choice in object.choice_set.all %}
<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}" />
<label for="choice{{ forloop.counter }}">{{ choice.choice_txt }} </label><br />
{% endfor %}
<input type="submit" value="vote" />
</form>