Reverse error in Django 1.10 - django

I'm new to Django and slowly learning how it works. I just upgraded to 1.10 and part of my app stopped working. I know it is related to the changes made into Reverse. I have been reading and I cannot find exactly what I'm doing wrong. Almost everything works as it should with a couple of exceptions. The behavior is as follows:
1) On my app I load reservations/create, it works perfectly I can create my reservation
2) When I click create, the reservation is actually created and saved into the database, but the browser is sent to the wrong address. It gets sent to reservations/create instead of reservations/reservation number (for example reservations/2 where it shows its details) and shows a Reverse error (included in this post)
3) If I test reservations/2 for example, it shows that it was actually created.
4) Also if a go straight to reservations/ it should show a list of all the ones already create, but instead shows a Reverse error too.
I would really appreciate any help in understanding what I'm doing wrong.
Models.py
class Reservation(models.Model):
res_number = models.AutoField(primary_key=True)
date = models.DateField(default=datetime.date.today())
status = models.CharField(max_length=10,default="Created")
reservation_type = models.CharField(max_length=11,choices=shced_type_choices, default="rental")
aircraft = models.ForeignKey('aircraft.Aircraft')
renter = models.CharField(max_length=30,blank=False,null=False)
instructor = models.CharField(max_length=30,blank=True,null=False)
def get_absolute_url(self):
return reverse("reservations:detail", kwargs={"res_number": self.res_number})
Main urls.py
url(r'^reservations/', include('dispatch.urls', namespace='reservations')),
Dispatch.urls
from django.conf.urls import include, url
from django.contrib import admin
from .views import (
reservations_list,
reservations_detail,
reservations_edit,
reservations_dispatch,
reservations_close,
reservations_cancel,
reservations_create,
reservations_close,
)
urlpatterns = [
url(r'^$', reservations_list),
url(r'^(?P<res_number>\d+)/$', reservations_detail),
url(r'^(?P<res_number>\d+)/edit/$', reservations_edit),
url(r'^(?P<res_number>\d+)/dispatch/$', reservations_dispatch),
url(r'^(?P<res_number>\d+)/close/$', reservations_close),
url(r'^(?P<res_number>\d+)/cancel/$', reservations_cancel),
url(r'^create/$', reservations_create),
url(r'^close/$', reservations_close),
]
Views.py
from django.contrib import messages
from django import forms
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render, get_object_or_404
from .forms import ReservationForm, CloseReservationForm
from .models import Reservation
def reservations_list(request):
queryset = Reservation.objects.all()
context = {
"object_list": queryset,
"title": "List of Reservations:"
}
return render(request, "dispatch/list.html", context)
def reservations_detail(request, res_number=None):
instance = get_object_or_404(Reservation, res_number=res_number)
context = {
"title": instance.renter,
"instance": instance,
}
return render(request, "dispatch/details.html", context)
def reservations_create(request):
form = ReservationForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
print(instance.aircraft.hobbs)
instance.save()
messages.success(request, "Reservation Created")
return HttpResponseRedirect(instance.get_absolute_url())
context = {
"form": form,
}
return render(request, "dispatch/create.html", context)
Details.html
{% extends "dispatch/base.html" %}
{% block head_title %}{{ block.super }} | {{instance.res_number}}{% endblock head_title %}
{% block content %}
<h1>Reservation for {{title}} on {{instance.date}}</h1>
Reservation Number: {{instance.res_number}}</br>
Date: {{instance.date}}</br>
Status: {{instance.status}}</br>
Reservation Type: {{instance.reservation_type}}</br>
Aircraft: {{instance.aircraft}}</br>
Renter's Name: {{instance.renter}}</br>
Instructor's Name: {{instance.instructor}}</br>
Expected Flight Hours: {{instance.expected_hours}} Hrs</br>
Actual Flown Hours: {{instance.flown_hours}} Hrs</br>
Reservation Created on: {{instance.created}}</br>
Last Updated on: {{instance.updated}}</br>
{% endblock content %}
Create.html
{% extends "dispatch/base.html" %}
{% block head_title %}{{ block.super }} | Create{% endblock head_title %}
{% block content %}
<h1>Create Reservation</h1>
<form method='POST' action=''>{% csrf_token %}
{{form.as_p}}
<input type="submit" name="Create Reservation">
</form>
{% endblock content %}
Reverse error screenshot

Your problem is your routes don't have names. So when you are using reverse('some_name'), you have to have such name defined. The name is detail in your case, so you want to do something like this (see the parameter name)
urlpatterns = [
url(r'^(?P<res_number>\d+)/$', reservations_detail, name='detail'),
]
Also please don't insert traceback as a screenshot. You see the link 'switch to copy-and-paste view'? Yeah, use that the next time.

Related

