Is there a way in Django to accept 'n' parameters which are delimited by a '/' (forward slash)?
I was thinking this may work, but it does not. Django still recognizes forward slashes as delimiters.
(r'^(?P<path>[-\w]+/)$', 'some.view', {}),
Add the right url to your urlpatterns:
# ...
("^foo/(.*)$", "foo"), # or whatever
# ...
And process it in your view, like AlbertoPL said:
fields = paramPassedInAccordingToThatUrl.split('/')
Certainly, Django can accept any URL which can be described by a regular expression - including one which has a prefix followed by a '/' followed by a variable number of segments separated by '/'. The exact regular expression will depend on what you want to accept - but an example in Django is given by /admin URLs which parse the suffix of the URL in the view.
Related
When one supplies URL args or kwargs to a django.urls.reverse() call,
Django will nicely URL-encode non-Ascii characters and URL-reserved characters.
For instance, given a declaration such as
path("prefix/<stuff>", view=MyView.as_view(), name="myurl")
we get
reverse('myurl', args=['aaa bbb']) == "/prefix/aaa%20bbb"
reverse('myurl', args=['aaa%bbb']) == "/prefix/aaa%25bbb"
reverse('myurl', args=['Ä']) == "/prefix/%C3%84"
and so on. So far, so good.
What Django will not encode, however, is the slash:
reverse('myurl', args=['aaa/bbb'])
will give us
django.urls.exceptions.NoReverseMatch:
Reverse for 'myurl' with arguments '('aaa/bbb',)' not found.
1 pattern(s) tried: ['prefix/(?P<stuff>[^/]+)$']
(The question what to encode and what not
has been discussed as a Django issue.
It's complicated.)
I found a remark in the code that may explain why
the slash is a special case:
_reverse_with_prefix in django/urls/resolvers.py contains a comment that says
# WSGI provides decoded URLs, without %xx escapes, and the URL
# resolver operates on such URLs. First substitute arguments
# without quoting to build a decoded URL and look for a match.
# Then, if we have a match, redo the substitution with quoted
# arguments in order to return a properly encoded URL.
Given that unencoded arguments are used in the matching initially,
it is no wonder that it does not work:
The slash looks like the end of the argument to Django and so there is
one more argument than expected.
My question:
I dearly want to use user-supplied data in natural-looking URLs,
so slashes occur occasionally. How can I make them work?
The URL structure I need is basically
/show_rooms/<organization>/<department>/<building>
I can think of these approaches:
Replace a slash in an argument with some exotic Unicode character
that will never occur otherwise. And back for received arguments.
This would sort of do the job, but is inconvenient,
non-standard, and therefore ugly.
Use slugs instead of the real names.
This would require extending my models to store the slugs (because
the ORM needs to find objects by them) and appears out of proportion to me.
URL-quote my arguments before passing them to reverse()
and unquote arguments when I receive them.
This is as inconvenient as (1).
It leads to URLs that are more difficult to read than
those from (1), because each % produced by quoting
will subsequently be encoded as %25.
But at least it is a standard-ish approach.
Sigh. Is this really the "right" way?
Any comments or fourth solutions are welcome!
Now that I've written it up, solution (1) does not look quite
so horrible to me. What replacement character would you use for a slash?
I suggest you try to pass the slash in as a regular URL and see if your view is able to match with it. If that's the case and the problem is in the reverse function itself not the view. How about passing the slash already encoded %2F?
It's me, the asker.
Here is the solution that I finally used:
I decided for solution (1).
It turned out less inconvenient than I had expected and
it works spectacularly well.
My Firefox browser shows the URL in text form, not urlencoded form,
and when you pick the right replacement character it looks almost natural.
Very nice.
Here is the code for the escaping (to be called in the template, hence a
custom templatetag)
and unescaping (to be called in the view):
import django.template as djt
register = djt.Library()
# see https://docs.djangoproject.com/en/stable/howto/custom-template-tags/
ALT_SLASH = '\N{DIVISION SLASH}'
#register.filter
def escape_slash(urlparam: str) -> str:
"""
Avoid having a slash in the urlparam URL part,
because it would not get URL-encoded.
See https://stackoverflow.com/questions/67849991/django-urls-reverse-url-encoding-the-slash
Possible replacement characters are
codepoint char utf8 name oldname
U+2044 ⁄ e2 81 84 FRACTION SLASH
U+2215 ∕ e2 88 95 DIVISION SLASH
U+FF0F / ef bc 8f FULLWIDTH SOLIDUS FULLWIDTH SLASH
None of them will look quite right if the browser shows the char rather than
the %-escape in the address line, but DIVISION SLASH comes close.
The normal slash is
U+002F / 2f SOLIDUS SLASH
To get back the urlparam after calling escape_slash,
the URL will be formed (via {% url ... } or reverse()) and URL-encoded,
sent to the browser,
received by Django in a request, URL-unencoded, split,
its param parts handed to a view as args or kwargs,
and finally unescape_slash will be called by the view.
"""
return urlparam.replace('/', ALT_SLASH)
def unescape_slash(urlparam_q: str) -> str:
return urlparam_q.replace(ALT_SLASH, '/')
I'm using a dynamic part in the URL in my Django project, as <str:item_code>, some times the str contains a slash / which causes an error not found.
here is how my URL pattern looks like :
path('find/the/item/<str:item_description>/', views.find_the_item, name="find_the_item"),
is there anyway to force the url to ignore all slashes inside this <str:item_description> part ?
I'm not familiar with Django, but reading the docs it looks as if you might be able to use the path specified instead of str:
path('find/the/item/<path:item_description>/', views.find_the_item, name="find_the_item"),
The path specifier "Matches any non-empty string, including the path separator, '/'. This allows you to match against a complete URL path rather than just a segment of a URL path as with str.".
(from https://docs.djangoproject.com/en/2.2/topics/http/urls/#path-converters)
Sorry in advance if this question look a bit superfluous but is something that is really bothering me.
I have a set of API's written in Django defined by the following urls.
# urls.py
import ...
urlpatterns = [
url(r"^api/v1/account", include(profile.urls))
]
and
# profile/urls.py
import ...
urlpatterns = [
url(r"^$", AccountAPI.as_view()),
url(r"^/login$", LoginAPI.as_view()),
url(r"^/logout$", LogoutAPI.as_view())
]
This configuration should allow only the urls:
/api/v1/account
/api/v1/account/login
/api/v1/account/logout
This work for my purpose but I keep having warnings like(I have several API defined with this rule and the warning list is way bigger):
?: (urls.W002) Your URL pattern '^/login$' has a regex beginning with a '/'. Remove this slash as it is unnecessary.
?: (urls.W002) Your URL pattern '^/logout$' has a regex beginning with a '/'. Remove this slash as it is unnecessary.
If I remove the slash the server will not validate the urls I defined. For that I have to had a slash to the first level of the urls like:
# urls.py
import ...
urlpatterns = [
url(r"^api/v1/account/", include(profile.urls))
]
And this will make the account call to end with a slash, something I don't want to.
I feel that I have defined the urls the most elegant way I found to serve my purpose and I keep having this sad warnings in my logs.
Am I doing something wrong? Is there a right way to define the url's without compromising the structure I choose for them? Or there's a way of turning this warnings off?
You can turn the warning(s) off using SILENCED_SYSTEM_CHECKS option.
Example:
SILENCED_SYSTEM_CHECKS = ['urls.W002', 'security.W019']
When I wrote the check, I incorrectly assumed urls with trailing slashes, like /api/v1/account/ and /api/v1/account/login/.
If you do not use trailing slashes, then starting an included url pattern with ^/ can be correct, and the W002 check gives a false positive.
As of Django 1.10.2, the check is disabled if you have APPEND_SLASH=False in your settings. See ticket 27238 for the discussion.
this will make the account call to end with a slash, something I don't want to.
Why? Is there any specific reason?
As far as Django is concerned, there's nothing incorrect in ending a url with a slash. Moreover, Django's admin urls end in a slash. Django polls tutorial also appends slash to root URLconfs.
If you read URL Dispacher example, it says:
There’s no need to add a leading slash, because every URL has that. For example, it’s ^articles, not ^/articles.
I used to pass data through django URL while passing #character is not able to pass through urls.py, I am using pattern as
url(r'^pass/(?P<sentence>[\w|\W]*)/$',pass)
I tried with these pattern also
url(r'^pass/(?P<sentence>[a-zA-Z0-9-/:-?##{-~!^_\'\[\]*]*)/$',pass)
Thanks in advance.
The "#" character marks inline anchors (links within the same page) in a URL, so the browser will never send it to Django.
For example, if the URL is /something/pass/#test/something-else/ the browser will sent only /something/pass/ to the server. You can try /something/pass/%23test/something-else/ instead, 23 is the hexadecimal ascii code for # - not pretty (ugly by ugly just pass it as a get variable instead).
There is nothing you can do on the Django side - you better avoid characters with special meanings in the URL path when designing your routes - of course it is a matter of taste, but I really think that strings passed in the URL path should be "slugfied" in order to remove any funny character.
Browsers won't send the url fragment part (ends with "#") to servers. Why not converting your data to base64 first, then pass the data via url.
RFC 1808 (Relative Uniform Resource Locators) : Note that the fragment identifier (and the "#" that precedes it) is
not considered part of the URL. However, since it is commonly used
within the same string context as a URL, a parser must be able to
recognize the fragment when it is present and set it aside as part of
the parsing process.
It seems Flask doesn't support routes with a URI encoded component. I'm curious if I'm doing something wrong, or if there is a special flag I need to include.
My route looks something like this:
#app.route('/foo/<encoded>/bar/')
def foo(encoded):
# ...
pass
The URL that this should match can look like these:
http://foobar.com/foo/xxx/bar/ # matched correctly, no URI component
http://foobar.com/foo/x%2Fx%2Fx%2F/bar/ # not matched correctly, URI component
Former URL works, latter spits out a lovely 404.
Thanks!
Add path to your url rule:
#app.route('/foo/<path:encoded>/bar/')
Update per comment: The route API docs are here: http://flask.pocoo.org/docs/api/#flask.Flask.route. The underlying classes that implement the path style route converter are here: http://werkzeug.pocoo.org/docs/routing/#custom-converters (this is one of the really nice parts of pocoostan.) As far as the trailing slashes, there are special rules that amount to:
If a rule ends with a slash and is requested without a slash by the
user, the user is automatically redirected to the same page with a
trailing slash attached.
If a rule does not end with a trailing slash and the user request the
page with a trailing slash, a 404 not found is raised.
Also keep in mind that if you are on Apache and are expecting a slash-trailed url, ie a bookmarklet that submits to http://ex.com/foo/<path:encoded>/bar and encoded gets something with double slashes, Apache will convert multiple slashes to a single one.