I have a ListView class in view.py
from django.shortcuts import get_object_or_404, render
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.views import generic
from entertainment.models import Entertainmentblog
class ListView(generic.ListView):
template_name = 'entertainment/index.html'
context_object_name = 'latest_article_list'
slug = None
id = None
def get_queryset(self):
return Entertainmentblog.objects.order_by('-posted')[:25]
class DetailView(generic.DetailView):
model = Entertainmentblog
template_name = 'entertainment/article.html'
and I'm using this view to display a list of articles in index.html.But,I would like to show the same list of articles in article.html after the article.I have used the blocks correctly but,it won't show any articles because in ListViewthe template name is index.html.How do I solve this?
Use a Mixin:
class LatestArticleMixin(object):
def get_context_data(self, **kwargs):
context = super(LatestArticleMixin, self).get_context_data(**kwargs)
try:
context['latest_article_list'] = Entertainmentblog.objects.order_by('-posted')[:25]
except:
pass
return context
Then refactor your DetailView:
class DetailView(LatestArticleMixin, generic.DetailView):
model = Entertainmentblog
template_name = 'entertainment/article.html'
In your template if there are articles:
{% if latest_article_list %}
....
{% endif %}
In urls.py you can set template_name as attribute to ListView url entry router.
urls.py
urlpatterns = patterns('',
(r'^a/$', ListView.as_view(model=Poll, template_name="a.html")),
(r'^b/$', ListView.as_view(model=Poll, template_name="b.html")),
)
In views.py even you don't need to set template.
views.py
class ListView(generic.ListView):
model = Poll
Related
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="">
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})
I'm trying to get all the post by a single user and display it using DetailView and I also want to pass the username of the user on the URL.
this is my urls.py:
from django.urls import path
from .views import ProfileDetail
from . import views
urlpatterns = [
path('<str:username>/', ProfileDetail.as_view(), name = 'profile'),
]
this is my views.py:
from django.views.generic import (DetailView)
from django.shortcuts import render , redirect, get_object_or_404
from django.contrib.auth.models import User
from blog.models import Post
class ProfileDetail(DetailView):
model = Post
template_name = 'users/myprofile.html'
context_object_name = 'posts'
paginate_by = 5
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(author = user).order_by('-date_posted')
I have a class-based view almost exactly just like this one and it is working.
This one always gives me this AttributeError: Generic detail view ProfileDetail must be called with either an object pk or a slug in the URLconf.
If you want to display multiple posts, then a ListView with model = Post would be more suitable.
from django.views.generic import ListView
class ProfileDetail(List):
model = Post
template_name = 'users/myprofile.html'
context_object_name = 'posts'
paginate_by = 5
def get_queryset(self):
user = get_object_or_404(User, username=self.kwargs.get('username'))
return Post.objects.filter(author = user).order_by('-date_posted')
Alternatively, if you want to use DetailView, then you should have model = User because you are showing the posts for a single user. You can avoid the "must be called with either an object pk or a slug" error by overriding get_object.
from django.views.generic import DetailView
class ProfileDetail(ListView):
model = User
template_name = 'users/myprofile.html'
def get_object(self):
return User.objects.get(username=self.kwargs['username'])
Then, in the template, you can loop over the user's posts with something like:
{% for post in user.post_set.all %}
{{ post }}
{% endfor %}
Note that by switching to DetailView, you lose the pagination features of ListView.
I have a problam in Django. I'm a amature Django developer. I can't show my "Category" data in Template. here is my code :
models.py
from django.db import models
from taggit.managers import TaggableManager
class Category(models.Model):
title = models.CharField(max_length=40)
def __unicode__(self):
return self.title
class Post (models.Model):
title = models.CharField(max_length=150)
body = models.TextField()
date = models.DateTimeField()
tags = TaggableManager ()
cats = models.ManyToManyField(Category)
def __unicode__ (self):
return self.title
views.py
from django.shortcuts import render
from django.template.loader import get_template
from django.template import Context
from django.http import HttpResponse
from blog.models import Category
render(request, 'index.html', args)
def cats(request):
t = get_template('category.html')
for i in Category.object.get :
html = t.render(Context({'cat': i}))
return HttpResponse(html)
urls.py
from django.conf.urls import include, url, patterns
from django.views.generic import ListView, DetailView
from blog.models import Post, Category
urlpatterns = patterns('blog.views',
url(r'^$',ListView.as_view(
queryset = Post.objects.all().order_by("-date")[:2],
template_name="index.html")),
url(r'^(?P<pk>\d+)$',DetailView.as_view(
model = Post,
template_name="post.html")),
url(r'^(?P<pk>\d+)$','cats'),
)
A part of template "post.html"
#some html code here
{% include "category.html" %}
#another some html code here
This is my category.html
<li>{{cat}}</li>
Thank you.
Your way of writing code is not quite correct. Views are meant for backend codes, urls.py is meant only for url specifications not for writing queries.
views.py
from blog.models import Category
from blog.models import Post
def cats(request):
queryset = Post.objects.all().order_by("-date")[:2]
return render_to_response('category.html',{'queryset': queryset},
context_instance=RequestContext(request))
add this in urls.py
url(r'^cats', 'blog.views.cats', name='cats')
In category.html you can access any field of the object that is passed from views.py (here queryset )
<li>{{querset.title}}</li>
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()]