1

First of all, I'm pretty new to Python & Django, so it might a simple question. Now, I have a model like this:

models.py

class Base(models.Model):
    class Meta:
        abstract = True
    # base properties

class X(Base):
    # extra props

class Y(Base):
    # extra props

Then I want to have a view for X and one for Y which will basically do the same, for example:

views.py

class BaseView(object):
    template_name = "app/view.html"

    Type = None

    def __init__(self, type):
        self.Type = type

    def get_context_data(self, **kwargs):
        context = super(BaseView, self).get_context_data(**kwargs)
        context['data'] = get_object_or_404(self.Type, pk=kwargs["id"])
        return context


class XView(BaseView, generic.TemplateView):
    def __init__(self):
        BaseView.__init__(self, X)

class YView(BaseView, generic.TemplateView):
    def __init__(self):
        BaseView.__init__(self, Y)

urls.py

urlpatterns = [
    url(r'^xxxx/(?P<id>[0-9]+)$', views.XView.as_view(), name="view_x"),
    url(r'^yyyy/(?P<id>[0-9]+)$', views.YView.as_view(), name="view_y"),
]

Like this, I will have to repeat probably all (or most of) the forms, views, etc. Is there anyway to avoid such duplication? The different between X and Y are just a few fields that are perfectly generated by django in the forms.

I need to differentiate the model based on the URL, example.com/X/ or example.com/Y/ being all the rest more or less the same.

FYI, using Python 3, Django 1.9.

2 Answers 2

2

You should use the Django generic view classes the way they are supposed to be used. If all your classes are doing is passing an object to the template based on the ID in the URL, you should use the built-in DetailView class; you can then pass the model and the template name as parameters directly in the URLconf, with no need for defining subclasses at all.

from django.views.generic import DetailView
urlpatterns = [
    url(r'^xxxx/(?P<pk>[0-9]+)$', DetailView.as_view(model=X, template_name = "app/view.html"), name="view_x"),
    url(r'^yyyy/(?P<pk>[0-9]+)$', DetailView.as_view(model=X, template_name = "app/view.html"), name="view_y"),
]

(Note DetailView expects the URL argument to be called pk rather than id.)

If you did want to add more custom behaviour to your views, you can subclass DetailView; it is still perfectly possible to use the same view class in multiple URL patterns.

Sign up to request clarification or add additional context in comments.

Comments

1

You can remove duplication in a few ways. In your urls.py, you can pass parameters directly to your view:

# urls.py
urlpatterns = [
    url(r'^xxxx/(?P<id>[0-9]+)$', views.BaseView.as_view(Type=X), name="view_x"),
    url(r'^yyyy/(?P<id>[0-9]+)$', views.BaseView.as_view(Type=Y), name="view_x"),
]

This way, you don't need to define XView and YView. You can take this a step further, and use your URL parameter for the model.

# urls.py
urlpatterns = [
    url(r'^(?P<model>\w+)/(?P<id>[0-9]+)$', views.BaseView.as_view(), name='view_base'),
]

# views.py
class BaseView(View):

    type_map = {
        'xxxx': X,
        'yyyy': Y,
    }

    def dispatch(self, request, *args, **kwargs):
        model = kwargs.get('model')
        self.type = self.type_map.get(model)
        if self.type is None:
            raise Http404
        return super(BaseView, self).dispatch(request, *args, **kwargs)

2 Comments

Awesome thank you! This is was I was looking for. @DanielRoseman is also right, no need to subclass (I've just realized that). I'll do this option as I still need custom behavior though.
It should be return super(BaseView, self)... the return is missing. With that, it works like a charm :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.