Django, redirected url is not proper - python

I am new in Django and I am creating a form,
#forms.py
class SetupForm(forms.Form):
pass
and corresponding view is
#views.py
class SetupView(FormView):
template_name = 'template/setup_form.html'
form_class = SetupForm
success_url = 'template/base_collect'
def form_valid(self,form):
return super(SetupView, self).form_valid(form)
def base(request):
return render_to_response('template/base_collect.html')
and url patterns are,
#urls.py
urlpatterns = patterns(url(r'^setup-form/$', views.SetupView.as_view(), name='setup-form'),
url(r'^base_collect/$', views.base),)
After submitting the form, the redirected URL is shown as
http://127.0.0.1:8000/template/setup-form/template/base_collect.html instead of http://127.0.0.1:8000/template/base_collect.
What am I missing here? How to achieve later?

Modify as below :
#urls.py
urlpatterns = patterns(
url(r'^setup-form/$', views.SetupView.as_view(), name='setup-form'),
url(r'^base_collect/$', views.base, name = 'base-form'),
)
And
Replace
success_url = 'template/base_collect.html'
with
success_url = reverse('base-form')
Import
from django.core.urlresolvers import reverse
In your views.py

You mistakenly put the template name in the success_url property for the class. You also don't need the other two methods, as they aren't adding any functionality to what the default FormView class provides.
class SetupView(FormView):
template_name = 'template/setup_form.html'
form_class = SetupForm
success_url = 'collect/base_collect' # the URL goes here
#def form_valid(self,form):
# return super(SetupView, self).form_valid(form)
#def base(request):
# return render_to_response('template/base_collect.html')

Related

Page not found After using UpdateView class

