django-ninja icon indicating copy to clipboard operation
django-ninja copied to clipboard

[BUG] url_name not accessible

Open limugob opened this issue 3 years ago • 6 comments
trafficstars

Describe the bug After implementing two endpoints with same url-path (one for GET, one for POST), the first one is not included in urls. In tests, I can not call reverse on the name from the first one.

File: api.py

from ninja import NinjaAPI

api = NinjaAPI(urls_namespace="frontend:api")

@api.get("recordings", url_name="recording_list")
def recording_list(request):
    pass

@api.post("recordings", url_name="recording_create")
def recording_create(request):
    pass

Current behavior:

reverse("api:recording_create")
'/api/recordings'

This works, but:

reverse_lazy("recording_list")
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/home/bogus/Dev/greenstreams/django-ninja-reverse/.venv/lib/python3.10/site-packages/django/urls/base.py", line 88, in reverse
    return resolver._reverse_with_prefix(view, prefix, *args, **kwargs)
  File "/home/bogus/Dev/greenstreams/django-ninja-reverse/.venv/lib/python3.10/site-packages/django/urls/resolvers.py", line 802, in _reverse_with_prefix
    raise NoReverseMatch(msg)
django.urls.exceptions.NoReverseMatch: Reverse for 'recording_list' not found. 'recording_list' is not a valid view function or pattern name.

raise an exception.

Expected behavior:

Both url_names are possible.

reverse("api:recording_create")
'/api/recordings'
reverse_lazy("recording_list")
'/api/recordings'

Versions:

  • Python version: 3.10.3
  • Django version: 4.0.4
  • Django-Ninja version: 0.17.0

Package to reproduce

django_ninja_same_url.zip

limugob avatar May 19 '22 11:05 limugob

Can somewhat confirm. Something with URLs and namespaces is off it feels like. I cannot get any URLs to work correctly.

from ninja import NinjaAPI

api = NinjaAPI(urls_namespace="api")

@api.post("/test_fx", url_name="post")
def test_fx(request):
    return {"post success": 200}

@api.get("/test_fx", url_name="get")
def test_fx(request):
    return {"get success": 200}
>>> from django.urls import reverse_lazy
>>> reverse_lazy("api:post")
>>> Traceback (most recent call last):
>>> ...

>>> reverse_lazy("api:get")
>>> '/api/test_fx'

And btw: in my templates.html this wouldn't work at all (not specifying the url_name-attribute of course):

<button href = {% url 'api:test_fx' %}>MyButton </button>

ghost avatar May 30 '22 10:05 ghost

I am finding this be an issue and somewhat counter intuitive when designing API's. I sense it's related to how the paths are registered as part of the Dict object in router.py. Ideally though the outcome should be that I should be able to declare my API like this:


@router.get('/', url_name="users-list")
@router.post('/', url_name="users-create")
@router.get('/{id}/', url_name="users-detail")
@router.put('/{id}/', url_name="users-update")
@router.delete('/{id}/', url_name="users-delete")

And all available url_names are available for reversal, not just the last one.

bencleary avatar Sep 01 '22 12:09 bencleary

@limugob @fantasticle @bencleary

Well.. django-ninja uses django url resolver under the hood, which does not allow to set more than one name to url

so whenever you use THE SAME url for different http methods - only last one is applied

@router.get('/{id}/', url_name="users-detail")
@router.put('/{id}/', url_name="users-update")
@router.delete('/{id}/', url_name="users-delete")

as you can se here ^ all 3 operations are perfomed to one url /id/

vitalik avatar Sep 02 '22 06:09 vitalik

@vitalik thanks for the response, i was sure i had the same URL configured for some views in Django turns out i don't so you can ignore me. It would be really nice if there was a way, but i guess that is a Django change not Ninja change. I withdraw my comments!

bencleary avatar Sep 02 '22 07:09 bencleary

Hello @vitalik, thanks for this interesting lib, we use it already!

The described bug can not come directly from django url resolver, because in django it is possible to give the same url two separate names. In the given package above, there is a working example for this also.

limugob avatar Sep 04 '22 17:09 limugob

@limugob

because in django it is possible to give the same url two separate names.

no, as I see it's there is only one name string argument for path function:

path(route, view, kwargs=None, name=None)

and no - you cannot create multiple same urls and give diferent names - because only first will match with resolver

vitalik avatar Sep 04 '22 18:09 vitalik

Thanks, I see now, that even in django-rest-framework it is the same.

limugob avatar Nov 05 '22 10:11 limugob