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?
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']
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)
qs=<Object>.objects.<method>
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.
How about just use Python decorators to make this intelligible:
class Yearly(TemplateView):
@property
def year(self):
return self.kwargs['year']
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
context['year'] = self.kwargs['year']
? It should be accessible anywhere in the class.
Success story sharing
year = self.kwargs['year']
in the view I getNameError: self not defined
.NameError
, where are you trying to doyear = 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 aTemplateView
which means that you would do the logic in yourget_context_data
override.def __init__(self):
function in the class if you want to access it outside other functions.