How can I print the url to the order id field of my django form?

I am doing a simple form site with Django. This is what my sites url is looks like: mysite.com/register/12345678
I want to print the part after the register (12345678) to the order id field. When someone goes this mysite.com/register/87654321 url then i want to print it.
How can i do that? These are my codes.(Currently using Django 1.11.10)
forms.py
from django import forms
from .models import Customer
from . import views
class CustomerForm(forms.ModelForm):
class Meta:
model = Customer
fields = (
'order_id','full_name','company','email',
'phone_number','note')
widgets = {
'order_id': forms.TextInput(attrs={'class':'orderidcls'}),
'full_name': forms.TextInput(attrs={'class':'fullnamecls'}),
'company': forms.TextInput(attrs={'class':'companycls'}),
'email': forms.TextInput(attrs={'class':'emailcls'}),
'phone_number': forms.TextInput(attrs={'class':'pncls'}),
'note': forms.Textarea(attrs={'class':'notecls'}),
}
views.py
from django.shortcuts import render
from olvapp.models import Customer
from olvapp.forms import CustomerForm
from django.views.generic import CreateView,TemplateView
def guaform(request,pk):
form = CustomerForm()
if request.method == "POST":
form = CustomerForm(request.POST)
if form.is_valid():
form.save(commit=True)
else:
print('ERROR FORM INVALID')
theurl = request.get_full_path()
orderid = theurl[10:]
return render(request,'forms.py',{'form':form,'orderid':orderid})
customer_form.html
{% extends 'base.html' %}
{% block content %}
<h1>REGİSTRATİON</h1>
<form class="registclass" method="POST">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn btn-default">REGISTER</button>
</form>
{% endblock %}
urls.py
from django.conf.urls import url
from django.contrib import admin
from olvapp import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^thanks/$',views.ThankView.as_view(),name='thank'),
url(r'^register/(?P<pk>\d+)',views.guaform,name='custform'),
]
You have passed the value to your view as 'pk' so you can use that to set the the initial value:
views.py
form = CustomerForm(initial={'order_id': pk})
SamSparx is right, here's some additional information to help prevent such errors in advance:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^thanks/$',views.ThankView.as_view(),name='thank'),
url(r'^register/(?P<pk>\d+)',views.guaform,name='custform'),
]
You are using regex to parse your path. Regex is generally speaking not recommended in this case according to the docs because it might introduce more errors and is harder to debug. (Unoptimized) RegEx also tends to be slower.
For simple use cases such as your path here, consider choosing the default pathing syntax as below:
urlpatterns = [
url('admin/', admin.site.urls),
url('thanks/',views.ThankView.as_view(),name='thank'),
url('register/<int:pk>',views.guaform,name='custform'),
]
You could of course also use string instead of int depending on your usage of pk.
Your paths do not all end with a slash consistently. This might impact your SEO and confuse users. See this and this.
Also your form is not imported .as_view() for some reason which could cause some problems.

No value returned in django powered HTML

Looked for the solution for almost 2 days without luck - any guidance is much appreciated.
Given 'views.py' looks like this (all needed lib are imported properly):
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from django.template.context_processors import request
from django.views import generic
class HomeView(generic.TemplateView):
template_name = 'polls/djIndex.html'
def test(request):
data = {'temperature': '53F', 'state': 'NY', 'mood': ['good', 'bad']}
return render(request, 'polls/djIndex.html',{'data':data})
These are not working (no value returned) on djIndex.html:
test #1:
{% for key, value in data.items %}
<h3>{{key}}-----: {{value}}</h3>
{% endfor %}
test #2:
{% for key in data.items %}
<p>Value of Key 1 : data["temperature"]</p>
{% endfor %}
test #3
{% for key, value in data %}
<p>Key {{ key }}, Value {{ value }}</p>
{% endfor %}
test #4
<h2> context2: {{data.temperature}}</h2>
But these are working on djIndex.html:
<h2> Temperature: {{temperature}}</h2>
<h2> State: {{state}}</h2>
<h2> Mood: {{mood}}</h2>
Ask: How to make those non-working test #1-#4 to work on my HTML page. Especially test #3 and #4. Am I doing something wrong in my Django config, as some of those test #1-#4 are accepted answers in some forums in different instances?
To pass some context to the template from a class-based TemplateView you can override its get_contecxt_data()
Give this a try
class HomeView(generic.TemplateView):
template_name = 'polls/djIndex.html'
def get_context_data(self, **kwargs):
context = super(HomeView, self).get_context_data(**kwargs)
context['temperature'] = '53F'
context['state'] = 'NY'
return context
Why didn't it work before
TemplatView or any other class-based views don't have a test() method to handle requests, so your test method wouldn't get called upon a request.

Django filtering database items

