Skip to content

fix: compatibility with django 5.2 #269

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Apr 28, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 29 additions & 41 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,16 @@ jobs:
include:
- python-version: "3.9"
django-version: "4.2"
grappelli: "1"
- python-version: "3.9"
django-version: "5.0"
grappelli: "0"
- python-version: "3.10"
django-version: "5.0"
django-version: "4.2"
grappelli: "1"
- python-version: "3.12"
- python-version: "3.11"
django-version: "5.1"
grappelli: "0"
- python-version: "3.13"
django-version: "5.2"
grappelli: "0"

runs-on: ubuntu-latest
name: Django ${{ matrix.django-version }} (Python ${{ matrix.python-version }})${{ matrix.grappelli == '1' && ' + grappelli' || '' }}
Expand Down Expand Up @@ -86,29 +86,35 @@ jobs:

- name: Run tests
run: |
tox -- -vvv --selenosis-driver=chrome-headless || \
tox -- -vvv --selenosis-driver=chrome-headless
tox -- -vvv --selenosis-driver=chrome-headless --retries 3 || \
tox -- -vvv --selenosis-driver=chrome-headless --retries 3
env:
PIXELMATCH_BIN: ${{ env.GITHUB_WORKSPACE }}/node_modules/.bin/pixelmatch

- name: Upload junit xml
if: always()
uses: actions/upload-artifact@v3
with:
name: junit-reports
path: reports/*.xml

- name: Upload python coverage
- name: Generate python coverage
run: |
tox -e coverage-report
tox -e codecov
env:
CODECOV_NAME: ${{ github.workflow }}

- name: Upload js coverage
run: npm run report && npm run codecov
env:
CODECOV_NAME: ${{ github.workflow }}
- uses: codecov/codecov-action@v5
name: Upload python coverage
with:
files: .tox/coverage/coverage.xml
flags: python
name: ${{ github.workflow }}
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true

- name: Generate js coverage report
run: npm run report

- uses: codecov/codecov-action@v5
name: Upload js coverage
with:
files: coverage/lcov.info
flags: javascript
name: ${{ github.workflow }}
token: ${{ secrets.CODECOV_TOKEN }}
verbose: true

lint:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -180,26 +186,8 @@ jobs:
run: |
npm run lint:${{ matrix.tool }}

report:
if: always()
needs: build
runs-on: ubuntu-latest
name: "Report Test Results"
steps:
- uses: actions/download-artifact@v3
with:
name: junit-reports

- name: Publish tests report
uses: mikepenz/action-junit-report@1a91e26932fb7ba410a31fab1f09266a96d29971
with:
report_paths: ./*.xml
require_tests: true
fail_on_failure: true
check_name: Test Report

success:
needs: [lint, report]
needs: [lint, build]
runs-on: ubuntu-latest
name: Test Successful
steps:
Expand Down
1 change: 1 addition & 0 deletions nested_admin/nested.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def _djn_js_deps(self):
"admin/js/jquery.init.js",
"admin/js/prepopulate.js",
"admin/js/prepopulate.min.js",
"admin/js/SelectBox.js",
"admin/js/SelectFilter2.js",
"admin/js/autocomplete.js",
"jquery.grp.autocomplete_fk.js",
Expand Down
2 changes: 1 addition & 1 deletion nested_admin/static/nested_admin/dist/nested_admin.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion nested_admin/static/nested_admin/dist/nested_admin.css.map

Large diffs are not rendered by default.

44 changes: 42 additions & 2 deletions nested_admin/static/nested_admin/dist/nested_admin.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion nested_admin/static/nested_admin/dist/nested_admin.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion nested_admin/static/nested_admin/dist/nested_admin.min.css

Large diffs are not rendered by default.

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion nested_admin/static/nested_admin/dist/nested_admin.min.js

Large diffs are not rendered by default.

Large diffs are not rendered by default.

46 changes: 44 additions & 2 deletions nested_admin/static/nested_admin/src/nested-admin/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -308,11 +308,19 @@ DJNesting.DjangoInlines = {
if (typeof window.SelectFilter !== "undefined") {
$form.find(".selectfilter").each(function (index, value) {
var namearr = value.name.split("-");
SelectFilter.init(value.id, namearr[namearr.length - 1], false);
SelectFilter.init(
value.id,
this.dataset.fieldName ?? namearr[namearr.length - 1],
false
);
});
$form.find(".selectfilterstacked").each(function (index, value) {
var namearr = value.name.split("-");
SelectFilter.init(value.id, namearr[namearr.length - 1], true);
SelectFilter.init(
value.id,
this.dataset.fieldName ?? namearr[namearr.length - 1],
true
);
});
}
},
Expand Down Expand Up @@ -340,6 +348,40 @@ if (typeof window.SelectFilter !== "undefined") {
}, 12);
}

function patchSelectBox() {
window.SelectBox.init = (function (oldFn) {
return function init(field_id, field_name, is_stacked) {
if (field_id.match(/\-empty\-/)) {
return;
} else {
oldFn.apply(this, arguments);
}
};
})(window.SelectBox.init);
window.SelectBox.add_to_cache = (function (oldFn) {
return function add_to_cache(id, option) {
if (!id.match(/_(from|to)$/)) return;
return oldFn.apply(this, arguments);
};
})(window.SelectBox.add_to_cache);
window.SelectBox.redisplay = (function (oldFn) {
return function redisplay(id) {
if (!id.match(/_(from|to)$/)) return;
return oldFn.apply(this, arguments);
};
})(window.SelectBox.redisplay);
}

if (typeof window.SelectBox !== "undefined") {
patchSelectBox();
} else {
setTimeout(function () {
if (typeof window.SelectBox !== "undefined") {
patchSelectBox();
}
}, 12);
}

const djangoFuncs = ["prepopulate", "djangoAdminSelect2"];

djangoFuncs.forEach((funcName) => {
Expand Down
2 changes: 1 addition & 1 deletion nested_admin/static/nested_admin/src/nested_admin.scss
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
background: transparent;
}

.djn-group-nested.grp-stacked h2.djn-collapse-handler,
.djn-group-nested.grp-stacked > h2.djn-collapse-handler,
.djn-group-nested.grp-stacked > .grp-tools {
display: none;
}
Expand Down
38 changes: 23 additions & 15 deletions nested_admin/templates/nesting/admin/includes/inline.html
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
{% load nested_admin %}
<fieldset class="module aligned djn-module {{ fieldset.classes }}{% if inline_admin_form.form.inlines %} has-inlines{% endif %}">
{% if fieldset.name %}<h2>{{ fieldset.name }}</h2>{% endif %}
{% if fieldset.name %}
{% if fieldset.is_collapsible %}<details><summary>{% endif %}
<h{{ heading_level|default:2 }} class="fieldset-heading">{{ fieldset.name }}</h{{ heading_level|default:2 }}>
{% if fieldset.is_collapsible %}</summary>{% endif %}
{% endif %}
{% if fieldset.description %}
<div class="description">{{ fieldset.description|safe }}</div>
{% endif %}
{% for line in fieldset %}
<div class="form-row{% if line.fields|length == 1 and line.errors %} errors{% endif %}{% if not line.has_visible_field %} hidden{% endif %}{% for field in line %}{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% endfor %}{% if forloop.last and forloop.parentloop.last %} djn-form-row-last{% endif %}">
{% if line.fields|length == 1 %}{{ line.errors }}{% elif "4.2"|django_version_gte %}<div class="flex-container form-multiline">{% endif %}
{% if line.fields|length == 1 %}{{ line.errors }}{% else %}{% if "5.0"|django_version_gte %}<div class="flex-container form-multiline">{% endif %}{% endif %}
{% for field in line %}
{% if "4.2"|django_version_gte %}
<div>
{% if not line.fields|length == 1 and not field.is_readonly %}{{ field.errors }}{% endif %}
{% endif %}
<div class="{% if "4.2"|django_version_gte %}flex-container {% endif %}{% if line.fields|length == 1 and field.is_checkbox %}checkbox-row{% else %}{% if not line.fields|length == 1 %}fieldBox{% if not field.is_readonly and field.errors %} errors{% endif %}{% if field.field.is_hidden %} hidden{% endif %} {% endif %}field-{{ field.field.name }}{% endif %}">
{% if "4.2"|django_version_lt %}{% if not line.fields|length == 1 and not field.is_readonly %}{{ field.errors }}{% endif %}{% endif %}
<div{% if "5.0"|django_version_lt %} class="{% if line.fields|length == 1 and field.is_checkbox %}checkbox-row{% else %}{% if not line.fields|length == 1 %}fieldBox{% if not field.is_readonly and field.errors %} errors{% endif %}{% if field.field.is_hidden %} hidden{% endif %} {% endif %}field-{{ field.field.name }}{% endif %}"{% endif %}>
{% if not line.fields|length == 1 and not field.is_readonly %}{{ field.errors }}{% endif %}
{% if "5.0"|django_version_gte %}
<div class="flex-container{% if not line.fields|length == 1 %} fieldBox{% if field.field.name %} field-{{ field.field.name }}{% endif %}{% if not field.is_readonly and field.errors %} errors{% endif %}{% if field.field.is_hidden %} hidden{% endif %}{% endif %}{% if field.is_checkbox %} checkbox-row{% endif %}">
{% endif %}
{% if field.is_checkbox %}
{{ field.field }}{{ field.label_tag }}
{% else %}
Expand All @@ -24,15 +27,20 @@
{{ field.field }}
{% endif %}
{% endif %}
{% if field.field.help_text %}
<div class="help{% if field.field.is_hidden %} hidden{% endif %}"{% if field.field.id_for_label %} id="{{ field.field.id_for_label }}_helptext"{% endif %}>
{% if "5.0"|django_version_lt %}
{{ field.field.help_text|safe }}
{% else %}
<div>{{ field.field.help_text|safe }}</div>
{% endif %}
</div>
{% endif %}
{% if "5.0"|django_version_gte %}</div>{% endif %}
</div>
{% if field.field.help_text %}
<div class="help"{% if field.field.id_for_label %} id="{{ field.field.id_for_label }}_helptext"{% endif %}>
{{ field.field.help_text|safe }}
</div>
{% endif %}
{% if "4.2"|django_version_gte %}</div>{% endif %}
{% endfor %}
{% if "4.2"|django_version_gte %}{% if not line.fields|length == 1 %}</div>{% endif %}{% endif %}
{% if "5.0"|django_version_gte and not line.fields|length == 1 %}</div>{% endif %}
</div>
{% endfor %}
{% if fieldset.name and fieldset.is_collapsible %}</details>{% endif %}
</fieldset>
19 changes: 7 additions & 12 deletions nested_admin/templates/nesting/admin/inlines/stacked.html
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
data-inline-formset="{{ inline_admin_formset.inline_formset_data }}"
data-inline-model="{{ inline_admin_formset.inline_model_id }}">

{% ifinlineclasses %}<fieldset class="djn-fieldset module {{ inline_admin_formset.classes }}" aria-labelledby="{{ inline_admin_formset.formset.prefix }}-heading">{% endifinlineclasses %}
{% ifinlineclasses %}<fieldset class="djn-fieldset module {{ inline_admin_formset.classes }}">{% endifinlineclasses %}
{% if inline_admin_formset.is_collapsible %}<details><summary>{% endif %}
<h2 id="{{ inline_admin_formset.formset.prefix }}-heading" class="inline-heading">
{% if inline_admin_formset.opts.title %}{{ inline_admin_formset.opts.title }}{% else %}{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}{% endif %}
<h2>
{% if inline_admin_formset.opts.title %}{{ inline_admin_formset.opts.title }}{% else %}{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}{% endif %}
</h2>
{% if inline_admin_formset.is_collapsible %}</summary>{% endif %}

Expand Down Expand Up @@ -50,18 +50,13 @@ <h3 class="{% if not inline_opts.sortable_options or not inline_opts.sortable_op
<li>{{ inline_admin_form.form.non_field_errors }}</li>
</ul>
{% endif %}

{% if "5.1"|django_version_gte %}
{% with parent_counter=forloop.counter0 %}
{% for fieldset in inline_admin_form %}
{% include inline_admin_formset.opts.fieldset_template with heading_level=4 id_prefix=parent_counter id_suffix=forloop.counter0 %}
{% endfor %}
{% endwith %}
{% else %}
{% for fieldset in inline_admin_form %}
{% if "5.0"|django_version_gte %}
{% include inline_admin_formset.opts.fieldset_template with heading_level=4 %}
{% else %}
{% include inline_admin_formset.opts.fieldset_template %}
{% endif %}
{% endfor %}
{% endif %}
{% if inline_admin_form.has_auto_field or inline_admin_form.needs_explicit_pk_field %}
{{ inline_admin_form.pk_field.field }}
{% endif %}
Expand Down
7 changes: 4 additions & 3 deletions nested_admin/templates/nesting/admin/inlines/tabular.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@
data-inline-formset="{{ inline_admin_formset.inline_formset_data }}"
data-inline-model="{{ inline_admin_formset.inline_model_id }}">
<div class="tabular inline-related {% if forloop.last and inline_admin_formset.has_add_permission %}last-related{% endif %}">
<fieldset class="module djn-fieldset {{ inline_admin_formset.classes }}" aria-labelledby="{{ inline_admin_formset.formset.prefix }}-heading">
<fieldset class="module djn-fieldset {{ inline_admin_formset.classes }}">

{% if inline_admin_formset.is_collapsible %}<details><summary>{% endif %}

<h2 id="{{ inline_admin_formset.formset.prefix }}-heading" class="inline-heading">
{% if inline_admin_formset.opts.title %}{{ inline_admin_formset.opts.title }}{% else %}{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}{% endif %}
<h2>
{% if inline_admin_formset.opts.title %}{{ inline_admin_formset.opts.title }}{% else %}{% if inline_admin_formset.formset.max_num == 1 %}{{ inline_admin_formset.opts.verbose_name|capfirst }}{% else %}{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}{% endif %}{% endif %}
</h2>
{% if inline_admin_formset.is_collapsible %}</summary>{% endif %}

Expand Down
9 changes: 7 additions & 2 deletions nested_admin/tests/admin_widgets/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from html import unescape
import time

import django
from django.contrib.contenttypes.models import ContentType
from django.conf import settings
from django.utils.text import slugify
Expand Down Expand Up @@ -135,8 +136,12 @@ def check_datetime(self, indexes):
self.assertNotEqual(time_el.get_attribute("value"), "", "Time was not set")

def check_m2m(self, indexes):
add_all_link = self.get_field("m2m_add_all_link", indexes)
remove_all_link = self.get_field("m2m_remove_all_link", indexes)
if django.VERSION >= (5, 2):
add_all_link = self.get_field("m2m_add_all", indexes)
remove_all_link = self.get_field("m2m_remove_all", indexes)
else:
add_all_link = self.get_field("m2m_add_all_link", indexes)
remove_all_link = self.get_field("m2m_remove_all_link", indexes)
self.click(remove_all_link)
self.click(add_all_link)
m2m_to_sel = self.get_form_field_selector("m2m_to", indexes)
Expand Down
2 changes: 1 addition & 1 deletion nested_admin/tests/one_deep/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ def get_admin_screenshot(self):
# Move mouse to a consistent place, to avoid hover styles confusing things
# body_element = self.selenium.execute_script('return document.body')
self.selenium.execute_script("document.body.scrollTop = 0")
self.selenium.execute_script('$("*:focus").blur()')
self.selenium.execute_script("document.activeElement.blur()")
time.sleep(0.2)
self.selenium.save_screenshot(image_path)
return image_path
Expand Down
Loading