I have a project that requires an update form, I am using the Django generic views, specifically the UpdateView.
I Think this is an error with the URL, but I dont find where it is.
Error also refers to the url The current path, actualizar_empleado/, didn’t match any of these.
BTW if you see something else in my code that should be corrected, or that I can add a better practice, feel free to let me now.
My code is the following:
Views.py
from django.shortcuts import render, get_object_or_404
from django.views.generic import (
CreateView,
DetailView,
ListView,
UpdateView,
ListView,
DeleteView
)
from . models import EmployeesInfo
from . forms import EmployeeForm
class EmployeeCreate(CreateView):
form_class = EmployeeForm
template_name = 'employeeCreate.html'
success_url = '/lista_empleados/'
def form_valid(self, form):
print(form.cleaned_data)
return super().form_valid(form)
class EmployeeList(ListView):
model = EmployeesInfo
template_name = 'employeeList.html'
success_url = 'lista-empleados/exitoso'
class EmployeeDetail(DetailView):
model = EmployeesInfo
template_name = 'employeeDetail.html'
success_url = 'detalle-empleado/exitoso'
def get_object(self):
id_ = self.kwargs.get("pk")
return get_object_or_404(EmployeesInfo, pk=id_)
class EmployeeUpdate(UpdateView):
form_class = EmployeeForm
queryset = EmployeesInfo.objects.all()
template_name = 'employeeUpdate.html'
success_url = '/listaempleados/'
def form_valid(self, form):
print(form.cleaned_data)
return super().form_valid(form)
def get_object(self):
id_ = self.kwargs.get("pk")
return get_object_or_404(EmployeesInfo, pk=id_)
urls.py
from django.contrib import admin
from django.urls import path, re_path
from . views import EmployeeCreate, EmployeeList, EmployeeDetail, EmployeeUpdate
urlpatterns = [
path('crear_empleado/', EmployeeCreate.as_view(), name = 'createmp'),
path('lista_empleados', EmployeeList.as_view(), name = 'listemp'),
path('detalle_empleado/<int:pk>', EmployeeDetail.as_view(), name = 'showemp'),
path('actualizar_empleado/<int:pk>', EmployeeUpdate.as_view(), name = 'updatemp'),
]
employeeUpdate.html
<body>
{%extends 'base.html'%}
{%block content%}
<div class="row">
<div class="col-md-9">
<div class="card card-body">
<form method="PUT" action="." enctype="multipart/form-data">
{%csrf_token%}
<table>
{{form.as_table}}
</table>
<input type="submit" value="Actualizar">
</form>
</div>
</div>
</div>
{%endblock%}
</body>
Use django.urls.reverse for resolving urls, don't put raw paths, it's harder to maintain. Also, your success_url in is wrong in the post (I'm guessing it's a wrong copy/paste). Anyway, you should use reverse for getting that URL. You also need to add a PK because your URL contains a PK.
As Arif Rasim mentioned in comments, instead of queryset, define model in your EmployeeUpdate view.
Your use of get_object is correct but not necessary as this is done by the views (SingleObjectMixin). See more here
Use absolute imports (recommended by PEP8). It's not wrong or incorrect to use relative imports but absolute imports give more precise error messages.
The action attribute in your form in the HTML template should be empty (right now it's ".") if you want to submit the form to the current page (recommended for flexibility).
Specifying enctype in your form is not necessary if the form does not handle file fields. Let the browser and framework take care of it.
The method attribute in your form can only take values of "GET" and "POST".
# ...
# Using absolute imports, yourapp is the package name of your app
from yourapp.models import EmployeesInfo
from yourapp.forms import EmployeeForm
class EmployeeCreate(CreateView):
form_class = EmployeeForm
template_name = 'employeeCreate.html'
# Here you can use reverse_lazy (reverse doesn't work) to keep things
# shorter (as opposed to using get_success_url method)
success_url = reverse_lazy('createmp') # shouldn't it be "createemp"?
def form_valid(self, form):
print(form.cleaned_data)
return super().form_valid(form)
class EmployeeList(ListView):
model = EmployeesInfo
template_name = 'employeeList.html'
success_url = reverse_lazy('listemp')
class EmployeeDetail(DetailView):
model = EmployeesInfo
template_name = 'employeeDetail.html'
# Unnecessary - done by DetailView
# def get_object(self):
# id_ = self.kwargs.get("pk")
# return get_object_or_404(EmployeesInfo, pk=id_)
def get_success_url(self):
# NOTE: don't forget the comma after the PK as "args"
# param should be a tuple
return reverse('showemp', args=(self.kwargs['pk'],))
class EmployeeUpdate(UpdateView):
form_class = EmployeeForm
model = EmployeeInfo
def form_valid(self, form):
print(form.cleaned_data)
return super().form_valid(form)
# Unnecessary - done by UpdateView
# def get_object(self):
# id_ = self.kwargs.get("pk")
# return get_object_or_404(EmployeesInfo, pk=id_)
def get_success_url(self):
# NOTE: don't forget the comma after the PK as "args"
# param should be a tuple
# BTW, Shouldn't this be "updateemp"?
return reverse('updatemp', args=(self.kwargs['pk'],))
<!-- employeeUpdate.html -->
<form method="POST" action="">

I have a problem with my Django URL pattern naming

For whatever reason when I give a name="..." - argument to a URL pattern and I want to refer to it by using the name it does not seem to work.
That's my 'webapp/urls.py' file:
from django.urls import path
from .views import PostListView, PostDetailView, PostCreateView
from .import views
app_name = 'webapp'
urlpatterns = [
path("", PostListView.as_view(), name="webapphome"),
path("post/<int:pk>/", PostDetailView.as_view(), name="postdetail"),
path('post/new/', PostCreateView.as_view(), name="postcreate"),
path("about/", views.About, name="webappabout"),
]
And that's my 'webapp/views.py' file:
from django.shortcuts import render
from django.views import generic
from django.views.generic import ListView, DetailView, CreateView
from .models import Post
def Home(request):
context = {
'posts': Post.objects.all() }
return render(request, "webapp/home.html", context)
class PostListView(ListView):
model = Post
template_name = 'webapp/home.html'
context_object_name = 'posts'
ordering = ['-date']
class PostDetailView(DetailView):
model = Post
template_name = 'webapp/detail.html'
class PostCreateView(CreateView):
model = Post
fields = ['title', 'content']
template_name = 'webapp/postform.html'
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def About(request):
return render(request, "webapp/about.html", {'title': 'About'})
And that's my 'webapp/models.py' file:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
class Post(models.Model):
title = models.CharField(max_length=50)
content = models.TextField(max_length=300)
date = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("postdetail", kwargs={'pk': self.pk})
As you can see, I'm using the name 'postdetail' I've given to the URL path from PostDetailView but however I receive an Error like this when I create a new Post:
NoReverseMatch at /post/new/
Reverse for 'postdetail' not found. 'postdetail' is not a valid view function or pattern name.
Request Method: POST
Exception Type: NoReverseMatch
I'd suggest you read the Namespace section in Django Documentation, here
The issue is due to you having an app_name = 'webapp' but not using it with postdetail
The objective of app_name is to ensure you know where to redirect if you have two url in different apps with same names.
change
return reverse("postdetail", kwargs={'pk': self.pk})
to
return reverse("webapp:postdetail", kwargs={'pk': self.pk})

Django TemplateHTMLRenderer - Rendering a HTML view

I'm trying to render some objects from my model to HTML. I initially thought this was going to be straightforward (and it likely is) but I'm coming up against numerous errors.
In this project I have built some API views that work fine (HintsListApiView and HintsRudView work). But I'd ideally like to use the API to produce a regular - read only HTML page that I can then style as I wish - my HTMLAPIView. I'm trying to use TemplateHTMLRenderer, but am coming up against errors. All I want is to get the text attribute to show up in HTML. The actual texts are just a sentence long each.
These are my files:
models.py:
from django.db import models
from rest_framework.reverse import reverse as api_reverse
class Hints(models.Model):
text = models.TextField(max_length=255)
author = models.CharField(max_length=20)
timestamp = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.text)
def timestamp_pretty(self):
return self.timestamp.strftime('%b %d %Y')
def get_api_url(self, request=None):
return api_reverse("api-hints1:hints-rud", kwargs={'pk': self.pk}, request=request)
views.py:
class HTMLAPIView(generics.RetrieveAPIView):
lookup_field = 'pk'
serializer_class = HTMLSerializer
renderer_classes = (TemplateHTMLRenderer,)
def get_queryset(self):
queryset = Hints.objects.value('text')
return Response({'queryset': queryset}, template_name='base.html')
class HintsListApiView(mixins.CreateModelMixin, generics.ListAPIView):
lookup_field = 'pk'
serializer_class = HintsSerializer
def get_queryset(self):
qs = Hints.objects.all()
query = self.request.GET.get("q")
if query is not None:
qs = qs.filter(
Q(text__icontains=query)|
Q(author__icontains=query)
).distinct()
return qs
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
def get_serializer_context(self, *args, **kwargs):
return {"request": self.request}
class HintsRudView(generics.RetrieveUpdateDestroyAPIView):
lookup_field = 'pk'
serializer_class = HintsSerializer
def get_queryset(self):
return Hints.objects.all()
def get_serializer_context(self, *args, **kwargs):
return {"request": self.request}
serializers.py:
class HintsSerializer(serializers.ModelSerializer):
url = serializers.SerializerMethodField(read_only=True)
class Meta:
model = Hints
fields = [
'url',
'id',
'text',
'author',
'timestamp_pretty'
]
read_only_fields = ['timestamp_pretty', 'id']
def get_url(self, obj):
request = self.context.get("request")
return obj.get_api_url(request=request)
class HTMLSerializer(serializers.ModelSerializer):
class Meta:
model = Hints
fields = [
'text',
]
read_only_fields = ['text',]
root urls.py:
from django.contrib import admin
from django.conf.urls import url, include
from rest_framework import routers, serializers, viewsets
urlpatterns = [
url('admin/', admin.site.urls),
url(r'^api/hints/', include(('hints1.api.urls', 'api'), namespace='api-hints1')),
url(r'^api-auth/', include('rest_framework.urls')),
]
urls.py:
from .views import HintsRudView, HintsListApiView, HTMLAPIView
from . import views
from django.contrib import admin
from django.conf.urls import url, include
from rest_framework import routers, serializers, viewsets
urlpatterns = [
url(r'^(?P<pk>\d+)$', HintsRudView.as_view(), name='hints-rud'),
url(r'^$', HintsListApiView.as_view(), name='hints-list'),
url(r'^html/', HTMLAPIView.as_view(), name='html' )
]
The errors I've faced were varied, currently I'm encountering AttributeError at /api/hints/html/
Manager object has no attribute 'value'.
I've tried with and without a serializer (because in the documentation it mentions TemplateHTMLRenderer doesn't need one). I think the problem lies within the view.py and the get_queryset function. I've tried various approaches but will get other errors like
TypeError context must be a dict rather than QuerySet.
Any help that can be provided will be greatly appreciated!
Thanks!
So I managed to do what I wanted. But to be honest I'm not completely sure why it worked, so if anyone can provide clarification, please do.
I changed my HTMLAPIView in views.py to a Viewset:
class HTMLAPIView(viewsets.ViewSet):
renderer_classes = [TemplateHTMLRenderer]
template_name = 'base.html'
serializer_class = HTMLSerializer
def list(self, request):
queryset = Hints.objects.order_by('pk')
return Response({'queryset': queryset})
I then got an error in my urls.py but was able to fix that by including the dictionary portion in the .as_view()
url(r'^html/', HTMLAPIView.as_view({'get': 'list'}), name='html' )
The reason why I'm not sure why this worked is that it returns my text attribute from my model as I wanted but I don't see where I specified that that's the correct attribute. Is it just because it is the first one?
I also tried commenting out the serializer and it still worked fine, so I concluded that wasn't the reason for the results.

