Django class-based view: How do I pass additional parameters to the as_view method?
Asked Answered
T

7

114

I have a custom class-based view

# myapp/views.py
from django.views.generic import *

class MyView(DetailView):
    template_name = 'detail.html'
    model = MyModel

    def get_object(self, queryset=None):
        return queryset.get(slug=self.slug)

I want to pass in the slug parameter (or other parameters to the view) like this

MyView.as_view(slug='hello_world')

Do I need to override any methods to be able to do this?

Toadinthehole answered 15/7, 2012 at 18:26 Comment(0)
T
105

Every parameter that's passed to the as_view method is an instance variable of the View class. That means to add slug as a parameter you have to create it as an instance variable in your sub-class:

# myapp/views.py
from django.views.generic import DetailView

class MyView(DetailView):
    template_name = 'detail.html'
    model = MyModel
    # additional parameters
    slug = None

    def get_object(self, queryset=None):
        return queryset.get(slug=self.slug)

That should make MyView.as_view(slug='hello_world') work.

If you're passing the variables through keywords, use what Mr Erikkson suggested: https://mcmap.net/q/188489/-django-class-based-view-how-do-i-pass-additional-parameters-to-the-as_view-method

Toadinthehole answered 15/7, 2012 at 19:4 Comment(5)
Never do import *. Edited your post.Postilion
@Postilion for the enlightenment of future readers, PEP8 says "Wildcard imports (from <module> import ) should be avoided". Should isn't as strong as must and this is an example but yes definitely *should avoid wildcard imports: python.org/dev/peps/pep-0008/#importsToadinthehole
Nothing is a must anywhere, we can break anything we want in any way we want, but pep8 is just recommendation of practices, and in python community it's a rule of thumb to use all of these practices as much as possible to avoid further problems. My linter is always empty when I commit my code :) no matter what.Postilion
What's the value of slug='hello_world' for an actual variable?Xylol
I guess that slug is a class variable and not an instance variable here.Balbinder
C
125

If your urlconf looks something like this:

url(r'^(?P<slug>[a-zA-Z0-9-]+)/$', MyView.as_view(), name = 'my_named_view')

then the slug will be available inside your view functions (such as 'get_queryset') like this:

self.kwargs['slug']
Cyprinid answered 15/7, 2012 at 18:54 Comment(5)
To avoid an exception in case this is an optional parameter: use self.kwargs.get('slug', None)Fistula
Just curious, when/where is this "self.kwargs" populated ? I am looking for the base class function where this is set .Sajovich
In github.com/django/django/blob/master/django/views/generic/… in class View: def as_view(cls, **initkwargs): def view(request, *args, **kwargs):Vincevincelette
Not answering the question.Subdominant
This method is now deprecated, now you can use url('<slug:slug>', MyView.as_view(), name='my_named_view')Karney
T
105

Every parameter that's passed to the as_view method is an instance variable of the View class. That means to add slug as a parameter you have to create it as an instance variable in your sub-class:

# myapp/views.py
from django.views.generic import DetailView

class MyView(DetailView):
    template_name = 'detail.html'
    model = MyModel
    # additional parameters
    slug = None

    def get_object(self, queryset=None):
        return queryset.get(slug=self.slug)

That should make MyView.as_view(slug='hello_world') work.

If you're passing the variables through keywords, use what Mr Erikkson suggested: https://mcmap.net/q/188489/-django-class-based-view-how-do-i-pass-additional-parameters-to-the-as_view-method

Toadinthehole answered 15/7, 2012 at 19:4 Comment(5)
Never do import *. Edited your post.Postilion
@Postilion for the enlightenment of future readers, PEP8 says "Wildcard imports (from <module> import ) should be avoided". Should isn't as strong as must and this is an example but yes definitely *should avoid wildcard imports: python.org/dev/peps/pep-0008/#importsToadinthehole
Nothing is a must anywhere, we can break anything we want in any way we want, but pep8 is just recommendation of practices, and in python community it's a rule of thumb to use all of these practices as much as possible to avoid further problems. My linter is always empty when I commit my code :) no matter what.Postilion
What's the value of slug='hello_world' for an actual variable?Xylol
I guess that slug is a class variable and not an instance variable here.Balbinder
R
19

