I'm using Django version 2.1.
I want to create this type of URL Path in my Project:
www.example.com/bachelor/germany/university-of-frankfurt/corporate-finance
Is it possible to do it in Django?
Yes, say for example that you have a slug for an Author, and one for a Book, you can define it as:
# app/urls.py
from django.urls import path
from app.views import book_details
urlpatterns = [
path('book/<slug:author_slug>/<slug:book_slug>/', book_details),
]
Then the view looks like:
# app/views.py
from django.http import HttpResponse
def book_details(request, author_slug, book_slug):
# ...
return HttpResponse()
The view thus takes two extra parameters author_slug (the slug for the author), and book_slug (the slug for the book).
If you thus query for /book/shakespeare/romeo-and-juliet, then author_slug will contains 'shakespeare', and book_slug will contain 'romeo-and-juliet'.
We can for example look up that specific book with:
def book_details(request, author_slug, book_slug):
my_book = Book.objects.get(author__slug=author_slug, slug=book_slug)
return HttpResponse()
Or in a DetailView, by overriding the get_object(..) method [Django-doc]:
class BookDetailView(DetailView):
model = Book
def get_object(self, queryset=None):
super(BookDetailView, self).get_object(queryset=queryset)
return qs.get(
author__slug=self.kwargs['author_slug'],
slug=self.kwargs['book_slug']
)
or for all views (including the DetailView), by overriding the get_queryset method:
class BookDetailView(DetailView):
model = Book
def get_queryset(self):
qs = super(BookDetailView, self).get_queryset()
return qs.filter(
author__slug=self.kwargs['author_slug'],
slug=self.kwargs['book_slug']
)
I Implemented very simple DetailView in Django 1.9.5:
class PostDetailView(DetailView):
Model = Post
template_name = "post/detail.html"
urls.py
from django.conf.urls import url
from chacha_dabang.views import *
urlpatterns = [
url(r'^$', PostListView.as_view(), name="post_list"),
url(r'^new/$', post_new, name="post_new"),
url(r'^(?P<pk>\d+)/$', PostDetailView.as_view(), name="post_detail"),
url(r'^(?P<pk>\d+)/edit$', post_edit, name="post_edit"),
url(r'^(?P<pk>\d+)/delete$', post_delete, name="post_delete"),
url(r'^(?P<pk>\d+)/comment/new/$', comment_new, name="comment_new"),
url(r'^(?P<pk>\d+)/comment/(?P<comment_pk>\d+)/edit$', comment_edit, name="comment_edit"),
]
Errors :
I don't know why it says I have to override query_set(). (As I Know, DetailView automatically set query according to pk)
If I used Function Based View like below,
def post_detail(request, pk):
post = Post.objects.get(pk=pk)
return render(
request,
'post/detail.html',
{
'post': post,
}
)
It totally works fine. Need your helps.
You capitalized model. It is not Model, it should be model.
as_view only accepts arguments that are already attributes of the class
This make no sense to me since template_name is an attribute. I checked for similar questions but couldn't find an indication of where I went wrong with the code. Here is my code.
urls.py:
from django.conf.urls import url
from .views import PostList
urlpatterns = [
url(r'^$',
PostList.as_view(
template_name='blog/post_list.html'),
name='blog_post_list'),
]
views.py
from django.views.generic import View
from .models import Post
class PostList(View):
def get(self, request):
return render(
request,
'blog/post_list.html',
{'post_list': Post.objects.all()})
The View class, doesn't have a template_name attribute, you either want to use TemplateView, or it would make more sense to use a ListView
class PostList(ListView):
context_object_name = 'post_list'
template_name = 'blog/post_list.html'
def get_queryset(self):
return Post.objects.all()
Note: Either way, since you already set the template name in your view, you don't actually need to include it in the call to as_view anyway
I have a database of both movies and tv shows. I want to filter these like so:
/productions/ = index (all), /productions/films/ = only films, and /productions/series/ = only tv shows
## urls.py
from django.conf.urls import patterns, url
from productions import views
urlpatterns = patterns('',
url(r'^$', views.IndexView.as_view(), name='index'),
url(r'^films/$', views.IndexView.as_view(), name='films'),
url(r'^series/$', views.IndexView.as_view(), name='series'),
)
## views.py
from django.shortcuts import render, get_object_or_404
from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse
from django.views import generic
from productions.models import Production, Director
class IndexView(generic.ListView):
template_name = 'productions/index.html'
context_object_name = 'productions_list'
def get_queryset(self):
return Production.objects.order_by('-release')
What would be the best practice for something like this? Create a new method in views.py for each, or could I reuse the main method, and call something like if(productions.is_movie) by somehow parsing the URL segments?
I'd capture the string from url, like this:
urlpatterns = patterns('',
url(r'^(?<query>(films|series|))/$', views.IndexView.as_view(), name='films_series'),
)
Then, in get_queryset() method I'd check whether you need to return all, films or series:
class IndexView(generic.ListView):
template_name = 'productions/index.html'
context_object_name = 'productions_list'
def get_queryset(self):
# analyze `self.kwargs` and decide should you filter or not, just for example:
is_all = self.kwargs['query'] == ''
is_movie = self.kwargs['query'] == 'films'
is_series = self.kwargs['query'] == 'series'
return Production.objects.order_by('-release') # TODO: filter movies or series
Does anyboy know how do I set the desired mimetype when using TemplateView, as in:
urlpatterns = patterns('',
url(r'^test\.txt$', TemplateView.as_view(template_name='staticpages/test.html')),
In this case, I want to set the mimtype as "text/plain"
For Django >= 1.5
TemplateView accepts a content_type argument.
Coping example from #Meilo
urlpatterns = patterns('',
url(r'^test\.txt$', TemplateView.as_view(template_name='staticpages/test.html', content_type='text/plain')),
For Django < 1.5
I think that just calling TemplateView.as_view() is not posible but maybe i missed it (from the source),
but you can do your own class
class TextTemplateView(TemplateView):
def render_to_response(self, context, **response_kwargs):
response_kwargs['content_type'] = 'text/plain'
return super(TemplateView, self).render_to_response(context, **response_kwargs)
You can take a look to:
django.template.response => TemplateResponse
django.views.generic.base => TemplateView
And if you need something more dynamic:
from django.utils.decorators import classonlymethod
class ContentTypeTemplateView(TemplateView):
#classonlymethod
def as_view(cls, content_type='text/plain', **initargs):
setattr(cls, 'content_type', content_type)
return super(ContentTypeTemplateView, cls).as_view(**initargs)
def render_to_response(self, context, **response_kwargs):
response_kwargs['content_type'] = self.content_type
return super(ContentTypeTemplateView, self).render_to_response(context, **response_kwargs)
urlpatterns = patterns('',
url(r'^$', ContentTypeTemplateView.as_view(content_type='text/plain',
template_name='staticpages/test.html'),
name='index'),
)
Using a Mixin
from django.core.exceptions import ImproperlyConfigured
class ContentTypeMixin(object):
content_type = None
def render_to_response(self, context, **response_kwargs):
if not self.content_type:
raise ImproperlyConfigured(
"MimeTypeMixin rquires a definition of content_type")
response_kwargs['content_type'] = self.content_type
return super(ContentTypeMixin, self).render_to_response(context,
**response_kwargs)
class MyTxtView(ContentTypeMixin, TemplateView):
content_type = 'text/plain'
....
In Django 1.5 the content_type argument in the TemplateView adds the same functionality that was in the function-based view before. That makes it easier to set the desired mimetype:
urlpatterns = patterns('',
url(r'^test\.txt$', TemplateView.as_view(template_name='staticpages/test.html', content_type='text/plain')),
I know that you ask for setting a content type with TemplateView, but I will give you different answer which I think that will be more clean and can be used in Django versions lower than 1.5.
url(r'^robots\.txt$', 'django.shortcuts.render', kwargs={
'template_name': 'robots.txt',
'content_type': 'text/plain',
})
With this approach you don't need to import anything or to subclass TemplateView and make ugly overwrites of some methods. You can simply use the old technique with function based views.
If you don't want to extend the TemplateView, you can extend the TemplateResponse to set the mimetype:
from django.template.response import TemplateResponse
class TextResponse(TemplateResponse):
def __init__(self, *args, **kwargs):
kwargs['mimetype'] = 'text/plain'
return super(TextResponse, self).__init__(*args, **kwargs)
Then pass it as the template_class to the TemplateView
urlpatterns = patterns('django.views.generic.simple',
(r'^robots\.txt$', TemplateView.as_view(template_name='robots.txt', response_class=TextResponse)),
)
The best way to do it is to subclass TemplateView and override the render_to_response() method:
class StaticPagesTest(TemplateView):
template_name = 'staticpages/test.html'
def render_to_response(self, context, **kwargs):
return super(StaticPagesTest, self).render_to_response(context,
mimetype='text/plain', **kwargs)
I know this is solved for 1.5, but the application I am working in is 1.4.
I had an issue with two url patterns in a row using sacabuche's answer:
url(r'^playlist1\.m3u$', ContentTypeTemplateView.as_view(template_name='playlist1.m3u', content_type='audio/x-mpegurl')),
url(r'^playlist2\.pls$', ContentTypeTemplateView.as_view(template_name='playlist2.pls', content_type='audio/x-scpls'))
I found playlist1 would return the correct template, but with playlist2's content type! Playlist2 was ok. Adding a 3rd url pattern with a content-type of 'foo' would cause all playlist views to return with content-type 'foo'.
I ended up using the render method instead with good results:
urls:
url(r'^playlist1\.m3u$', 'content_type_to_template', {'template_name': 'playlist1.m3u', 'content_type': 'audio/x-mpegurl'}),
url(r'^playlist2\.pls$', 'content_type_to_template', {'template_name': 'playlist2.pls', 'content_type':'audio/x-scpls'})
views:
from django.shortcuts import render
def content_type_to_template(request, template_name='', content_type='text/plain'):
return render(request, template_name, content_type=content_type)
A simple example of how change content type of a TemplateView:
#views.py
from django.views.generic import TemplateView
class HomeView(TemplateView):
template_name = "home/index.html"
content_type = 'text/html'
# urls.py
url(r'^home/$', HomeView.as_view(), name='home_page'),
url(r'^test/(?P<template>.*)', lambda request, template: TemplateView.as_view(template_name=template)(request)),