Why am I getting recursion error?

Why am I getting an error when trying to run my server to access the database to see if my code works? While in my projects folder in Terminal, I ran sudo python manage.py runserver to try to run the server but it doesn't work because of the aforementioned error. I've looked around SO but can't find one directly related to my problem.
I'm getting my if() statement is the problem.
The error I'm getting says:
RuntimeError: maximum recursion depth exceeded while calling a Python object
Here's my views.py file:
from .models import Album
from django.shortcuts import render, redirect
from django.contrib.auth import authenticate, login
from django.core.urlresolvers import reverse_lazy
from django.views import generic
from django.views.generic import View
from django.views.generic.edit import CreateView, UpdateView, DeleteView
from .forms import UserForm
class IndexView(generic.ListView):
template_name = 'music/index.html'
context_object_name = 'all_albums'
def get_queryset(self):
return Album.objects.all()
class DetailView(generic.DeleteView):
model = Album
template_name = 'music/detail.html'
class AlbumCreate(CreateView):
model = Album
fields = ['artist', 'album_title', 'genre', 'album_logo']
class AlbumUpdate(UpdateView):
model = Album
fields = ['artist', 'album_title', 'genre', 'album_logo']
class AlbumDelete(DeleteView):
model = Album
success_url = reverse_lazy('music:index')
class UserFormView(View):
form_class = UserForm
template_name = 'music/registration_form'
# display blank form
def get(self, request):
form = self.form_class(None)
return render(request, self.template_name, {'form': form})
def post(self):
form = self.form_class(request.POST)
if form.is_valid():
user = form.save(commit=False)
#cleaned normalized data
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user.set_password(password)
user.save()
Tail of error:
File "/Library/Python/2.7/site-packages/Django-1.11.2-py2.7.egg/django/urls/resolvers.py", line 255, in check
warnings.extend(check_resolver(pattern))
File "/Library/Python/2.7/site-packages/Django-1.11.2-py2.7.egg/django/core/checks/urls.py", line 26, in check_resolver
return check_method()
File "/Library/Python/2.7/site-packages/Django-1.11.2-py2.7.egg/django/urls/resolvers.py", line 172, in check
warnings = self._check_pattern_startswith_slash()
File "/Library/Python/2.7/site-packages/Django-1.11.2-py2.7.egg/django/urls/resolvers.py", line 140, in _check_pattern_startswith_slash
regex_pattern = self.regex.pattern
Here's my forms.py file:
from django.contrib.auth.models import User
from django import forms
class UserForm(forms.ModelForm): # UserForm inherits from forms.
passwords = forms.CharField(widget=forms.PasswordInput)
class Meta: # Information about your class.
model = User # whenevr a creates sign up to your site it's gonna go in same table
fields = ['username', 'email', 'password']
Here's my urls.py file:
from django.conf.urls import include, url
from django.contrib import admin
from django.conf import settings
from django.conf.urls.static import static
app_name = 'music'
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^music/', include('music.urls'))
]
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root = settings.STATIC_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.MEDIA_ROOT)
You didn't pass request into your post method, It should be,
class UserFormView(View):
form_class = UserForm
template_name = 'music/registration_form.html'
def post(self, request, *args, **kwargs):
# blaah
Also, missed the extension of your template.
I think you should use FormView for this and don't need to override post or get method in your View, Because you will get form object in template if user assign form_class in View.You have to override form_valid() method for your custom operations
class UserFormView(FormView):
form_class = UserForm
template_name = 'music/registration_form'
def form_valid(self, form)
user = form.save(commit=False)
username = form.cleaned_data['username']
password = form.cleaned_data['password']
user.set_password(password)
user.save()

