Skip to content

Commit 738d9af

Browse files
committed
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
1 parent 4e292da commit 738d9af

File tree

8 files changed

+178
-114
lines changed

8 files changed

+178
-114
lines changed

django/contrib/admin/templates/admin/edit_inline_stacked.html

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,5 @@ <h2>{{ bound_related_object.relation.opts.verbose_name|capfirst }}&nbsp;#{{ forl
1212
{% admin_field_line bound_field %}
1313
{% endif %}
1414
{% endfor %}
15-
<div class="item actions">
16-
<button class="deletebutton" name="command" value="{{bound_related_object.relation.var_name}}.{{fcw.index}}.delete">Delete</button>
17-
</div>
1815
{% endfor %}
19-
<div class="collection actions">
20-
<button class="addbutton" name="command" value="{{bound_related_object.relation.var_name}}.add">Add</button>
21-
</div>
22-
</fieldset>
16+
</fieldset>
Lines changed: 41 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,43 @@
11
{% load admin_modify %}
2+
<fieldset class="module">
3+
<h2>{{ bound_related_object.relation.opts.verbose_name_plural|capfirst }}</h2><table>
4+
<thead><tr>
5+
{% for fw in bound_related_object.field_wrapper_list %}
6+
{% if fw.needs_header %}
7+
<th{{ fw.header_class_attribute }}>{{ fw.field.verbose_name|capfirst }}</th>
8+
{% endif %}
9+
{% endfor %}
10+
{% for fcw in bound_related_object.form_field_collection_wrappers %}
11+
{% if change %}{% if original_row_needed %}
12+
{% if fcw.obj.original %}
13+
<tr class="row-label {% cycle row1,row2 %}"><td colspan="{{ num_headers }}"><strong>{{ fcw.obj.original }}</strong></tr>
14+
{% endif %}
15+
{% endif %}{% endif %}
16+
{% if fcw.obj.errors %}
17+
<tr class="errorlist"><td colspan="{{ num_headers }}">
18+
{{ fcw.obj.html_combined_error_list }}
19+
</tr>
20+
{% endif %}
21+
<tr class="{% cycle row1,row2 %}">
22+
{% for bound_field in fcw.bound_fields %}
23+
{% if not bound_field.hidden %}
24+
<td {{ bound_field.cell_class_attribute }}>
25+
{% field_widget bound_field %}
26+
</td>
27+
{% endif %}
28+
{% endfor %}
29+
{% if bound_related_object.show_url %}<td>
30+
{% if fcw.obj.original %}<a href="/r/{{ fcw.obj.original.content_type_id }}/{{ fcw.obj.original.id }}/">View on site</a>{% endif %}
31+
</td>{% endif %}
32+
</tr>
233

3-
<fieldset class="module editinline">
4-
<h2>{{ bound_related_object.relation.opts.verbose_name_plural|capfirst }}</h2>
5-
<table>
6-
<thead>
7-
<tr>
8-
{% for fw in bound_related_object.field_wrapper_list %}
9-
{% if fw.needs_header %}
10-
<th{{ fw.header_class_attribute }}>{{ fw.field.verbose_name|capfirst }}</th>
11-
{% endif %}
12-
{% endfor %}
13-
<th>&nbsp;</th>
14-
</tr>
15-
</thead>
16-
<tfoot>
17-
<tr>
18-
<td colspan='{{ num_headers }}'>
19-
<button class="addlink" name="command" value="{{ bound_related_object.relation.var_name }}.add">
20-
Add another {{ bound_related_object.relation.opts.verbose_name }}
21-
</button>
22-
</td>
23-
</tr>
24-
</tfoot>
25-
<tbody>
26-
{% for fcw in bound_related_object.form_field_collection_wrappers %}
27-
<tr class="{% cycle row1,row2 %}{% if fcw.obj.errors %} error{% endif %}">
28-
{% for bound_field in fcw.bound_fields %}
29-
{% if not bound_field.hidden %}
30-
<td {{ bound_field.cell_class_attribute }}>
31-
{{ bound_field.html_error_list }}
32-
{% field_widget bound_field %}
33-
</td>
34-
{% endif %}
35-
{% endfor %}
36-
<td class="controls" >
37-
<button class="inline-deletelink" name="command" value="{{ bound_related_object.relation.var_name }}.{{ fcw.index }}.delete">
38-
Delete {{ bound_related_object.relation.opts.verbose_name }}
39-
</button>
40-
</td>
41-
</tr>
42-
{% endfor %}
43-
</tbody>
44-
</table>
45-
</fieldset>
34+
{% endfor %} </table>
35+
36+
{% for fcw in bound_related_object.form_field_collection_wrappers %}
37+
{% for bound_field in fcw.bound_fields %}
38+
{% if bound_field.hidden %}
39+
{% field_widget bound_field %}
40+
{% endif %}
41+
{% endfor %}
42+
{% endfor %}
43+
</fieldset>

