Django内置password_reset视图报错:找不到'reset_password'反向解析
I recently hit a frustrating issue while setting up Django's built-in password reset flow: after following tutorials to configure the password_reset views, accessing the corresponding URL kept throwing this error:
Reverse for 'reset_password' not found. 'reset_password' is not a valid view function or pattern name.
I tried several fixes with no luck—until I moved the password_reset and password_reset_confirm URL configurations from my app's urls.py to the project's root urls.py, which resolved the problem instantly. Let's break down the root causes and alternative solutions so you don't have to move your URLs if you don't want to.
Deep Dive into the Root Cause
The core issue boils down to URL namespace handling and how Django's built-in password reset views generate and resolve URLs:
Unaccounted-for Namespaces
If you placed the password reset URLs inside a sub-app'surls.pyand added anamespacewhen including the app's URLs in the rooturls.py, Django expects all URL reversals for that app to include the namespace prefix. However, Django's default password reset views (and their associated email templates) generate URL reversals without specifying this namespace—so when it tries to findreset_password, it can't locate the version tucked away in your namespaced sub-app.URL Resolution Scope
Django resolves URLs starting from the rooturls.py. When you include a sub-app's URLs with a namespace, those URLs are only visible within that namespace scope. The built-in views don't know to look inside your app's namespace when generating reset links or redirecting after actions like password reset submission.Hardcoded Reverse Logic
Under the hood, Django'sPasswordResetViewand related views usereverse()orreverse_lazy()with plain view names (likepassword_reset_done) without any namespace. If your URLs are in a namespaced sub-app, these calls fail because they're looking for a global URL pattern that doesn't exist.
Alternative Solutions (No Need to Move URLs to Root)
If you prefer keeping your password reset URLs organized in a sub-app, here are three workarounds:
1. Update Namespace References Everywhere
If you want to keep the URLs in your sub-app:
- First, add an
app_nameto your sub-app'surls.py:# your_app/urls.py app_name = 'your_app' urlpatterns = [ path('password-reset/', PasswordResetView.as_view(), name='password_reset'), path('password-reset/done/', PasswordResetDoneView.as_view(), name='password_reset_done'), # ... other password reset URLs ] - Include the app's URLs in the root
urls.pywith the namespace:# project/urls.py path('accounts/', include('your_app.urls', namespace='your_app')) - Update all URL reversals to include the namespace:
- In your templates, change
{% url 'password_reset' %}to{% url 'your_app:password_reset' %} - Customize Django's default password reset email template (copy it from
django/contrib/admin/templates/registration/password_reset_email.htmlto your project's templates folder) and update the reset link to use the namespace:{% url 'your_app:password_reset_confirm' uidb64=uid token=token %}
- In your templates, change
2. Add a Namespace-Free Alias in Root urls.py
You can keep the URLs in your sub-app but add a "global" alias in the root urls.py so the built-in views can find them:
# project/urls.py from your_app.urls import urlpatterns as password_reset_urls urlpatterns += [ path('accounts/', include(password_reset_urls)), ]
This creates a namespace-free version of the URLs that Django's built-in views can resolve without needing to specify a namespace.
3. Customize the Built-in Views to Use Your Namespace
Create custom subclasses of Django's password reset views, overriding the parts that generate URLs to include your namespace:
# your_app/views.py from django.contrib.auth.views import PasswordResetView, PasswordResetDoneView from django.urls import reverse_lazy class NamespacedPasswordResetView(PasswordResetView): success_url = reverse_lazy('your_app:password_reset_done') class NamespacedPasswordResetDoneView(PasswordResetDoneView): # Add custom behavior here if needed pass
Then use these custom views in your sub-app's urls.py:
# your_app/urls.py app_name = 'your_app' from .views import NamespacedPasswordResetView, NamespacedPasswordResetDoneView urlpatterns = [ path('password-reset/', NamespacedPasswordResetView.as_view(), name='password_reset'), path('password-reset/done/', NamespacedPasswordResetDoneView.as_view(), name='password_reset_done'), # ... other password reset URLs with custom views as needed ]
You'll still need to update template URL reversals to use the namespace, but this ensures the views themselves generate correct URLs.
内容的提问来源于stack exchange,提问作者CatalinC

