using django.views.generic.list.ListView in urlpatterns - django

I am new to Django and would greatly appreciate your help.
I have the below mentioned piece of code from an older book about Django. However, django.views.generic.list_detail has been deprecated. Can someone tell me how I could re-write this code with django.views.generic.list.ListView?
from django.conf.urls import patterns, include, url
from cmsproject.cms.models import Story
info_dict = {'queryset': Story.objects.all(), 'template_object_name': 'story'}
urlpatterns = patterns('django.views.generic.list_detail',
url(r'^(?P<slug>[-\w]+)/$', 'object_detail', info_dict, name="cms-story"),
url(r'^$', 'object_list', info_dict, name="cms-home"),
)

Assuming all you want to do is fetch a list of Story model objects, this is one way to write your views.py and urls.py:
In views.py:
from django.views.generic.list import ListView, DetailView
from cmsproject.cms.models import Story
class StoryListView(ListView):
model = Story
template_name = "cms/story_list.html"
class StoryDetailView(DetailView):
model = Story
template_name = "cms/story_detail.html"
template_name depends on where in the project you placed your html files. By setting model = Story, ListView will fetch Story.objects.all(). To customize, filter, add context etc., you can override any of the methods that your class based view inherits from its parent view (ex. in StoryListView you can override ListView methods).
In urls.py
from django.conf.urls import patterns, url
from cmsproject.cms.views import StoryDetailView, StoryListView
urlpatterns = patterns('',
url(r'^(?P<slug>[-\w]+)/$', StoryDetailView.as_view(), name="cms-story"),
url(r'^$', StoryListView.as_view(), name="cms-home"),
)
Think of urls.py as a mapping between the url and the View object. Defining name allows you to refer/link to other views by including name as a parameter to url template tag in the templates.
Some Extremely Useful References:
Effective Django - Class Based Views
CCBV List View
Django Project Generic Display Views
Built-in template Tags and filters -> look under url

Related

Django rest_framework DefaultRouter() class vs normal url

I'm using REST in Django, And I couldn't understand what is the main difference between classic URL and instantiating DefaultRouter() for registering URL by ViewSet.
I have a model:
class Article(models.Model):
title = models.CharField()
body = models.TextField()
author = models.ForeignKey()
Serializing model like this:
from blog.models import Article
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = ['title', 'body', 'author']
View Class:
from blog.models import Article
from rest_framework import viewsets
from .serializers import ArticleSerializer
class ArticleViewSet(viewsets.ModelViewSet):
serializer_class = ArticleSerializer
queryset = Article.objects.all()
and URLS:
router = DefaultRouter()
router.register(r'articles', ArticleViewSet)
urlpatterns = [
path('', include(router.urls)),
]
Is it possible to use classic URL in URLS.py instead of instantiating the object for a ViewSet like this:
urlpatterns = [
path('api/', 'views.someAPI'),
]
I just know HTTP method in ViewSet translate methods to retrieve, list and etc...
The Question is can we use traditional(Classic) URL style in this situation, Should we ?
Thanks for your help.
Well, in a nutshell as a django developer it is notorious how it is hard to deal with normal urls in django in some cases. Every now and again we get confused with the id type of the detail page that in some case are strings or integers with its regex, and so on.
For example:
urlpatterns = [
url(r'^(?P<content_type_name>[a-zA-z-_]+)$', views.content_type, name = 'content_type'),
]
# or
urlpatterns = [
url(r'^(?P<content_type_name>comics|articles|videos)$', views.content_type, name='content_type'),
]
Not mentioning that in almost every case its needed to have two urls like:
URL pattern: ^users/$ Name: 'user-list'
URL pattern: ^users/{pk}/$ Name: 'user-detail'
THE MAIN DIFFERENCE
However, using DRF routers the example above is done automatically:
# using routers -- myapp/urls.py
router.register(r"store", StoreViewSet, basename="store")
How django will understand it:
^store/$ [name='store-list']
^store\.(?P<format>[a-z0-9]+)/?$ [name='store-list']
^store/(?P<pk>[^/.]+)/$ [name='store-detail']
^store/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='store-detail']
See how much job and headache you have saved with a line of code only?
To contrast, according to DRF documentation the routers is a type of standard to make it easy to declare urls. A pattern brought from ruby-on-rails.
Here is what the documentation details:
Resource routing allows you to quickly declare all of the common
routes for a given resourceful controller. Instead of declaring
separate routes for your index... a resourceful route declares them in
a single line of code.
— Ruby on Rails Documentation
Django rest framework documentation:
Some Web frameworks such as Rails provide functionality for
automatically determining how the URLs for an application should be
mapped to the logic that deals with handling incoming requests.
REST framework adds support for automatic URL routing to Django, and
provides you with a simple, quick and consistent way of wiring your
view logic to a set of URLs.
For more details follow the django rest framework documentation.

