Skip to content

Commit

Permalink
Fixes to polymorphic functionality. refs #138
Browse files Browse the repository at this point in the history
  • Loading branch information
fdintino committed Apr 19, 2019
1 parent fd3d930 commit 831abbf
Show file tree
Hide file tree
Showing 18 changed files with 356 additions and 41 deletions.
27 changes: 22 additions & 5 deletions nested_admin/nested.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@ def get_method_function(fn):
return fn.im_func if six.PY2 else fn


def get_model_id(model_cls):
opts = model_cls._meta
return "%s-%s" % (opts.app_label, opts.model_name)


lazy_reverse = lazy(reverse, str)
server_data_js_url = lazy_reverse('nesting_server_data')

Expand Down Expand Up @@ -70,7 +75,12 @@ def __init__(self, inline, *args, **kwargs):
self.classes = ''

def __iter__(self):
initial_form_count = self.formset.initial_form_count()
i = 0
for inline_admin_form in super(NestedInlineAdminFormsetMixin, self).__iter__():
if i >= initial_form_count and not self.has_add_permission:
continue
i += 1
if not getattr(inline_admin_form.form, 'inlines', None):
form = inline_admin_form.form
obj = form.instance if form.instance.pk else None
Expand Down Expand Up @@ -158,6 +168,10 @@ def inline_formset_data(self):
'sortableOptions': self.opts.sortable_options,
},
})
if hasattr(self.opts, 'parent_model'):
data['nestedOptions'].update({
'inlineParentModel': get_model_id(self.opts.parent_model),
})
return json.dumps(data)

@property
Expand Down Expand Up @@ -258,16 +272,19 @@ def _create_formsets(self, request, obj, change):
form_obj = None
InlineFormSet = inline.get_formset(request, form_obj)

if has_polymorphic and form_obj and hasattr(InlineFormSet, 'fk'):
rel_model = compat_rel_to(InlineFormSet.fk)
if not isinstance(form_obj, rel_model):
continue

prefix = '%s-%s' % (form_prefix, InlineFormSet.get_default_prefix())
prefixes[prefix] = prefixes.get(prefix, 0) + 1
if prefixes[prefix] != 1:
prefix = "%s-%s" % (prefix, prefixes[prefix])

if has_polymorphic and form_obj:
if hasattr(InlineFormSet, 'fk'):
rel_model = compat_rel_to(InlineFormSet.fk)
if not isinstance(form_obj, rel_model):
continue
if not isinstance(form_obj, inline.parent_model):
continue

