Search code examples
djangodjango-generic-views

Django - Pass model name as parameter to generic view


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


Solution

  • 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.