djangoregexdjango-urls

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.


Solution

  • 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

    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