Search code examples
pythondjangodjango-viewsdjango-templatesdjango-urls

url routing to same view sometimes with kwargs, sometimes without


I have multiple tools in my app (about 20). Each can be accessed with an object already selected, or without connection to an object. Currently I am utilizing this like:

urlpatterns = [
    path('tool_one/', views.ToolOneView.as_view(), name='tool-one'),
    path('<int:id>/tool_one/', views.ToolOneView.as_view(), name='tool-one'),

    # [... 18 more paths ...]

    path('tool_twenty/', views.ToolTwentyView.as_view(), name='tool-twenty'),
    path('<int:id>/tool_twenty/', views.ToolTwentyView.as_view(), name='tool-twenty')
]

Now I can link to my tool in two ways:

<a href="{% url 'tools:tool-one' id=object.id %}">
  Option A: Tool one with object attached
</a>
<!--or-->
<a href="{% url 'tools:tool-one' %}">
  Option B: Tool one without object attached
</a>

It would be much easier for me if I could always just use option A but allowing that value for object.id to be None. In the case of object.id = None it should direct the user the the view without the object attached.

With my current approach I dislike the repetitiveness in the urlpatterns and also within the templates. Example: I have a navigation bar linking to all the tools without an object attached. On the DetailView of my Object I have linking to all the tools with the object attached. It is basically the same snippet that in theory I could reuse but apparently can't because with the latter I have to add id=object.id %}

This should be easy to do since the usecase probably occurs very often, so please enlighten me!


Solution

  • Use id=0 for no object. Then you don't need two sets of URLS. Just code that doesn't attempt to get an object if kwarg id=0. I'm assuming that id is a Django default primary key. These are always positive integers.

    If your CBV is using the SingleObjectMixin get_object method to fetch the object, you can subclass it to handle zero:

    def get_object(self):
        if self.kwargs['id'] == '0':
            self.object = None
            return
        return super().get_object()
    

    Depending on the similarity or not of the views tool_one to tool_twenty you might also use a single URL and view

     path('<int:id>/<str:tool_id>/', views.ToolAny.as_view(), name='tool-any'),
    

    check that kwarg tool_id is valid early in the view, returning Http404 if not. Further in, use it to dispatch to the correct processing.