I am trying to build a project where I have a DB with toys where there are brands, each brand have products, the products have variety of sizes and sizes have colors. What I want to achieve is to have a home page with filtered the brands which I manage to achieve with the views.py below:
def Index(request):
toys = Toys.objects.values('brand').distinct().order_by('brand')
context = {'index': toys }
return render(request, 'index.html', context)
with models.py
class Toys(models.Model):
brand= models.CharField(max_length=255)
product = models.CharField(max_length=255)
size = models.CharField(max_length=255)
color = models.CharField(max_length=255)
Now on the main page I have a list with all the different brans, what I'm trying to do is when I click on any Brand for the home page to redirect me to a result page with list of the Brand's products, product will lead to all available sizes and so on.
How to implement that in the views?
Many approaches are possible. If you do not wish to have better functionality (e.g. all toys from brand 'X' are deleted when the brand is deleted from database), you could do the following.
Write a view function for each "level" of your listing. In the context dictionary, index contains the query result, listing is a heading to be displayed and level is a variable to select how the link is constructed in the template.
from django.shortcuts import render
from toyapp.models import Toys
def Index(request):
toys = Toys.objects.values('brand').distinct().order_by('brand')
context = { 'index': toys,
'listing': 'Toy brands:',
'level': 'brands',
}
return render(request,'toyapp/index.html', context)
def brandview(request, slug):
toys = Toys.objects.filter(brand=slug).order_by('product')
context = {'level': 'products',
'index': toys,
'listing': 'Products from brand ' + slug,
}
return render(request,'toyapp/index.html', context)
def productview(request, slug1, slug2):
toys_set = Toys.objects.filter(brand=slug1)
toys = toys_set.filter(product=slug2).order_by('size')
context = {'level': 'sizes',
'index': toys,
'listing': 'Sizes for product ' + slug2 + ' from brand ' + slug1
}
return render(request,'toyapp/index.html', context)
def sizeview(request, slug1, slug2, slug3):
toys_set = Toys.objects.filter(brand=slug1)
toys = toys_set.filter(product=slug2).order_by('size')
context = {'level': 'colors',
'index': toys,
'listing': 'Colors for size ' + slug3 + ' products ' + slug2 + ' from brand ' + slug1
}
return render(request,'toyapp/index.html', context)
The template index.html is quite simple with a {% for %} loop to go through items and {% if %} tags to select what kind of a link to create and what text to display.
<!DOCTYPE html>
<body>
<h2>{{ listing }}</h2>
{% for item in index %}
<li>
{% if level == 'brands' %}
<a href="{% url 'brandview' slug=item.brand %}" >{{item.brand}}</a>
{% elif level == 'products' %}
<a href="{% url 'productview' slug1=item.brand slug2=item.product %}" >{{item.product}}</a>
{% elif level == 'sizes' %}
<a href="{% url 'sizeview' slug1=item.brand slug2=item.product slug3=item.size %}" >{{item.size}}</a>
{% elif level == 'colors' %}
{{item.color}}
{% endif %}
</li>
{% endfor %}
</body>
Finally, you need to write urls.py with urlpatterns that parses the HTTP request address and passes the arguments to the view.
from django.urls import path, include
from . import views
urlpatterns = [
path('', views.Index, name='index'),
path('<slug:slug>', views.brandview, name='brandview'),
path('<slug:slug1>,<slug:slug2>', views.productview, name='productview'),
path('<slug:slug1>,<slug:slug2>,<slug:slug3>', views.sizeview, name='sizeview'),
]

can not render ' / ' in url in Django templates

Update :: Problem solved.just follow the guy below.
in my urls.py
path('', store_view, name='store'),
path('category/<str:category_name>/', category_view, name='category'),
in views.py
def store_view(request):
categories = list(Category.objects.all())
context = {
'categories': categories,
}
return render(request, 'store/store.html', context)
def category_view(request, category_name):
category = Category.objects.get(name=category_name)
context = {
'category': category,
}
return render(request, 'store/single-category-view.html', context)
in my template : store.html , that is rendered by store_view >>
{% for item in categories %}
<a href="{% url 'category' item.name %}">
{{item.name}}
</a>
{% endfor %}
Now,the problem is, in the category column in my DB, i have got one category called 'Laptop/MacBook'.when ever this name is passed to the url, it says >>
"Reverse for 'category' with arguments '('Laptop/MacBook',)' not
found. 1 pattern(s) tried: ['category/(?P<category_name>[^/]+)/$']
But when i changed the category name from Laptop/MacBook to Laptop and MacBook , it worked fine and showed no error.
But i want to keep it as it was,'Laptop/MacBook'.How can i do that??and how do you guys deal with that?
Try encoding and decoding your DB values. Assuming its Python 3:
from urllib.parse import quote, unquote
encoded = quote("Laptop/Macbook", safe="")
decoded = unquote(encoded)
print(encoded, decoded)
Output:
Laptop%2FMacbook Laptop/Macbook
With this your route should take in the right param.
from django.http import HttpResponse, request
from django.shortcuts import render
def store_view(request):
name = "Laptop/Macbook"
return render(request, './store.html', context={"name": name})
def category_view(request, category_name):
print(category_name)
return HttpResponse(b"Here we go!")
templatetags/tags.py
from urllib.parse import quote, unquote
from django import template
register = template.Library()
#register.filter(name='encode')
def encode(name):
return quote(name, safe="")
#register.filter(name='decode')
def decode(name):
return unquote(name)
Template:
{% load tags %}
<a href="{% url 'category' name|encode %}">
{{name}}
</a>
Don't forget to add in settings:
'OPTIONS': {
'libraries':{
'tags': 'templatetags.tags',
}
},
When using a "/", django thinks that you are passing more than one parameter. To fix this, replace str by path in your urls like so:
path('', store_view, name='store'),
path('category/<path:category_name>/', category_view, name='category'),
This will make django understand that the / does not mean there are two separate parameters in your url.

