[feat] Add Select2Widget for choice fields #254#642
[feat] Add Select2Widget for choice fields #254#642shivsubh wants to merge 1 commit intoopenwisp:masterfrom
Conversation
📝 WalkthroughWalkthroughAdds a reusable Select2 integration: a new Select2Widget (subclassing Django's Select) that injects Select2 assets and ensures the Sequence Diagram(s)sequenceDiagram
actor User
participant Browser
participant DjangoAdmin as Django Admin
participant Widget as Select2Widget
participant JS as select2.js
participant Select2 as Select2 Library
User->>Browser: Request admin form page
Browser->>DjangoAdmin: GET /admin/... (render form)
DjangoAdmin->>Widget: Render select field with ow-select2 class
Widget->>Browser: Serve HTML + Media (CSS/JS)
Browser->>Browser: Load assets (jQuery, Select2, select2.js, i18n)
Browser->>JS: Execute initialization on DOM ready
JS->>JS: Find select.ow-select2 (exclude __prefix__)
JS->>Select2: Call $el.select2() for uninitialized elements
Select2->>Browser: Enhance UI (select2-container)
User->>Browser: Interact with enhanced select
sequenceDiagram
actor User
participant Browser
participant Form as Formset
participant JS as select2.js
participant Select2 as Select2 Library
User->>Form: Click "Add row"
Form->>Browser: Append new row and emit formset:added
Browser->>JS: formset:added handler runs
JS->>JS: Locate select.ow-select2 in new row (exclude __prefix__)
JS->>Select2: Initialize Select2 if not select2-hidden-accessible
Select2->>Browser: Enhance new select element
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Code Review SummaryStatus: No Issues Found | Recommendation: Merge The implementation is clean and follows Django best practices. The PR introduces a reusable Select2Widget that uses Django's native admin assets to avoid extra dependencies. Incremental Review (latest commit)Changes since commit
Files Reviewed (5 files)
Reviewed by kimi-k2.5-0127 · 302,104 tokens |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@tests/test_project/tests/test_widgets.py`:
- Around line 17-23: The current test_select2_widget_media only checks default
asset inclusion; add two edge-case tests to cover minified assets and i18n:
create test_select2_widget_media_minified that patches
openwisp_utils.widgets.settings.DEBUG to False, instantiate Select2Widget and
assert the media string contains minified filenames (e.g., select2.min.css,
jquery.min.js, select2.full.min.js), and create test_select2_widget_media_i18n
that patches openwisp_utils.widgets.get_language to return a supported locale
like 'es', instantiate Select2Widget and assert the media string contains the
i18n path (e.g., i18n/es.js); use unittest.mock.patch to scope these changes and
reference Select2Widget and its media property in the assertions.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 9142d548-73bd-4704-b953-8e623f00fd20
📒 Files selected for processing (4)
openwisp_utils/static/openwisp-utils/js/select2.jsopenwisp_utils/widgets.pytests/test_project/admin.pytests/test_project/tests/test_widgets.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.11 | django~=4.2.0
- GitHub Check: Python==3.11 | django~=5.0.0
- GitHub Check: Python==3.10 | django~=5.2.0
- GitHub Check: Python==3.13 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=4.2.0
- GitHub Check: Python==3.12 | django~=5.1.0
- GitHub Check: Python==3.13 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=5.0.0
- GitHub Check: Python==3.10 | django~=5.1.0
- GitHub Check: Python==3.11 | django~=5.1.0
- GitHub Check: Python==3.11 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=5.0.0
- GitHub Check: Python==3.12 | django~=5.2.0
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{py,html,txt}
📄 CodeRabbit inference engine (Custom checks)
For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework
Files:
tests/test_project/admin.pytests/test_project/tests/test_widgets.pyopenwisp_utils/widgets.py
🔇 Additional comments (4)
openwisp_utils/static/openwisp-utils/js/select2.js (1)
1-18: LGTM!The initialization script is well-structured with appropriate safeguards:
- Correctly excludes formset template rows via
__prefix__check- Idempotency guard using
select2-hidden-accessibleclass prevents double initialization- Proper handling of dynamically added formset rows via
formset:addedeventopenwisp_utils/widgets.py (1)
1-36: LGTM!The
Select2Widgetimplementation is clean and well-structured:
- Properly leverages Django's admin assets to avoid external dependencies
- Correctly handles DEBUG setting for minified/non-minified assets
- i18n support via
SELECT2_TRANSLATIONSgracefully handles missing translations- Asset ordering ensures dependencies load before initialization script
- Class attribute handling in
__init__correctly preserves user-provided classestests/test_project/admin.py (1)
126-129: LGTM!The
formfield_for_choice_fieldoverride correctly appliesSelect2Widgetonly to thebooks_typefield while preserving default behavior for other choice fields viasuper().tests/test_project/tests/test_widgets.py (1)
1-23: LGTM overall - tests cover core widget functionality.The test file verifies the essential behavior: CSS class handling and asset inclusion. The widget rendering and media composition are adequately tested for the basic use case.
| def test_select2_widget_media(self): | ||
| widget = Select2Widget() | ||
| media = str(widget.media) | ||
| self.assertIn('admin/css/vendor/select2/select2', media) | ||
| self.assertIn('admin/js/vendor/jquery/jquery', media) | ||
| self.assertIn('admin/js/vendor/select2/select2.full', media) | ||
| self.assertIn('openwisp-utils/js/select2.js', media) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider adding test coverage for edge cases.
The current test_select2_widget_media test verifies asset inclusion but could be more comprehensive:
- No assertion for i18n file inclusion when
get_language()returns a supported language - No verification of minified vs non-minified asset paths based on
DEBUGsetting
💡 Suggested additional test cases
from unittest.mock import patch
def test_select2_widget_media_minified(self):
"""Test that minified assets are used when DEBUG=False."""
with patch('openwisp_utils.widgets.settings') as mock_settings:
mock_settings.DEBUG = False
widget = Select2Widget()
media = str(widget.media)
self.assertIn('select2.min.css', media)
self.assertIn('jquery.min.js', media)
self.assertIn('select2.full.min.js', media)
def test_select2_widget_media_i18n(self):
"""Test that i18n file is included for supported languages."""
with patch('openwisp_utils.widgets.get_language', return_value='es'):
widget = Select2Widget()
media = str(widget.media)
self.assertIn('i18n/es.js', media)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/test_project/tests/test_widgets.py` around lines 17 - 23, The current
test_select2_widget_media only checks default asset inclusion; add two edge-case
tests to cover minified assets and i18n: create
test_select2_widget_media_minified that patches
openwisp_utils.widgets.settings.DEBUG to False, instantiate Select2Widget and
assert the media string contains minified filenames (e.g., select2.min.css,
jquery.min.js, select2.full.min.js), and create test_select2_widget_media_i18n
that patches openwisp_utils.widgets.get_language to return a supported locale
like 'es', instantiate Select2Widget and assert the media string contains the
i18n path (e.g., i18n/es.js); use unittest.mock.patch to scope these changes and
reference Select2Widget and its media property in the assertions.
a695791 to
7ec95e2
Compare
Code Style FailuresHello @shivsubh, Your commit failed due to multiple code style violations. Please run the Specifically, the following files have formatting errors that
Additionally, there are ReStructuredText (RST) formatting errors detected in |
fbf6555 to
780d3cb
Compare
pandafy
left a comment
There was a problem hiding this comment.
Thank you @shivsubh for contributing. We want to have a re-usable solution which means minimal code changes should be required in the downstream projects to utilize the new widget.
I did a quick review and found the following code problematic.
We will need new selenium tests which verifies the functioning of the added field.
780d3cb to
2ccf8d1
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@openwisp_utils/widgets.py`:
- Around line 31-34: The __init__ mutates the caller-provided attrs dict in
place; change it to copy attrs first (e.g., attrs = dict(attrs) or attrs.copy()
when attrs is truthy) before updating the "class" key so the original dict is
not modified; update the __init__ implementation (the attrs handling and the
super().__init__(attrs, choices) call) in the widget class to use the copied
dict.
In `@tests/test_project/tests/test_widgets.py`:
- Around line 37-41: The test test_select2_widget_renders_on_shelf_add_form
currently only checks for the select element and its .ow-select2 class; update
it to also assert the Select2 JS enhancement by waiting for the Select2
container for that field (for example by waiting for a selector such as
".select2-container" or the field-specific container tied to "id_books_type") to
be present in the DOM after opening reverse("admin:test_project_shelf_add"); use
the existing helper wait_for_presence to locate the Select2 container adjacent
to or tied to the select#id_books_type element to ensure initialization rather
than just markup.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 736a54cb-1fcd-4b9c-b43f-6add1068f1cd
📒 Files selected for processing (5)
openwisp_utils/admin.pyopenwisp_utils/static/openwisp-utils/js/select2.jsopenwisp_utils/widgets.pytests/test_project/admin.pytests/test_project/tests/test_widgets.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (14)
- GitHub Check: Python==3.13 | django~=5.2.0
- GitHub Check: Python==3.12 | django~=5.2.0
- GitHub Check: Python==3.10 | django~=4.2.0
- GitHub Check: Python==3.13 | django~=5.1.0
- GitHub Check: Python==3.11 | django~=4.2.0
- GitHub Check: Python==3.11 | django~=5.0.0
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.10 | django~=5.2.0
- GitHub Check: Python==3.12 | django~=5.1.0
- GitHub Check: Python==3.11 | django~=5.2.0
- GitHub Check: Python==3.12 | django~=5.0.0
- GitHub Check: Python==3.10 | django~=5.0.0
- GitHub Check: Python==3.11 | django~=5.1.0
- GitHub Check: Python==3.10 | django~=5.1.0
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{py,html,txt}
📄 CodeRabbit inference engine (Custom checks)
For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework
Files:
tests/test_project/admin.pyopenwisp_utils/admin.pytests/test_project/tests/test_widgets.pyopenwisp_utils/widgets.py
🧠 Learnings (2)
📚 Learning: 2026-03-14T20:44:14.568Z
Learnt from: CR
Repo: openwisp/openwisp-utils PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-03-14T20:44:14.568Z
Learning: Bug Fixes: If the bug affects the user interface, include a Selenium browser test; if missing, raise a warning
Applied to files:
tests/test_project/tests/test_widgets.py
📚 Learning: 2026-03-14T20:44:14.568Z
Learnt from: CR
Repo: openwisp/openwisp-utils PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-03-14T20:44:14.568Z
Learning: Features: Add tests for new features and ensure coverage does not decrease significantly; prefer Selenium browser tests for UI-impacting features
Applied to files:
tests/test_project/tests/test_widgets.py
🔇 Additional comments (5)
openwisp_utils/admin.py (1)
223-231: Clean reusable admin hook for Select2-enabled choice fields.This mixin-based opt-in is scoped and composable, and the
formfield_for_choice_fieldoverride keeps behavior limited to choice fields.tests/test_project/admin.py (1)
114-125: Test admin wiring is correct for feature coverage.Applying
Select2AdminMixinwithselect2_fields = ("books_type",)is the right setup to validate the feature end-to-end.openwisp_utils/static/openwisp-utils/js/select2.js (1)
4-17: Initialization flow handles both initial render and dynamic formsets well.The
__prefix__skip and already-initialized guard are good safeguards against duplicate/broken Select2 setup.tests/test_project/tests/test_widgets.py (1)
12-29: Widget-level tests are solid and focused.These assertions cover class behavior and media asset inclusion clearly.
openwisp_utils/widgets.py (1)
10-29: Media assembly is correct and complete for admin Select2 usage.The DEBUG-aware assets, optional i18n file, and custom initializer script are wired consistently.
| def __init__(self, attrs=None, choices=()): | ||
| attrs = attrs or {} | ||
| attrs["class"] = "ow-select2 {0}".format(attrs.get("class", "")).strip() | ||
| super().__init__(attrs, choices) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Avoid mutating caller-provided attrs in place.
Copy attrs first to prevent side effects when the same dict instance is reused.
Suggested refactor
def __init__(self, attrs=None, choices=()):
- attrs = attrs or {}
+ attrs = dict(attrs or {})
attrs["class"] = "ow-select2 {0}".format(attrs.get("class", "")).strip()
super().__init__(attrs, choices)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def __init__(self, attrs=None, choices=()): | |
| attrs = attrs or {} | |
| attrs["class"] = "ow-select2 {0}".format(attrs.get("class", "")).strip() | |
| super().__init__(attrs, choices) | |
| def __init__(self, attrs=None, choices=()): | |
| attrs = dict(attrs or {}) | |
| attrs["class"] = "ow-select2 {0}".format(attrs.get("class", "")).strip() | |
| super().__init__(attrs, choices) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@openwisp_utils/widgets.py` around lines 31 - 34, The __init__ mutates the
caller-provided attrs dict in place; change it to copy attrs first (e.g., attrs
= dict(attrs) or attrs.copy() when attrs is truthy) before updating the "class"
key so the original dict is not modified; update the __init__ implementation
(the attrs handling and the super().__init__(attrs, choices) call) in the widget
class to use the copied dict.
| def test_select2_widget_renders_on_shelf_add_form(self): | ||
| url = reverse("admin:test_project_shelf_add") | ||
| self.open(url) | ||
| self.wait_for_presence(By.CSS_SELECTOR, "select#id_books_type.ow-select2") | ||
|
|
There was a problem hiding this comment.
Add-page Selenium test should also assert Select2 initialization, not just CSS class.
This test currently proves widget markup but not JS enhancement on add view. Please also assert the Select2 container is present on this path.
Suggested test tightening
def test_select2_widget_renders_on_shelf_add_form(self):
url = reverse("admin:test_project_shelf_add")
self.open(url)
self.wait_for_presence(By.CSS_SELECTOR, "select#id_books_type.ow-select2")
+ self.wait_for_presence(By.CSS_SELECTOR, ".select2-container")Based on learnings: Features: Add tests for new features and ensure coverage does not decrease significantly; prefer Selenium browser tests for UI-impacting features.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/test_project/tests/test_widgets.py` around lines 37 - 41, The test
test_select2_widget_renders_on_shelf_add_form currently only checks for the
select element and its .ow-select2 class; update it to also assert the Select2
JS enhancement by waiting for the Select2 container for that field (for example
by waiting for a selector such as ".select2-container" or the field-specific
container tied to "id_books_type") to be present in the DOM after opening
reverse("admin:test_project_shelf_add"); use the existing helper
wait_for_presence to locate the Select2 container adjacent to or tied to the
select#id_books_type element to ensure initialization rather than just markup.
Implement a reusable Select2Widget that uses Django's native admin assets to avoid extra dependencies. Closes openwisp#254
2ccf8d1 to
0be733c
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (2)
tests/test_project/tests/test_widgets.py (1)
37-41:⚠️ Potential issue | 🟡 MinorAssert Select2 initialization on the add page too.
This currently checks only the rendered
<select>class; add the same container assertion used on the change page.💚 Proposed test tightening
def test_select2_widget_renders_on_shelf_add_form(self): url = reverse("admin:test_project_shelf_add") self.open(url) self.wait_for_presence(By.CSS_SELECTOR, "select#id_books_type.ow-select2") + self.wait_for_presence(By.CSS_SELECTOR, ".select2-container")Based on learnings: Features: Add tests for new features and ensure coverage does not decrease significantly; prefer Selenium browser tests for UI-impacting features.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_project/tests/test_widgets.py` around lines 37 - 41, The add-page test test_select2_widget_renders_on_shelf_add_form currently only asserts the rendered <select> class; update it to also assert the Select2 container is initialized by calling wait_for_presence with By.CSS_SELECTOR for the Select2 container (the same container selector used in the change-page test) immediately after the existing select#id_books_type.ow-select2 check so the test verifies both the original select element and the Select2 container are present.openwisp_utils/widgets.py (1)
31-34: 🧹 Nitpick | 🔵 TrivialCopy
attrsbefore adding the Select2 class.This mutates the caller-owned dict and can leak
ow-select2into reused attrs.♻️ Proposed fix
def __init__(self, attrs=None, choices=()): - attrs = attrs or {} + attrs = dict(attrs or {}) attrs["class"] = "ow-select2 {0}".format(attrs.get("class", "")).strip() super().__init__(attrs, choices)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openwisp_utils/widgets.py` around lines 31 - 34, The __init__ currently mutates the caller-supplied attrs dict which can leak the "ow-select2" class into reused dicts; fix by making a shallow copy of attrs before modifying (e.g., use attrs = dict(attrs or {}) or attrs.copy()) so you add attrs["class"] = "ow-select2 {0}".format(attrs.get("class", "")).strip() on the copied dict and then call super().__init__(attrs, choices) — update the __init__ method in the widget class to perform this non-mutating change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@openwisp_utils/static/openwisp-utils/js/select2.js`:
- Around line 15-17: The formset added handler in
openwisp-utils/static/openwisp-utils/js/select2.js assumes the added row is
passed as the second argument ($row) but Django 4.1 emits a CustomEvent with the
row at event.target; update the handler to normalize the row variable before
calling initSelect2—e.g., in the $(document).on("formset:added", ...) callback
determine rowEl = $row ? $row : $(event.target) (or wrap event.target with $ if
needed) and then call initSelect2(rowEl.find("select.ow-select2")); ensure you
reference the existing event handler and initSelect2 function names so
compatibility with Django >=2.2 and Django 4.1+ is preserved.
---
Duplicate comments:
In `@openwisp_utils/widgets.py`:
- Around line 31-34: The __init__ currently mutates the caller-supplied attrs
dict which can leak the "ow-select2" class into reused dicts; fix by making a
shallow copy of attrs before modifying (e.g., use attrs = dict(attrs or {}) or
attrs.copy()) so you add attrs["class"] = "ow-select2
{0}".format(attrs.get("class", "")).strip() on the copied dict and then call
super().__init__(attrs, choices) — update the __init__ method in the widget
class to perform this non-mutating change.
In `@tests/test_project/tests/test_widgets.py`:
- Around line 37-41: The add-page test
test_select2_widget_renders_on_shelf_add_form currently only asserts the
rendered <select> class; update it to also assert the Select2 container is
initialized by calling wait_for_presence with By.CSS_SELECTOR for the Select2
container (the same container selector used in the change-page test) immediately
after the existing select#id_books_type.ow-select2 check so the test verifies
both the original select element and the Select2 container are present.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 6f6811f8-d619-4264-aa9c-a5ebf336cd61
📒 Files selected for processing (5)
openwisp_utils/admin.pyopenwisp_utils/static/openwisp-utils/js/select2.jsopenwisp_utils/widgets.pytests/test_project/admin.pytests/test_project/tests/test_widgets.py
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (15)
- GitHub Check: Python==3.13 | django~=5.2.0
- GitHub Check: Python==3.12 | django~=5.0.0
- GitHub Check: Python==3.12 | django~=5.2.0
- GitHub Check: Python==3.11 | django~=5.2.0
- GitHub Check: Python==3.13 | django~=5.1.0
- GitHub Check: Python==3.12 | django~=5.1.0
- GitHub Check: Python==3.11 | django~=5.1.0
- GitHub Check: Python==3.10 | django~=5.0.0
- GitHub Check: Python==3.12 | django~=4.2.0
- GitHub Check: Python==3.11 | django~=4.2.0
- GitHub Check: Python==3.10 | django~=4.2.0
- GitHub Check: Python==3.11 | django~=5.0.0
- GitHub Check: Python==3.10 | django~=5.1.0
- GitHub Check: Python==3.10 | django~=5.2.0
- GitHub Check: Kilo Code Review
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{py,html,txt}
📄 CodeRabbit inference engine (Custom checks)
For Django pull requests, ensure all user-facing strings are marked as translatable using the Django i18n framework
Files:
openwisp_utils/admin.pyopenwisp_utils/widgets.pytests/test_project/admin.pytests/test_project/tests/test_widgets.py
🧠 Learnings (3)
📚 Learning: 2026-03-14T20:44:14.568Z
Learnt from: CR
Repo: openwisp/openwisp-utils PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-03-14T20:44:14.568Z
Learning: Features: Add tests for new features and ensure coverage does not decrease significantly; prefer Selenium browser tests for UI-impacting features
Applied to files:
tests/test_project/tests/test_widgets.py
📚 Learning: 2026-03-14T20:44:14.568Z
Learnt from: CR
Repo: openwisp/openwisp-utils PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-03-14T20:44:14.568Z
Learning: Changes: Update tests to cover non-trivial changes and ensure proper validation of modified behavior
Applied to files:
tests/test_project/tests/test_widgets.py
📚 Learning: 2026-03-14T20:44:14.568Z
Learnt from: CR
Repo: openwisp/openwisp-utils PR: 0
File: coderabbit-custom-pre-merge-checks-unique-id-file-non-traceable-F7F2B60C-1728-4C9A-8889-4F2235E186CA.txt:0-0
Timestamp: 2026-03-14T20:44:14.568Z
Learning: Bug Fixes: If the bug affects the user interface, include a Selenium browser test; if missing, raise a warning
Applied to files:
tests/test_project/tests/test_widgets.py
🔇 Additional comments (2)
tests/test_project/admin.py (1)
10-10: LGTM: reusable Select2 admin configuration is applied to a real choice field.
books_typeis a model choice field, so this exercises the new mixin without requiring per-adminformfield_for_choice_fieldoverrides.Also applies to: 113-125
openwisp_utils/admin.py (1)
5-5: LGTM: the mixin keeps the widget swap narrowly scoped to choice fields.The
super()delegation preserves Django admin’s normal choice-field handling while applyingSelect2Widgetonly for configured fields.Also applies to: 223-231
| $(document).on("formset:added", function (event, $row) { | ||
| initSelect2($row.find("select.ow-select2")); | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Inspect repository dependency constraints for supported Django versions.
set -euo pipefail
fd -H '^(setup\.py|setup\.cfg|pyproject\.toml|tox\.ini|requirements.*\.txt)$' \
-x sh -c 'echo "### $1"; rg -n "Django|django" "$1" || true' sh {}Repository: openwisp/openwisp-utils
Length of output: 595
🌐 Web query:
Django admin formset:added event signature event target row Django 4.1
💡 Result:
In Django 4.1 admin, the formset:added event is a JavaScript CustomEvent triggered when a new inline form row is added. Event signature: formset:added (dispatched on document). Event properties: - event.target: The newly added row element (HTMLElement). - event.detail.formsetName: The name of the formset (string). Usage example: document.addEventListener('formset:added', (event) => { if (event.detail.formsetName === 'my_formset') { // Access the new row const row = event.target; // Perform actions on the row } }); This changed in Django 4.1 from pre-4.1 jQuery events (event, $row, formsetName). For backward compatibility, check event.detail. Register in admin change_form.html by extending admin_change_form_document_ready block and including your JS.
Citations:
- 1: https://docs.djangoproject.com/en/4.1/ref/contrib/admin/javascript
- 2: https://django.fun/docs/django/4.1/ref/contrib/admin/javascript
- 3: https://docs.djangoproject.com/en/6.0/ref/contrib/admin/javascript/
- 4: https://django.readthedocs.io/en/latest/releases/4.1.html
🏁 Script executed:
#!/bin/bash
# Search for the main Django version requirement in setup.py
cat setup.py | grep -A 5 -B 5 "install_requires" | head -30Repository: openwisp/openwisp-utils
Length of output: 450
🏁 Script executed:
#!/bin/bash
# Check if Django version is specified anywhere else (requirements, tox, etc.)
rg "Django.*>=|Django.*<|django.*version" --no-headingRepository: openwisp/openwisp-utils
Length of output: 684
🏁 Script executed:
#!/bin/bash
# Check the full select2.js file to see if there's any version handling or comments about Django
cat openwisp_utils/static/openwisp-utils/js/select2.jsRepository: openwisp/openwisp-utils
Length of output: 494
Normalize the added formset row before calling .find() for Django 4.1+ compatibility.
The formset:added event API changed in Django 4.1: the added row is now available via event.target as a CustomEvent, not as a second parameter ($row). The current code will fail with $row undefined and prevent Select2 initialization on dynamically added inline rows in Django 4.1+. The codebase supports Django >= 2.2 with no upper bound, so compatibility is required.
🐛 Proposed cross-version fix
- $(document).on("formset:added", function (event, $row) {
- initSelect2($row.find("select.ow-select2"));
+ $(document).on("formset:added", function (event, $row) {
+ var $targetRow = $row ? $($row) : $(event.target);
+ initSelect2($targetRow.find("select.ow-select2"));
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| $(document).on("formset:added", function (event, $row) { | |
| initSelect2($row.find("select.ow-select2")); | |
| }); | |
| $(document).on("formset:added", function (event, $row) { | |
| var $targetRow = $row ? $($row) : $(event.target); | |
| initSelect2($targetRow.find("select.ow-select2")); | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@openwisp_utils/static/openwisp-utils/js/select2.js` around lines 15 - 17, The
formset added handler in openwisp-utils/static/openwisp-utils/js/select2.js
assumes the added row is passed as the second argument ($row) but Django 4.1
emits a CustomEvent with the row at event.target; update the handler to
normalize the row variable before calling initSelect2—e.g., in the
$(document).on("formset:added", ...) callback determine rowEl = $row ? $row :
$(event.target) (or wrap event.target with $ if needed) and then call
initSelect2(rowEl.find("select.ow-select2")); ensure you reference the existing
event handler and initSelect2 function names so compatibility with Django >=2.2
and Django 4.1+ is preserved.
|
Hi @shivsubh 👋, This is a friendly reminder that this pull request has had no activity for 7 days since changes were requested. We'd love to see this contribution merged! Please take a moment to:
If you're busy or need more time, no worries! Just leave a comment to let us know you're still working on it. Note: within 7 more days, the linked issue will be unassigned to allow other contributors to work on it. Thank you for your contribution! 🙏 |
Implement a reusable Select2Widget that uses Django's native admin assets to avoid extra dependencies.
Closes #254
Checklist
Reference to Existing Issue
Closes #254 .