diff --git a/JAZZMIN_FIX_README.md b/JAZZMIN_FIX_README.md index 250bdba..0ae96d5 100644 --- a/JAZZMIN_FIX_README.md +++ b/JAZZMIN_FIX_README.md @@ -3,33 +3,38 @@ ## Problem Django 6.0 introduced a breaking change where `format_html()` now requires at least one argument (args or kwargs). The jazzmin package (v3.0.1) uses `format_html(html_str)` which causes a `TypeError: args or kwargs must be provided` error when accessing admin list views with pagination. +### Error Example +``` +TypeError at /admin/core/booking/ +args or kwargs must be provided. + +Exception Location: /opt/Finoa/.venv/lib/python3.12/site-packages/django/utils/html.py, line 137, in format_html +``` + ## Solution This fix patches the jazzmin package's `jazzmin_paginator_number` template tag to use `mark_safe()` instead of `format_html()` for Django 6.0 compatibility. ### Implementation -1. **Automated Patch Script** (`fix_jazzmin.sh`): - - Automatically locates the jazzmin package installation - - Patches line 256 in `jazzmin/templatetags/jazzmin.py` - - Replaces `return format_html(html_str)` with `return mark_safe(html_str)` +**Automated Patch Script** (`fix_jazzmin.sh`): +- Automatically locates the jazzmin package installation +- Patches line 256 in `jazzmin/templatetags/jazzmin.py` +- Replaces `return format_html(html_str)` with `return mark_safe(html_str)` -2. **Usage**: - ```bash - # Run after installing requirements.txt - ./fix_jazzmin.sh - ``` +### Usage +```bash +# Run after installing requirements.txt +cd /path/to/Finoa +pip install -r requirements.txt +./fix_jazzmin.sh +python manage.py runserver +``` ## Why This Works - `mark_safe()` marks a string as safe for HTML output without requiring additional arguments - The HTML string is already properly formatted by the template tag - No security impact since the string is built from trusted sources (Django's own ChangeList object) - -## Files Changed -- `fix_jazzmin.sh` - New patch script -- `finoa/settings.py` - Updated INSTALLED_APPS order (core before jazzmin) for template tag override precedence -- `core/templatetags/__init__.py` - Template tags package init -- `core/templatetags/jazzmin.py` - Custom jazzmin template tag override (fallback solution) -- `core/apps.py` - Reverted to original (monkey patch approach removed) +- The patch is applied directly to the installed jazzmin package ## Deployment Run the patch script as part of your deployment process: @@ -39,10 +44,22 @@ pip install -r requirements.txt python manage.py runserver ``` +**Note**: The patch needs to be reapplied after each time you reinstall or upgrade the jazzmin package. + ## Alternative Solutions Considered 1. ✗ Monkey patching in `CoreConfig.ready()` - Doesn't work because template tags are loaded before AppConfig.ready() -2. ✗ Custom template tag override - Doesn't work reliably due to Python module caching +2. ✗ Custom template tag override - Doesn't work because Django loads the first matching template tag library name from INSTALLED_APPS, and both modules use the same name "jazzmin" 3. ✓ Direct file patch via shell script - Works reliably and is easy to integrate into deployment +## Technical Details +The issue is in `/path/to/site-packages/jazzmin/templatetags/jazzmin.py` at line 256: +```python +# Before (causes error in Django 6.0): +return format_html(html_str) + +# After (works in Django 6.0): +return mark_safe(html_str) +``` + ## Note -This is a temporary fix until jazzmin releases a Django 6.0 compatible version. Monitor the jazzmin GitHub repository for official updates. +This is a temporary fix until jazzmin releases a Django 6.0 compatible version. Monitor the [jazzmin GitHub repository](https://github.com/farridav/django-jazzmin) for official updates. diff --git a/core/tests_jazzmin_templatetag.py b/core/tests_jazzmin_templatetag.py new file mode 100644 index 0000000..e46a9e4 --- /dev/null +++ b/core/tests_jazzmin_templatetag.py @@ -0,0 +1,84 @@ +""" +Tests for Django 6.0 compatibility fixes in core.templatetags.jazzmin +""" +from django.contrib.admin.views.main import ChangeList +from django.test import TestCase, RequestFactory +from django.contrib.admin.sites import AdminSite +from django.contrib.auth.models import User +from django.contrib.admin import ModelAdmin + +from core.templatetags.jazzmin import jazzmin_paginator_number + + +class JazzminPaginatorNumberTestCase(TestCase): + """Test the jazzmin_paginator_number template tag for Django 6.0 compatibility.""" + + def setUp(self): + """Set up test fixtures.""" + self.factory = RequestFactory() + self.site = AdminSite() + + def test_jazzmin_paginator_number_returns_safe_html(self): + """Test that jazzmin_paginator_number returns SafeText without errors.""" + # Create a mock request + request = self.factory.get('/admin/auth/user/') + request.user = User(username='admin', is_staff=True, is_superuser=True) + + # Create a ModelAdmin instance + model_admin = ModelAdmin(User, self.site) + + # Create a ChangeList - this would normally be done by Django admin + changelist = ChangeList( + request=request, + model=User, + list_display=['username'], + list_display_links=['username'], + list_filter=[], + date_hierarchy=None, + search_fields=[], + list_select_related=[], + list_per_page=100, + list_max_show_all=200, + list_editable=[], + model_admin=model_admin, + sortable_by=[], + search_help_text=None, + ) + + # Test the template tag with different page numbers + # This should not raise "TypeError: args or kwargs must be provided" + try: + result = jazzmin_paginator_number(changelist, 1) + self.assertIsNotNone(result) + self.assertIn('page-item', str(result)) + except TypeError as e: + if "args or kwargs must be provided" in str(e): + self.fail("Template tag still uses format_html incorrectly") + raise + + def test_jazzmin_paginator_number_start_page(self): + """Test pagination rendering for the first page.""" + request = self.factory.get('/admin/auth/user/') + request.user = User(username='admin', is_staff=True, is_superuser=True) + + model_admin = ModelAdmin(User, self.site) + changelist = ChangeList( + request=request, + model=User, + list_display=['username'], + list_display_links=['username'], + list_filter=[], + date_hierarchy=None, + search_fields=[], + list_select_related=[], + list_per_page=100, + list_max_show_all=200, + list_editable=[], + model_admin=model_admin, + sortable_by=[], + search_help_text=None, + ) + + result = jazzmin_paginator_number(changelist, 1) + self.assertIn('previous', str(result)) + self.assertIn('«', str(result))