Django re_path to catch all URLs except /admin/ not working properly
I'm trying to define a re_path in Django that catches all URLs except those that start with /admin/. My goal is to redirect unknown URLs to a custom view (RedirectView), while ensuring that all admin URLs (including subpaths like /admin/mymodel/something/) are excluded.
from django.urls import re_path, path
from django.contrib import admin
from . import views
urlpatterns = [
path('admin/', admin.site.urls), # Ensure Django admin URLs are handled
]
# Catch-all pattern that should exclude /admin/ URLs
urlpatterns += [
re_path(r"^.*/(?!admin/$)[^/]+/$", views.RedirectView.as_view(), name="my-view"),
]
Issue:
URLs like localhost:8000/admin/mymodel/something/ are correctly ignored, which is good.
However, single-segment URLs like localhost:8000/something/ are not being matched, even though they should be redirected.
adjust your re_path that should exclude /admin/ URLs
urlpatterns += [
re_path(r"^(?!admin/).*", views.RedirectView.as_view(), name="my-view"),
]
My goal is to redirect unknown URLs to a custom view (RedirectView), while ensuring that all admin URLs (including subpaths like /admin/mymodel/something/) are excluded.
The good news is, Django matches paths top-to-bottom. So if the path is an admin/…
it will first look for the admin.site.urls
. Unless you set the .final_catch_all_view
attribute [Django-doc] of the admin site to False
, it has a catchall that will handle thus all paths with code/
.
You thus can use a catchall with:
from django.contrib import admin
from django.urls import path, re_path
from . import views
urlpatterns = [
path('admin/', admin.site.urls), # Ensure Django admin URLs are handled
]
urlpatterns += [
re_path(r'\A.*/\Z', views.RedirectView.as_view(), name='my-view'),
]
I would also include a trailing slash to let Django's APPEND_SLASH
[Django-doc] logic first let do some work if the path has no trailing slash. But if not, you can remove that.
The new_pattern
below might do the trick:
PATTERNS
original_pattern = r"^.*/(?!admin/$)[^/]+/$"
new_pattern = r"^[^/]*/(?!admin/)(?:[^/\n]+/)+$"
Demo to original_pattern: https://regex101.com/r/fp5aYg/1
Demo to new_pattern: https://regex101.com/r/2AWQcK/5
NOTES
The new_pattern
can start with any non-forward slash characters or no characters.
First forward slash /
must not be followed by admin/
.
Each forward slash /
must be followed by at least one or more non-forward slash characters [^/]+
followed by a forward slash /
: /([^/]+/)+
.
The string MUST end in a forward slash /$
.
There must be at least two forward slashes in the string separated by a non-forward slash character.
For example, the match will not match strings with two consecutive forward slashes like these: //something//something//
, or something/something//something
^[^/]*
Start at the beginning of the string. Capture 0 or more non-forward slash characters[^/]
, i.e. match everything before the first forward slash/
./
the first forward slash.(?!admin/)
The first slash cannot be followed byadmin/
(?:...)+
Non-capturing group (?:...)
that repeats 1 or more times (+
).[^/\n]+
Capture any non-forward slash[^/]
, non-newline\n
character 1 or more times. (Note: I had to ad the newline character\n
to get the multi-line demo to work correctly.)[^/\n]+/
A non-forward slash character strings are followed by one forward slash/
.$
End of string$
follows the second or later forward slash/$
.
TEST STRINGS:
localhost:8000/admin/mymodel/something/
localhost:8000/something
localhost:8000/something/
localhost:8000/something/something/something/
localhost:8000
something/
/someting/a/
/someting//a/a/
/someting//
/someting//something//
/someting/something//
/someting//something//
something/something/
something/admin/something/
/something/admin/something/
/admin/
/admin/green/yellow/
/admin/hello/
/admin/hello/hello/
/admin/hello/hello/hello/
/admin/hello/hello
admin/green/yellow/
admin/hello/
admin/
/notadmin/path/something/
/notadmin/path/something
/something/admin/something/
/what/is/happening/
/no_admin/yellow/
/no_admin/yellow/green/red/
MATCHES:
localhost:8000/something/
localhost:8000/something/something/something/
/someting/a/
something/something/
/something/admin/something/
admin/green/yellow/
admin/hello/
/something/admin/something/
/what/is/happening/
/no_admin/yellow/
/no_admin/yellow/green/red/
NON-MATCHES:
localhost:8000/admin/mymodel/something/
localhost:8000/something
localhost:8000
something/
/someting//a/a/
/someting//
/someting//something//
/someting/something//
/someting//something//
something/admin/something/
/admin/
/admin/green/yellow/
/admin/hello/
/admin/hello/hello/
/admin/hello/hello/hello/
/admin/hello/hello
admin/
/notadmin/path/something