1
0
mirror of https://github.com/django/django.git synced 2025-07-04 17:59:13 +00:00

[soc2009/admin-ui] A far better way to implement adding inlines using javascript. Instead of handling all the prefix incrementing in Javascript and losing any default values, added a way to generate a template form that can be cloned every time a new inline is added.

This is for stacked inlines. Support for tabular and selector inlines coming next. 


git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/admin-ui@11152 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Zain Memon 2009-07-02 07:19:44 +00:00
parent f5bb5b5f98
commit d2d6301455
5 changed files with 55 additions and 45 deletions

View File

@ -115,6 +115,8 @@ class InlineAdminFormSet(object):
for form in self.formset.extra_forms: for form in self.formset.extra_forms:
yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, None) yield InlineAdminForm(self.formset, form, self.fieldsets, self.opts.prepopulated_fields, None)
yield InlineAdminForm(self.formset, self.formset.empty_form, self.fieldsets, self.opts.prepopulated_fields, None)
def fields(self): def fields(self):
fk = getattr(self.formset, "fk", None) fk = getattr(self.formset, "fk", None)
for field_name in flatten_fieldsets(self.fieldsets): for field_name in flatten_fieldsets(self.fieldsets):

View File

@ -350,6 +350,10 @@ table.orderable-initalized .order-cell, body>tr>td.order-cell {
background-color: #F6F6F6; background-color: #F6F6F6;
} }
.empty_form {
display: none;
}
/* FORM DEFAULTS */ /* FORM DEFAULTS */
input, textarea, select { input, textarea, select {

View File

@ -5,7 +5,7 @@
{{ inline_admin_formset.formset.non_form_errors }} {{ inline_admin_formset.formset.non_form_errors }}
{% for inline_admin_form in inline_admin_formset %} {% for inline_admin_form in inline_admin_formset %}
<div class="inline-related{% if forloop.last %} last-related{% endif %}" id="{{ inline_admin_formset.opts.verbose_name}}{{ forloop.counter }}"> <div class="inline-related{% if forloop.last %} empty_form{% endif %}" id="{{ inline_admin_formset.opts.verbose_name}}{% if not forloop.last %}{{ forloop.counter }}{% else %}-empty{% endif %}">
<h3><b>{{ inline_admin_formset.opts.verbose_name|title }}:</b>&nbsp;<span class="inline_label">{% if inline_admin_form.original %}{{ inline_admin_form.original }}{% else %} #{{ forloop.counter }}{% endif %}</span> <h3><b>{{ inline_admin_formset.opts.verbose_name|title }}:</b>&nbsp;<span class="inline_label">{% if inline_admin_form.original %}{{ inline_admin_form.original }}{% else %} #{{ forloop.counter }}{% endif %}</span>
{% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}<span class="delete">{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}</span>{% endif %} {% if inline_admin_formset.formset.can_delete and inline_admin_form.original %}<span class="delete">{{ inline_admin_form.deletion_field.field }} {{ inline_admin_form.deletion_field.label_tag }}</span>{% endif %}
</h3> </h3>
@ -22,62 +22,48 @@
</div> </div>
{% endfor %} {% endfor %}
<ul class="tools add_inline"> <ul class="tools add_inline" id="{{ inline_admin_formset.opts.verbose_name}}-addinline">
<li><a id="{{ inline_admin_formset.opts.verbose_name }}-add" class="add" href="#">Add a {{ inline_admin_formset.opts.verbose_name }}</a></li> <li><a id="{{ inline_admin_formset.opts.verbose_name }}-add" class="add" href="#">Add a {{ inline_admin_formset.opts.verbose_name }}</a></li>
</ul> </ul>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
function increment_fields(el) {
el.attr('id', el.attr('id').replace(/-(\d+)-/, function (num) {
var newnum = parseInt(num.replace(/-/g,''))+1;
return '-' + newnum + '-';
}));
el.attr('name', el.attr('name').replace(/-(\d+)-/, function (num) {
var newnum = parseInt(num.replace(/-/g,''))+1;
return '-' + newnum + '-';
}));
}
$(function() { $(function() {
var id_prefix = "{{ inline_admin_formset.opts.verbose_name }}"; var prefix = "{{ inline_admin_formset.opts.verbose_name }}";
var total_forms = $('#' + id_prefix + '-group input[id$="TOTAL_FORMS"]'); var id_prefix = "#" + prefix;
var initial_forms = $('#' + id_prefix + '-group').find('input[id$="INITIAL_FORMS"]'); var total_forms = $(id_prefix + '-group input[id$="TOTAL_FORMS"]');
var initial_forms = $(id_prefix + '-group').find('input[id$="INITIAL_FORMS"]');
// since javascript is turned on, unhide the "add new <inline>" link and hide the extras // since javascript is turned on, unhide the "add new <inline>" link
$('.add_inline').show(); $('.add_inline').show();
// hide the extras, but only if there were no form errors
if (!$('.errornote').html()) {
if (parseInt(initial_forms.val()) > 0) { if (parseInt(initial_forms.val()) > 0) {
$('#' + id_prefix + '-group .inline-related:gt(' + (initial_forms.val() - 1) + ')').remove(); $(id_prefix + '-group .inline-related:gt(' + (initial_forms.val() - 1) + ')')
.not('.empty_form').remove();
} }
else { else {
$('#' + id_prefix + '-group .inline-related:gt(0)').remove(); $(id_prefix + '-group .inline-related').not('.empty_form').remove();
$('#' + id_prefix + '-group .inline-related:first').hide();
} }
total_forms.val(parseInt(initial_forms.val())); total_forms.val(parseInt(initial_forms.val()));
// clicking on the "add" link will add a blank form to add a new inline object
$('#' + id_prefix + "-add").click(function() {
if (parseInt(total_forms.val()) == 0) {
$('#' + id_prefix + '-group .inline-related:first').fadeIn('normal');
total_forms.val(parseInt(total_forms.val()) + 1);
return false;
} }
var last_inline = $('#' + id_prefix + '-group .inline-related:last'); $(id_prefix + "-add").click(function() {
var new_inline = last_inline.clone(true).hide().insertAfter(last_inline).fadeIn('normal'); var new_inline = $(id_prefix + '-empty').clone(true)
.insertBefore(id_prefix + '-addinline').fadeIn('normal');
new_inline.find('input, select').each(function(i) { var inline_template = $(new_inline).html();
increment_fields($(this)); var new_inline_html = inline_template.replace(/__prefix__/g, total_forms.val().toString());
$(this).val("");
});
total_forms.val(parseInt(total_forms.val()) + 1); total_forms.val(parseInt(total_forms.val()) + 1);
new_inline.find(".inline_label").text('#' + total_forms.val()); $(new_inline).html(new_inline_html);
$(new_inline).attr('id', prefix + total_forms.val().toString());
$(new_inline).find('.inline_label').html('#' + total_forms.val().toString());
$(new_inline).removeClass('empty_form');
return false; return false;
}); });

View File

@ -118,6 +118,21 @@ class BaseFormSet(StrAndUnicode):
return self.forms[self.initial_form_count():] return self.forms[self.initial_form_count():]
extra_forms = property(_get_extra_forms) extra_forms = property(_get_extra_forms)
def _get_empty_form(self, **kwargs):
defaults = {'auto_id': self.auto_id, 'prefix': self.add_prefix("__prefix__")}
if self.data or self.files:
defaults['data'] = self.data
defaults['files'] = self.files
defaults['empty_permitted'] = True
defaults.update(kwargs)
form = self.form(**defaults)
self.add_fields(form, None)
return form
empty_form = property(_get_empty_form)
# Maybe this should just go away? # Maybe this should just go away?
def _get_cleaned_data(self): def _get_cleaned_data(self):
""" """
@ -133,7 +148,7 @@ class BaseFormSet(StrAndUnicode):
Returns a list of forms that have been marked for deletion. Raises an Returns a list of forms that have been marked for deletion. Raises an
AttributeError if deletion is not allowed. AttributeError if deletion is not allowed.
""" """
if not self.can_delete: if not self.is_valid or not self.can_delete:
raise AttributeError("'%s' object has no attribute 'deleted_forms'" % self.__class__.__name__) raise AttributeError("'%s' object has no attribute 'deleted_forms'" % self.__class__.__name__)
# construct _deleted_form_indexes which is just a list of form indexes # construct _deleted_form_indexes which is just a list of form indexes
# that have had their deletion widget set to True # that have had their deletion widget set to True
@ -154,7 +169,7 @@ class BaseFormSet(StrAndUnicode):
Returns a list of form in the order specified by the incoming data. Returns a list of form in the order specified by the incoming data.
Raises an AttributeError if ordering is not allowed. Raises an AttributeError if ordering is not allowed.
""" """
if not self.can_order: if not self.is_valid or not self.can_order:
raise AttributeError("'%s' object has no attribute 'ordered_forms'" % self.__class__.__name__) raise AttributeError("'%s' object has no attribute 'ordered_forms'" % self.__class__.__name__)
# Construct _ordering, which is a list of (form_index, order_field_value) # Construct _ordering, which is a list of (form_index, order_field_value)
# tuples. After constructing this list, we'll sort it by order_field_value # tuples. After constructing this list, we'll sort it by order_field_value
@ -267,7 +282,7 @@ class BaseFormSet(StrAndUnicode):
"""A hook for adding extra fields on to each form instance.""" """A hook for adding extra fields on to each form instance."""
if self.can_order: if self.can_order:
# Only pre-fill the ordering field for initial forms. # Only pre-fill the ordering field for initial forms.
if index < self.initial_form_count(): if index != None and index < self.initial_form_count():
form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_(u'Order'), initial=index+1, required=False) form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_(u'Order'), initial=index+1, required=False)
else: else:
form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_(u'Order'), required=False) form.fields[ORDERING_FIELD_NAME] = IntegerField(label=_(u'Order'), required=False)

View File

@ -664,7 +664,10 @@ class BaseModelFormSet(BaseFormSet):
or (pk.rel and pk.rel.parent_link and pk_is_not_editable(pk.rel.to._meta.pk))) or (pk.rel and pk.rel.parent_link and pk_is_not_editable(pk.rel.to._meta.pk)))
if pk_is_not_editable(pk) or pk.name not in form.fields: if pk_is_not_editable(pk) or pk.name not in form.fields:
try: try:
if index:
pk_value = self.get_queryset()[index].pk pk_value = self.get_queryset()[index].pk
else:
pk_value = None
except IndexError: except IndexError:
pk_value = None pk_value = None
if isinstance(pk, OneToOneField) or isinstance(pk, ForeignKey): if isinstance(pk, OneToOneField) or isinstance(pk, ForeignKey):