How to call form in template when using generic.FormView in django?

This is my form class:
from django import forms
from .models import Category
class AddResource(forms.Form):
CATEGORY_CHOICES = [cat.title for cat in Category.objects.all()]
ResourceTitle = forms.CharField()
Description = forms.Textarea()
Link = forms.URLField()
Categories = forms.MultipleChoiceField(choices=CATEGORY_CHOICES)
This is my view for the form URL:
from django.shortcuts import render
from django.views.generic import View, TemplateView, FormView
from .models import *
from .forms import AddResource
class AddResourceView(FormView):
template_name = 'addRes.html'
form_class = AddResource
success_url = '/'
def get_form(self, form_class):
return form_class()
def form_valid(self, form):
print form
return super(AddResourceView, self).form_valid(form)
The trouble that I'm having is calling this inside templates. How do I call the form that I assigned inside of django-templates?
The FormView supplies a context variable called form that holds your actual form object, so you can use:
{{ form.as_table }}
Or any of the other rendering options that form supplies. See: https://docs.djangoproject.com/en/dev/topics/forms/#displaying-a-form-using-a-template
I found the answer. Its how you include choices in django forms. So CATEGORY_CHOICES has to be a list of tuples, which it was not in this case.
Thus, this simple change made it work:
CATEGORY_CHOICES = [('', cat.title)
for cat in Category.objects.all()]

Categories