Skip to content

Commit

Permalink
fix: prepopulated fields in grappelli 2.15+
Browse files Browse the repository at this point in the history
  • Loading branch information
fdintino committed Oct 14, 2022
1 parent 5bbdb41 commit 0e41419
Show file tree
Hide file tree
Showing 15 changed files with 1,219 additions and 109 deletions.
36 changes: 11 additions & 25 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,24 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10"]
django-version: ["2.2", "3.2", "4.0", "4.1"]
grappelli: ["0"]
exclude:
- python-version: "3.7"
django-version: "4.0"
- python-version: "3.7"
django-version: "4.1"
- python-version: "3.10"
django-version: "2.2"
# Exclude some version combos that don't need to be tested (since
# the combination of python and django versions is unlikely to
# be germane to django-nested-admin)
- python-version: "3.8"
django-version: "3.2"
- python-version: "3.8"
django-version: "4.1"
- python-version: "3.9"
django-version: "4.0"
- python-version: "3.9"
django-version: "4.1"
python-version: ["3.8"]
django-version: ["3.2"]
include:
- grappelli: "0"
name-suffix: ""
- python-version: "3.9"
django-version: "4.0"
- python-version: "3.10"
django-version: "4.1"
- grappelli: "1"
name-suffix: " + grappelli"
python-version: "3.7"
django-version: "2.2"
# - grappelli: "1"
# name-suffix: " + grappelli"
# python-version: "3.10"
# django-version: "3.2"
django-version: "3.2"
- grappelli: "1"
name-suffix: " + grappelli"
python-version: "3.8"
django-version: "4.0"

runs-on: ubuntu-latest
name: Django ${{ matrix.django-version }} (Python ${{ matrix.python-version }})${{ matrix.name-suffix }}
Expand Down
6 changes: 4 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ Changelog

**4.0.0 (unreleased)**

* Remove python 2.x compatibility, drop support for EOL Django versions
(all versions before 3.2)
* Officially support Django 4.1
* Fixed: Django 4.1 autocomplete on newly added nested inlines by dispatching
native javascript CustomEvent events for ``formset:added`` and
``formset:removed``. Fixes `#229`_.
* Fixed: prepopulated fields in django-grappelli 2.15+
* Remove python 2.x compatibility, drop support for EOL Django versions
(all versions before 3.2)

.. _#229: https://github.com/theatlantic/django-nested-admin/issues/229

Expand Down
1,169 changes: 1,106 additions & 63 deletions nested_admin/static/nested_admin/dist/nested_admin.js

Large diffs are not rendered by default.

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.js

Large diffs are not rendered by default.

Large diffs are not rendered by default.

20 changes: 20 additions & 0 deletions nested_admin/static/nested_admin/src/nested-admin/django$.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import $ from "jquery";

/**
* Converts a grp.jQuery instance to a django.jQuery instance.
*/
function django$($sel) {
if (typeof window.grp === "undefined") {
return $($sel);
}
if (window.grp.jQuery.fn.init === $.fn.init) {
return $($sel);
}
const $djangoSel = $($sel);
if ($sel.prevObject) {
$djangoSel.prevObject = django$($sel.prevObject);
}
return $djangoSel;
}

export default django$;
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import DJNesting from "./utils";
import * as grappelli from "grappelli";
import grp from "grp";
import grp$ from "./grp$";
// const grp = require('grp');
// const grp$ = require('./grp$');
import django$ from "./django$";

var pluginName = "djangoFormset";