Django adding a feedback form on every page

I have a form view for user feedback:
urls.py:
url(
r'^feedback/$',
'tool.views.Feedback',
name='feedback'
),
url(
r'^thanks/$',
direct_to_template, {
'template': 'tool_feedback_thanks.html'
},
name='feedback_thanks'
),
forms.py:
class FeedbackForm(forms.Form):
yes_no = forms.ChoiceField(
choices=YES_NO_CHOICE,
initial=1,
widget=forms.RadioSelect(attrs={'class': 'can_reveal_input'}),
label="Are you happy with Our service?"
)
comments = forms.CharField(
widget=forms.Textarea(attrs={
'class': 'hidden', 'placeholder': 'Leave us your comments...'
}),
required=False,
label=""
)
views.py:
def Feedback(request,
template_name='tool_feedback.html'):
title = u'Leave us some feedback'
form = FeedbackForm(request.POST or None)
if form.is_valid():
yes_no = form.cleaned_data['yes_no']
comments = form.cleaned_data['comments']
sender = "A Unirac website user"
recipients = ['person#example.com']
send_mail(yes_no, comments, sender, recipients)
return HttpResponseRedirect(
reverse('feedback_thanks')
)
return render_to_response(template_name, {
'title': title,
'form': form,
}, RequestContext(request))
This works a treat, but now the client is asking that this form be included on every single page. I guess the form can be submitted via js to the appropriate url, but what is the best way to include the unbound form on every page?
Any help would be much appreciated.
I'd create a context processor, to include the form in every view.
EDIT:
To get the user to the previous URL he/she was browsing, you can use just URLs.
# yourapp/context_processors.py
def feedback_form_context_processor(request):
return {
'feedback_form': FeedbackForm(),
'feedback_form_url': reverse("feed_app:form_process", args=(request.path))
}
This is how urls.py could look like:
urlpatterns = patterns('feed_app.views',
url(r'^process-feedback-form/(?P<next_url>\d+)', 'form_process', name='form_process'),
)
And the view for the form:
def form_process(request, next_url):
# Process form, do your stuff here
# if its valid redirect to the url
return redirect(next_url)
And you should structure your templates to have the correct layout. For example, having a base template:
# templates/base.html
<html>
<body>
..
{% block maincontent %}
{% endblock %}
..
{# The form!!! #}
<form action='{{feedback_form_url}}' method='POST'>
#csrftoken
{{ feedback_form.as_p }}
</form>
</body>
</html>
To create a simple view just use the correct template.
# templates/just_a_random_view.html
{% extends base.html %}
{% block maincontent %}
<h1>Content!</h1>
{% endblock %}
Finally, include it in your settings:
# settings.py
TEMPLATE_CONTEXT_PROCESSORS = (
"django.contrib.auth.context_processors.auth",
...
"yourapp.context_processors.feedback_form_context_processor"
)
I believe that easiest way to include form would be to use a assignment_tag:
In template library:
#register.assignment_tag
def feedback_form(format_string):
return FeedbackForm()
In template
{% feedback_form as form %}
{# display form... %}
{{ form.as_p }}
To add on to #bmihelac, who's answer worked really well for me. Since django 2.0 assignment_tag is deprecated in favor of simple_tag. So you can pretty much follow his answer exactly by replacing assignment_tag with simple_tag, like so:
from django import template
from .forms import FeedbackForm
register = template.Library()
#register.simple_tag
def feedback_form():
return FeedbackForm()
And then just refer to https://docs.djangoproject.com/en/2.1/howto/custom-template-tags/#code-layout for info about how to import it into the template!