Ticket #1330: django_edit_inline.diff
File django_edit_inline.diff, 23.4 KB (added by , 19 years ago) |
---|
-
django/db/models/manipulators.py
139 139 140 140 if child_follow: 141 141 obj_list = expanded_data[related.var_name].items() 142 if not obj_list: 143 continue 144 142 145 obj_list.sort(lambda x, y: cmp(int(x[0]), int(y[0]))) 143 146 144 147 # For each related item... … … 187 190 if param != None: 188 191 params[f.attname] = param 189 192 190 # Related links are a special case, because we have to191 # 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).id195 params['object_id'] = new_object.id196 197 193 # Create the related item. 198 new_rel_obj = related. opts.get_model_module().Klass(**params)194 new_rel_obj = related.model(**params) 199 195 200 196 # If all the core fields were provided (non-empty), save the item. 201 197 if all_cores_given: -
django/db/models/fields/__init__.py
74 74 self.primary_key = primary_key 75 75 self.maxlength, self.unique = maxlength, unique 76 76 self.blank, self.null = blank, null 77 self. rel, self.default =rel, default77 self.core, self.rel, self.default = core, rel, default 78 78 self.editable = editable 79 79 self.validator_list = validator_list or [] 80 80 self.prepopulate_from = prepopulate_from … … 88 88 # Set db_index to True if the field has a relationship and doesn't explicitly set db_index. 89 89 self.db_index = db_index 90 90 91 self.deprecated_args = []92 if core:93 self.deprecated_args.append('core')94 95 91 # Increase the creation counter, and save our local copy. 96 92 self.creation_counter = Field.creation_counter 97 93 Field.creation_counter += 1 … … 219 215 params['validator_list'].append(curry(manipulator_validator_unique, self, opts, manipulator)) 220 216 221 217 # 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 224 222 225 223 # BooleanFields (CheckboxFields) are a special case. They don't take 226 224 # is_required or validator_list. 227 225 if isinstance(self, BooleanField): 228 226 del params['validator_list'], params['is_required'] 229 227 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 230 241 # Finally, add the field_names. 231 242 field_names = self.get_manipulator_field_names(name_prefix) 232 243 return [man(field_name=field_names[i], **params) for i, man in enumerate(field_objs)] … … 239 250 Given the full new_data dictionary (from the manipulator), returns this 240 251 field's data. 241 252 """ 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] 245 255 val = new_data.get(self.name, self.get_default()) 246 256 if not self.empty_strings_allowed and val == '' and self.null: 247 257 val = None … … 397 407 398 408 def get_manipulator_new_data(self, new_data, rel=False): 399 409 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) 406 416 if d is not None and t is not None: 407 417 return datetime.datetime.combine(d, t) 408 418 return self.get_default() … … 492 502 upload_field_name = self.get_manipulator_field_names('')[0] 493 503 if new_data.get(upload_field_name, False): 494 504 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"]) 496 509 497 510 def get_directory_name(self): 498 511 return os.path.normpath(datetime.datetime.now().strftime(self.upload_to)) -
django/db/models/fields/related.py
418 418 kwargs['edit_inline'] = kwargs.pop('edit_inline_type') 419 419 420 420 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), 421 425 edit_inline=kwargs.pop('edit_inline', False), 422 426 related_name=kwargs.pop('related_name', None), 423 427 limit_choices_to=kwargs.pop('limit_choices_to', None), … … 427 431 428 432 self.db_index = True 429 433 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 434 434 def get_attname(self): 435 435 return '%s_id' % self.name 436 436 … … 501 501 kwargs['edit_inline'] = kwargs.pop('edit_inline_type') 502 502 503 503 kwargs['rel'] = OneToOne(to, to_field, 504 num_in_admin=kwargs.pop('num_in_admin', 0), 504 505 edit_inline=kwargs.pop('edit_inline', False), 505 506 related_name=kwargs.pop('related_name', None), 506 507 limit_choices_to=kwargs.pop('limit_choices_to', None), … … 511 512 512 513 self.db_index = True 513 514 514 for name in ('num_in_admin',):515 if name in kwargs:516 self.deprecated_args.append(name)517 518 515 def get_attname(self): 519 516 return '%s_id' % self.name 520 517 … … 534 531 def __init__(self, to, **kwargs): 535 532 kwargs['verbose_name'] = kwargs.get('verbose_name', None) 536 533 kwargs['rel'] = ManyToMany(to, kwargs.pop('singular', None), 534 num_in_admin=kwargs.pop('num_in_admin', 0), 537 535 related_name=kwargs.pop('related_name', None), 538 536 filter_interface=kwargs.pop('filter_interface', None), 539 537 limit_choices_to=kwargs.pop('limit_choices_to', None), … … 542 540 if kwargs["rel"].raw_id_admin: 543 541 kwargs.setdefault("validator_list", []).append(self.isValidIDList) 544 542 Field.__init__(self, **kwargs) 545 for name in ('num_in_admin'):546 if name in kwargs:547 self.deprecated_args.append(name)548 543 549 544 if self.rel.raw_id_admin: 550 545 msg = gettext_lazy('Separate multiple IDs with commas.') … … 641 636 pass 642 637 643 638 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, 645 641 related_name=None, limit_choices_to=None, lookup_overrides=None, raw_id_admin=False): 646 642 try: 647 643 to._meta 648 except AttributeError: 644 except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT 649 645 assert isinstance(to, basestring), "'to' must be either a model, a model name or the string %r" % RECURSIVE_RELATIONSHIP_CONSTANT 650 646 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 653 650 self.limit_choices_to = limit_choices_to or {} 654 651 self.lookup_overrides = lookup_overrides or {} 655 652 self.raw_id_admin = raw_id_admin … … 660 657 return self.to._meta.get_field(self.field_name) 661 658 662 659 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, 664 661 related_name=None, limit_choices_to=None, lookup_overrides=None, 665 662 raw_id_admin=False): 666 663 self.to, self.field_name = to, field_name 667 self. edit_inline =edit_inline664 self.num_in_admin, self.edit_inline = num_in_admin, edit_inline 668 665 self.related_name = related_name 669 666 self.limit_choices_to = limit_choices_to or {} 670 667 self.lookup_overrides = lookup_overrides or {} 671 668 self.raw_id_admin = raw_id_admin 672 669 self.multiple = False 673 670 674 671 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, 676 673 filter_interface=None, limit_choices_to=None, raw_id_admin=False, symmetrical=True): 677 674 self.to = to 678 675 self.singular = singular or None 676 self.num_in_admin = num_in_admin 679 677 self.related_name = related_name 680 678 self.filter_interface = filter_interface 681 679 self.limit_choices_to = limit_choices_to or {} -
django/db/models/related.py
41 41 """ 42 42 return data # TODO 43 43 44 def get_list(self, parent_instance=None): 45 "Get the list of this type of object from an instance of the parent class." 46 if parent_instance is not None: 47 attr = getattr(parent_instance, self.get_accessor_name()) 48 if self.field.rel.multiple: 49 # For many-to-many relationships, return a list of objects 50 # corresponding to the xxx_num_in_admin options of the field 51 objects = list(attr.all()) 52 53 count = len(objects) + self.field.rel.num_extra_on_change 54 if self.field.rel.min_num_in_admin: 55 count = max(count, self.field.rel.min_num_in_admin) 56 if self.field.rel.max_num_in_admin: 57 count = min(count, self.field.rel.max_num_in_admin) 58 59 change = count - len(objects) 60 if change > 0: 61 return objects + [None for _ in range(change)] 62 if change < 0: 63 return objects[:change] 64 else: # Just right 65 return objects 66 else: 67 # A one-to-one relationship, so just return the single related 68 # object 69 return [attr] 70 else: 71 return [None for _ in range(self.field.rel.num_in_admin)] 72 44 73 def editable_fields(self): 45 74 "Get the fields in this class that should be edited inline." 46 75 return [f for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field] … … 62 91 over[self.field.name] = False 63 92 return self.opts.get_follow(over) 64 93 94 def get_manipulator_fields(self, opts, manipulator, change, follow): 95 if self.field.rel.multiple: 96 if change: 97 attr = getattr(manipulator.original_object, self.get_accessor_name()) 98 count = attr.count() 99 count += self.field.rel.num_extra_on_change 100 if self.field.rel.min_num_in_admin: 101 count = max(count, self.field.rel.min_num_in_admin) 102 if self.field.rel.max_num_in_admin: 103 count = min(count, self.field.rel.max_num_in_admin) 104 else: 105 count = self.field.rel.num_in_admin 106 else: 107 count = 1 108 109 fields = [] 110 for i in range(count): 111 for f in self.opts.fields + self.opts.many_to_many: 112 if follow.get(f.name, False): 113 prefix = '%s.%d.' % (self.var_name, i) 114 fields.extend(f.get_manipulator_fields(self.opts, manipulator, change, 115 name_prefix=prefix, rel=True)) 116 return fields 117 65 118 def __repr__(self): 66 119 return "<RelatedObject: %s related to %s>" % (self.name, self.field.name) 67 120 -
django/forms/__init__.py
131 131 def fill_inline_collections(self): 132 132 if not self._inline_collections: 133 133 ic = [] 134 children = self.manipulator.children.items()135 for rel_obj , child_manips in children:134 related_objects = self.manipulator.get_related_objects() 135 for rel_obj in related_objects: 136 136 data = rel_obj.extract_data(self.data) 137 inline_collection = InlineObjectCollection(self.manipulator, rel_obj, child_manips,data, self.error_dict)137 inline_collection = InlineObjectCollection(self.manipulator, rel_obj, data, self.error_dict) 138 138 ic.append(inline_collection) 139 139 self._inline_collections = ic 140 140 … … 213 213 214 214 class InlineObjectCollection: 215 215 "An object that acts like a sparse list of form field collections." 216 def __init__(self, parent_manipulator, rel_obj, child_manips,data, errors):216 def __init__(self, parent_manipulator, rel_obj, data, errors): 217 217 self.parent_manipulator = parent_manipulator 218 218 self.rel_obj = rel_obj 219 219 self.data = data 220 220 self.errors = errors 221 self.child_manips = child_manips222 221 self._collections = None 223 222 self.name = rel_obj.name 224 223 … … 240 239 241 240 def __iter__(self): 242 241 self.fill() 243 return self._collections.values().__iter__()242 return iter(self._collections.values()) 244 243 245 244 def items(self): 246 245 self.fill() … … 250 249 if self._collections: 251 250 return 252 251 else: 253 #var_name = self.rel_obj.opts.object_name.lower() 254 cols = {} 255 #orig = hasattr(self.parent_manipulator, 'original_object') and self.parent_manipulator.original_object or None 256 #orig_list = self.rel_obj.get_list(orig) 252 var_name = self.rel_obj.opts.object_name.lower() 253 collections = {} 254 orig = None 255 if hasattr(self.parent_manipulator, 'original_object'): 256 orig = self.parent_manipulator.original_object 257 orig_list = self.rel_obj.get_list(orig) 257 258 258 for i, manip in enumerate(self.child_manips) : 259 if manip and not manip.needs_deletion: 260 collection = {'original': manip.original_object} 261 for field in manip.fields: 262 errors = self.errors.get(field.field_name, []) 259 for i, instance in enumerate(orig_list): 260 collection = {'original': instance} 261 for f in self.rel_obj.editable_fields(): 262 for field_name in f.get_manipulator_field_names(''): 263 full_field_name = '%s.%d.%s' % (var_name, i, field_name) 264 field = self.parent_manipulator[full_field_name] 263 265 data = field.extract_data(self.data) 264 last_part = field.field_name[field.field_name.rindex('.') + 1:] 265 collection[last_part] = FormFieldWrapper(field, data, errors) 266 errors = self.errors.get(full_field_name, []) 267 collection[field_name] = FormFieldWrapper(field, data, errors) 268 collections[i] = FormFieldCollection(collection) 269 self._collections = collections 266 270 267 cols[i] = FormFieldCollection(collection)268 self._collections = cols269 271 270 272 class FormField: 271 273 """Abstract class representing a form field. -
django/core/management.py
956 956 except models.FieldDoesNotExist: 957 957 e.add(opts, '"ordering" refers to "%s", a field that doesn\'t exist.' % field_name) 958 958 959 # Check core=True, if needed. 960 for related in opts.get_followed_related_objects(): 961 try: 962 for f in related.opts.fields: 963 if f.core: 964 raise StopIteration 965 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)) 966 except StopIteration: 967 pass 968 959 969 # Check unique_together. 960 970 for ut in opts.unique_together: 961 971 for field_name in ut: -
django/contrib/admin/templates/admin/edit_inline_stacked.html
12 12 {% admin_field_line bound_field %} 13 13 {% endif %} 14 14 {% 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>18 15 {% 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> 23 No newline at end of file 16 </fieldset> -
django/contrib/admin/templates/admin/edit_inline_tabular.html
1 1 {% 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> 2 33 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> </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> 46 No newline at end of file 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>