I have been following this Django tutorial, and I am trying to remove hardcoded URLs, but I can't make the reverse function work.
That's my detail view:
def detail(request, question_id):
try:
question = Question.objects.get(pk=question_id)
except Question.DoesNotExist:
raise Http404("Question does not exist")
# How it was before:
# return render(request, 'polls/detail.html', {'question': question})
return HttpResponse(reverse('polls:detail', kwargs={'question': question}))
path('<int:question_id>/', views.detail, name='detail')
This is the urls.py:
app_name = 'polls'
urlpatterns = [
# ex: /polls/
path('', views.index, name='index'),
# ex: /polls/5/
path('<int:question_id>/', views.detail, name='detail'),
# ex: /polls/5/results/
path('<int:question_id>/results/', views.results, name='results'),
# ex: /polls/5/vote/
path('<int:question_id>/vote/', views.vote, name='vote'),
]
And this is the template for the detail view:
<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" />
Index
</form>
Can somebody help me with that or give any tips?
Thanks in advance.
I think your original code was fine. If you were in another view (say, the vote view) and wanted to redirect the user to the detail view, you could do this:
return HttpResponseRedirect(reverse('polls:detail', args=(question.id,)))
Copied from part 4 of the tutorial: https://docs.djangoproject.com/en/2.0/intro/tutorial04/#write-a-simple-form:
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,)))
The last line redirects the user from the vote view to the results view and uses reverse() to avoid hard-coding the url.
Related
I'm very new to django. So I'm studying using tutorial site.
I think type perfectly same on site, but it's not work.
so plz give me advice.
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/urls.py
from django.urls import path
from . import views
app_name = 'polls'
urlpatterns = [
path('', views.index, name='index'), #url : base/polls/
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'),
]
mysite/polls/templates/polls/detail.html
<h1>{{ question.question_text }}</h1>
{% if errer_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>
part of def vote in mysite/polls/views.py
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,)))
error
NoReverseMatch at /polls/1/
Reverse for 'vote' with arguments '('',)' not found. 1 pattern(s) tried: ['polls/(?P<question_id>[0-9]+)/vote/$']
Request Method: GET
Request URL: http://127.0.0.1:8000/polls/1/
Django Version: 3.0.7
Exception Type: NoReverseMatch
Exception Value:
Reverse for 'vote' with arguments '('',)' not found. 1 pattern(s) tried: ['polls/(?P<question_id>[0-9]+)/vote/$']
Exception Location: C:\Users\hdh45\crawling_practice\firstP\venv\lib\site-packages\django\urls\resolvers.py in _reverse_with_prefix, line 677
Python Executable: C:\Users\hdh45\crawling_practice\firstP\venv\Scripts\python.exe
Python Version: 3.8.3
Error during template rendering
In template X:\python\practice_well\polls\templates\polls\detail.html, error at line 5
I guess vote url isnt work. maybe something wrong. but i dont know exact anwser. My googling power is useless.. help me.
<form action="{% url '***polls***:vote' question.id %}" method="post">
polls was a string and your urls.py in a
path('**<int:question_id>**/vote/', views.vote, name='vote')
<int:question_id> is a int. i think that's a mistake
and I'm also begginer
I'm receiving the following error
Error: Reverse for placeinterest with arguments ('',) and keyword arguments {} not found. 1 pattern(s) tried:
[u'polls/(?P<section_id>[0-9]+)/placeinterest/$']
I worked through the Django example for a polling app, and now I'm trying to create a class registration app adapting what I already have. Error points to line 5 here:
<h1>{{ section.class_name }}</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="{% url 'polls:placeinterest' section.id %}" method="post">
{% csrf_token %}
<input type="radio" name="incr" id="incr" value="incr" />
<label for="incr"></label><br />
<input type="submit" value="Placeinterest" />
</form>
But I think the problem is in my placeinterest function:
def placeinterest(request, section_id):
section = get_object_or_404(Section, pk=section_id)
try:
selected_choice = section.num_interested
except (KeyError, Section.num_interested.DoesNotExist):
# Redisplay the question voting form.
return render(request, 'polls/detail.html', {
'section': section,
'error_message': "You didn't select a choice.",
})
else:
section.num_interested += 1
section.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=(section.id,)))
I'm not sure what my POST call should be if my Section class looks like this:
class Section(models.Model):
class_name = models.CharField(max_length=200)
num_interested = models.IntegerField(default=0)
pub_date = models.DateTimeField('date published')
def __str__(self):
return self.class_name
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(minutes=3)
def some_interested(self):
if self.num_interested > 0:
return "There is currently interest in this class"
else:
return "There is currently no interest in this class"
The results part comes up fine in my browser, and here is urls.py for good measure:
from django.conf.urls import url
from . import views
app_name = 'polls'
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<section_id>[0-9]+)/placeinterest/$', views.placeinterest, name='placeinterest'),
]
Edit: adding the original code from the example in hopes that might help someone see where I went wrong:
urls:
from django.conf.urls import url
from . import views
app_name = 'polls'
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'),
]
vote function in views.py:
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,)))
detail.html, where I'm having problems after making my changes:
<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>
The error has nothing to do with your view. It specifically says it's trying to match the URL named placeinterest in the error. It knows the URL exists, but the arguments are wrong.
It thinks you're trying to pass an empty string as a positional argument. This means that section.id is probably None when the template is rendered. It's converting the null value into an empty string and passing it as a positional argument, hence ('',). That's a tuple with one empty string value in it.
The problem is here:
<form action="{% url 'polls:placeinterest' section.id %}" method="post">
Django can't figure out the url argument as it's misconfigured. Use this:
<form action="{% url 'polls:placeinterest' section_id=section.id %}" method="post">
You also need to ensure that section id is valid a number (be careful of what you supply as "section" as your context.
I am working on extending the webapp we're left off with after completing the official Django Tutorial.
One of the functionalities I am looking to add is the ability for users to add polls themselves.
I am struggling with getting the page to process the data and then redirect to the index page ('/polls').
When I submit a new poll as a logged in user, I am returned to my index page, which is supposed to show most recently published polls, or in the event of no polls, the message "No polls are available."
For some reason, I always see "No polls are available", but once I click to the index page via a link on the site, it displays all of my polls, including my most recently created one, data intact!
Any thoughts here? I think I have included the relevant info below but happy to supply more. Thanks in advance for any help/advice.
views.py
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 Poll.objects.order_by('-pub_date')[:15]
#login_required
def add_poll(request):
ChoiceFormSet = formset_factory(ChoiceForm, extra=3, min_num=2, validate_min=2)
if request.method == 'POST':
form = PollForm(request.POST)
formset = ChoiceFormSet(request.POST)
if all([form.is_valid(), formset.is_valid()]):
poll = form.save()
for inline_form in formset:
if inline_form.cleaned_data:
choice = inline_form.save(commit=False)
choice.question = poll
choice.save()
return render(request, 'polls/index.html', {})
else:
form = PollForm()
formset = ChoiceFormSet()
return render(request, 'polls/add_poll.html', {'form': form, 'formset': formset})
add_poll.html
{% extends 'polls/base.html' %}
{% block title %}Add Poll{% endblock %}
{% block body_block %}
<form role="form" id="poll_form" method="post" action="{% url 'polls:add_poll' %}">
<h2 class="form-signin-heading">Add a Poll</h2>
{% csrf_token %}
<table>
{{ form }}
{{ formset }}
</table>
<br/>
<button class="btn btn-primary" type="submit" name="submit">Create Poll</button>
</form>
{% endblock %}
index.html
{% extends 'polls/base.html' %}
{% block body_block %}
{% if latest_question_list %}
<ul>
{% for poll in latest_question_list %}
<li>{{ poll.question_text }}</li>
{% endfor %}
</ul>
{% else %}
<p>No polls are available.</p>
{% endif %}
{% endblock %}
urls.py
from django.conf.urls import patterns, url
from . import views
urlpatterns = patterns('',
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^(?P<pk>\d+)/$', views.DetailView.as_view(), name='detail'),
url(r'^(?P<pk>\d+)/results/$', views.ResultsView.as_view(), name='results'),
url(r'^add_poll/$', views.add_poll, name='add_poll'),
url(r'^(?P<question_id>\d+)/vote/$', views.vote, name='vote'),
url(r'^profile_page/$', views.ProfileView.as_view(), name='profile_page'),
url(r'^edit_profile/$', views.edit_profile, name='edit_profile'),
)
When you save your form you are not redirecting.
Your are returning 'polls/index.html' with empty polls data, that's why you always get "No polls are available". But this is very incorrect, you must follow the Post/Redirect/Get (PRG) pattern, so instead of:
return render(request, 'polls/index.html', {})
do this:
return HttpResponseRedirect(reverse('polls:index'))
You don't do any redirect right now, you are just rendering your index template with an empty context (that's why you don't see anything). To redirect, you need to use HttpResponseRedirect when your form is valid.
So, please change line:
return render(request, 'polls/index.html', {})
(just over the else) to
return HttpResponseRedirect(reverse('index'))
I finally figured it out. I had to change my html form to this:
<form method="POST" action="{% url 'new_beam:beam_diagram' beam_id=1 %}" enctype="multipart/form-data">
This refers to the django 1.6 tutorial.
I keep getting this error message when I try to open the /polls/1/ page in part 4 of the tutorial:
NoReverseMatch at /polls/1/
Reverse for 'vote' with arguments '('',)' and keyword arguments '{}' not found. 1 pattern(s) tried: [u'polls/(?P<poll_id>\\d+)/vote/$']
Request Method: GET
Request URL: http://*/polls/1/
Django Version: 1.6.4
Exception Type: NoReverseMatch
Exception Value:
Reverse for 'vote' with arguments '('',)' and keyword arguments '{}' not found. 1 pattern(s) tried: [u'polls/(?P<poll_id>\\d+)/vote/$']
Exception Location: /home/iamal/lib/python2.7/Django-1.6.4-py2.7.egg/django/core/urlresolvers.py in _reverse_with_prefix, line 452
Python Executable: /package/host/localhost/python-2.7.3/bin/python
Python Version: 2.7.3
Python Path:
['/home/iamal/tutorial',
'/home/iamal/bin',
'/home/iamal/lib/python2.7/Django-1.6.4-py2.7.egg',
'/home/iamal/lib/python2.7/gunicorn-19.1.1-py2.7.egg',
'/package/host/localhost/python-2.7.3/lib/python2.7/site-packages/MySQL_python-1.2.3-py2.7-linux-x86_64.egg',
'/package/host/localhost/python-2.7.3/lib/python2.7/site-packages/distribute-0.6.35-py2.7.egg',
'/package/host/localhost/python-2.7.3/lib/python27.zip',
'/package/host/localhost/python-2.7.3/lib/python2.7',
'/package/host/localhost/python-2.7.3/lib/python2.7/plat-linux2',
'/package/host/localhost/python-2.7.3/lib/python2.7/lib-tk',
'/package/host/localhost/python-2.7.3/lib/python2.7/lib-old',
'/package/host/localhost/python-2.7.3/lib/python2.7/lib-dynload',
'/package/host/localhost/python-2.7.3/lib/python2.7/site-packages',
'/home/iamal/lib/python2.7']
Server time: So, 11 Jan 2015 00:03:43 +0100
I noticed that there seems to be no argument to be passed in the error message. When I replaced poll.id from line 5 in the details.html with '1', it works. I have also checked that I included namespace="polls" in the project/urls.py file which seems to have been a common issue.
My details.html:
<h1>{{ poll.question }}</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="{% url 'polls:vote' poll.id %}" method="post">
{% csrf_token %}
{% for choice in poll.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>
My polls/views.py:
from django.http import HttpResponse, Http404, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from django.template import Context, loader
from django.core.urlresolvers import reverse
from polls.models import Poll, Choice
def index(request):
latest_poll_list = Poll.objects.order_by('-pub_date')[:5]
context = {'latest_poll_list': latest_poll_list}
return render(request, 'polls/index.html', context)
def detail(request, poll_id):
poll = get_object_or_404(Poll, pk=poll_id)
return render(request, 'polls/detail.html', {'poll': poll_id})
def results(request, poll_id):
poll = get_object_or_404(Poll, pk=poll_id)
return render(request, 'polls/results.html', {'poll': poll})
def vote(request, poll_id):
p = get_object_or_404(Poll, pk=poll_id)
try:
selected_choice = p.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
return render(request, 'polls/detail.html', {
'poll': p,
'error_message': "You didn't select a choice.",
})
else:
selected_choice.votes += 1
selected_choice.save()
return HttpResponseRedirect(reverse('polls:results', args=(p.id,)))
Your detail view is passing the poll_id to the template target instead of the poll object itself. It should be:
return render(request, 'polls/detail.html', {'poll': poll})
I'm creating this django app using the tutorial and i'm up to part 4 https://docs.djangoproject.com/en/dev/intro/tutorial04/
The app display a basic poll and when you click on it , it display some choices and a button to vote.
The problem is when I click on vote . It display Page not found.
I think the problem is the redirection but I don't know where to pin point the problem.
The first page is the index.html which display the questions then it's the detail.html which display the choices and question. I know when I click on vote , it goes back to the app URLconf then the urlconf execute the view function and the view function execute the results.
My detail.html are
<h1>{{ poll.question }}</h1>
{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
<form action="/myapp/{{ poll.id }}/vote/" method="post">
{% csrf_token %}
{% for choice in poll.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>
My urls.py inside myapp are :
from django.conf.urls import patterns, include, url
from django.contrib import admin
from django.conf import settings
from django.conf.urls import patterns, include, url
urlpatterns = patterns('myapp.views',
url(r'^$', 'index'),
url(r'^(?P<poll_id>\d+)/$', 'detail'),
url(r'^(?P<poll_id>\d+)/results/$', 'results'),
url(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
)
My views.py are :
from django.http import HttpResponse
from myapp.models import Poll ,choice
from django.template import Context, loader
from django.http import Http404
from django.shortcuts import render_to_response, get_object_or_404
from django.template import RequestContext
def index(request):
latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
return render_to_response('myapp/index.html', {'latest_poll_list': latest_poll_list})
def results(request, poll_id):
p = get_object_or_404(Poll, pk=poll_id)
return render_to_response('myapp/results.html', {'poll': p})
def vote(request, poll_id):
p = get_object_or_404(Poll, pk=poll_id)
try:
selected_choice = p.choice_set.get(pk=request.POST['choice'])
except (KeyError, Choice.DoesNotExist):
# Redisplay the poll voting form.
return render_to_response('myapp/detail.html', {
'poll': p,
'error_message': "You didn't select a choice.",
}, context_instance=RequestContext(request))
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('myapp.views.results', args=(p.id,)))
def detail(request, poll_id):
p = get_object_or_404(Poll, pk=poll_id)
return render_to_response('myapp/detail.html', {'poll': p},
context_instance=RequestContext(request))
My results.html are:
<h1>{{ poll.question }}</h1>
<ul>
{% for choice in poll.choice_set.all %}
<li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>
Vote again?
Thank you for helping me . This will be my first breakthrough app if I can get this to work.
My Main URLconf is :
from django.conf.urls import patterns, include, url
from django.contrib import admin
from django.conf import settings
# Uncomment the next two lines to enable the admin:
# from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^polls/', include('myapp.urls')),
url(r'^admin/', include(admin.site.urls)),
)
Don't hardcode urls
You should not hardcode a URL anywhere - just like for file system paths. You're not only killing kittens but also making your code less solid !
Reverse urls instaed !
Read about reversing urls for starters, and using named urls for main dish, and about {% url %} templatetag for dessert.
At the time of digestive, you will be a master of Django url system B)
Read the tutorial
In the tutorial you linked, they don't hardcode urls:
{% url 'polls:vote' poll.id %}
That's the way to go !!
Make sure you don't have a hardcoded url anywhere in your templates and your problem will go away.
Loose the myapp bit of the form action.
It should be
<form action="polls/{{poll.id}}/vote" method="post">
This matches the regex in your urls.py file -
url(r'^polls/', include('myapp.urls')),
and then in myapp.urls -
url(r'^(?P<poll_id>\d+)/vote/$', 'vote'),
The include function means that django is trying to match ^polls/(?P<poll_id>\d+)/vote/$
If you look at the error page your getting you can see the url's that django's trying to match against (none of them contain 'myapp' it should be polls).
IMPORTANT
When you get further on in the tutorial you'll see that you shouldn't be hardcoding urls in your templates (as jpic rightly points out). At this stage you need to swap out the form action for {% url 'polls:vote' poll.id %}.