121

It is unclear to me how it is best to access URL-parameters in class-based-views in Django 1.5.

Consider the following:

View:

from django.views.generic.base import TemplateView


class Yearly(TemplateView):
    template_name = "calendars/yearly.html"

    current_year = datetime.datetime.now().year
    current_month = datetime.datetime.now().month

    def get_context_data(self, **kwargs):
        context = super(Yearly, self).get_context_data(**kwargs)
        context['current_year'] = self.current_year
        context['current_month'] = self.current_month
        return context

URLCONF:

from .views import Yearly


urlpatterns = patterns('',
    url(
        regex=r'^(?P<year>\d+)/$',
        view=Yearly.as_view(),
        name='yearly-view'
    ),
)

I want to access the year parameter in my view, so I can do logic like:

month_names = [
    "January", "February", "March", "April", 
    "May", "June", "July", "August", 
    "September", "October", "November", "December"
]

for month, month_name in enumerate(month_names, start=1):
    is_current = False
    if year == current_year and month == current_month:
        is_current = True
        months.append({
            'month': month,
            'name': month_name,
            'is_current': is_current
        })

How would one best access the url parameter in CBVs like the above that is subclassed of TemplateView and where should one ideally place the logic like this, eg. in a method?

Nayan
  • 1,521
  • 2
  • 13
  • 27
  • 1
    There is the option of the simple `extra_context` dict in `django2`, see [here](https://docs.djangoproject.com/en/2.0/ref/class-based-views/mixins-simple/) – Timo Mar 20 '18 at 17:29

5 Answers5

149

To access the url parameters in class based views, use self.args or self.kwargs so you would access it by doing self.kwargs['year']

Ngenator
  • 10,909
  • 4
  • 41
  • 46
  • 1
    Is it correctly understood that I'm not supposed to create variables directly in the view like I have above? (something about them being persistent). Also I don't understand where I'm supposed to place logic like the above, eg. in which method? Also when I do `year = self.kwargs['year']` in the view I get `NameError: self not defined`. –  Apr 02 '13 at 11:53
  • 4
    Technically you shouldn't since they are at the class level and are class variables. As for the `NameError`, where are you trying to do `year = self.kwargs['year']`? You should be doing it in a method, you can't do it at the class level. So for example, you are using a `TemplateView` which means that you would do the logic in your `get_context_data` override. – Ngenator Apr 02 '13 at 12:58
  • 5
    Just for referencing: The documentation on self.request, self.args etc can be found in https://docs.djangoproject.com/en/1.10/topics/class-based-views/generic-display/ – LShi Mar 17 '17 at 15:29
  • Also you can do it in `def __init__(self):` function in the class if you want to access it outside other functions. – Rahat Zaman Mar 06 '19 at 18:01
82

In case you pass URL parameter like this:

http://<my_url>/?order_by=created

You can access it in class based view by using self.request.GET (its not presented in self.args nor in self.kwargs):

from django.views.generic.list import ListView

class MyClassBasedView(ListView):
    ...
    def get_queryset(self):
        order_by = self.request.GET.get('order_by') or '-created'
        qs = super().get_queryset()
        return qs.order_by(order_by)
niekas
  • 8,187
  • 7
  • 40
  • 58
  • 6
    Thanks! This has been confusing me... I keep reading stuff that implies HTTP parameters will be in the kwargs. – foobarbecue Jan 28 '14 at 06:27
  • Can you show the get_queryset() of the superclass of MyClassBasedView? I would just do `qs=.objects.` – Timo May 09 '17 at 06:13
28

I found this elegant solution, and for django 1.5 or higher, as pointed out here:

Django’s generic class based views now automatically include a view variable in the context. This variable points at your view object.

In your views.py:

from django.views.generic.base import TemplateView    

class Yearly(TemplateView):
    template_name = "calendars/yearly.html"
    # Not here 
    current_year = datetime.datetime.now().year
    current_month = datetime.datetime.now().month

    # dispatch is called when the class instance loads
    def dispatch(self, request, *args, **kwargs):
        self.year = kwargs.get('year', "any_default")

    # other code

    # needed to have an HttpResponse
    return super(Yearly, self).dispatch(request, *args, **kwargs)

The dispatch solution found in this question.
As the view is already passed within Template context, you don't really need to worry about it. In your template file yearly.html, it is possible to access those view attributes simply by:

{{ view.year }}
{{ view.current_year }}
{{ view.current_month }}

You can keep your urlconf as it is.

It's worth mentioning that getting information into your template’s context overwrites the get_context_data(), so it is somehow breaking the django's action bean flow.

John R Perry
  • 3,916
  • 2
  • 38
  • 62
Evhz
  • 8,852
  • 9
  • 51
  • 69
15

How about just use Python decorators to make this intelligible:

class Yearly(TemplateView):

    @property
    def year(self):
       return self.kwargs['year']
danizen
  • 431
  • 5
  • 4
8

So far I've only been able to access these url parameters from within the get_queryset method, although I've only tried it with a ListView not a TemplateView. I'll use the url param to create an attribute on the object instance, then use that attribute in get_context_data to populate the context:

class Yearly(TemplateView):
    template_name = "calendars/yearly.html"

    current_year = datetime.datetime.now().year
    current_month = datetime.datetime.now().month

    def get_queryset(self):
        self.year = self.kwargs['year']
        queryset = super(Yearly, self).get_queryset()
        return queryset

    def get_context_data(self, **kwargs):
        context = super(Yearly, self).get_context_data(**kwargs)
        context['current_year'] = self.current_year
        context['current_month'] = self.current_month
        context['year'] = self.year
        return context
hellsgate
  • 5,905
  • 5
  • 32
  • 47
  • 1
    I find that odd, is there an error or something when you try to do `context['year'] = self.kwargs['year']`? It should be accessible anywhere in the class. – Ngenator Jun 24 '13 at 13:15
  • @Ngenator: I just set up a clean django project to double check and it turns out you are correct. I'm not sure what was preventing this in my original code but I'll find out :). Thanks for the heads-up – hellsgate Jun 24 '13 at 14:12