How to solve the Identical URL Pattern Conflict in Django?

while practicing on Django I have faced the following problem of Identical URL Pattern. I have tried but fail to solve.
My project has one apps and the models are
class Category(models.Model):
title = models.CharField(max_length=150)
slug = models.SlugField(unique=True,blank=True, max_length=255)
class Post(models.Model):
title = models.CharField(max_length=200)
cats = models.ForeignKey(Category, on_delete=models.CASCADE)
slug = models.SlugField(unique=True,blank=True, max_length=255)
My urls.py
from django.contrib import admin
from django.urls import path
from post import views as apps
urlpatterns = [
path('admin/', admin.site.urls),
path('<slug:slug>/', apps.categoryView, name='category'),
path('<slug:slug>/',apps.PostView, name='calc_detail'),
]
Problem:
When I Put '/' in the second line of the urlpattern for Category View, category View works but post View doesn't work (404).
If remove '/' urlpattern for CategoryView , then post view Works but Category Views shows 404.
How should I configure these urls. I am using function based views
There's really no way to handle these except using different patterns or handling both in the same view, I would suggest different patterns:
from django.contrib import admin
from django.urls import path
from post import views as apps
urlpatterns = [
path('admin/', admin.site.urls),
path('pattern1/<slug:slug>/', apps.categoryView, name='category'),
path('pattern2/<slug:slug>/',apps.PostView, name='calc_detail'),
]
Also have you thought what would happen if a post and a category ended up having the same slug? Best to have different patterns.
About putting '/' making some url work and not it is because django appends slashes by default to each url, so if you really want to do this (NOT RECOMMENDED) you may set APPEND_SLASH = False in your setting and have one url with a slash and other without.

The smartest way to redirect in django without creating view

The task is simple:
If user visits site root then:
if user is authenticated then:
redirect to /dashboard/
else:
redirect to settings.LOGIN_URL
There are many ways to implement that, but I wonder if there is such way in which I do need to use only urls.py.
I found a solution with RedirectView login_required(RedirectView.as_view(url=my_url)), however then I can only write static my_url instead of reverse(), which is not flexible.
You could use reverse_lazy (Django 1.4) in you url configuration, like so:
from django.conf.urls.defaults import url, patterns
from django.core.urlresolvers import reverse_lazy
from django.shortcuts import redirect
urlpatterns = patterns('',
url(r'^/$', lambda request: return redirect(reverse_lazy('url_name')),
)
Another possibility is to define LOGIN_URL using reverse_lazy, so you could continue to use settings.LOGIN_URL in your redirects.
Code is untested, might have a typo somewhere.
You just need to mixin LoginRequired to your view. You can find an example of the mixin here:
http://djangosnippets.org/snippets/2442/
Then where you define that view, you just do:
class RedirectView(LoginRequiredMixin, DetailView):
....
Or whatever Class Based View you're inheriting from. Hope that helps!

How to use url pattern named group with generic view?

I'm trying to display blog records for particular author using generic view:
urlpatterns = patterns('',
url(r'^blog/(?P<uid>[\d+])/$', ListView.as_view(
queryset=Blog.objects.filter(published=True, author=uid),
), name='blog_list'),
But I get NameError: name 'uid' is not defined
Is it possible to use urlconf named groups this way?
You need to create your own implementation of ListView like so:
class BlogListView(ListView):
model = Blog
def get_queryset(self):
return super(BlogListView, self).get_queryset().filter(
published=True, author__id=self.kwargs['uid'])
and then use it in your URLconf:
urlpatterns = patterns('',
url(r'^blog/(?P<uid>[\d+])/$', BlogListView.as_view(),
name='blog_list'),
The documentation for class-based generic views is, in my opinion, not quite up to scratch with the rest of the Django project yet - but there are some examples which show how to use ListView in this way:
https://docs.djangoproject.com/en/1.3/topics/class-based-views/#viewing-subsets-of-objects

How to get current application in Django

I'm using the Django 1.3 alpha to create a project with two applications. I use 1.3 because of the class-based views. For these two applications I have a set of common base view-classes, that are inherited by the actual views in the applications. In the base class, is there a way to find out what application the view is "called" from? E.g. can I use the URL to get the "current" application?
If you are inheriting from the generic list and detail views that django provides you could access self.model to gain access to the model that the view displays information about, otherwise you will probably have use django's resolve(): resolve(self.request.path).
You also could make your own View subclass that you call with a keyword of your choice:
# views.py
from django.views.generic.base import View
class MyView(View):
app_name = None
# urls.py
from django.conf.urls.defaults import *
from some_app.views import MyView
urlpatterns = patterns('',
(r'^myview/', MyView.as_view(app_name='app_name')),
)
Then you should be able to access it through self.app_name.