113

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?

Serjik
  • 10,543
  • 8
  • 61
  • 70

7 Answers7

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']
Daniel Eriksson
  • 3,814
  • 3
  • 16
  • 11
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://stackoverflow.com/a/11494666/9903

holms
  • 9,112
  • 14
  • 65
  • 95
  • 3
    Never do `import *`. Edited your post. – holms Apr 04 '18 at 09:18
  • 1
    @holms for the enlightenment of future readers, PEP8 says "Wildcard imports (from import *) should be avoided". Should isn't as strong as must and this is an example but yes definitely *should avoid* wildcard imports: https://www.python.org/dev/peps/pep-0008/#imports –  Apr 04 '18 at 15:55
  • 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. – holms Apr 06 '18 at 18:23
  • What's the value of slug='hello_world' for an actual variable? – Gonzalo Dambra Jun 18 '20 at 02:14
  • I guess that slug is a class variable and not an instance variable here. – Aagam Sheth Dec 15 '20 at 04:02
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')

Fush
  • 2,469
  • 21
  • 19
  • 1
    should I turn my answer into a wiki answer and add your code to it? –  Jun 19 '13 at 16:17
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.

Yaroslav Nikitenko
  • 1,695
  • 2
  • 23
  • 31
  • 1
    Just updated the link for Django 3.2: https://docs.djangoproject.com/en/3.2/topics/http/urls/#passing-extra-options-to-view-functions – Ali Rn Jun 18 '21 at 18:09
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
Aleck Landgraf
  • 1,545
  • 14
  • 13
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
Community
  • 1
  • 1
Emile Bergeron
  • 17,074
  • 5
  • 83
  • 129
  • 1
    I wanted to edit this in [Yaroslav Nikitenko](http://stackoverflow.com/a/29288952/1218980)'s answer, but it was rejected, so I made my own because I felt it was the missing information when I needed it. – Emile Bergeron Sep 20 '16 at 19:58
  • @YaroslavNikitenko In hindsight, it was too big for an edit and best as a reply in the form of a new answer. – Emile Bergeron Jan 25 '17 at 20:26
  • @EmileBergeron The initial question was about generic views such as the `DetailView` class. Could you explain how to use it there? – bartaelterman Nov 19 '18 at 10:44
  • 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. – kiloton Dec 12 '21 at 10:26
  • @kiloton you should ask a new question if searching the documentation and Stack Overflow didn't solve your issue. – Emile Bergeron Dec 13 '21 at 01:29
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'),
]
Bruno Morais
  • 1,029
  • 11
  • 12