|
5 | 5 |
|
6 | 6 | from django.conf import settings
|
7 | 7 | from django.urls import NoReverseMatch, reverse
|
8 |
| -from django.urls.base import reverse_lazy |
| 8 | +from django.utils import translation |
9 | 9 | from django.utils.html import escape
|
10 |
| -from django.utils.translation import get_language |
11 | 10 |
|
12 | 11 | from django_tomselect.logging import package_logger
|
13 | 12 |
|
|
22 | 21 |
|
23 | 22 | def safe_reverse(viewname: str, args: list | None = None, kwargs: dict | None = None) -> str:
|
24 | 23 | """Safely reverse url, handling i18n edge cases when USE_I18N is True but i18n URL patterns aren't included."""
|
| 24 | + # Store the current language |
| 25 | + current_language = translation.get_language() |
| 26 | + |
25 | 27 | try:
|
26 |
| - # First try normal reversal |
27 |
| - return reverse(viewname, args=args, kwargs=kwargs) |
28 |
| - except NoReverseMatch as e: |
29 |
| - # Check if this might be an i18n-related issue |
30 |
| - if settings.USE_I18N and get_language(): |
31 |
| - # Try to extract the actual error message |
32 |
| - error_msg = str(e) |
33 |
| - language_code = get_language() |
34 |
| - |
35 |
| - # Check if the error is about a language namespace |
36 |
| - if f"'{language_code}' is not a registered namespace" in error_msg: |
37 |
| - package_logger.debug( |
38 |
| - "URL reversal failed due to missing i18n namespace '%s'. " |
39 |
| - "This usually means django.conf.urls.i18n is not included in urlpatterns. " |
40 |
| - "Attempting fallback URL reversal.", |
41 |
| - language_code, |
42 |
| - ) |
43 |
| - |
44 |
| - # Try to reverse without language prefix by temporarily deactivating translations |
45 |
| - from django.utils import translation |
46 |
| - |
47 |
| - current_language = translation.get_language() |
48 |
| - try: |
49 |
| - # Deactivate translations temporarily |
| 28 | + # If i18n is enabled but there's a problem with url reversal, temporarily deactivate |
| 29 | + # translations to avoid issues with missing i18n URL patterns. |
| 30 | + if settings.USE_I18N and current_language: |
| 31 | + # First, try with language activated |
| 32 | + try: |
| 33 | + return reverse(viewname, args=args, kwargs=kwargs) |
| 34 | + except NoReverseMatch as e: |
| 35 | + error_msg = str(e) |
| 36 | + # Check if the error is about a language namespace |
| 37 | + if f"'{current_language}' is not a registered namespace" in error_msg: |
| 38 | + package_logger.debug( |
| 39 | + "URL reversal failed due to missing i18n namespace '%s'. " |
| 40 | + "This usually means django.conf.urls.i18n is not included in urlpatterns. " |
| 41 | + "Attempting fallback URL reversal without language activation.", |
| 42 | + current_language, |
| 43 | + ) |
| 44 | + # Deactivate translations and try again |
50 | 45 | translation.deactivate()
|
51 |
| - return reverse(viewname, args=args, kwargs=kwargs) |
52 |
| - except NoReverseMatch: |
53 |
| - # If it still fails, re-raise the original error |
54 |
| - raise e |
55 |
| - finally: |
56 |
| - # Restore the original language |
57 |
| - if current_language: |
58 |
| - translation.activate(current_language) |
59 |
| - |
60 |
| - # If not i18n related or fallback didn't work, re-raise |
| 46 | + try: |
| 47 | + return reverse(viewname, args=args, kwargs=kwargs) |
| 48 | + finally: |
| 49 | + # Always restore the original language |
| 50 | + if current_language: |
| 51 | + translation.activate(current_language) |
| 52 | + else: |
| 53 | + # Different error, re-raise |
| 54 | + raise |
| 55 | + else: |
| 56 | + # No i18n or no language activated, use regular reversal |
| 57 | + return reverse(viewname, args=args, kwargs=kwargs) |
| 58 | + except Exception as e: |
| 59 | + package_logger.error("Failed to reverse URL %s: %s", viewname, e) |
61 | 60 | raise
|
62 | 61 |
|
63 | 62 |
|
64 | 63 | def safe_reverse_lazy(viewname: str, args: list | None = None, kwargs: dict | None = None):
|
65 |
| - """ |
66 |
| - Lazy version of safe_reverse that handles i18n edge cases. |
| 64 | + """Lazy version of safe_reverse that handles i18n edge cases. |
67 | 65 |
|
68 | 66 | Returns a lazy object that won't be evaluated until it's used as a string.
|
69 | 67 | """
|
|
0 commit comments