Expand Down Expand Up @@ -416,7 +415,7 @@ class DjangoFormset {
if (grappelli) {
grappelli.reinitDateTimeFields(grp$($form));
}
DJNesting.DjangoInlines.initPrepopulatedFields($form);
DJNesting.DjangoInlines.initPrepopulatedFields(django$($form));
DJNesting.DjangoInlines.reinitDateTimeShortCuts();
DJNesting.DjangoInlines.updateSelectFilter($form);
DJNesting.initRelatedFields(this.prefix);
Expand Down
71 changes: 66 additions & 5 deletions nested_admin/static/nested_admin/src/nested-admin/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import $ from "jquery";
import "./jquery.djnutils";
import { createSortable, updatePositions } from "./sortable";
import regexQuote from "./regexquote";
import django$ from "./django$";
import grp$ from "./grp$";

var DJNesting = typeof window.DJNesting != "undefined" ? window.DJNesting : {};
Expand Down Expand Up @@ -61,6 +62,9 @@ DJNesting.updateFormAttributes = function ($elem, search, replace, selector) {
// update prepopulate ids for function initPrepopulatedFields
$elem.find(".prepopulated_field").each(function () {
var $node = grp$(this);
if (typeof $node.prepopulate !== "function") {
$node = django$(this);
}
var dependencyIds = $.makeArray($node.data("dependency_ids") || []);
$node.data(
"dependency_ids",
Expand Down Expand Up @@ -212,25 +216,82 @@ DJNesting.initAutocompleteFields = function (prefix, groupData) {
});
};

function getLevelPrefix(id) {
return id
.replace(/^\#?id_/, "")
.split(/-(?:empty|__prefix__|\d+)-/g)
.slice(0, -1)
.join("-");
}

// I very much regret that these are basically copy-pasted from django's
// inlines.js, but they're hidden in closure scope so I don't have much choice.
DJNesting.DjangoInlines = {
initPrepopulatedFields: function (row) {
const formPrefix = row.djangoFormPrefix();
if (!formPrefix) return;
const fields = $("#django-admin-prepopulated-fields-constants").data(
"prepopulatedFields"
);
const fieldNames = new Set();
const fieldDependencies = {};

if (Array.isArray(fields)) {
fields.forEach(
({ id, name, dependency_list, maxLength, allowUnicode }) => {
fieldNames.add(name);
const levelPrefix = getLevelPrefix(id);
if (typeof fieldDependencies[levelPrefix] !== "object") {
fieldDependencies[levelPrefix] = {};
}
fieldDependencies[levelPrefix][name] = {
dependency_list,
maxLength,
allowUnicode,
};
}
);
fieldNames.forEach((name) => {
row
.find(`.form-row .field-${name}, .form-row.field-${name}`)
.each(function () {
const $el = $(this);
const prefix = $el.djangoFormPrefix();
if (!prefix) return;
const levelPrefix = getLevelPrefix(prefix);
const dep = (fieldDependencies[levelPrefix] || {})[name];
if (dep) {
$el.addClass("prepopulated_field");
const $field = $el.is(":input") ? $el : $el.find(":input");
$field.data("dependency_list", dep.dependency_list);
$field.data("maxLength", dep.maxLength);
$field.data("allowUnicode", dep.allowUnicode);
}
});
});
}
if (formPrefix.match(/__prefix__/)) return;
row.find(".prepopulated_field").each(function () {
var field = $(this),
input = field.is(":input") ? field : field.find(":input"),
$input = grp$(input),
inputFormPrefix = input.djangoFormPrefix(),
dependencyList = $input.data("dependency_list") || [],
formPrefix = input.djangoFormPrefix(),
dependencies = [];
if (!formPrefix || formPrefix.match(/__prefix__/)) {
return;
if (!inputFormPrefix || inputFormPrefix.match(/__prefix__/)) return;
if (!dependencyList.length || !$input.prepopulate) {
$input = django$(input);
dependencyList = $input.data("dependency_list") || [];
}
$.each(dependencyList, function (i, fieldName) {
dependencies.push("#id_" + formPrefix + fieldName);
dependencies.push("#id_" + inputFormPrefix + fieldName);
});
if (dependencies.length) {
$input.prepopulate(dependencies, input.attr("maxlength"));
$input.prepopulate(
dependencies,
$input.data("maxLength") || $input.attr("maxlength"),
$input.data("allowUnicode")
);
}
});
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
{% if fieldset.name %}<h4 class="djn-collapse-handler grp-collapse-handler">{{ fieldset.name }}</h4>{% endif %}
{% if fieldset.description %}<div class="grp-row"><p class="grp-description">{{ fieldset.description|safe }}</p></div>{% endif %}
{% for line in fieldset %}
<div class="form-row djn-row grp-row grp-cells-{{ line.fields|length }}{% if not line.fields|length_is:"1" %} grp-cells{% else %}{% if line.errors %} grp-errors{% endif %}{% endif %}{% if not line.has_visible_field %} grp-row-hidden{% endif %}{% for field in line %} {{ field.field.name }}{% endfor %} ">
<div class="form-row djn-row grp-row grp-cells-{{ line.fields|length }}{% if not line.fields|length_is:"1" %} grp-cells{% else %}{% if line.errors %} grp-errors{% endif %}{% for field in line %} {{ field.field.name }} field-{{ field.field.name }}{% endfor %}{% endif %}{% if not line.has_visible_field %} grp-row-hidden{% endif %}">
{% for field in line %}
{# <div{% if not line.fields|length_is:"1" %} class="cell {{ field.field.name }}{% if field.errors %} error{% endif %}"{% endif %}> #}
<div class="field-box l-2c-fluid l-d-4{% if line.fields|length_is:"1" %}{% else %} grp-cell{% if field.field.name %} {{ field.field.name }} field-{{ field.field.name }}{% endif %}{% if field.field.errors %} grp-errors{% endif %}{% endif %}">
Expand Down
4 changes: 3 additions & 1 deletion nested_admin/templates/nesting/admin/includes/inline.html
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
{% endif %}
{% endif %}
{% if field.field.help_text %}
<div class="help">{{ field.field.help_text|safe }}</div>
<div class="help"{% if field.field.id_for_label %} id="{{ field.field.id_for_label }}_helptext"{% endif %}>
{{ field.field.help_text|safe }}
</div>
{% endif %}
</div>
{% endfor %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ <h2 class="djn-collapse-handler grp-collapse-handler">
{% if forloop.first %}
<div class="djn-item djn-no-drag"><div></div></div>
{% endif %}
<div class="{% if not forloop.last or not inline_admin_formset.has_add_permission %}djn-item{% endif %} grp-module djn-module djn-inline-form {% if inline_admin_formset.opts.inline_classes %}{{ inline_admin_formset.opts.inline_classes|join:" "|default:"grp-collapse grp-closed" }}{% else %}grp-collapse grp-closed{% endif %}{% if inline_admin_form.original or inline_admin_form.show_url %} has_original{% endif %}{% if forloop.last and inline_admin_formset.has_add_permission %} djn-empty-form grp-empty-form{% endif %} inline-related"
<div class="{% if not forloop.last or not inline_admin_formset.has_add_permission %}djn-item{% endif %} grp-module djn-module djn-inline-form {% if inline_admin_formset.opts.inline_classes %}{{ inline_admin_formset.opts.inline_classes|join:" "|default:"grp-collapse grp-closed" }}{% else %}grp-collapse grp-closed{% endif %}{% if inline_admin_form.original or inline_admin_form.show_url %} has_original{% endif %}{% if forloop.last and inline_admin_formset.has_add_permission %} empty-form djn-empty-form grp-empty-form{% endif %} inline-related"
data-inline-model="{{ inline_admin_form.model_admin.opts.app_label }}-{{ inline_admin_form.model_admin.opts.model_name }}"
{% if inline_admin_form.pk_field.field %}
data-is-initial="{% if inline_admin_form.pk_field.field.value %}true{% else %}false{% endif %}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ <h2 class="djn-collapse-handler grp-collapse-handler">

{% with inline_admin_formset.opts.sortable_field_name|default:"" as sortable_field_name %}
{% for inline_admin_form in inline_admin_formset|formsetsort:sortable_field_name %}
<tbody class="grp-tbody djn-tbody{% if not forloop.last or not inline_admin_formset.has_add_permission %} djn-item{% endif %} form-row grp-module djn-inline-form{% if inline_admin_form.original or inline_admin_form.show_url %} has_original{% endif %}{% if forloop.last and inline_admin_formset.has_add_permission %} djn-empty-form grp-empty-form{% endif %}{% if inline_admin_form.form.inlines %} djn-has-inlines{% endif %}"
<tbody class="grp-tbody djn-tbody{% if not forloop.last or not inline_admin_formset.has_add_permission %} djn-item{% endif %} form-row grp-module djn-inline-form{% if inline_admin_form.original or inline_admin_form.show_url %} has_original{% endif %}{% if forloop.last and inline_admin_formset.has_add_permission %} empty-form djn-empty-form grp-empty-form{% endif %}{% if inline_admin_form.form.inlines %} djn-has-inlines{% endif %}"
data-inline-model="{{ inline_admin_form.model_admin.opts.app_label }}-{{ inline_admin_form.model_admin.opts.model_name }}"
{% if inline_admin_form.pk_field.field %}
data-is-initial="{% if inline_admin_form.pk_field.field.value %}true{% else %}false{% endif %}"
Expand Down
3 changes: 0 additions & 3 deletions nested_admin/tests/admin_widgets/tests.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
from contextlib import contextmanager
from html import unescape
import time
from unittest import SkipTest

from django.contrib.contenttypes.models import ContentType
from django.conf import settings
Expand Down Expand Up @@ -402,8 +401,6 @@ def test_gfk_related_lookup_add_three_deep(self):
self.check_gfk_related_lookup([1, 0, [1, 0]])

def test_nested_autocomplete_extra(self):
if self.has_grappelli:
raise SkipTest("Not testing autocomplete on grappelli")
self.load_admin()
self.add_inline([0, [0]])
self.add_inline([0, 1, [0]])
Expand Down
2 changes: 1 addition & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
envlist =
py{36,37,38,39}-dj22-{grp,nogrp}
py{36,37,38,39,310}-dj32-{grp,nogrp}
py{38,39,310}-dj40-nogrp
py{38,39,310}-dj40-{grp,nogrp}
py{38,39,310}-dj41-nogrp
black,flake8
skipsdist=True
Expand Down

0 comments on commit 0e41419

Please sign in to comment.