formset_params = {
'instance': form_obj,
'prefix': prefix,
Expand Down
22 changes: 0 additions & 22 deletions nested_admin/polymorphic.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,28 +67,6 @@ class NestedBasePolymorphicInlineFormSet(
class NestedPolymorphicInlineAdminFormset(
NestedInlineAdminFormsetMixin, PolymorphicInlineAdminFormSet):

def __iter__(self):
if isinstance(self.formset, BasePolymorphicModelFormSet):
super_iter = super(NestedPolymorphicInlineAdminFormset, self).__iter__()
else:
super_iter = super(PolymorphicInlineAdminFormSet, self).__iter__()

for inline_admin_form in super_iter:
if not getattr(inline_admin_form.form, 'inlines', None):
form = inline_admin_form.form
obj = form.instance if form.instance.pk else None
formsets, inlines = [], []
obj_with_nesting_data = form
if form.prefix.endswith('__prefix__'):
obj_with_nesting_data = self.formset
formsets = getattr(obj_with_nesting_data, 'nested_formsets', None) or []
inlines = getattr(obj_with_nesting_data, 'nested_inlines', None) or []
form.inlines = self.model_admin.get_inline_formsets(self.request, formsets,
inlines, obj=obj, allow_nested=True)
for nested_inline in inline_admin_form.form.inlines:
for nested_form in nested_inline:
inline_admin_form.prepopulated_fields += nested_form.prepopulated_fields
yield inline_admin_form

def inline_formset_data(self):
json_str = super(NestedPolymorphicInlineAdminFormset, self).inline_formset_data()
Expand Down
14 changes: 11 additions & 3 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.js

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -300,8 +300,16 @@ class DjangoFormset {
$form.find('> .djn-group').each((i, el) => {
const fkModel = $(el).djnData('formsetFkModel');
const compatModels = compatibleParents[ctype] || [];
const isPolymorphic = !!($(el).data('inlineFormset').options.childTypes);
if (isPolymorphic && fkModel !== ctype && compatModels.indexOf(fkModel) === -1) {
const $el = $(el);
const parentModel = $el.djnData('inlineParentModel');
const isPolymorphic = !!($el.data('inlineFormset').options.childTypes);
const formPrefix = $el.data('inlineFormset').options.prefix;
if (parentModel !== ctype || (isPolymorphic && fkModel !== ctype && compatModels.indexOf(fkModel) === -1)) {
$el.find('input[id$="_FORMS"]').each((i, input) => {
input.value = 0
input.setAttribute('value', '0');
el.parentNode.appendChild(input);
});
el.parentNode.removeChild(el);
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ <h2 class="djn-collapse-handler grp-collapse-handler">
<div class="djn-item djn-no-drag"><div></div></div>
{% endif %}
<div class="{% if not forloop.last %}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"
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 %}"
{% endif %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,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 %} 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 %}"
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 %}"
{% endif %}
Expand Down
1 change: 1 addition & 0 deletions nested_admin/templates/nesting/admin/inlines/stacked.html
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ <h2>
<div class="djn-item djn-no-drag"><div></div></div>
{% endif %}
<div class="{% if not forloop.last %}djn-item{% endif %} djn-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 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 %}"
{% endif %}
Expand Down
5 changes: 3 additions & 2 deletions nested_admin/templates/nesting/admin/inlines/tabular.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
data-inline-formset="{{ inline_admin_formset.inline_formset_data }}"
data-inline-model="{{ inline_admin_formset.inline_model_id }}">
<div class="tabular inline-related">
<fieldset class="module {{ inline_admin_formset.classes }}">
<fieldset class="module djn-fieldset {{ inline_admin_formset.classes }}">

<h2>
{% if inline_admin_formset.opts.title %}{{ inline_admin_formset.opts.title }}{% else %}{{ inline_admin_formset.opts.verbose_name_plural|capfirst }}{% endif %}
Expand All @@ -19,7 +19,7 @@ <h2>
{{ inline_admin_formset.formset.non_form_errors }}

<table class="djn-items inline-related djn-table{% ifsuit %} table table-bordered table-condensed table-striped{% else %} tabular{% endifsuit %}">
<thead class="djn-module djn-thead djn-item">
<thead class="djn-module djn-thead">
<tr>
{% if "1.10"|django_version_gte %}<th class="original"></th>{% endif %}
{% for field in inline_admin_formset.fields %}
Expand All @@ -36,6 +36,7 @@ <h2>
{% 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="djn-tbody{% if not forloop.last %} djn-item{% endif %} djn-inline-form{% if inline_admin_formset.opts.classes %} {{ inline_admin_formset.opts.classes|join:" " }}{% 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 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 %}"
{% endif %}
Expand Down
1 change: 1 addition & 0 deletions nested_admin/tests/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ def initialize_page(self):
});
}
""")
self.selenium.execute_script("window.$ = window.django.jQuery")

def get_test_filename_base(self):
"""Returns a unique filename based on the current test conditions"""
Expand Down
15 changes: 12 additions & 3 deletions nested_admin/tests/nested_polymorphic/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,11 @@ def get_inline_model_names(self):
return self.selenium.execute_script("""
return (function getGroup($group) {
$group = (typeof $group === 'undefined') ? $('.djn-group-root') : $($group);
var $djnItems = $group.find('> .djn-fieldset > .djn-items, > .djn-items');
var $djnItems = $group.find([
'> .djn-fieldset > .djn-items',
'> .djn-items',
'> .tabular.inline-related > .djn-fieldset > .djn-items'
].join(', '));
var $forms = $djnItems.find('> .djn-inline-form:not(.djn-empty-form)');
return {
model: $group.attr('data-inline-model'),
Expand Down Expand Up @@ -146,11 +150,14 @@ def get_item(self, indexes):
base_model_cls = model_cls
base_model_id = "%s-%s" % (
base_model_cls._meta.app_label, base_model_cls._meta.model_name)
group_indexes.append(base_model_id)
group = self.get_group(indexes=group_indexes)
try:
group = self.get_group(indexes=group_indexes + [base_model_id])
except TypeError:
group = self.get_group(indexes=group_indexes + [model_id])
group_id = group.get_attribute('id')
djn_items = self.selenium.find_element_by_css_selector(
"#%(id)s > .djn-fieldset > .djn-items, "
"#%(id)s > .tabular.inline-related > .djn-fieldset > .djn-items, "
"#%(id)s > .djn-items" % {'id': group_id})
model_name, item_index = indexes[-1]
return djn_items.find_element_by_xpath(
Expand Down Expand Up @@ -198,6 +205,7 @@ def add_inline(self, indexes=None, model=None, **kwargs):

items_el = self.selenium.find_element_by_css_selector(
'#%(id)s > .djn-fieldset > .djn-items, '
"#%(id)s > .tabular.inline-related > .djn-fieldset > .djn-items, "
'#%(id)s > .djn-items' % {'id': group_id})

num_inlines = len(items_el.find_elements_by_xpath(
Expand All @@ -215,6 +223,7 @@ def get_num_inlines(self, indexes=None):
group_id = group.get_attribute('id')
djn_items = self.selenium.find_element_by_css_selector(
"#%(id)s > .djn-fieldset > .djn-items, "
"#%(id)s > .tabular.inline-related > .djn-fieldset > .djn-items, "
"#%(id)s > .djn-items" % {'id': group_id})
selector = "> .djn-item:not(.djn-no-drag,.djn-item-dragging,.djn-thead,.djn-empty-form)"
return self.selenium.execute_script(
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
from django.contrib import admin
from django.db import models
from django import forms

import nested_admin

from .models import (
FreeText, Poll, Question, MultipleChoiceGroup, MultipleChoice, Survey, Text, Textarea)


class TextInline(nested_admin.NestedTabularInline):
model = Text
extra = 1
min_num = 1
max_num = 1
sortable_field_name = "position"
formfield_overrides = {
models.PositiveSmallIntegerField: {'widget': forms.HiddenInput},
}


class TextareaInline(nested_admin.NestedTabularInline):
model = Textarea
extra = 1
min_num = 1
max_num = 1
sortable_field_name = "position"
formfield_overrides = {
models.PositiveSmallIntegerField: {'widget': forms.HiddenInput},
}


class RadioInline(nested_admin.NestedTabularInline):
model = MultipleChoice
sortable_field_name = "position"
extra = 0
min_num = 1
max_num = 8
radio_fields = {'style': admin.HORIZONTAL}
formfield_overrides = {
models.PositiveSmallIntegerField: {'widget': forms.HiddenInput},
}


class RadioGroupInline(nested_admin.NestedTabularInline):
model = MultipleChoiceGroup
inlines = (RadioInline,)
extra = 0
min_num = 1
max_num = 1
sortable_field_name = "position"
formfield_overrides = {
models.PositiveSmallIntegerField: {'widget': forms.HiddenInput},
}


class DropDownInline(nested_admin.NestedTabularInline):
model = MultipleChoice
sortable_field_name = "position"
extra = 0
min_num = 1
max_num = 8
formfield_overrides = {
models.PositiveSmallIntegerField: {'widget': forms.HiddenInput},
}


class DropDownGroupInline(nested_admin.NestedTabularInline):
model = MultipleChoiceGroup
inlines = (DropDownInline,)
extra = 0
min_num = 1
max_num = 1
sortable_field_name = "position"
formfield_overrides = {
models.PositiveSmallIntegerField: {'widget': forms.HiddenInput},
}


class QuestionInline(nested_admin.NestedStackedPolymorphicInline):
class FreeTextInline(nested_admin.NestedStackedPolymorphicInline.Child):
model = FreeText
inlines = (TextInline, TextareaInline, DropDownGroupInline)
sortable_field_name = "position"
formfield_overrides = {
models.PositiveSmallIntegerField: {'widget': forms.HiddenInput},
}

class PollInline(nested_admin.NestedStackedPolymorphicInline.Child):
model = Poll
inlines = (TextInline, RadioGroupInline,)
sortable_field_name = "position"
formfield_overrides = {
models.PositiveSmallIntegerField: {'widget': forms.HiddenInput},
}

model = Question
extra = 0
sortable_field_name = "position"
child_inlines = (FreeTextInline, PollInline,)
formfield_overrides = {
models.PositiveSmallIntegerField: {'widget': forms.HiddenInput},
}


@admin.register(Survey)
class SurveyAdmin(nested_admin.NestedPolymorphicModelAdmin):
inlines = (QuestionInline,)
Loading

0 comments on commit 831abbf

Please sign in to comment.