magic-removal: fixed #1330: edit-inline works again on magic-removal. Note that the API will change *substantailly* before we're done (for example, this reintroduces core fields, which suck) but this at least gives us a place to start with.
Many many thanks for Christopher Lenz, my new hero. git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@2502 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
4e292dabc0
commit
738d9af1e8
|
@ -12,11 +12,5 @@
|
||||||
{% admin_field_line bound_field %}
|
{% admin_field_line bound_field %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<div class="item actions">
|
|
||||||
<button class="deletebutton" name="command" value="{{bound_related_object.relation.var_name}}.{{fcw.index}}.delete">Delete</button>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<div class="collection actions">
|
|
||||||
<button class="addbutton" name="command" value="{{bound_related_object.relation.var_name}}.add">Add</button>
|
|
||||||
</div>
|
|
||||||
</fieldset>
|
</fieldset>
|
|
@ -1,45 +1,43 @@
|
||||||
{% load admin_modify %}
|
{% load admin_modify %}
|
||||||
|
<fieldset class="module">
|
||||||
<fieldset class="module editinline">
|
<h2>{{ bound_related_object.relation.opts.verbose_name_plural|capfirst }}</h2><table>
|
||||||
<h2>{{ bound_related_object.relation.opts.verbose_name_plural|capfirst }}</h2>
|
<thead><tr>
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
{% for fw in bound_related_object.field_wrapper_list %}
|
{% for fw in bound_related_object.field_wrapper_list %}
|
||||||
{% if fw.needs_header %}
|
{% if fw.needs_header %}
|
||||||
<th{{ fw.header_class_attribute }}>{{ fw.field.verbose_name|capfirst }}</th>
|
<th{{ fw.header_class_attribute }}>{{ fw.field.verbose_name|capfirst }}</th>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<th> </th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tfoot>
|
|
||||||
<tr>
|
|
||||||
<td colspan='{{ num_headers }}'>
|
|
||||||
<button class="addlink" name="command" value="{{ bound_related_object.relation.var_name }}.add">
|
|
||||||
Add another {{ bound_related_object.relation.opts.verbose_name }}
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tfoot>
|
|
||||||
<tbody>
|
|
||||||
{% for fcw in bound_related_object.form_field_collection_wrappers %}
|
{% for fcw in bound_related_object.form_field_collection_wrappers %}
|
||||||
<tr class="{% cycle row1,row2 %}{% if fcw.obj.errors %} error{% endif %}">
|
{% if change %}{% if original_row_needed %}
|
||||||
|
{% if fcw.obj.original %}
|
||||||
|
<tr class="row-label {% cycle row1,row2 %}"><td colspan="{{ num_headers }}"><strong>{{ fcw.obj.original }}</strong></tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}{% endif %}
|
||||||
|
{% if fcw.obj.errors %}
|
||||||
|
<tr class="errorlist"><td colspan="{{ num_headers }}">
|
||||||
|
{{ fcw.obj.html_combined_error_list }}
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
<tr class="{% cycle row1,row2 %}">
|
||||||
{% for bound_field in fcw.bound_fields %}
|
{% for bound_field in fcw.bound_fields %}
|
||||||
{% if not bound_field.hidden %}
|
{% if not bound_field.hidden %}
|
||||||
<td {{ bound_field.cell_class_attribute }}>
|
<td {{ bound_field.cell_class_attribute }}>
|
||||||
{{ bound_field.html_error_list }}
|
|
||||||
{% field_widget bound_field %}
|
{% field_widget bound_field %}
|
||||||
</td>
|
</td>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<td class="controls" >
|
{% if bound_related_object.show_url %}<td>
|
||||||
<button class="inline-deletelink" name="command" value="{{ bound_related_object.relation.var_name }}.{{ fcw.index }}.delete">
|
{% if fcw.obj.original %}<a href="/r/{{ fcw.obj.original.content_type_id }}/{{ fcw.obj.original.id }}/">View on site</a>{% endif %}
|
||||||
Delete {{ bound_related_object.relation.opts.verbose_name }}
|
</td>{% endif %}
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
{% endfor %} </table>
|
||||||
|
|
||||||
|
{% for fcw in bound_related_object.form_field_collection_wrappers %}
|
||||||
|
{% for bound_field in fcw.bound_fields %}
|
||||||
|
{% if bound_field.hidden %}
|
||||||
|
{% field_widget bound_field %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</fieldset>
|
</fieldset>
|
|
@ -954,6 +954,16 @@ def get_validation_errors(outfile, app=None):
|
||||||
except models.FieldDoesNotExist:
|
except models.FieldDoesNotExist:
|
||||||
e.add(opts, '"ordering" refers to "%s", a field that doesn\'t exist.' % field_name)
|
e.add(opts, '"ordering" refers to "%s", a field that doesn\'t exist.' % field_name)
|
||||||
|
|
||||||
|
# Check core=True, if needed.
|
||||||
|
for related in opts.get_followed_related_objects():
|
||||||
|
try:
|
||||||
|
for f in related.opts.fields:
|
||||||
|
if f.core:
|
||||||
|
raise StopIteration
|
||||||
|
e.add(related.opts, "At least one field in %s should have core=True, because it's being edited inline by %s.%s." % (related.opts.object_name, opts.module_name, opts.object_name))
|
||||||
|
except StopIteration:
|
||||||
|
pass
|
||||||
|
|
||||||
# Check unique_together.
|
# Check unique_together.
|
||||||
for ut in opts.unique_together:
|
for ut in opts.unique_together:
|
||||||
for field_name in ut:
|
for field_name in ut:
|
||||||
|
|
|
@ -74,7 +74,7 @@ class Field(object):
|
||||||
self.primary_key = primary_key
|
self.primary_key = primary_key
|
||||||
self.maxlength, self.unique = maxlength, unique
|
self.maxlength, self.unique = maxlength, unique
|
||||||
self.blank, self.null = blank, null
|
self.blank, self.null = blank, null
|
||||||
self.rel, self.default = rel, default
|
self.core, self.rel, self.default = core, rel, default
|
||||||
self.editable = editable
|
self.editable = editable
|
||||||
self.validator_list = validator_list or []
|
self.validator_list = validator_list or []
|
||||||
self.prepopulate_from = prepopulate_from
|
self.prepopulate_from = prepopulate_from
|
||||||
|
@ -88,10 +88,6 @@ class Field(object):
|
||||||
# Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
|
# Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
|
||||||
self.db_index = db_index
|
self.db_index = db_index
|
||||||
|
|
||||||
self.deprecated_args = []
|
|
||||||
if core:
|
|
||||||
self.deprecated_args.append('core')
|
|
||||||
|
|
||||||
# Increase the creation counter, and save our local copy.
|
# Increase the creation counter, and save our local copy.
|
||||||
self.creation_counter = Field.creation_counter
|
self.creation_counter = Field.creation_counter
|
||||||
Field.creation_counter += 1
|
Field.creation_counter += 1
|
||||||
|
@ -219,14 +215,29 @@ class Field(object):
|
||||||
params['validator_list'].append(curry(manipulator_validator_unique, self, opts, manipulator))
|
params['validator_list'].append(curry(manipulator_validator_unique, self, opts, manipulator))
|
||||||
|
|
||||||
# Only add is_required=True if the field cannot be blank. Primary keys
|
# Only add is_required=True if the field cannot be blank. Primary keys
|
||||||
# are a special case.
|
# are a special case, and fields in a related context should set this
|
||||||
params['is_required'] = not self.blank and not self.primary_key
|
# as False, because they'll be caught by a separate validator --
|
||||||
|
# RequiredIfOtherFieldGiven.
|
||||||
|
params['is_required'] = not self.blank and not self.primary_key and not rel
|
||||||
|
|
||||||
# BooleanFields (CheckboxFields) are a special case. They don't take
|
# BooleanFields (CheckboxFields) are a special case. They don't take
|
||||||
# is_required or validator_list.
|
# is_required or validator_list.
|
||||||
if isinstance(self, BooleanField):
|
if isinstance(self, BooleanField):
|
||||||
del params['validator_list'], params['is_required']
|
del params['validator_list'], params['is_required']
|
||||||
|
|
||||||
|
# If this field is in a related context, check whether any other fields
|
||||||
|
# in the related object have core=True. If so, add a validator --
|
||||||
|
# RequiredIfOtherFieldsGiven -- to this FormField.
|
||||||
|
if rel and not self.blank and not isinstance(self, AutoField) and not isinstance(self, FileField):
|
||||||
|
# First, get the core fields, if any.
|
||||||
|
core_field_names = []
|
||||||
|
for f in opts.fields:
|
||||||
|
if f.core and f != self:
|
||||||
|
core_field_names.extend(f.get_manipulator_field_names(name_prefix))
|
||||||
|
# Now, if there are any, add the validator to this FormField.
|
||||||
|
if core_field_names:
|
||||||
|
params['validator_list'].append(validators.RequiredIfOtherFieldsGiven(core_field_names, gettext_lazy("This field is required.")))
|
||||||
|
|
||||||
# Finally, add the field_names.
|
# Finally, add the field_names.
|
||||||
field_names = self.get_manipulator_field_names(name_prefix)
|
field_names = self.get_manipulator_field_names(name_prefix)
|
||||||
return [man(field_name=field_names[i], **params) for i, man in enumerate(field_objs)]
|
return [man(field_name=field_names[i], **params) for i, man in enumerate(field_objs)]
|
||||||
|
@ -239,9 +250,8 @@ class Field(object):
|
||||||
Given the full new_data dictionary (from the manipulator), returns this
|
Given the full new_data dictionary (from the manipulator), returns this
|
||||||
field's data.
|
field's data.
|
||||||
"""
|
"""
|
||||||
#if rel:
|
if rel:
|
||||||
# return new_data.get(self.name, [self.get_default()])[0]
|
return new_data.get(self.name, [self.get_default()])[0]
|
||||||
#else:
|
|
||||||
val = new_data.get(self.name, self.get_default())
|
val = new_data.get(self.name, self.get_default())
|
||||||
if not self.empty_strings_allowed and val == '' and self.null:
|
if not self.empty_strings_allowed and val == '' and self.null:
|
||||||
val = None
|
val = None
|
||||||
|
@ -397,10 +407,10 @@ class DateTimeField(DateField):
|
||||||
|
|
||||||
def get_manipulator_new_data(self, new_data, rel=False):
|
def get_manipulator_new_data(self, new_data, rel=False):
|
||||||
date_field, time_field = self.get_manipulator_field_names('')
|
date_field, time_field = self.get_manipulator_field_names('')
|
||||||
#if rel:
|
if rel:
|
||||||
# d = new_data.get(date_field, [None])[0]
|
d = new_data.get(date_field, [None])[0]
|
||||||
# t = new_data.get(time_field, [None])[0]
|
t = new_data.get(time_field, [None])[0]
|
||||||
#else:
|
else:
|
||||||
d = new_data.get(date_field, None)
|
d = new_data.get(date_field, None)
|
||||||
t = new_data.get(time_field, None)
|
t = new_data.get(time_field, None)
|
||||||
if d is not None and t is not None:
|
if d is not None and t is not None:
|
||||||
|
@ -492,6 +502,9 @@ class FileField(Field):
|
||||||
upload_field_name = self.get_manipulator_field_names('')[0]
|
upload_field_name = self.get_manipulator_field_names('')[0]
|
||||||
if new_data.get(upload_field_name, False):
|
if new_data.get(upload_field_name, False):
|
||||||
func = getattr(new_object, 'save_%s_file' % self.name)
|
func = getattr(new_object, 'save_%s_file' % self.name)
|
||||||
|
if rel:
|
||||||
|
func(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"])
|
||||||
|
else:
|
||||||
func(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"])
|
func(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"])
|
||||||
|
|
||||||
def get_directory_name(self):
|
def get_directory_name(self):
|
||||||
|
|
|
@ -418,6 +418,10 @@ class ForeignKey(RelatedField, Field):
|
||||||
kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
|
kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
|
||||||
|
|
||||||
kwargs['rel'] = ManyToOne(to, to_field,
|
kwargs['rel'] = ManyToOne(to, to_field,
|
||||||
|
num_in_admin=kwargs.pop('num_in_admin', 3),
|
||||||
|
min_num_in_admin=kwargs.pop('min_num_in_admin', None),
|
||||||
|
max_num_in_admin=kwargs.pop('max_num_in_admin', None),
|
||||||
|
num_extra_on_change=kwargs.pop('num_extra_on_change', 1),
|
||||||
edit_inline=kwargs.pop('edit_inline', False),
|
edit_inline=kwargs.pop('edit_inline', False),
|
||||||
related_name=kwargs.pop('related_name', None),
|
related_name=kwargs.pop('related_name', None),
|
||||||
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
||||||
|
@ -427,10 +431,6 @@ class ForeignKey(RelatedField, Field):
|
||||||
|
|
||||||
self.db_index = True
|
self.db_index = True
|
||||||
|
|
||||||
for name in ('num_in_admin', 'min_num_in_admin', 'max_num_in_admin', 'num_extra_on_change'):
|
|
||||||
if name in kwargs:
|
|
||||||
self.deprecated_args.append(name)
|
|
||||||
|
|
||||||
def get_attname(self):
|
def get_attname(self):
|
||||||
return '%s_id' % self.name
|
return '%s_id' % self.name
|
||||||
|
|
||||||
|
@ -501,6 +501,7 @@ class OneToOneField(RelatedField, IntegerField):
|
||||||
kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
|
kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
|
||||||
|
|
||||||
kwargs['rel'] = OneToOne(to, to_field,
|
kwargs['rel'] = OneToOne(to, to_field,
|
||||||
|
num_in_admin=kwargs.pop('num_in_admin', 0),
|
||||||
edit_inline=kwargs.pop('edit_inline', False),
|
edit_inline=kwargs.pop('edit_inline', False),
|
||||||
related_name=kwargs.pop('related_name', None),
|
related_name=kwargs.pop('related_name', None),
|
||||||
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
||||||
|
@ -511,10 +512,6 @@ class OneToOneField(RelatedField, IntegerField):
|
||||||
|
|
||||||
self.db_index = True
|
self.db_index = True
|
||||||
|
|
||||||
for name in ('num_in_admin',):
|
|
||||||
if name in kwargs:
|
|
||||||
self.deprecated_args.append(name)
|
|
||||||
|
|
||||||
def get_attname(self):
|
def get_attname(self):
|
||||||
return '%s_id' % self.name
|
return '%s_id' % self.name
|
||||||
|
|
||||||
|
@ -534,6 +531,7 @@ class ManyToManyField(RelatedField, Field):
|
||||||
def __init__(self, to, **kwargs):
|
def __init__(self, to, **kwargs):
|
||||||
kwargs['verbose_name'] = kwargs.get('verbose_name', None)
|
kwargs['verbose_name'] = kwargs.get('verbose_name', None)
|
||||||
kwargs['rel'] = ManyToMany(to, kwargs.pop('singular', None),
|
kwargs['rel'] = ManyToMany(to, kwargs.pop('singular', None),
|
||||||
|
num_in_admin=kwargs.pop('num_in_admin', 0),
|
||||||
related_name=kwargs.pop('related_name', None),
|
related_name=kwargs.pop('related_name', None),
|
||||||
filter_interface=kwargs.pop('filter_interface', None),
|
filter_interface=kwargs.pop('filter_interface', None),
|
||||||
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
limit_choices_to=kwargs.pop('limit_choices_to', None),
|
||||||
|
@ -542,9 +540,6 @@ class ManyToManyField(RelatedField, Field):
|
||||||
if kwargs["rel"].raw_id_admin:
|
if kwargs["rel"].raw_id_admin:
|
||||||
kwargs.setdefault("validator_list", []).append(self.isValidIDList)
|
kwargs.setdefault("validator_list", []).append(self.isValidIDList)
|
||||||
Field.__init__(self, **kwargs)
|
Field.__init__(self, **kwargs)
|
||||||
for name in ('num_in_admin'):
|
|
||||||
if name in kwargs:
|
|
||||||
self.deprecated_args.append(name)
|
|
||||||
|
|
||||||
if self.rel.raw_id_admin:
|
if self.rel.raw_id_admin:
|
||||||
msg = gettext_lazy('Separate multiple IDs with commas.')
|
msg = gettext_lazy('Separate multiple IDs with commas.')
|
||||||
|
@ -641,15 +636,17 @@ class ManyToManyField(RelatedField, Field):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
class ManyToOne:
|
class ManyToOne:
|
||||||
def __init__(self, to, field_name, edit_inline=False,
|
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
|
||||||
|
max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,
|
||||||
related_name=None, limit_choices_to=None, lookup_overrides=None, raw_id_admin=False):
|
related_name=None, limit_choices_to=None, lookup_overrides=None, raw_id_admin=False):
|
||||||
try:
|
try:
|
||||||
to._meta
|
to._meta
|
||||||
except AttributeError:
|
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
|
||||||
assert isinstance(to, basestring), "'to' must be either a model, a model name or the string %r" % RECURSIVE_RELATIONSHIP_CONSTANT
|
assert isinstance(to, basestring), "'to' must be either a model, a model name or the string %r" % RECURSIVE_RELATIONSHIP_CONSTANT
|
||||||
self.to, self.field_name = to, field_name
|
self.to, self.field_name = to, field_name
|
||||||
self.edit_inline = edit_inline
|
self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
|
||||||
self.related_name = related_name
|
self.min_num_in_admin, self.max_num_in_admin = min_num_in_admin, max_num_in_admin
|
||||||
|
self.num_extra_on_change, self.related_name = num_extra_on_change, related_name
|
||||||
self.limit_choices_to = limit_choices_to or {}
|
self.limit_choices_to = limit_choices_to or {}
|
||||||
self.lookup_overrides = lookup_overrides or {}
|
self.lookup_overrides = lookup_overrides or {}
|
||||||
self.raw_id_admin = raw_id_admin
|
self.raw_id_admin = raw_id_admin
|
||||||
|
@ -660,11 +657,11 @@ class ManyToOne:
|
||||||
return self.to._meta.get_field(self.field_name)
|
return self.to._meta.get_field(self.field_name)
|
||||||
|
|
||||||
class OneToOne(ManyToOne):
|
class OneToOne(ManyToOne):
|
||||||
def __init__(self, to, field_name, edit_inline=False,
|
def __init__(self, to, field_name, num_in_admin=0, edit_inline=False,
|
||||||
related_name=None, limit_choices_to=None, lookup_overrides=None,
|
related_name=None, limit_choices_to=None, lookup_overrides=None,
|
||||||
raw_id_admin=False):
|
raw_id_admin=False):
|
||||||
self.to, self.field_name = to, field_name
|
self.to, self.field_name = to, field_name
|
||||||
self.edit_inline = edit_inline
|
self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
|
||||||
self.related_name = related_name
|
self.related_name = related_name
|
||||||
self.limit_choices_to = limit_choices_to or {}
|
self.limit_choices_to = limit_choices_to or {}
|
||||||
self.lookup_overrides = lookup_overrides or {}
|
self.lookup_overrides = lookup_overrides or {}
|
||||||
|
@ -672,10 +669,11 @@ class OneToOne(ManyToOne):
|
||||||
self.multiple = False
|
self.multiple = False
|
||||||
|
|
||||||
class ManyToMany:
|
class ManyToMany:
|
||||||
def __init__(self, to, singular=None, related_name=None,
|
def __init__(self, to, singular=None, num_in_admin=0, related_name=None,
|
||||||
filter_interface=None, limit_choices_to=None, raw_id_admin=False, symmetrical=True):
|
filter_interface=None, limit_choices_to=None, raw_id_admin=False, symmetrical=True):
|
||||||
self.to = to
|
self.to = to
|
||||||
self.singular = singular or None
|
self.singular = singular or None
|
||||||
|
self.num_in_admin = num_in_admin
|
||||||
self.related_name = related_name
|
self.related_name = related_name
|
||||||
self.filter_interface = filter_interface
|
self.filter_interface = filter_interface
|
||||||
self.limit_choices_to = limit_choices_to or {}
|
self.limit_choices_to = limit_choices_to or {}
|
||||||
|
|
|
@ -139,6 +139,9 @@ class AutomaticManipulator(forms.Manipulator):
|
||||||
|
|
||||||
if child_follow:
|
if child_follow:
|
||||||
obj_list = expanded_data[related.var_name].items()
|
obj_list = expanded_data[related.var_name].items()
|
||||||
|
if not obj_list:
|
||||||
|
continue
|
||||||
|
|
||||||
obj_list.sort(lambda x, y: cmp(int(x[0]), int(y[0])))
|
obj_list.sort(lambda x, y: cmp(int(x[0]), int(y[0])))
|
||||||
|
|
||||||
# For each related item...
|
# For each related item...
|
||||||
|
@ -187,15 +190,8 @@ class AutomaticManipulator(forms.Manipulator):
|
||||||
if param != None:
|
if param != None:
|
||||||
params[f.attname] = param
|
params[f.attname] = param
|
||||||
|
|
||||||
# Related links are a special case, because we have to
|
|
||||||
# manually set the "content_type_id" and "object_id" fields.
|
|
||||||
if self.opts.has_related_links and related.opts.module_name == 'relatedlinks':
|
|
||||||
contenttypes_mod = get_module('core', 'contenttypes')
|
|
||||||
params['content_type_id'] = contenttypes_mod.get_object(package__label__exact=self.opts.app_label, python_module_name__exact=self.opts.module_name).id
|
|
||||||
params['object_id'] = new_object.id
|
|
||||||
|
|
||||||
# Create the related item.
|
# Create the related item.
|
||||||
new_rel_obj = related.opts.get_model_module().Klass(**params)
|
new_rel_obj = related.model(**params)
|
||||||
|
|
||||||
# If all the core fields were provided (non-empty), save the item.
|
# If all the core fields were provided (non-empty), save the item.
|
||||||
if all_cores_given:
|
if all_cores_given:
|
||||||
|
|
|
@ -41,6 +41,35 @@ class RelatedObject(object):
|
||||||
"""
|
"""
|
||||||
return data # TODO
|
return data # TODO
|
||||||
|
|
||||||
|
def get_list(self, parent_instance=None):
|
||||||
|
"Get the list of this type of object from an instance of the parent class."
|
||||||
|
if parent_instance is not None:
|
||||||
|
attr = getattr(parent_instance, self.get_accessor_name())
|
||||||
|
if self.field.rel.multiple:
|
||||||
|
# For many-to-many relationships, return a list of objects
|
||||||
|
# corresponding to the xxx_num_in_admin options of the field
|
||||||
|
objects = list(attr.all())
|
||||||
|
|
||||||
|
count = len(objects) + self.field.rel.num_extra_on_change
|
||||||
|
if self.field.rel.min_num_in_admin:
|
||||||
|
count = max(count, self.field.rel.min_num_in_admin)
|
||||||
|
if self.field.rel.max_num_in_admin:
|
||||||
|
count = min(count, self.field.rel.max_num_in_admin)
|
||||||
|
|
||||||
|
change = count - len(objects)
|
||||||
|
if change > 0:
|
||||||
|
return objects + [None for _ in range(change)]
|
||||||
|
if change < 0:
|
||||||
|
return objects[:change]
|
||||||
|
else: # Just right
|
||||||
|
return objects
|
||||||
|
else:
|
||||||
|
# A one-to-one relationship, so just return the single related
|
||||||
|
# object
|
||||||
|
return [attr]
|
||||||
|
else:
|
||||||
|
return [None for _ in range(self.field.rel.num_in_admin)]
|
||||||
|
|
||||||
def editable_fields(self):
|
def editable_fields(self):
|
||||||
"Get the fields in this class that should be edited inline."
|
"Get the fields in this class that should be edited inline."
|
||||||
return [f for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field]
|
return [f for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field]
|
||||||
|
@ -62,6 +91,30 @@ class RelatedObject(object):
|
||||||
over[self.field.name] = False
|
over[self.field.name] = False
|
||||||
return self.opts.get_follow(over)
|
return self.opts.get_follow(over)
|
||||||
|
|
||||||
|
def get_manipulator_fields(self, opts, manipulator, change, follow):
|
||||||
|
if self.field.rel.multiple:
|
||||||
|
if change:
|
||||||
|
attr = getattr(manipulator.original_object, self.get_accessor_name())
|
||||||
|
count = attr.count()
|
||||||
|
count += self.field.rel.num_extra_on_change
|
||||||
|
if self.field.rel.min_num_in_admin:
|
||||||
|
count = max(count, self.field.rel.min_num_in_admin)
|
||||||
|
if self.field.rel.max_num_in_admin:
|
||||||
|
count = min(count, self.field.rel.max_num_in_admin)
|
||||||
|
else:
|
||||||
|
count = self.field.rel.num_in_admin
|
||||||
|
else:
|
||||||
|
count = 1
|
||||||
|
|
||||||
|
fields = []
|
||||||
|
for i in range(count):
|
||||||
|
for f in self.opts.fields + self.opts.many_to_many:
|
||||||
|
if follow.get(f.name, False):
|
||||||
|
prefix = '%s.%d.' % (self.var_name, i)
|
||||||
|
fields.extend(f.get_manipulator_fields(self.opts, manipulator, change,
|
||||||
|
name_prefix=prefix, rel=True))
|
||||||
|
return fields
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return "<RelatedObject: %s related to %s>" % (self.name, self.field.name)
|
return "<RelatedObject: %s related to %s>" % (self.name, self.field.name)
|
||||||
|
|
||||||
|
|
|
@ -131,10 +131,10 @@ class FormWrapper:
|
||||||
def fill_inline_collections(self):
|
def fill_inline_collections(self):
|
||||||
if not self._inline_collections:
|
if not self._inline_collections:
|
||||||
ic = []
|
ic = []
|
||||||
children = self.manipulator.children.items()
|
related_objects = self.manipulator.get_related_objects()
|
||||||
for rel_obj, child_manips in children:
|
for rel_obj in related_objects:
|
||||||
data = rel_obj.extract_data(self.data)
|
data = rel_obj.extract_data(self.data)
|
||||||
inline_collection = InlineObjectCollection(self.manipulator, rel_obj, child_manips, data, self.error_dict)
|
inline_collection = InlineObjectCollection(self.manipulator, rel_obj, data, self.error_dict)
|
||||||
ic.append(inline_collection)
|
ic.append(inline_collection)
|
||||||
self._inline_collections = ic
|
self._inline_collections = ic
|
||||||
|
|
||||||
|
@ -213,12 +213,11 @@ class FormFieldCollection(FormFieldWrapper):
|
||||||
|
|
||||||
class InlineObjectCollection:
|
class InlineObjectCollection:
|
||||||
"An object that acts like a sparse list of form field collections."
|
"An object that acts like a sparse list of form field collections."
|
||||||
def __init__(self, parent_manipulator, rel_obj,child_manips, data, errors):
|
def __init__(self, parent_manipulator, rel_obj, data, errors):
|
||||||
self.parent_manipulator = parent_manipulator
|
self.parent_manipulator = parent_manipulator
|
||||||
self.rel_obj = rel_obj
|
self.rel_obj = rel_obj
|
||||||
self.data = data
|
self.data = data
|
||||||
self.errors = errors
|
self.errors = errors
|
||||||
self.child_manips = child_manips
|
|
||||||
self._collections = None
|
self._collections = None
|
||||||
self.name = rel_obj.name
|
self.name = rel_obj.name
|
||||||
|
|
||||||
|
@ -240,7 +239,7 @@ class InlineObjectCollection:
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
self.fill()
|
self.fill()
|
||||||
return self._collections.values().__iter__()
|
return iter(self._collections.values())
|
||||||
|
|
||||||
def items(self):
|
def items(self):
|
||||||
self.fill()
|
self.fill()
|
||||||
|
@ -250,22 +249,25 @@ class InlineObjectCollection:
|
||||||
if self._collections:
|
if self._collections:
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
#var_name = self.rel_obj.opts.object_name.lower()
|
var_name = self.rel_obj.opts.object_name.lower()
|
||||||
cols = {}
|
collections = {}
|
||||||
#orig = hasattr(self.parent_manipulator, 'original_object') and self.parent_manipulator.original_object or None
|
orig = None
|
||||||
#orig_list = self.rel_obj.get_list(orig)
|
if hasattr(self.parent_manipulator, 'original_object'):
|
||||||
|
orig = self.parent_manipulator.original_object
|
||||||
|
orig_list = self.rel_obj.get_list(orig)
|
||||||
|
|
||||||
for i, manip in enumerate(self.child_manips) :
|
for i, instance in enumerate(orig_list):
|
||||||
if manip and not manip.needs_deletion:
|
collection = {'original': instance}
|
||||||
collection = {'original': manip.original_object}
|
for f in self.rel_obj.editable_fields():
|
||||||
for field in manip.fields:
|
for field_name in f.get_manipulator_field_names(''):
|
||||||
errors = self.errors.get(field.field_name, [])
|
full_field_name = '%s.%d.%s' % (var_name, i, field_name)
|
||||||
|
field = self.parent_manipulator[full_field_name]
|
||||||
data = field.extract_data(self.data)
|
data = field.extract_data(self.data)
|
||||||
last_part = field.field_name[field.field_name.rindex('.') + 1:]
|
errors = self.errors.get(full_field_name, [])
|
||||||
collection[last_part] = FormFieldWrapper(field, data, errors)
|
collection[field_name] = FormFieldWrapper(field, data, errors)
|
||||||
|
collections[i] = FormFieldCollection(collection)
|
||||||
|
self._collections = collections
|
||||||
|
|
||||||
cols[i] = FormFieldCollection(collection)
|
|
||||||
self._collections = cols
|
|
||||||
|
|
||||||
class FormField:
|
class FormField:
|
||||||
"""Abstract class representing a form field.
|
"""Abstract class representing a form field.
|
||||||
|
|
Loading…
Reference in New Issue