django/core/management.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,16 @@ def get_validation_errors(outfile, app=None):
954954
except models.FieldDoesNotExist:
955955
e.add(opts, '"ordering" refers to "%s", a field that doesn\'t exist.' % field_name)
956956

957+
# Check core=True, if needed.
958+
for related in opts.get_followed_related_objects():
959+
try:
960+
for f in related.opts.fields:
961+
if f.core:
962+
raise StopIteration
963+
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))
964+
except StopIteration:
965+
pass
966+
957967
# Check unique_together.
958968
for ut in opts.unique_together:
959969
for field_name in ut:

django/db/models/fields/__init__.py

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ def __init__(self, verbose_name=None, name=None, primary_key=False,
7474
self.primary_key = primary_key
7575
self.maxlength, self.unique = maxlength, unique
7676
self.blank, self.null = blank, null
77-
self.rel, self.default = rel, default
77+
self.core, self.rel, self.default = core, rel, default
7878
self.editable = editable
7979
self.validator_list = validator_list or []
8080
self.prepopulate_from = prepopulate_from
@@ -88,10 +88,6 @@ def __init__(self, verbose_name=None, name=None, primary_key=False,
8888
# Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
8989
self.db_index = db_index
9090

91-
self.deprecated_args = []
92-
if core:
93-
self.deprecated_args.append('core')
94-
9591
# Increase the creation counter, and save our local copy.
9692
self.creation_counter = Field.creation_counter
9793
Field.creation_counter += 1
@@ -219,14 +215,29 @@ def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=
219215
params['validator_list'].append(curry(manipulator_validator_unique, self, opts, manipulator))
220216

221217
# Only add is_required=True if the field cannot be blank. Primary keys
222-
# are a special case.
223-
params['is_required'] = not self.blank and not self.primary_key
218+
# are a special case, and fields in a related context should set this
219+
# as False, because they'll be caught by a separate validator --
220+
# RequiredIfOtherFieldGiven.
221+
params['is_required'] = not self.blank and not self.primary_key and not rel
224222

225223
# BooleanFields (CheckboxFields) are a special case. They don't take
226224
# is_required or validator_list.
227225
if isinstance(self, BooleanField):
228226
del params['validator_list'], params['is_required']
229227

228+
# If this field is in a related context, check whether any other fields
229+
# in the related object have core=True. If so, add a validator --
230+
# RequiredIfOtherFieldsGiven -- to this FormField.
231+
if rel and not self.blank and not isinstance(self, AutoField) and not isinstance(self, FileField):
232+
# First, get the core fields, if any.
233+
core_field_names = []
234+
for f in opts.fields:
235+
if f.core and f != self:
236+
core_field_names.extend(f.get_manipulator_field_names(name_prefix))
237+
# Now, if there are any, add the validator to this FormField.
238+
if core_field_names:
239+
params['validator_list'].append(validators.RequiredIfOtherFieldsGiven(core_field_names, gettext_lazy("This field is required.")))
240+
230241
# Finally, add the field_names.
231242
field_names = self.get_manipulator_field_names(name_prefix)
232243
return [man(field_name=field_names[i], **params) for i, man in enumerate(field_objs)]
@@ -239,9 +250,8 @@ def get_manipulator_new_data(self, new_data, rel=False):
239250
Given the full new_data dictionary (from the manipulator), returns this
240251
field's data.
241252
"""
242-
#if rel:
243-
# return new_data.get(self.name, [self.get_default()])[0]
244-
#else:
253+
if rel:
254+
return new_data.get(self.name, [self.get_default()])[0]
245255
val = new_data.get(self.name, self.get_default())
246256
if not self.empty_strings_allowed and val == '' and self.null:
247257
val = None
@@ -397,12 +407,12 @@ def get_manipulator_field_names(self, name_prefix):
397407

398408
def get_manipulator_new_data(self, new_data, rel=False):
399409
date_field, time_field = self.get_manipulator_field_names('')
400-
#if rel:
401-
# d = new_data.get(date_field, [None])[0]
402-
# t = new_data.get(time_field, [None])[0]
403-
#else:
404-
d = new_data.get(date_field, None)
405-
t = new_data.get(time_field, None)
410+
if rel:
411+
d = new_data.get(date_field, [None])[0]
412+
t = new_data.get(time_field, [None])[0]
413+
else:
414+
d = new_data.get(date_field, None)
415+
t = new_data.get(time_field, None)
406416
if d is not None and t is not None:
407417
return datetime.datetime.combine(d, t)
408418
return self.get_default()
@@ -492,7 +502,10 @@ def save_file(self, new_data, new_object, original_object, change, rel):
492502
upload_field_name = self.get_manipulator_field_names('')[0]
493503
if new_data.get(upload_field_name, False):
494504
func = getattr(new_object, 'save_%s_file' % self.name)
495-
func(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"])
505+
if rel:
506+
func(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"])
507+
else:
508+
func(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"])
496509

497510
def get_directory_name(self):
498511
return os.path.normpath(datetime.datetime.now().strftime(self.upload_to))

django/db/models/fields/related.py

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,10 @@ def __init__(self, to, to_field=None, **kwargs):
418418
kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
419419

420420
kwargs['rel'] = ManyToOne(to, to_field,
421+
num_in_admin=kwargs.pop('num_in_admin', 3),
422+
min_num_in_admin=kwargs.pop('min_num_in_admin', None),
423+
max_num_in_admin=kwargs.pop('max_num_in_admin', None),
424+
num_extra_on_change=kwargs.pop('num_extra_on_change', 1),
421425
edit_inline=kwargs.pop('edit_inline', False),
422426
related_name=kwargs.pop('related_name', None),
423427
limit_choices_to=kwargs.pop('limit_choices_to', None),
@@ -427,10 +431,6 @@ def __init__(self, to, to_field=None, **kwargs):
427431

428432
self.db_index = True
429433

430-
for name in ('num_in_admin', 'min_num_in_admin', 'max_num_in_admin', 'num_extra_on_change'):
431-
if name in kwargs:
432-
self.deprecated_args.append(name)
433-
434434
def get_attname(self):
435435
return '%s_id' % self.name
436436

@@ -501,6 +501,7 @@ def __init__(self, to, to_field=None, **kwargs):
501501
kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
502502

503503
kwargs['rel'] = OneToOne(to, to_field,
504+
num_in_admin=kwargs.pop('num_in_admin', 0),
504505
edit_inline=kwargs.pop('edit_inline', False),
505506
related_name=kwargs.pop('related_name', None),
506507
limit_choices_to=kwargs.pop('limit_choices_to', None),
@@ -511,10 +512,6 @@ def __init__(self, to, to_field=None, **kwargs):
511512

512513
self.db_index = True
513514

514-
for name in ('num_in_admin',):
515-
if name in kwargs:
516-
self.deprecated_args.append(name)
517-
518515
def get_attname(self):
519516
return '%s_id' % self.name
520517

@@ -534,6 +531,7 @@ class ManyToManyField(RelatedField, Field):
534531
def __init__(self, to, **kwargs):
535532
kwargs['verbose_name'] = kwargs.get('verbose_name', None)
536533
kwargs['rel'] = ManyToMany(to, kwargs.pop('singular', None),
534+
num_in_admin=kwargs.pop('num_in_admin', 0),
537535
related_name=kwargs.pop('related_name', None),
538536
filter_interface=kwargs.pop('filter_interface', None),
539537
limit_choices_to=kwargs.pop('limit_choices_to', None),
@@ -542,9 +540,6 @@ def __init__(self, to, **kwargs):
542540
if kwargs["rel"].raw_id_admin:
543541
kwargs.setdefault("validator_list", []).append(self.isValidIDList)
544542
Field.__init__(self, **kwargs)
545-
for name in ('num_in_admin'):
546-
if name in kwargs:
547-
self.deprecated_args.append(name)
548543

549544
if self.rel.raw_id_admin:
550545
msg = gettext_lazy('Separate multiple IDs with commas.')
@@ -641,15 +636,17 @@ def set_attributes_from_rel(self):
641636
pass
642637

643638
class ManyToOne:
644-
def __init__(self, to, field_name, edit_inline=False,
639+
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
640+
max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,
645641
related_name=None, limit_choices_to=None, lookup_overrides=None, raw_id_admin=False):
646642
try:
647643
to._meta
648-
except AttributeError:
644+
except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
649645
assert isinstance(to, basestring), "'to' must be either a model, a model name or the string %r" % RECURSIVE_RELATIONSHIP_CONSTANT
650646
self.to, self.field_name = to, field_name
651-
self.edit_inline = edit_inline
652-
self.related_name = related_name
647+
self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
648+
self.min_num_in_admin, self.max_num_in_admin = min_num_in_admin, max_num_in_admin
649+
self.num_extra_on_change, self.related_name = num_extra_on_change, related_name
653650
self.limit_choices_to = limit_choices_to or {}
654651
self.lookup_overrides = lookup_overrides or {}
655652
self.raw_id_admin = raw_id_admin
@@ -660,22 +657,23 @@ def get_related_field(self):
660657
return self.to._meta.get_field(self.field_name)
661658

662659
class OneToOne(ManyToOne):
663-
def __init__(self, to, field_name, edit_inline=False,
660+
def __init__(self, to, field_name, num_in_admin=0, edit_inline=False,
664661
related_name=None, limit_choices_to=None, lookup_overrides=None,
665662
raw_id_admin=False):
666663
self.to, self.field_name = to, field_name
667-
self.edit_inline = edit_inline
664+
self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
668665
self.related_name = related_name
669666
self.limit_choices_to = limit_choices_to or {}
670667
self.lookup_overrides = lookup_overrides or {}
671668
self.raw_id_admin = raw_id_admin
672669
self.multiple = False
673-
670+
674671
class ManyToMany:
675-
def __init__(self, to, singular=None, related_name=None,
672+
def __init__(self, to, singular=None, num_in_admin=0, related_name=None,
676673
filter_interface=None, limit_choices_to=None, raw_id_admin=False, symmetrical=True):
677674
self.to = to
678675
self.singular = singular or None
676+
self.num_in_admin = num_in_admin
679677
self.related_name = related_name
680678
self.filter_interface = filter_interface
681679
self.limit_choices_to = limit_choices_to or {}

django/db/models/manipulators.py

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ def save(self, new_data):
139139

140140
if child_follow:
141141
obj_list = expanded_data[related.var_name].items()
142+
if not obj_list:
143+
continue
144+
142145
obj_list.sort(lambda x, y: cmp(int(x[0]), int(y[0])))
143146

144147
# For each related item...
@@ -187,15 +190,8 @@ def save(self, new_data):
187190
if param != None:
188191
params[f.attname] = param
189192

190-
# Related links are a special case, because we have to
191-
# manually set the "content_type_id" and "object_id" fields.
192-
if self.opts.has_related_links and related.opts.module_name == 'relatedlinks':
193-
contenttypes_mod = get_module('core', 'contenttypes')
194-
params['content_type_id'] = contenttypes_mod.get_object(package__label__exact=self.opts.app_label, python_module_name__exact=self.opts.module_name).id
195-
params['object_id'] = new_object.id
196-
197193
# Create the related item.
198-
new_rel_obj = related.opts.get_model_module().Klass(**params)
194+
new_rel_obj = related.model(**params)
199195

200196
# If all the core fields were provided (non-empty), save the item.
201197
if all_cores_given:

0 commit comments

Comments
 (0)