简体   繁体   中英

Django: passing a URL parameter to every instance of a reverse URL lookup in a template tag

Sorry if the title is unclear, I'm not sure the best way to describe the issue. I have an application with a Ticket model and a Team model. All Ticket s are associated with a single Team . The issue I'm having is a problem of URL reversing. I'm trying to set it up my URLs like so: /<team_pk>/tickets/ displays a list of tickets associated with the Team specified by team_pk . So /1/tickets/ would display all of the tickets for the first Team. Both of these objects are in the app tracker .

To do this, I've set up my project/urls.py files like so:

project/urls.py

urlpatterns = [ path('<team_pk>/', include('tracker.urls', namespace='tracker')), ]

tracker/urls.py

urlpatterns = [ path('tickets/', views.TicketTable.as_view(), name='ticket_list'), ]

Then, inside my html templates, I have the following URL tag:

href="{% url 'tracker:ticket_list' %}"

This results in a NoReverseMatch error:

NoReverseMatch at /1/tickets/
Reverse for 'ticket_list' with no arguments not found. 1 pattern(s) tried: ['(?P<team_pk>[^/]+)/tickets/$']

What I would like is for the reverse match to just use the current value for the team_pk URL kwarg.

What I have tried to fix it:

I have found the following solution to the problem, but it involves a lot of repetition, and I feel like there must be a DRYer way.

First, I extend the get_context_data() method for every view on the site.

def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context['current_team_pk'] = self.kwargs['team_pk']
    return context

Then I reference this context in every URL template tag:

href="{% url 'tracker:ticket_list' team_pk=current_team_pk %}"

This results in the desired behavior, but it's a lot of repetition. So, is there a better way?

Edit:

Based on Willem Van Onsem's suggestion, I changed the URL template tag to href="{% url 'tracker:ticket_list' team_pk=team_pk %}" , referencing the URL kwarg directly. But this doesn't seem to be working reliably. On the index page /<team_pk>/ loads just fine, and it includes two relevant URLs: /<team_pk>/ and /<team_pk>/tickets/ . When I navigate to /<team_pk>/tickets/ , however, I get another NoReverseMatch error:

NoReverseMatch at /1/tickets/
Reverse for 'home' with keyword arguments '{'team_pk': ''}' not found. 1 pattern(s) tried: ['(?P<team_pk>[^/]+)/$']

It seems the URL kwarg <team_pk> is not being passed for some reason. But the only link to 'home' is part of my base.html , which the other templates are extending. So the relevant template tags are the same.

Edit 2:

The view in question:

class TicketTable(LoginRequiredMixin, SingleTableMixin, FilterView):
    table_class = my_tables.TicketTable
    template_name = 'tracker/ticket_list.html'
    filterset_class = TicketFilter
    context_object_name = 'page'
    table_pagination = {"per_page": 10}
    PAGE_TITLE = 'Open Tickets'
    TICKET_STATUS_TOGGLE = 'Show Closed Tickets'
    TICKET_STATUS_TOGGLE_URL = 'tracker:closed_ticket_list'
    DISPLAY_DEV_FILTER = True
    DISPLAY_STATUS_FILTER = True

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['page_title'] = self.PAGE_TITLE
        context['ticket_status_toggle'] = self.TICKET_STATUS_TOGGLE
        context['ticket_status_toggle_url'] = self.TICKET_STATUS_TOGGLE_URL
        context['display_dev_filter'] = self.DISPLAY_DEV_FILTER
        context['display_status_filter'] = self.DISPLAY_STATUS_FILTER
        return context

    def get_queryset(self):
        return models.Ticket.objects.filter_for_team(self.kwargs['team_pk']).filter_for_user(self.request.user).exclude(status='closed')

Edit 3:

I found a solution to access the URL kwargs without modifying context data. I'm not sure why team_pk=team_pk didn't work in the URL tag, but team_pk=view.kwargs.team_pk does work.

Based on the comments and responses from Willem Van Onsem, a friend of mine, and some of my own digging, I found what I think is the DRYest way to do what I was trying to do. I created a custom mixin:

class CommonTemplateContextMixin:
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        if self.kwargs.get('team_pk'):
            context.setdefault('team_pk', self.kwargs.get('team_pk'))
        else:
            context.setdefault('team_pk', self.request.user.teams.all()[0].pk)
        return context

Then I subclass this mixin with all my views. Then I have a team_pk template tag inside all my templates, and I just add team_pk=team_pk to all my URL template tags. The only advantage to using this instead of team_pk=view.kwargs.team_pk is that I can add some additional logic for the case when the URL kwarg isn't available.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM