Fixed a bug of views django: Page not found - python

My english is not good !
I have tried many ways but still get error: page not found.
File views.py:
def create_article(request, template_name='vlogntruong/create_article.html'):
if request.method == "POST":
create_form = VlogForm(request.POST)
if create_form.is_valid():
new_article = create_form.save()
return HttpResponeRedirect(new_article.get_absolute_url())
else:
create_form = VlogForm(request)
template_context = {
'create_form': create_form,
}
return render_to_response(
template_name,
template_context,
RequestContext(request)
)
File forms.py
from django import forms
from .models import VlogNTruong
class VlogForm(forms.Form):
title= forms.CharField(max_length=100,
widget=forms.TextInput(attrs={'size':50}))
name = forms.CharField(max_length=50,
widget = forms.TextInput(attrs={'size':50}))
sumary = forms.CharField(widget = forms.Textarea(attrs={"rows":10, "cols":80}))
content = forms.CharField(widget = forms.Textarea(attrs={"rows":20, "cols":80}))
video = forms.CharField(required = False,
widget = forms.TextInput(attrs={'size':60}))
def save(self):
create_form = VlogNTruong(title = self.cleaned_data['title'],
name = self.cleaned_data['name'],
sumary = self.cleaned_data['content'],
content = self.cleaned_data['content'],
video = self.cleaned_data['video'])
create_form.save()
return create_form
File models.py:
from django.db import models
from django.db.models import permalink
from embed_video.fields import EmbedVideoField
from django.template.defaultfilters import slugify
from django.contrib import admin
import datetime
class VlogNTruong (models.Model):
title = models.CharField(max_length=100)
slug_field = models.TextField(null=True, blank=True)
name = models.CharField(max_length=50)
sumary = models.TextField()
content = models.TextField()
time_create = models.DateTimeField(auto_now_add=True)
video = EmbedVideoField(null=True, blank=True)
class Meta:
ordering = ('-time_create',)
def __unicode__(self):
return "%s" % self.title
#permalink
def get_absolute_url(self):
return ('vlogntruong.views.article_detail', [self.slug_field])
def save(self, *args, **kwargs):
if not self.slug_field:
self.slug_field = slugify(self.title)
super(VlogNTruong, self).save(*args, **kwargs)
file html
{% extends 'vlogntruong/base.html' %}
{% block panel_content %}
<form action="/" method="post">{% csrf_token %}
<table>
{{form.as_table}}
</table>
<input type="submit" name="Create" />
</form>
{% endblock %}
file urls
urlpatterns = patterns('vlogntruong.views',
url(r'^$', 'list_article',
{'template_name':'vlogntruong/list_article.html'}),
url(r'^(?P<slug_field>[-\w\d]+)/$', 'article_detail'),
url(r'^create/$', 'create_article'),
)
Help me
Error
Page not found (404)
Request Method: GET
Request URL: /nhuttruong/create/
No VlogNTruong matches the given query.
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.

Your problem is that your save() method in your form is not returning the object it creates in the database, it is returning the unsaved instance, for which you are trying to get the redirect url.
I would suggest you read the modelforms guide to help you along.
Your form should look like this:
from django import forms
from .models import VlogNTruong
class VlogForm(forms.ModelForm):
title= forms.CharField(max_length=100,
widget=forms.TextInput(attrs={'size':50}))
name = forms.CharField(max_length=50,
widget = forms.TextInput(attrs={'size':50}))
sumary = forms.CharField(widget = forms.Textarea(attrs={"rows":10, "cols":80}))
content = forms.CharField(widget = forms.Textarea(attrs={"rows":20, "cols":80}))
video = forms.CharField(required = False,
widget = forms.TextInput(attrs={'size':60}))
class Meta:
model = VlogNTruong
Then, in your views, you need to adjust it like this:
from django.shortcuts import render
def create_article(request, template_name='vlogntruong/create_article.html'):
create_form = VlogForm(request.POST or None)
if create_form.is_valid():
new_article = create_form.save()
return HttpResponeRedirect(new_article.get_absolute_url())
template_context = {'create_form': create_form,}
return render(request, template_name, template_context)
You also need to make sure that the VlogNTruong model has a get_absolute_url method which returns a URL. Your method just returns a tuple, fix it like this:
def get_absolute_url(self):
from django.core.urlresolvers import reverse
return reverse('vlogntruong.views.article_detail', args=[self.slug_field])
Finally your __unicode__ method must return a unicode object, yours is returning a string:
def __unicode__(self):
return unicode('{}'.format(self.title))