It's worth noting you don't need to override get_object() in order to look up an object based on a slug passed as a keyword arg - you can use the attributes of a SingleObjectMixin https://docs.djangoproject.com/en/1.5/ref/class-based-views/mixins-single-object/#singleobjectmixin

# views.py
class MyView(DetailView):
    model = MyModel
    slug_field = 'slug_field_name'
    slug_url_kwarg = 'model_slug'
    context_object_name = 'my_model'

# urls.py
url(r'^(?P<model_slug>[\w-]+)/$', MyView.as_view(), name = 'my_named_view')

# mymodel_detail.html
{{ my_model.slug_field_name }}

(both slug_field and slug_url_kwarg default to 'slug')

Reagan answered 19/6, 2013 at 3:17 Comment(1)
should I turn my answer into a wiki answer and add your code to it?Toadinthehole
S
16

You can pass parameters from urls.py https://docs.djangoproject.com/en/1.7/topics/http/urls/#passing-extra-options-to-view-functions

This also works for generic views. Example:

url(r'^$', views.SectionView.as_view(), { 'pk': 'homepage', 'another_param':'?'}, name='main_page'),

In this case the parameters passed to the view should not necessarily be instance variables of the View class. Using this method you don't need to hardcode default page name into YourView model, but you can just pass it as a parameter from urlconf.

Sophi answered 26/3, 2015 at 21:12 Comment(1)
Just updated the link for Django 3.2: docs.djangoproject.com/en/3.2/topics/http/urls/…Notable
H
15

If you want to add an object to the context for the template you can override get_context_data and add to its context. The request is also a part of self in case you need the request.user.

def get_context_data(self, **kwargs):
        context = super(MyTemplateView, self).get_context_data(**kwargs)
        if 'slug' in self.kwargs:
            context['object'] = get_object_or_404(MyObject, slug=self.kwargs['slug'])
            context['objects'] = get_objects_by_user(self.request.user)

        return context
Henotheism answered 8/10, 2012 at 18:24 Comment(1)
What's MyObject?Contentment
T
9

As stated by Yaroslav Nikitenko, if you don't want to hardcode a new instance variable to the View class, you can pass extra options to view functions from urls.py like this:

url(r'^$', YourView.as_view(), {'slug': 'hello_world'}, name='page_name')

I just wanted to add how to use it from the view. You can implement one of the following methods:

# If slug is optional
def the_function(self, request, slug=None):
    # use slug here

# if slug is an optional param among others
def the_function(self, request, **kwargs):
    slug = kwargs.get("slug", None)
    other_param = kwargs.get("other_param", None)

# If slug is required
def the_function(self, request, slug):
    # use slug here
Torsion answered 12/5, 2015 at 19:53 Comment(5)
I wanted to edit this in Yaroslav Nikitenko's answer, but it was rejected, so I made my own because I felt it was the missing information when I needed it.Torsion
@YaroslavNikitenko In hindsight, it was too big for an edit and best as a reply in the form of a new answer.Torsion
@EmileBergeron The initial question was about generic views such as the DetailView class. Could you explain how to use it there?Undergarment
Is there a way to make these extra parameters available to all HTTP methods in the class based views? Right now I'm doing get(self, request, model: Model): pass, put(self, request, model: Model): pass, etc. If I could assign and type 'model' in one place it would be neater.Reportage
@Reportage you should ask a new question if searching the documentation and Stack Overflow didn't solve your issue.Torsion
B
4

For django 3.0, this is what worked for me:

# myapp/views.py
from django.views.generic import DetailView

class MyView(DetailView):
    template_name = 'detail.html'
    slug = None

    def get_object(self, queryset=None):
        self.slug = self.kwargs.get('slug', None)
        return queryset.get(slug=self.slug)

# myapp/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('slug/<slug:slug>/', views.MyView.as_view(), name='myview_by_tag'),
]
Bromidic answered 27/5, 2020 at 15:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.