Home > Software design >  Forwarding to another view swallows one argument
Forwarding to another view swallows one argument

Time:02-19

I have a ListView that checks for a user right and forwards a queryset or redirects to another view depending on the user's rights:

class SelectQualityListView(StoreMixin, ListView):
    template_name = f"{TEMPLATE_ROOT_SHELF}/select_register.html"

    def get_queryset(self):
        registers = Register.objects.filter(store = self.store)
        print(registers, registers.first().pk) ## works ... and 4
        if check_custom_registers_allowed(self.request) == True:
            return registers
        return redirect("select-quality", ins_id = self.kwargs["ins_id"], pg = self.kwargs["pg"], reg = registers.first().pk)

I have this in my urls.py:

path("selectquality/<int:ins_id>/<int:pg>/<int:reg>", views.SelectQualityListView.as_view(), name = 'select-quality'),

which fails with a NoReverseMatch error:

Reverse for 'select-quality' with keyword arguments '{'ins_id': 1849332, 'pg': 3, 'reg': ''}' not found.

I now tried different values instead of registers.first().pk and I need help understanding this:

when adding an int -> reg stays empty - also happens when adding str(4)

return redirect("select-quality", ins_id = self.kwargs["ins_id"], pg = self.kwargs["pg"], reg = 4)

Reverse for 'select-quality' with keyword arguments '{'ins_id': 1849332, 'pg': 3, 'reg': ''}' not found.

when adding a str -> reg has value, but fails

return redirect("select-quality", ins_id = self.kwargs["ins_id"], pg = self.kwargs["pg"], reg = "test")

Reverse for 'select-quality' with keyword arguments '{'ins_id': 1849332, 'pg': 3, 'reg': 'test'}' not found.

The last case is obvious, reg needs to be of type int, not str. But why does the first approach not work with reg = registers.first().pk?

Full Error log:

Internal Server Error: /selectquality/1849332/3
Traceback (most recent call last):
  File "...\venv\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "...\venv\lib\site-packages\django\core\handlers\base.py", line 204, in _get_response
    response = response.render()
  File "...\venv\lib\site-packages\django\template\response.py", line 105, in render
    self.content = self.rendered_content
  File "...\venv\lib\site-packages\django\template\response.py", line 83, in rendered_content
    return template.render(context, self._request)
  File "...\venv\lib\site-packages\django\template\backends\django.py", line 61, in render
    return self.template.render(context)
  File "...\venv\lib\site-packages\django\template\base.py", line 170, in render
    return self._render(context)
  File "...\venv\lib\site-packages\django\template\base.py", line 162, in _render
    return self.nodelist.render(context)
  File "...\venv\lib\site-packages\django\template\base.py", line 938, in render
    bit = node.render_annotated(context)
  File "...\venv\lib\site-packages\django\template\base.py", line 905, in render_annotated
    return self.render(context)
  File "...\venv\lib\site-packages\django\template\defaulttags.py", line 211, in render
    nodelist.append(node.render_annotated(context))
  File "...\venv\lib\site-packages\django\template\base.py", line 905, in render_annotated
    return self.render(context)
  File "...\venv\lib\site-packages\django\template\defaulttags.py", line 446, in render
    url = reverse(view_name, args=args, kwargs=kwargs, current_app=current_app)
  File "...\venv\lib\site-packages\django\urls\base.py", line 86, in reverse
    return resolver._reverse_with_prefix(view, prefix, *args, **kwargs)
  File "...\venv\lib\site-packages\django\urls\resolvers.py", line 698, in _reverse_with_prefix
    raise NoReverseMatch(msg)
django.urls.exceptions.NoReverseMatch: Reverse for 'select-quality' with arguments '(1849332, 3, '')' not found. 4 pattern(s) tried: 
['selectquality/(?P<ins_id>[0-9] )/(?P<pg>[0-9] )/(?P<reg>[0-9] )/(?P<prod1>[0-9] )\\Z', 
'selectquality/(?P<ins_id>[0-9] )/(?P<pg>[0-9] )/(?P<reg>[0-9] )/(?P<prod1>[0-9] )/(?P<prod2>[0-9] )\\Z', 
'selectquality/(?P<ins_id>[0-9] )/(?P<pg>[0-9] )/(?P<reg>[0-9] )/(?P<prod1>[0-9] )/(?P<prod2>[0-9] )/(?P<prod3>[0-9] )\\Z', 
'selectquality/(?P<ins_id>[0-9] )/(?P<pg>[0-9] )/(?P<reg>[0-9] )\\Z']

urls.py of this:

path("selectquality/<int:ins_id>/<int:pg>/<int:reg>",                 views.SelectQualityListView.as_view(), name = 'select-quality'),
path("selectquality/<int:ins_id>/<int:pg>/<int:reg>/<int:p1>/<int:p2>/<int:p3>",     views.SelectQualityListView.as_view(), name = 'select-quality'),
path("selectquality/<int:ins_id>/<int:pg>/<int:reg>/<int:p1>/<int:p2>",     views.SelectQualityListView.as_view(), name = 'select-quality'),
path("selectquality/<int:ins_id>/<int:pg>/<int:reg>/<int:p1>",     views.SelectQualityListView.as_view(), name = 'select-quality'),

Interestingly enough, these links in my template work correctly:

{% url 'select-quality' ins_id pg reg.pk %}

CodePudding user response:

In a ListView the method get_query_set() is used to define more complex queries that should be performed to narrow down the elements of the model listed in the View. Here is the example from django docs https://docs.djangoproject.com/en/4.0/ref/class-based-views/generic-display/#listview:

class PublisherBookListView(ListView):

    template_name = 'books/books_by_publisher.html'

    def get_queryset(self):
        self.publisher = get_object_or_404(Publisher, name=self.kwargs['publisher'])
        return Book.objects.filter(publisher=self.publisher)

So get_queryset must alwas return a queryset.

During a GET-request, this returned queryset is used to render the content of the ListView via the get() method and the html-template defined as "template_name". For the rendering step, the queryset is added to the render context.

If like in your case you return a "redirect" HTTPResponse and hand it over as "queryset", anything can be the result depending on your html-template.

If you want to react on the queryset, then overwrite the get() method, call get_queryset(), do your checks and redirect from there. Inside the get_queryset() just do the basic query.

class SelectQualityListView(StoreMixin, ListView):
    template_name = f"{TEMPLATE_ROOT_SHELF}/select_register.html"

    def get_queryset(self):
        return Register.objects.filter(store = self.store)

    def get(self, request, *args, **kwargs):
        if check_custom_registers_allowed(self.request) is not True:
           return redirect("select-quality", ins_id = self.kwargs["ins_id"], pg = self.kwargs["pg"], reg = registers.first().pk)
        super().get(request, *args, **kwargs)

note: this is only to show the idea ... their might be mistakes in the code

CodePudding user response:

See if you can run the following and what you get:

from django.urls import reverse

reverse('select-quality', args=(1,2,3))
reverse('select-quality', kwargs={'ins_id': 1, 'pg': 2, 'reg': 3})

If both calls resolve successfully, see what are the types of values you're passing to the redirect call.

  • Related