The regular expression that you are using in the third url() does not match with the requested URL:
r'^create/$' will not match '/nhuttruong/create/'
To achieve your objective you should use something like r'.+/create/$'

Related

Django form fields are not displayed

I want to make a form for adding comments, but I only display a form without fields for input, it does not give any errors.
how it looks on the web-site, there should be a form to post a comment.
I would also like to know how this code can be improved.
Any help is appreciated!
Here is my forms.py:
class CommentForm(forms.ModelForm):
class Meta:
model = Comment
fields = ['body']
views.py:
def comments(request, id):
if request.method == 'POST':
cf = CommentForm(request.POST or None)
if cf.is_valid():
body = request.POST.get('body')
comment = Comment.objects.create(post=post, user=request.name, body=body)
comment.save()
return redirect(post.get_absolute_url())
else:
cf = CommentForm()
context = {
'cf': cf,
}
return render(request, 'main/article_detail.html', context)
urls.py:
from django.urls import path, include
from . import views
from .views import *
urlpatterns = [
path('', PostListViews.as_view(), name='articles'),
path('article/<int:pk>/', PostDetailViews.as_view(), name='articles-detail'),
path('register/', register, name='register'),
path('login/', login, name='login'),
]
My form in template:
<form method="POST">
{% csrf_token %}
{{ cf.as_p }}
<button type="submit" class="btn btn-primary" style="width: 100px;position: relative;">Submit</button>
</form>
my models.py:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Article(models.Model):
title = models.CharField(max_length=30)
content = models.TextField()
pub_date = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
class Comment(models.Model):
post = models.ForeignKey(Article, related_name='comments', on_delete=models.CASCADE)
name = models.ForeignKey(User, on_delete=models.CASCADE)
body = models.TextField()
date = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['date']
def __str__(self):
return f'Comment {self.body} by {self.name}'
In your views.py
def comments(request, id):
if request.method == 'POST':
cf = CommentForm(request.POST or None)
if cf.is_valid():
comment = cf.save(commit=False)
comment.user = request.user
comment.post = post # you also will have to query your post first (i presume thats what the id is for in the function declaration)
comment.save()
return redirect(post.get_absolute_url())
else: # fix this
cf = CommentForm()
context = {
'cf': cf,
}
return render(request, 'main/article_detail.html', context) # fix this
Your indentation is implying that the template will render only with the POST method, since when you're accessing the view you are calling a GET method, your render function never gets called.
Also just use regular form.save() method to save your objects to database, its much easier to understand. And you do have to query your post before assigning a comment to it.
In your views, your else block is not indented with request.method if block and also use return render method...
Also use data=request.post in your parentheses bracket instead of "request.post or none". Try this....it should work then....

How to use Django url template tag with variable as url path

I encountered a No Reverse Match Error in trying to render a template. Can you explain what this No Reverse Match Error mean for easier understanding and how to solve the error?
models.py
from django.contrib.sites.models import Site
from django.contrib.gis.db import models
from django.utils.crypto import get_random_string
from django.contrib.gis.geos import GEOSGeometry,Point
from django.contrib.auth.models import AbstractUser
from django.shortcuts import render
# Create your models here.
class User(AbstractUser):
pass
geos_pnt=Point(4314498.56, 1003834.600,srid=3857)
#pnt=GEOSGeometry('POINT(4314498.56, 1003834.600)').wkt
class Apartment(models.Model):
SUBCITY_CHOICES = (
('ADK', 'Addis Ketema'), ('AKLTY', 'Akaki-Kality'), ('ARD', 'Arada'), ('BL', 'Bole'), ('GL', 'Gulele'),
('KLF', 'Kolfe-Keranio'), ('LDTA', 'Ledeta'), ('NFS', 'Nefas Silk'), ('YK', 'Yeka'))
apt_id = models.CharField(str(SUBCITY_CHOICES)+"-"+get_random_string(length=4), max_length=8,primary_key=True)
location = models.PointField(default=geos_pnt,extent=(4282586.10,996190.90,4346411.02,1011478.31),
blank=True, null=True, srid=3857, help_text="Point(longitude latitude)")
no_bedrooms= models.IntegerField(null=True)
apt_area = models.IntegerField(default=0, null=True)
apt_cost = models.IntegerField(default=0, null=True)
apt_subcity = models.CharField(default='Nefas Silk',max_length=100, choices=SUBCITY_CHOICES,null=True)
register_date = models.DateTimeField('Register Date',auto_now_add=True,null=True)
slug = models.SlugField(unique=True)
objects = models.Manager()
sites =models.ManyToManyField(Site)
#change points from apt_rent_db to kml
def pointkml(self):
points = Apartment.objects.kml()
return render("placemarks.kml", {'places': points})
def get_absolute_url(self):
return reverse('apartment_create', kwargs={'pk': self.pk, 'apt_id': self.apt_id.pk})
def save(self, *args, **kwargs):
#self.Latitude = self..y
#self.Longitude = self.location.x
self.slug = slugify(self.apt_id)
super(Apartment, self).save(*args, **kwargs)
class Meta:
# order of drop-down list items
verbose_name = ("Apartment")
verbose_name_plural = ("Apartments")
ordering = ('apt_cost',)
app_label = 'rent_app'
def __unicode__(self):
return self.apt_id
urls.py:
from django.urls import path
from . import views
app_name = 'rent_app'
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('apartment_create/<slug:apt_id>)', views.ApartmentCreate.as_view(), name='apartment_create'),
path('apartments/<int:pk>/', views.ApartmentUpdate.as_view(), name='apartment_update'),
path('apartments/<int:pk>/delete/', views.ApartmentDelete.as_view(), name='apartment_delete'),
]
views.py:
from django.urls import reverse_lazy
from django.views import generic
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from .models import Apartment
from .forms import ApartmentCreateForm
class IndexView(generic.ListView):
template_name = 'djnago_leaflet.html'
context_object_name = 'latest_apartments_list'
def get_queryset(self):
"""Return the last five published apartments."""
return Apartment.objects.order_by('-register_date')[:5]
class ApartmentCreate(CreateView):
template_name = 'rent_app/apartment-create-success.html'
form_class = ApartmentCreateForm
fields = ['apt_id','location','apt_area','apt_cost']
success_url= reverse_lazy('apartment-create')
class ApartmentUpdate(UpdateView):
model = Apartment
fields = ['apt_id','location','apt_area', 'apt_cost']
template_name='index_leaflet.html'
template_name_suffix='apartments'
class ApartmentDelete(DeleteView):
model = Apartment
template_name = 'index_leaflet.html'
template_name_suffix='apartments'
success_url = reverse_lazy('apartment-list')
the html:
<html>
<head>
{% leaflet_js plugins="forms" %}
{% leaflet_css plugins="forms" %}
</head>
<body>
{% leaflet_map "map" callback="window.map_init_basic" %}
<h2>Edit Apartment ID {{ Apartment.apt_id }}</h2>
<h2>Edit Apartment Location {{ Apartment.location }}</h2>
<form action="{% url 'rent_app:apartment_create' apt_id %}" method="post">
{% csrf_token %}
{{ form }}
<input type="submit"/>
</form>
</body>
</html>
I think there are a number of issues in your code.
First regarding the error you are seeing. In your urls you have defined a pattern with the name 'apartment_create' which expects a slug as a parameter. However, apt_id in your template is an empty field. So django cannot find a pattern with the name 'apartment_create' and a valid slug. To solve this change the url pattern to
path('apartment_create/', views.ApartmentCreate.as_view(), name='apartment_create')
And in your template either take out the apt_id from your action in the form (or take out the action all together.
However, in your ApartmenCreate view you are missing the model parameter. In addition the fields and the form parameter are redundant. And are you sure the template_name parameter in that view is correct?
In your model the field apt_id looks strange. You are creating a field with some very obscure verbose_name. If you want to have a choice field you have to set the choices parameter of the field, e.g.:
apt_id = models.CharField(choices=SUBCITY_CHOICES, max_length=8, primary_key=True)
Your get_absolute_url is also not correct: First of all there is no valid url matching that pattern and secondly a field (apt_id) does not have a pk.
In your template you have statements like {{ Apartment.apt_id }}. However, Apartment is a class. So in your views add context_object_name='apartment' so you can access the values in your template like so {{ apartment.apt_id }}.
There are probably some other issues which I have overlooked.

Django filter generic form Select attribute from generic class-based view by Username or prepopulate and hide the form Select-attribute

I Need to Restrict the Options in a Select Field of a Django Form for not staff users.
So these are my models.py:
#!/usr/bin/python3
from django.db import models
from django.urls import reverse
class Extension(models.Model):
username = models.CharField(primary_key=True, max_length=200, help_text='')
callerid = models.CharField(max_length=200, help_text='')
extension = models.CharField(max_length=3, help_text='')
firstname = models.CharField(max_length=200, help_text='')
lastname = models.CharField(max_length=200, help_text='')
password = models.CharField(max_length=200, help_text='')
context = models.ForeignKey('Context', on_delete=models.SET_NULL, null=True)
def get_absolute_url(self):
return reverse('extension-detail', args=[str(self.username)])
def my_get_absolute_url(self):
return reverse('my-extension-detail', args=[str(self.username)])
def __str__(self):
return self.username
class Context(models.Model):
name = models.CharField(primary_key=True, max_length=200, help_text='')
countryprefix = models.CharField(max_length=200, help_text='')
cityprefix = models.CharField(max_length=200, help_text='')
number = models.CharField(max_length=200, help_text='')
extensionsfrom = models.CharField(max_length=200, help_text='')
extensionstill = models.CharField(max_length=200, help_text='')
portscount = models.CharField(max_length=200, help_text='')
def get_absolute_url(self):
return reverse('context-detail', args=[str(self.name)])
def my_get_absolute_url(self):
return reverse('my-context-detail', args=[str(self.name)])
def __str__(self):
return self.name
views.py:
#!/usr/bin/python3
from django.contrib.auth.mixins import LoginRequiredMixin, PermissionRequiredMixin
from django.contrib.auth.models import Permission
from catalog.models import Extension, Context
from django.shortcuts import render
from django.urls import reverse_lazy
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from django.views.generic.detail import DetailView
from django.views.generic.list import ListView
class ExtensionCreate(LoginRequiredMixin, PermissionRequiredMixin, CreateView):
model = Extension
fields = '__all__'
permission_required = 'catalog.add_extension'
class ExtensionUpdate(LoginRequiredMixin, PermissionRequiredMixin, UpdateView):
model = Extension
fields = '__all__'
permission_required = 'catalog.change_extension'
class ExtensionDelete(LoginRequiredMixin, PermissionRequiredMixin, DeleteView):
model = Extension
success_url = reverse_lazy('extensions')
permission_required = 'catalog.delete_extension'
urls.py:
#!/usr/bin/python3
from . import views
from django.urls import path
urlpatterns = [
path('', views.index, name='index'),
path('extensions/', views.ExtensionListView.as_view(), name='extensions'),
path('extension/<str:pk>', views.ExtensionDetailView.as_view(), name='extension-detail'),
path('extension/create/', views.ExtensionCreate.as_view(), name='extension-create'),
path('extension/<str:pk>/update/', views.ExtensionUpdate.as_view(), name='extension-update'),
path('extension/<str:pk>/delete/', views.ExtensionDelete.as_view(), name='extension-delete'),
path('myextensions/', views.ExtensionsByUserListView.as_view(), name='my-extensions'),
path('myextension/<str:pk>', views.ExtensionsByUserDetailView.as_view(), name='my-extension-detail'),
path('contexts/', views.ContextListView.as_view(), name='contexts'),
path('context/<str:pk>', views.ContextDetailView.as_view(), name='context-detail'),
path('context/create/', views.ContextCreate.as_view(), name='context-create'),
path('context/<str:pk>/update/', views.ContextUpdate.as_view(), name='context-update'),
path('context/<str:pk>/delete/', views.ContextDelete.as_view(), name='context-delete'),
path('mycontexts/', views.ContextByUserListView.as_view(), name='my-contexts'),
path('mycontext/<str:pk>', views.ContextByUserDetailView.as_view(), name='my-context-detail'),
]
template:
{% extends "base_generic.html" %}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Submit">
</form>
{% endblock %}
In this is how it looks like:
The Username is always the same as one of the contexts.
Only Staff User can create a new Context and new Users.
The users then should add their extensions.
While employees should be able to select the context when creating a new extension, customers should only be able to see or select their context in the list.
Therefore it is needed to filter the Select-attribute for non-staff members so that only the user's context is visible, which is equal to his Username.
Alternatively, I want to prepopulate and hide the context form field with the Username (=own context)
How can I do either of this in the easiest way possible?
Here is what I tried myself so far:
Like described in this how-to: https://developer.mozilla.org/en-US/docs/Learn/Server-side/Django/Forms#Renew-book_form_using_a_Form_and_function_view I Defined a Form in forms.py:
#!/usr/bin/python3
from django import forms
class MyExtensionCreateForm(forms.Form):
# username = forms.CharField(help_text="")
# callerid = forms.CharField(help_text="")
# context = forms.CharField(help_text="")
firstname = forms.CharField(help_text="")
lastname = forms.CharField(help_text="")
extension = forms.CharField(help_text="")
password = forms.CharField(help_text="")
def clean_data(self):
data = self.cleaned_data
# Remember to always return the cleaned data.
return data
I then added the folowing to the views.py:
from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404
from django.urls import reverse
from catalog.forms import MyExtensionCreateForm
def MyExtensionCreate(request):
# extension = get_object_or_404(Extension)
# If this is a POST request then process the Form data
if request.method == 'POST':
# Create a form instance and populate it with data from the request (binding):
form = MyExtensionCreateForm(request.POST)
# Check if the form is valid:
if form.is_valid():
# process the data in form.cleaned_data as required
form.firstname = form.cleaned_data['firstname']
form.lastname = form.cleaned_data['lastname']
form.extension = form.cleaned_data['extension']
form.password = form.cleaned_data['password']
# Prepopulated Fields
form.context = request.user
form.callerid = str(form.cleaned_data['firstname'])+" "+str(form.cleaned_data['lastname'])+" "+str(form.cleaned_data['extension'])
form.username = str(request.user)+"_"+str(form.cleaned_data['extension'])
form.save()
# redirect to a new URL:
return HttpResponseRedirect(reverse('my-extensions'))
# If this is a GET (or any other method) create the default form.
else:
form = MyExtensionCreateForm({'context': request.user})
context = {
'form': form,
}
return render(request, 'catalog/extension_form-by-user.html', context)
# class MyExtensionCreate(LoginRequiredMixin, CreateView):
# model = Extension
# fields = '__all__'
# form_class = MyExtensionCreateForm
Then I added a new URL to URL patterns in urls.py and added the new Link to the base_generic.html
path('myextensions/create/', views.MyExtensionCreate, name='my-extension-create'),
<li>Add Extension</li>
I can view the form, and if I add the context field to the visible form fields, I can see that the context will initially fill with the logged-in username (See First Picture Below). But as soon as I submit the form I'll get Error's no matter what I try, So basically the GET is working, but the POST isn't really.
See Folowing Pictures:
I was able to Filter the Choices by the Username in the ModelForm but since then I'm not able to save the form to the database anymore.
Here is the new Stack Post with the Resolution of this Post here and the new Question/Problem.
Django: saving the Form does not work (The ModelForm Filters the ForeignKey choices by request.user)

Django pagination - how to redirect back to a ListView and a page number

In a ListView I render a table using the Paginator with paginate_by = 5.
In each row I have a Button that opens the UpdateView.
After a successful update I'm back on my ListView but always on the first page.
How can I change the success_url so that I'm back on the page number, from where I opened the UpdateView?
Is there a proper way in Django to solve this?
#views.py
class Orders_list(ListView):
model = Order
context_object_name = "orders"
queryset = Order.objects.all()
paginate_by = 5
template_name = "orders/orders_list.html"
#http://127.0.0.1:8000/orders/?page=4
class Orders_update(UpdateView):
model = Order
form_class = Order_UpdateForm
template_name = "orders/orders_update.html"
#success_url = '../../../orders/'
success_url = reverse_lazy('orders')
#urls.py
urlpatterns = [
url(r'^orders/$', Orders_list.as_view()),
url(r'^orders/detail/(?P<pk>\d+)/$', Orders_detail.as_view()),
]
#forms.py
class Order_UpdateForm(forms.ModelForm):
class Meta:
model = Order
fields = ['order_text', 'customer', 'cyclist']
def __init__(self, *args, **kwargs):
super(Order_UpdateForm, self).__init__(*args, **kwargs)
self.fields['cyclist'].queryset = Cyclist.objects.filter(active=True)
#models.py
class Order(models.Model):
customer = models.ForeignKey('Customer')
cyclist = models.ForeignKey('Cyclist')
order_text = models.CharField(max_length=200)
pick_up = models.CharField(max_length=200)
destination = models.CharField(max_length=200)
created_date = models.DateTimeField(default=timezone.now)
changed_date = models.DateTimeField(blank=True, null=True)
def created(self):
self.changed_date = timezone.now()
self.save()
def __str__(self):
return self.order_text
class Customer(models.Model):
company_name = models.CharField(max_length=200)
created_date = models.DateTimeField(default=timezone.now)
changed_date = models.DateTimeField(blank=True, null=True)
def created(self):
self.changed_date = timezone.now()
self.save()
def __str__(self): return self.company_name
class Cyclist(models.Model):
lastname = models.CharField(max_length=20)
firstname = models.CharField(max_length=20)
created_date = models.DateTimeField(default=timezone.now)
changed_date = models.DateTimeField(blank=True, null=True)
active = models.BooleanField(default=True)
def created(self):
self.changed_date = timezone.now()
self.save()
def __str__(self):
return self.lastname
Update
I found a way to solve this (may be). But I still miss something.
What I did so far:
In the ListView template I extended the Url...
window.open("{{ request.path }}update/" + id + "/?page=" + {{ page_obj.number }});
...then in the view I overwrote the get_context_data method...
#views.py
class Orders_update(UpdateView):
model = Order
form_class = Order_UpdateForm
template_name = "orders/orders_update.html"
page = "1" # Assigned
def get_context_data(self, **kwargs):
context = super(Orders_update, self).get_context_data(**kwargs)
page = self.request.GET.get('page') # Modified
return context
success_url = '../../../orders/?page=' + str(page) # Should be used here to redirect to a specific page on a ListView
...but now...how can I access the variable page in views.py in the success_url?
I think this is something very basic (hopefully) and I think I don't understand how scope works in Python.
This is what finally worked for me. Basically I just overwrote the get_success_url to have access to the page parameter from the request.
class Orders_update(UpdateView):
model = Order
form_class = Order_UpdateForm
template_name = "orders/orders_update.html"
def get_success_url(self):
global page
page = self.request.GET.get('page')
return reverse_lazy('orders')
I just got finished pulling my hair out with this same problem. Here is what I did. Start in views.py in your ListView, Order_list, and add context data:
def get_context_data(self, **kwargs):
kwargs['current_page'] = self.request.GET.get('page', 1)
return super().get_context_data(**kwargs)
The above code passes only the current page number into the ListView template.
Then go to the corresponding list view template and add the following in the object list loop:
{% if is_paginated %}
<a href="{% url 'order_update' pk=order.pk %}?next={{current_page}}">Update
</a>
{% else %}
<a href="{% url 'order_update' pk=order.pk %}">Update
{% endif %}
The if statement allows the page to work without pagination. Note the ?next={{current_page}} variable. The "current_page" comes from the context_data in the view. This bit passes the current page number to the "next" variable in the url of the page in the anchor tag. This is what my update view url looks like with this variable in it: http://localhost:8000/food/406/update?next=1. See the ?next=1 in the url.
Next, go back to views.py to your UpdateView, Order_update, and change the success url with this code:
def get_success_url(self, **kwargs):
if self.request.GET.get('next'):
return f"/orders?page={self.request.GET.get('next', 1)}"
else:
return '/orders'
You'll have to remove the success_url declaration in this view. The def get_success_url function wants a string. Reverse seems to work well, but a formatted string will not work in a redirect() statement. It needs to be a plain string. See TypeError: quote_from_bytes() expected bytes after redirect for more on that.
If I broke that down too much, I'm sorry. I don't mean to insult your knowledge. I'm still learning and I wrote this the way I would have needed it earlier today before I figured this out.

How do I prepopulate a form with values from a database in Django?

I'm writing what should be a very simple todo app. The problem is that the edit view is giving me fits! I'm trying to populate a form with data from the database, and it's just not doing the right thing. I've tried the info from this page, but the translation into class-based views must have broken something, or I'm just not using the right kind of form.
Here's the code for the model:
class Todo(models.Model):
id = models.AutoField(primary_key=True)
todo = models.CharField(max_length=255, unique=True)
todo_detail = models.TextField(default='')
date_created = models.DateField(default=timezone.now())
estimated_completion = models.DateTimeField(default=timezone.now())
maybe_completed = models.BooleanField("Completed?", default=False)
def __unicode__(self):
return self.todo
The view code, the commented out bit is from the link:
class TodoEditView(FormView):
model = Todo
form_class = TodoEditForm
template_name = 'todo_edit.html'
#def get(self, request, *args, **kwargs):
# form = self.form_class()
# form.fields['todo'].queryset = Todo.objects.get(id=self.kwargs['pk'])
# form.fields['todo_detail'].queryset = Todo.objects.get(
# id=self.kwargs['pk'])
# form.fields['date_created'].queryset = Todo.objects.get(
# id=self.kwargs['pk'])
# form.fields['estimated_completion'].queryset = Todo.objects.get(
# id=self.kwargs['pk'])
# form.fields['maybe_completed'].queryset = Todo.objects.get(
# id=self.kwargs['pk'])
# template_vars = RequestContext(request, {
# 'form': form
# })
# return render_to_response(self.template_name, template_vars)
def get_context_data(self, **kwargs):
context = super(TodoEditView, self).get_context_data(**kwargs)
context['todo'] = Todo.objects.get(id=self.kwargs['pk'])
return context
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
if form.is_valid():
todo = request.POST['todo']
todo_detail = request.POST['todo_detail']
estimated_completion = request.POST['estimated_completion']
date_created = request.POST['date_created']
t = Todo(todo=todo, todo_detail=todo_detail,
estimated_completion=estimated_completion,
date_created=date_created)
t.save()
return redirect('home')
The form code:
class TodoEditForm(forms.ModelForm):
class Meta:
model = Todo
exclude = ('id', )
And the template code:
{% extends 'todos.html'%}
{% block content %}
<form method="post" action="{% url 'add' %}">
<ul>
{{ form.as_ul }}
{% csrf_token %}
</ul>
{{todo.todo}}
</form>
{% endblock %}
What the heck am I doing wrong?
You should use an UpdateView, not a FormView. That will take care of prepopulating your form.
Also note you don't need any of the logic in the post method - that is all taken care of by the generic view class.

Categories

Resources