Let's say I have some Models that inherit from a base class, Animal. I can use generic views and route Cat/12 to a detail view and Dod/10 to the same detail view with a different context. But I would like to get the Model name from the url so that I dont have to define a route.
I have something like this:
url(r'^cat/(?P<slug>[-\w]+)/$',
DetailView.as_view(
queryset=Cat.objects.filter(),
model=Cat,
context_object_name='animal',
template_name='animal/detail.html'),
name='detail'),
url(r'^dog/(?P<slug>[-\w]+)/$',
DetailView.as_view(
queryset=Dog.objects.filter(),
model=Dog,
context_object_name='animal',
template_name='animal/detail.html'),
name='detail'),
...
Obviously, this is too much repetitive code. I would rather do something like this:
url(r'^?P<my_animal>\w+/(?P<slug>[-\w]+)/$',
DetailView.as_view(
queryset=my_animal.objects.filter(),
model=my_animal,
context_object_name='animal',
template_name='animal/detail.html'),
name='detail'),
...
Can I do this?
EDIT
Here's what I ended up with, thanks to Darwin's help. It avoids the if/else to get the Model name:
class AnimalDetailView(DetailView):
context_object_name='animal'
template_name='animals/detail.html'
def dispatch(self, request, *args, **kwargs):
my_animal = kwargs.get('my_animal', None)
self.model = get_model('animals',my_animal.capitalize())
try:
ret = super(AnimalDetailView, self).dispatch(request, *args, **kwargs)
except AttributeError:
raise Http404
return ret
def get_queryset(self):
return self.model.objects.filter()
Next time I have a question about Inheritance, I'll consult Darwin! LoL
You can inherit from DetailView and override the dispatch method to build your own rules with something like this:
class AnimalDetailView(DetailView):
context_object_name='animal'
template_name='animal/detail.html'
def dispatch(self, request, *args, **kwargs):
my_animal = kwargs.get('my_animal', None)
if my_animal == 'dog':
self.model = Dog
elif my_animal == 'cat':
self.model = Cat
return super(AnimalDetailView, self).dispatch(request, *args, **kwargs)
def get_queryset(self):
return self.model.objects.filter()
and use an urlpattern like this:
url(r'^?P<my_animal>\w+/(?P<slug>[-\w]+)/$', AnimalDetailView.as_view())
Edit: I've make a mistake the last time because we can't instantiate a view class, just only using the 'as_view()' method. Try the new approach, I think this can help.
Yes, any named parameters (i.e. ?P<my_animal>
) in your urls will automatically be passed as keyword arguments to your views,:
The key part to making this [Class Based Views] work is that when class-based views are called, various useful things are stored on self; as well as the request (self.request) this includes the positional (self.args) and name-based (self.kwargs) arguments captured according to the URLconf.
so you will have access to them as self.kwargs['my_animal']
in the view.
If you look at the __init__
method of BaseDetailView
(from which DetailView
inherits) you'll see that all it is doing is taking the kwargs
and assigning them to instance attributes, so you could easily do the following:
url(r'^?P<model>\w+/(?P<slug>[-\w]+)/$',...
and the view should automatically assign the value passed in the URL to self.model
. Of course you need to be careful and make sure to validate the input here, but it's a nice way to dynamically grab objects from models specified by the user
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With