Skip to content

Commit 5e45358

Browse files
committed
fix: max_num show/hide bug with recursively nested models
fixes #230
1 parent 54f469a commit 5e45358

File tree

12 files changed

+130
-34
lines changed

12 files changed

+130
-34
lines changed

nested_admin/nested.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,13 @@ def inline_formset_data(self):
218218
@property
219219
def handler_classes(self):
220220
classes = set(getattr(self.opts, "handler_classes", None) or [])
221-
return tuple(classes | {"djn-model-%s" % self.inline_model_id})
221+
return tuple(
222+
classes
223+
| {
224+
"djn-model-%s" % self.inline_model_id,
225+
"djn-level-%s" % getattr(self.formset, "nesting_depth", 0),
226+
}
227+
)
222228

223229

224230
class NestedBaseInlineAdminFormSet(helpers.InlineAdminFormSet):

nested_admin/static/nested_admin/dist/nested_admin.js

Lines changed: 7 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nested_admin/static/nested_admin/dist/nested_admin.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nested_admin/static/nested_admin/dist/nested_admin.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nested_admin/static/nested_admin/dist/nested_admin.min.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

nested_admin/static/nested_admin/src/nested-admin/jquery.djangoformset.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ class DjangoFormset {
2424
this._$template = $("#" + this.prefix + "-empty");
2525

2626
var inlineModelClassName = this.$inline.djnData("inlineModel");
27+
const nestingLevel = this.$inline.djnData("nestingLevel");
28+
const handlerSelector = `.djn-model-${inlineModelClassName}.djn-level-${nestingLevel}`;
2729

2830
this.opts = $.extend({}, this.opts, {
2931
childTypes: this.$inline.data("inlineFormset").options.childTypes,
3032
formsetFkModel: this.$inline.djnData("formsetFkModel"),
31-
addButtonSelector: ".djn-add-handler.djn-model-" + inlineModelClassName,
32-
removeButtonSelector:
33-
".djn-remove-handler.djn-model-" + inlineModelClassName,
34-
deleteButtonSelector:
35-
".djn-delete-handler.djn-model-" + inlineModelClassName,
33+
addButtonSelector: ".djn-add-handler" + handlerSelector,
34+
removeButtonSelector: ".djn-remove-handler" + handlerSelector,
35+
deleteButtonSelector: ".djn-delete-handler" + handlerSelector,
3636
formClass:
3737
"dynamic-form grp-dynamic-form djn-dynamic-form-" +
3838
inlineModelClassName,
@@ -209,7 +209,7 @@ class DjangoFormset {
209209
if (maxForms - totalForms >= 0) {
210210
this.$inline
211211
.find(this.opts.addButtonSelector)
212-
.parents(".djn-add-item")
212+
.parent(".djn-add-item,li")
213213
.show();
214214
}
215215

@@ -400,7 +400,7 @@ class DjangoFormset {
400400
if (maxForms - (index + 1) <= 0) {
401401
this.$inline
402402
.find(this.opts.addButtonSelector)
403-
.parents(".djn-add-item")
403+
.parent(".djn-add-item,li")
404404
.hide();
405405
}
406406

nested_admin/tests/base.py

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,12 @@ def add_inline(self, indexes=None, name=None, slug=None):
377377
indexes = self._normalize_indexes(indexes, is_group=True)
378378
new_index = self.get_num_inlines(indexes)
379379
model_name = indexes[-1]
380-
add_selector = "#{} .djn-add-item a.djn-add-handler.djn-model-{}".format(
381-
self.get_group(indexes).get_attribute("id"), model_name
380+
add_selector = (
381+
"#{} .djn-add-item a.djn-add-handler.djn-model-{}.djn-level-{}".format(
382+
self.get_group(indexes).get_attribute("id"),
383+
model_name,
384+
len(indexes),
385+
)
382386
)
383387
with self.clickable_selector(add_selector) as el:
384388
self.click(el)
@@ -392,8 +396,8 @@ def add_inline(self, indexes=None, name=None, slug=None):
392396
def remove_inline(self, indexes):
393397
indexes = self._normalize_indexes(indexes)
394398
model_name = indexes[-1][0]
395-
remove_selector = "#{} .djn-remove-handler.djn-model-{}".format(
396-
self.get_item(indexes).get_attribute("id"), model_name
399+
remove_selector = "#{} .djn-remove-handler.djn-model-{}.djn-level-{}".format(
400+
self.get_item(indexes).get_attribute("id"), model_name, len(indexes)
397401
)
398402
with self.clickable_selector(remove_selector) as el:
399403
self.click(el)
@@ -402,33 +406,29 @@ def delete_inline(self, indexes):
402406
indexes = self._normalize_indexes(indexes)
403407
model_name = indexes[-1][0]
404408
item_id = self.get_item(indexes).get_attribute("id")
405-
delete_selector = "#{} .djn-delete-handler.djn-model-{}".format(
406-
item_id, model_name
409+
delete_selector = "#{} .djn-delete-handler.djn-model-{}.djn-level-{}".format(
410+
item_id, model_name, len(indexes)
407411
)
408412
with self.clickable_selector(delete_selector) as el:
409413
self.click(el)
410414
if self.has_grappelli:
411-
undelete_selector = (
412-
"#{}.grp-predelete .grp-delete-handler.djn-model-{}".format(
413-
item_id, model_name
414-
)
415+
undelete_selector = "#{}.grp-predelete .grp-delete-handler.djn-model-{}.djn-level-{}".format(
416+
item_id, model_name, len(indexes)
415417
)
416418
self.wait_until_clickable_selector(undelete_selector)
417419

418420
def undelete_inline(self, indexes):
419421
indexes = self._normalize_indexes(indexes)
420422
model_name = indexes[-1][0]
421423
item_id = self.get_item(indexes).get_attribute("id")
422-
undelete_selector = "#{} .djn-delete-handler.djn-model-{}".format(
423-
item_id, model_name
424+
undelete_selector = "#{} .djn-delete-handler.djn-model-{}.djn-level-{}".format(
425+
item_id, model_name, len(indexes)
424426
)
425427
with self.clickable_selector(undelete_selector) as el:
426428
self.click(el)
427429
if self.has_grappelli:
428-
delete_selector = (
429-
"#{}:not(.grp-predelete) .grp-delete-handler.djn-model-{}".format(
430-
item_id, model_name
431-
)
430+
delete_selector = "#{}:not(.grp-predelete) .grp-delete-handler.djn-model-{}.djn-level-{}".format(
431+
item_id, model_name, len(indexes)
432432
)
433433
self.wait_until_clickable_selector(delete_selector)
434434

nested_admin/tests/nested_polymorphic/base.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ def add_inline(self, indexes=None, model=None, **kwargs):
196196
)
197197

198198
if indexes:
199+
level = len(self._normalize_indexes(indexes, is_group=True))
199200
item = self.get_item(indexes)
200201
ctx_id = item.get_attribute("id")
201202
group_el = self.selenium.execute_script(
@@ -204,11 +205,14 @@ def add_inline(self, indexes=None, model=None, **kwargs):
204205
else:
205206
group_el = self.get_group([base_model_identifier])
206207
ctx_id = group_el.get_attribute("id")
208+
level = 1
207209

208210
error_desc = "{} in inline {}".format(model, indexes)
209211

210-
add_selector = "#{} .djn-add-item a.djn-add-handler.djn-model-{}".format(
211-
ctx_id, base_model_identifier
212+
add_selector = (
213+
"#{} .djn-add-item a.djn-add-handler.djn-model-{}.djn-level-{}".format(
214+
ctx_id, base_model_identifier, level
215+
)
212216
)
213217
add_els = self.selenium.find_elements_by_css_selector(add_selector)
214218
self.assertNotEqual(

nested_admin/tests/nested_recursive/__init__.py

Whitespace-only changes.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
from django.contrib import admin
2+
import nested_admin
3+
from .models import MenuItem
4+
5+
6+
class Level2Inline(nested_admin.NestedTabularInline):
7+
model = MenuItem
8+
extra = 0
9+
min_num = 0
10+
title = "Level 2"
11+
12+
13+
class Level1Inline(nested_admin.NestedTabularInline):
14+
model = MenuItem
15+
inlines = [Level2Inline]
16+
extra = 0
17+
min_num = 1
18+
max_num = 2
19+
title = "Level 1"
20+
21+
22+
@admin.register(MenuItem)
23+
class ParentAdmin(nested_admin.NestedModelAdmin):
24+
inlines = [Level1Inline]
25+
title = "Menu"
26+
fields = ["label"]

0 commit comments

Comments
 (0)