Skip to content
Advertisement

Is there a more DRY way to create these repetitive django class based views, and URL patterns?

Is there a better, more DRY way to create repetitive sets of views and urls in django? Below are my views and urls. As you can probably see, There are currently two sets of views and urls that are would be identical if not for the different model name. Is there a way to create 3 classes at once as a mixin or something similar to that so that all I have to do to add a new set of classes is pass the model name to one function or child class? Is there something similar I could do for the urls? I found a related question here Class Based Generic Views and DRY but am not sure if and how the answer to that question applies here or is relevant.

relevant section in views:

class AlertMessageUpdate(LoginRequiredMixin, UpdateView):
    template_name = "LibreBadge/applicationadmin/AlertMessage/alertMessageForm.html"
    model = AlertMessage
    fields = "__all__"

class AlertMessageCreate(LoginRequiredMixin, CreateView):
    template_name = "LibreBadge/applicationadmin/AlertMessage/alertMessageForm.html"
    model = AlertMessage
    fields = "__all__"

class AlertMessageList(LoginRequiredMixin, ListView):
    template_name = "LibreBadge/applicationadmin/AlertMessage/alertMessageList.html"
    model = AlertMessage

class BadgeTemplateUpdate(LoginRequiredMixin, UpdateView):
    template_name = "LibreBadge/applicationadmin/BadgeTemplate/badgeTemplateForm.html"
    model = BadgeTemplate
    fields = "__all__"

class BadgeTemplateCreate(LoginRequiredMixin, CreateView):
    template_name = "LibreBadge/applicationadmin/BadgeTemplate/badgeTemplateList.html"
    model = BadgeTemplate

class BadgeTemplateList(LoginRequiredMixin, ListView):
    template_name = "LibreBadge/applicationadmin/BadgeTemplate/badgeTemplateList.html"
    model = BadgeTemplate

relevant section in urls.py:

    url(r'^applicationadmin/alertmessages/update/(?P<pk>[-w]+)/$', views.AlertMessageUpdate.as_view(), name='AlertMessageUpdate'),
    url(r'^applicationadmin/alertmessages/create/$', views.AlertMessageCreate.as_view(), name='AlertMessageCreate'),
    url('applicationadmin/alertmessages/$', views.AlertMessageList.as_view(), name='AlertMessageList'),
    url(r'^applicationadmin/badgetemplates/update/(?P<pk>[-w]+)/$', views.BadgeTemplateUpdate.as_view(), name='BadgeTemplateUpdate'),
    url(r'^applicationadmin/badgetemplates/create/$', views.BadgeTemplateCreate.as_view(), name='BadgeTemplateCreate'),
    url('applicationadmin/badgetemplates/$', views.BadgeTemplateList.as_view(), name='BadgeTemplateList'),

The answer to this question will be used in LibreBadge, LibreBadge GitHub, an open source, MIT licensed, ID badge solution. Feel free to answer this question in the form of a pull request if that’s your style!

Advertisement

Answer

I found a solution to the repetitive views problem!

def applicationAdminCRUDFunction(model, modelName, fields):
    def templateNameGenerator(suffix):
        return("LibreBadge/applicationadmin/" + modelName + "/" + modelName + suffix + ".html")
    viewsDictionary = {}
    viewsDictionary['CreateView'] = type(modelName + 'CreateView', (CreateView,), {'template_name': templateNameGenerator('Form'),'model': model, 'fields': fields})
    viewsDictionary['ListView'] = type(modelName + 'ListView', (ListView,), {'template_name': templateNameGenerator('List'),'model': model})
    viewsDictionary['UpdateView'] = type(modelName + 'UpdateView', (UpdateView,), {'template_name': templateNameGenerator('Form'),'model': model, 'fields': fields})
    viewsDictionary['DeleteView'] = type(modelName + 'DeleteView', (CreateView,), {'success_url': reverse_lazy(modelName + 'Create'),'model': model})
    return(viewsDictionary)

AlertMessageViews = applicationAdminCRUDFunction(AlertMessage, 'AlertMessage', '__all__')
BadgeTemplateViews = applicationAdminCRUDFunction(BadgeTemplate, 'BadgeTemplate', '__all__')

I created a function that uses type to dynamically create class based views (learn more about using type to create classes here) and assign them to a dictionary.

Now I only have to call the applicationAdminCRUDFunction and assign it’s values to a variable. I can then call the views in urls.py like so

    url(r'^alertmessages/update/(?P<pk>[-w]+)/$', views.AlertMessageViews.get('UpdateView').as_view(), name='AlertMessageUpdate'),

To solve the repetitive URL pattern problem, I created a function to return a list of urls with the modelName parameter

def applicationAdminURLS(modelName):
    return[
        path(modelName + '/create', eval('views.' + modelName + 'Views' + ".get('CreateView').as_view()"), name= modelName + 'Create'),
        path('', eval('views.' + modelName + 'Views' + ".get('ListView').as_view()"), name= modelName + 'List'),
        path(modelName + '/update/(<int:pk>/', eval('views.' + modelName + 'Views' + ".get('UpdateView').as_view()"), name= modelName + 'Update'),
        ]

I then called that function by creating one path pattern with include and passed the applicationAdminURLS function as the parameter to include

    path('alertmessages/', include(applicationAdminURLS('AlertMessage')), name='alertmessages'),
    path('badgetemplates/', include(applicationAdminURLS('BadgeTemplate')), name='badgetemplates'),
User contributions licensed under: CC BY-SA
5 People found this is helpful
Advertisement