Ticket #535: django-admin-refactor-2.patch
File django-admin-refactor-2.patch, 53.5 KB (added by , 19 years ago) |
---|
-
django/conf/urls/admin.py
48 48 49 49 urlpatterns += ( 50 50 # Metasystem admin pages 51 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/add_new/$', 'django.views.admin.main.add_stage_new'), 52 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)_new/$', 'django.views.admin.main.change_stage_new'), 51 53 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/$', 'django.views.admin.main.change_list'), 52 54 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/add/$', 'django.views.admin.main.add_stage'), 53 55 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/jsvalidation/$', 'django.views.admin.jsvalidation.jsvalidation'), -
django/conf/admin_templates/admin_change_form.html
1 {% extends "base_site" %} 2 {% load admin_modify %} 3 {% load adminmedia %} 4 {% block extrahead %} 5 6 {% for js in javascript_imports %} 7 {% include_admin_script js %} 8 {% endfor %} 9 10 {% endblock %} 11 12 {% block coltype %}{{ coltype }}{% endblock %} 13 14 {% block bodyclass %}{{app_label}}-{{object_name.lower}} change-form{% endblock %} 15 16 {% block breadcrumbs %}{% if not is_popup %} 17 <div class="breadcrumbs"> 18 <a href="../../../">Home</a> › 19 <a href="../">{{verbose_name_plural|capfirst}}</a> › 20 {% if add %} 21 Add {{verbose_name}} 22 {% else %} 23 {{original|striptags|truncatewords:"18"}} 24 {% endif %} 25 </div> 26 {% endif %} 27 {% endblock %} 28 29 {% block content %}<div id="content-main"> 30 {% if change %} 31 {% if not is_popup %} 32 <ul class="object-tools"><li><a href="history/" class="historylink">History</a></li> 33 {% if has_absolute_url %} 34 <li><a href="/r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">View on site</a></li> 35 {% endif%} 36 </ul> 37 {% endif %} 38 {% endif %} 39 40 <form {{ form_enc_attrib }} action='{{ form_url }}' method="post"> 41 42 {% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %} 43 44 {% if save_on_top %} 45 {% submit_row %} 46 {% endif %} 47 48 {% if form.error_dict %} 49 <p class="errornote">Please correct the error{{ form.error_dict.items|pluralize }} below.</p> 50 {% endif %} 51 <b> 52 </b> 53 {% for fieldset in admin_fieldsets %} 54 <fieldset class="module aligned {{ fieldset.classes }}"> 55 {% if fieldset.name %} 56 <h2>{{fieldset.name }}</h2> 57 {% endif %} 58 {% for bound_field_set in fieldset.bound_field_sets %} 59 {% for bound_field in bound_field_set %} 60 {% admin_field_bound bound_field %} 61 {% for field in bound_field.form_fields %} 62 {% if field.needs_filter_script %} 63 <script type="text/javascript"> 64 addEvent(window, load, function(e){ 65 SelectFilter.init("id_{{f.name}}", {{ f.verbose_name}}, {{f.filterthing}}); 66 } 67 </script> 68 {% endif %} 69 {% endfor %} 70 {% endfor%} 71 {% endfor %} 72 </fieldset> 73 {% endfor %} 74 75 {% if change %} 76 {% if ordered_objects %} 77 <fieldset class="module"><h2>Ordering</h2> 78 <div class="form-row{% if form.order_.errors %} error{% endif %} "> 79 {% if form.order_.errors %}{{ form.order_.html_error_list }}{% endif %} 80 <p><label for="id_order_">Order:</label> {{ form.order_ }}</p> 81 </div></fieldset> 82 {% endif %} 83 {% endif %} 84 85 {% for relation in inline_related_objects %} 86 {% edit_inline relation %} 87 {% endfor %} 88 89 {% submit_row %} 90 91 {% if add %} 92 <script type="text/javascript">document.getElementById("id_{{first_field}}").focus();</script>' 93 {% endif %} 94 95 {% if auto_populated_fields %} 96 <script type="text/javascript"> 97 {% auto_populated_field_script auto_populated_fields %} 98 </script> 99 {% endif %} 100 101 {% if change %} 102 {% if ordered_objects %} 103 {% if form.order_objects %}<ul id="orderthese"> 104 {% for object in form.order_objects %} 105 <li id="p{% firstof ordered_object_names %}"> 106 <span id="handlep{% firstof ordered_object_names %}">{{ object|truncatewords:"5" }}</span> 107 </li> 108 {% endfor%} 109 {% endif %} 110 {% endif %} 111 {% endif%} 112 </form> 113 114 {% endblock %} -
django/conf/admin_templates/admin_edit_inline_stacked.html
1 <fieldset class="module aligned"> 2 {% for ow in form_object_wrapper_list %} 3 <h2>{{relation.obj.verbose_name|capfirst }} #{{ forloop.counter }}</h2> 4 {% if ow.show_url %}{% if ow.obj.original %} 5 <p><a href="/r/{{ ow.obj.original.content_type_id }}/{{ ow.obj.original.id }}/">View on site</a></p> 6 {% endif %}{% endif %} 7 {% for bound_field in ow.bound_fields %} 8 {% if bound_field.not_in_table %} 9 {% field_widget bound_field %} 10 {% else %} 11 {% admin_field_bound bound_field %} 12 {% endif %} 13 {% endfor %} 14 {%endfor%} 15 </fieldset> 16 -
django/conf/admin_templates/admin_field.html
1 <div class="{{ class_names }}"> 2 {% for bound_field in bound_fields %} 3 {% if bound_field.field.errors %} 4 {{ bound_field.field.html_error_list }} 5 {% endif %} 6 {% endfor %} 7 8 {% for bound_field in bound_fields %} 9 {% if bound_field.has_label_first %} 10 {% field_label bound_field %} 11 {% endif %} 12 13 {% field_widget bound_field %} 14 15 {% if not bound_field.has_label_first %} 16 {% field_label bound_field %} 17 {% endif %} 18 19 {% if change %} 20 {% if bound_field.field.primary_key %} 21 {{ bound_field.original_value }} 22 {% endif %} 23 24 {% if bound_field.raw_id_admin %} 25 {% if bound_field.existing_repr %} 26 <strong>{{ bound_field.existing_repr|truncatewords:"14" }}</strong> 27 {% endif %} 28 {% endif %} 29 {% endif %} 30 31 {% if bound_field.field.help_text %} 32 <p class="help"> 33 {{bound_field.field.help_text}} 34 </p> 35 {% endif %} 36 {% endfor %} 37 38 </div> -
django/conf/admin_templates/admin_field_widget.html
1 {% if bound_field.is_date_time %} 2 <p class="datetime"> 3 Date: {{ bound_field.form_fields.0 }}<br /> 4 Time: {{ bound_field.form_fields.1 }} 5 </p> 6 {% else %} 7 {% if bound_field.is_file_field %} 8 {% if bound_field.original_value %} 9 Currently: <a href="{{ bound_field.original_url }}" > {{ bound_field.original_value }} </a><br /> 10 Change: {% output_all bound_field.form_fields %} 11 {% else %} 12 {% output_all bound_field.form_fields %} 13 {% endif %} 14 {% else %} 15 {% output_all bound_field.form_fields %} 16 {% if bound_field.raw_id_admin %} 17 <a href="../../../{{ bound_field.field.rel.to.app_label }}/{{ bound_field.field.rel.to.module_name }}/" class="related-lookup" id="lookup_{{bound_field.label_name}}" onclick="return showRelatedObjectLookupPopup(this);"> <img src="{% admin_media_prefix %}img/admin/selector-search.gif" width="16" height="16" alt="Lookup"></a> 18 {% else %} 19 {% if bound_field.needs_add_label %} 20 <a href="../../../{{ bound_field.field.rel.to.app_label }}/{{ bound_field.field.rel.to.module_name }}/add/" class="add-another" id="add_{{ bound_field.label_name}}"> <img src="{% admin_media_prefix %}img/admin/icon_addlink.gif" width="10" height="10" alt="Add Another"/></a> 21 {% endif %} 22 {% endif %} 23 {% endif %} 24 {% endif %} 25 26 27 -
django/conf/admin_templates/admin_edit_inline_tabular.html
1 <fieldset class="module"> 2 <h2>{{relation.obj.verbose_name_plural|capfirst}}</h2><table> 3 <thead><tr> 4 {% for fw in field_wrapper_list %} 5 {% if fw.needs_header %} 6 <th{{fw.header_class_attribute}}> {{fw.field.verbose_name|capfirst}} </th> 7 {% endif %} 8 {% endfor %} 9 {% for ow in form_object_wrapper_list %} 10 11 {% if change %}{% if original_row_needed %} 12 {% if ow.obj.original %} 13 <tr class="row-label {% cycle row1,row2 %}"><td colspan="{{num_headers}}"><strong>{{ ow.obj.original }}</strong></tr> 14 {% endif %} 15 {% endif %}{% endif %} 16 {% if ow.has_errors %} 17 <tr class="errorlist"><td colspan="{{num_headers}}"> 18 {{ ow.html_combined_error_list }} 19 </tr> 20 {% endif %} 21 <tr class="{% cycle row1,row2 %}"> 22 {% for bound_field in ow.bound_fields %} 23 {% if not bound_field.not_in_table %} 24 <td "{{ bound_field.cell_class_attribute}}"> 25 {% field_widget bound_field %} 26 </td> 27 {% endif %} 28 {% endfor %} 29 {% if ow.show_url %}<td> 30 {% if ow.obj.original %}<a href="/r/{{ ow.obj.original.content_type_id }}/{{ ow.obj.original.id }}/">View on site</a>{% endif %} 31 </td>{% endif %} 32 </tr> 33 34 {% endfor %} </table> 35 36 {% for ow in form_object_wrapper_list %} 37 {% for bound_field in ow.bound_fields %} 38 {% if bound_field.not_in_table %} 39 {% field_widget bound_field %} 40 {% endif %} 41 {% endfor %} 42 {% endfor %} 43 </fieldset> 44 -
django/core/formfields.py
87 87 must happen after validation because html2python functions aren't 88 88 expected to deal with invalid input. 89 89 """ 90 for field in self.fields: 91 if new_data.has_key(field.field_name): 92 new_data.setlist(field.field_name, 93 [field.__class__.html2python(data) for data in new_data.getlist(field.field_name)]) 94 else: 95 try: 96 # individual fields deal with None values themselves 97 new_data.setlist(field.field_name, [field.__class__.html2python(None)]) 98 except EmptyValue: 99 new_data.setlist(field.field_name, []) 90 """ 91 for field in self.fields: 92 """ 100 93 94 for field in self.fields: 95 field.convert_post_data(new_data) 96 101 97 class FormWrapper: 102 98 """ 103 99 A wrapper linking a Manipulator to the template system. … … 114 110 def __getitem__(self, key): 115 111 for field in self.manipulator.fields: 116 112 if field.field_name == key: 117 if hasattr(field, 'requires_data_list') and hasattr(self.data, 'getlist'): 118 data = self.data.getlist(field.field_name) 119 else: 120 data = self.data.get(field.field_name, None) 121 if data is None: 122 data = '' 123 return FormFieldWrapper(field, data, self.error_dict.get(field.field_name, [])) 113 114 data = field.extract_data(self.data) 115 116 return FormFieldWrapper(field, data, self.error_dict.get(field.field_name, [])) 124 117 raise KeyError 125 118 126 119 def has_errors(self): … … 209 202 def render(self, data): 210 203 raise NotImplementedError 211 204 205 def get_member_name(self): 206 return self.field_name 207 208 def extract_data(self, data_dict): 209 if hasattr(self, 'requires_data_list') and hasattr(data_dict, 'getlist'): 210 data = data_dict.getlist(self.get_member_name()) 211 else: 212 data = data_dict.get(self.get_member_name(), None) 213 if data is None: 214 data = '' 215 self.data_dict = data_dict 216 return data 217 218 def convert_post_data(self, new_data): 219 name = self.get_member_name() 220 if new_data.has_key(self.field_name): 221 d = new_data.getlist(self.field_name) 222 #del new_data[self.field_name] 223 new_data.setlist(name, 224 [self.__class__.html2python(data) 225 for data in d]) 226 else: 227 try: 228 # individual fields deal with None values themselves 229 new_data.setlist(name, [self.__class__.html2python(None)]) 230 except EmptyValue: 231 new_data.setlist(name, []) 232 212 233 #################### 213 234 # GENERIC WIDGETS # 214 235 #################### … … 319 340 output.append(' </select>') 320 341 return '\n'.join(output) 321 342 343 def get_member_name(self): 344 return "%s_id" % self.field_name 345 322 346 def isValidChoice(self, data, form): 323 347 str_data = str(data) 324 348 str_choices = [str(item[0]) for item in self.choices] -
django/core/meta/__init__.py
592 592 new_mod.get_latest = curry(function_get_latest, opts, new_class, does_not_exist_exception) 593 593 594 594 for f in opts.fields: 595 if len(f.choices) != 0: 596 # Add an accessor method to get to the human readable value 597 func = curry(method_get_display_value, f) 598 setattr(new_class, 'get_%s_display' % f.name , func) 595 599 if isinstance(f, DateField) or isinstance(f, DateTimeField): 596 600 # Add "get_next_by_thingie" and "get_previous_by_thingie" methods 597 601 # for all DateFields and DateTimeFields that cannot be null. … … 782 786 # If it does already exist, do an UPDATE. 783 787 if cursor.fetchone(): 784 788 db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.column), False)) for f in non_pks] 785 cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % (opts.db_table, 786 ','.join(['%s=%%s' % f.column for f in non_pks]), opts.pk.column), 789 while 1: 790 try: 791 idx = db_values.index('') 792 non_pks[idx:idx+1] = [] 793 db_values[idx:idx +1] = [] 794 except: break 795 cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % (opts.db_table, 796 ','.join(['%s=%%s' % f.column for f in non_pks]), opts.pk.column), 787 797 db_values + [pk_val]) 788 798 else: 789 799 record_exists = False … … 991 1001 kwargs['limit'] = 1 992 1002 return get_object_func(**kwargs) 993 1003 1004 # CHOICE-RELATED METHODS ################### 1005 1006 def method_get_display_value(field, self): 1007 value = getattr(self, field.name) 1008 for (v, d) in field.choices: 1009 if v==value: 1010 return d 1011 # Couldn't find it in the list 1012 return value 1013 994 1014 # FILE-RELATED METHODS ##################### 995 1015 996 1016 def method_get_file_filename(field, self): -
django/core/meta/fields.py
263 263 rel_obj = self.rel.to 264 264 return first_choice + [(getattr(x, rel_obj.pk.column), repr(x)) for x in rel_obj.get_model_module().get_list(**self.rel.limit_choices_to)] 265 265 266 def flatten_data(self, val): 267 """ 268 Returns a dictionary mapping the field's manipulator field names to its 269 "flattened" string values for the admin view. "val" is an instance of the 270 field's value. 271 """ 272 return { self.get_db_column(): val} 273 274 266 275 class AutoField(Field): 267 276 empty_strings_allowed = False 268 277 def __init__(self, *args, **kwargs): … … 327 336 def get_manipulator_field_objs(self): 328 337 return [formfields.DateField] 329 338 339 def flatten_data(self, val): 340 return {self.get_db_column(): (val is not None and val.strftime("%Y-%m-%d") or '')} 341 330 342 class DateTimeField(DateField): 331 343 def get_db_prep_save(self, value): 332 344 # Casts dates into string format for entry into database. … … 356 368 return datetime.datetime.combine(d, t) 357 369 return self.get_default() 358 370 371 def flatten_data(self, val): 372 date_field, time_field = self.get_manipulator_field_names('') 373 return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''), 374 time_field: (val is not None and val.strftime("%H:%M:%S") or '')} 375 359 376 class EmailField(Field): 360 377 def get_manipulator_field_objs(self): 361 378 return [formfields.EmailField] … … 539 556 def get_manipulator_field_objs(self): 540 557 return [formfields.TimeField] 541 558 559 def flatten_data(self,val): 560 return {self.get_db_column(): val} 561 542 562 class URLField(Field): 543 563 def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs): 544 564 if verify_exists: -
django/templatetags/admin_modify.py
1 from django.core import template, template_loader, meta 2 from django.conf.settings import ADMIN_MEDIA_PREFIX 3 from django.utils.text import capfirst 4 from django.utils.html import escape 5 6 7 from django.views.admin.main import BoundField 8 9 class IncludeAdminScriptNode(template.Node): 10 def __init__(self, var): 11 self.var = var 12 13 def render(self, context): 14 resolved = template.resolve_variable(self.var, context) 15 return '<script type="text/javascript" src="%s%s"></script>' % \ 16 (ADMIN_MEDIA_PREFIX, resolved) 17 18 class SubmitRowNode(template.Node): 19 def __init__(self): 20 pass 21 22 def render(self, context): 23 change = context['change'] 24 add = context['add'] 25 show_delete = context['show_delete'] 26 ordered_objects = context['ordered_objects'] 27 save_as = context['save_as'] 28 has_delete_permission = context['has_delete_permission'] 29 is_popup = context['is_popup'] 30 31 t = ['<div class="submit-row">'] 32 onclick_attrib = ordered_objects and change and 'onclick="submitOrderForm();"' or '' 33 34 if not is_popup: 35 if has_delete_permission and (change or show_delete): 36 t.append('<p class="float-left"><a href="delete/" class="deletelink">Delete</a></p>') 37 if change and save_as: 38 t.append('<input type="submit" value="Save as new" name="_saveasnew" %s/>' % onclick_attrib) 39 if (not save_as or add): 40 t.append('<input type="submit" value="Save and add another" name="_addanother" %s/>' % onclick_attrib) 41 t.append('<input type="submit" value="Save and continue editing" name="_continue" %s/>' % onclick_attrib ) 42 t.append('<input type="submit" value="Save" class="default" %s/>' % onclick_attrib) 43 t.append('</div>\n') 44 45 return ''.join(t) 46 47 48 49 50 class AdminFieldBoundNode(template.Node): 51 def __init__(self, argument): 52 self.argument = argument 53 54 def render(self, context): 55 argument_val = template.resolve_variable(self.argument, context) 56 if (isinstance(argument_val, list)): 57 bound_fields = argument_val 58 else: 59 bound_fields = [argument_val] 60 add = context['add'] 61 change = context['change'] 62 63 context.push() 64 context['bound_fields'] = bound_fields 65 context['class_names'] = " ".join(self.get_class_names(bound_fields)) 66 t = template_loader.get_template("admin_field") 67 output = t.render(context) 68 context.pop() 69 70 return output 71 72 def get_class_names(self, bound_fields): 73 74 class_names = ['form-row'] 75 for bound_field in bound_fields: 76 for f in bound_field.form_fields: 77 if f.errors(): 78 class_names.append('errors') 79 break 80 81 # Assumes BooleanFields won't be stacked next to each other! 82 if isinstance(bound_fields[0].field, meta.BooleanField): 83 class_names.append('checkbox-row') 84 85 return class_names 86 87 class FieldWidgetNode(template.Node): 88 def __init__(self, bound_field_var): 89 self.bound_field_var = bound_field_var 90 91 def render(self, context): 92 bound_field = template.resolve_variable(self.bound_field_var, context) 93 add = context['add'] 94 change = context['change'] 95 96 context.push() 97 context['bound_field'] = bound_field 98 t = template_loader.get_template("admin_field_widget") 99 output = t.render(context) 100 context.pop() 101 102 return output 103 104 105 106 class FieldWrapper(object): 107 def __init__(self, field ): 108 self.field = field 109 110 def needs_header(self): 111 return not isinstance(self.field, meta.AutoField) 112 113 def header_class_attribute(self): 114 return self.field.blank and ' class="optional"' or '' 115 116 def use_raw_id_admin(self): 117 return isinstance(self.field.rel, (meta.ManyToOne, meta.ManyToMany)) \ 118 and self.field.rel.raw_id_admin 119 120 class FormObjectWrapper(object): 121 def __init__(self, obj, field_wrappers, i, object_name): 122 self.obj = obj 123 self.field_wrappers = field_wrappers 124 125 form_prefix = '%s.%s.' % ( object_name, i) 126 127 name_prefix = '' 128 129 self.bound_fields = [ BoundField(0, fw.field, obj['original'], form_prefix, 130 name_prefix, True, self.resolve_form_fields(fw.field, name_prefix) ) \ 131 for fw in self.field_wrappers ] 132 133 134 def resolve_form_fields(self,field, name_prefix): 135 return [self.obj[name] for name in field.get_manipulator_field_names(name_prefix)] 136 137 #HACK 138 def has_errors(self): 139 return max([ bool( len( self.obj[fw.field.name].errors() ) ) for fw in self.field_wrappers]) 140 141 def html_combined_error_list(self): 142 return ''.join( [ self.obj[fw.field.name].html_error_list() for fw in self.field_wrappers]) 143 144 145 146 147 class EditInlineNode(template.Node): 148 def __init__(self, rel_var): 149 self.rel_var = rel_var 150 151 def render(self, context): 152 relation = template.resolve_variable(self.rel_var, context) 153 add, change = context['add'], context['change'] 154 155 context.push() 156 157 self.fill_context(relation, add, change, context) 158 159 if relation.field.rel.edit_inline == meta.TABULAR: 160 t = template_loader.get_template("admin_edit_inline_tabular") 161 else: # meta.STACKED 162 t = template_loader.get_template("admin_edit_inline_stacked") 163 164 output = t.render(context) 165 166 context.pop() 167 return output 168 169 170 def fill_context(self, relation, add, change, context): 171 field_wrapper_list = [FieldWrapper(f) for f in relation.obj.fields + relation.obj.many_to_many if f.editable and f != relation.field] 172 173 var_name = relation.obj.object_name.lower() 174 175 form_objects = template.resolve_variable('form.%s' % relation.obj.module_name , context) 176 form_object_wrapper_list = [FormObjectWrapper(o,field_wrapper_list, i, var_name) for i,o in enumerate(form_objects)] 177 178 context['field_wrapper_list'] = field_wrapper_list 179 context['form_object_wrapper_list'] = form_object_wrapper_list 180 context['num_headers'] = len(field_wrapper_list) 181 context['original_row_needed'] = max([fw.use_raw_id_admin() for fw in field_wrapper_list]) 182 context['name_prefix'] = "%s." % (var_name,) 183 184 class FieldLabelNode(template.Node): 185 def __init__(self, bound_field_var): 186 self.bound_field_var = bound_field_var 187 188 def render(self, context): 189 bound_field = template.resolve_variable(self.bound_field_var, context) 190 class_names = [] 191 if isinstance(bound_field.field, meta.BooleanField): 192 class_names.append("vCheckboxLabel") 193 else: 194 if not bound_field.field.blank: 195 class_names.append('required') 196 if bound_field.index > 0: 197 class_names.append('inline') 198 199 class_str = class_names and ' class="%s"' % ' '.join(class_names) or '' 200 return '<label for="%s"%s>%s:</label> ' % (bound_field.label_name, class_str, capfirst(bound_field.field.verbose_name) ) 201 202 class OutputAllNode(template.Node): 203 def __init__(self, form_fields_var): 204 self.form_fields_var = form_fields_var 205 206 def render(self, context): 207 form_fields = template.resolve_variable(self.form_fields_var, context) 208 return ''.join([str(f) for f in form_fields]) 209 210 class AutoPopulatedFieldScriptNode(template.Node): 211 def __init__(self, auto_pop_var): 212 self.auto_pop_var = auto_pop_var 213 214 def render(self,context): 215 auto_pop_fields = template.resolve_variable(self, context) 216 change = context['change'] 217 for field in auto_populated_fields: 218 t = [] 219 if change: 220 t.append('document.getElementById("id_%s")._changed = true;' % field.name ) 221 else: 222 t.append('document.getElementById("id_%s").onchange = function() { this._changed = true; };' % field.name) 223 224 add_values = ' + " " + '.join(['document.getElementById("id_%s").value' % g for g in field.prepopulate_from]) 225 for f in field.prepopulate_from: 226 t.append(""" 227 document.getElementById("id_%s").onkeyup = function() { 228 var e = document.getElementById("id_%s"); 229 if(!e._changed) { e.value = URLify(%s, %s);} 230 } 231 """ % (f, field.name, add_values, field.maxlength) 232 ) 233 234 return ''.join(t) 235 236 def do_include_admin_script(parser, token): 237 tokens = token.contents.split() 238 if len(tokens) != 2: 239 raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 240 241 return IncludeAdminScriptNode(tokens[1]) 242 243 def do_dummy(parser, token): 244 return DummyNode(token.contents) 245 246 def do_submit_row(parser, token): 247 return SubmitRowNode() 248 249 def do_admin_field_bound(parser, token): 250 tokens = token.contents.split() 251 if len(tokens) != 2: 252 raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 253 return AdminFieldBoundNode(tokens[1]) 254 255 256 def do_field_label(parser, token): 257 tokens = token.contents.split() 258 if len(tokens) != 2: 259 raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 260 return FieldLabelNode(tokens[1]) 261 262 def do_field_widget(parser, token): 263 tokens = token.contents.split() 264 if len(tokens) != 2: 265 raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 266 return FieldWidgetNode(tokens[1]) 267 268 def do_output_all(parser, token): 269 tokens = token.contents.split() 270 if len(tokens) != 2: 271 raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 272 return OutputAllNode(tokens[1]) 273 274 275 def do_edit_inline(parser, token): 276 tokens = token.contents.split() 277 if len(tokens) != 2: 278 raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 279 return EditInlineNode(tokens[1]) 280 281 def do_auto_populated_field_script(parser, token): 282 tokens = token.contents.split() 283 if len(tokens) != 2: 284 raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 285 return AutoPopulatedFieldScriptNode(tokens[1]) 286 287 288 289 template.register_tag('include_admin_script', do_include_admin_script) 290 template.register_tag('submit_row', do_submit_row ) 291 template.register_tag('admin_field_bound', do_admin_field_bound) 292 template.register_tag('edit_inline', do_edit_inline) 293 template.register_tag('auto_populated_field_script', do_auto_populated_field_script) 294 template.register_tag('field_label', do_field_label) 295 template.register_tag('field_widget', do_field_widget) 296 template.register_tag('output_all', do_output_all) 297 298 -
django/views/admin/main.py
1 1 # Generic admin views, with admin templates created dynamically at runtime. 2 2 3 from django.core import formfields, meta, template_loader 3 from django.core import formfields, meta, template_loader, template 4 4 from django.core.exceptions import Http404, ObjectDoesNotExist, PermissionDenied 5 5 from django.core.extensions import DjangoContext as Context 6 6 from django.models.auth import log … … 493 493 }) 494 494 return HttpResponse(t.render(c), mimetype='text/html; charset=utf-8') 495 495 496 def _get_flattened_data(field, val):497 """498 Returns a dictionary mapping the field's manipulator field names to its499 "flattened" string values for the admin view. "val" is an instance of the500 field's value.501 """502 if isinstance(field, meta.DateTimeField):503 date_field, time_field = field.get_manipulator_field_names('')504 return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''),505 time_field: (val is not None and val.strftime("%H:%M:%S") or '')}506 elif isinstance(field, meta.DateField):507 return {field.name: (val is not None and val.strftime("%Y-%m-%d") or '')}508 elif isinstance(field, meta.TimeField):509 return {field.name: (val is not None and val.strftime("%H:%M:%S") or '')}510 else:511 return {field.name: val}512 513 496 use_raw_id_admin = lambda field: isinstance(field.rel, (meta.ManyToOne, meta.ManyToMany)) and field.rel.raw_id_admin 514 497 515 498 def _get_submit_row_template(opts, app_label, add, change, show_delete, ordered_objects): … … 530 513 t.append('</div>\n') 531 514 return t 532 515 516 def get_javascript_imports(opts,auto_populated_fields, ordered_objects, admin_field_objs): 517 # Put in any necessary JavaScript imports. 518 js = ['js/core.js', 'js/admin/RelatedObjectLookups.js'] 519 if auto_populated_fields: 520 js.append('js/urlify.js') 521 if opts.has_field_type(meta.DateTimeField) or opts.has_field_type(meta.TimeField) or opts.has_field_type(meta.DateField): 522 js.extend(['js/calendar.js', 'js/admin/DateTimeShortcuts.js']) 523 if ordered_objects: 524 js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js']) 525 if opts.admin.js: 526 js.extend(opts.admin.js) 527 seen_collapse = False 528 for _, options in admin_field_objs: 529 if not seen_collapse and 'collapse' in options.get('classes', ''): 530 seen_collapse = True 531 js.append('js/admin/CollapsedFieldsets.js' ) 532 try: 533 for field_list in options['fields']: 534 for f in field_list: 535 if f.rel and isinstance(f, meta.ManyToManyField) and f.rel.filter_interface: 536 js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js']) 537 raise StopIteration 538 except StopIteration: 539 break 540 return js 541 542 def auto_populate_field_script(): 543 for field in auto_populated_fields: 544 if change: 545 t.append('document.getElementById("id_%s")._changed = true;' % field.name) 546 else: 547 t.append('document.getElementById("id_%s").onchange = function() { this._changed = true; };' % field.name) 548 for f in field.prepopulate_from: 549 t.append('document.getElementById("id_%s").onkeyup = function() { var e = document.getElementById("id_%s"); if (!e._changed) { e.value = URLify(%s, %s);}};' % \ 550 (f, field.name, ' + " " + '.join(['document.getElementById("id_%s").value' % g for g in field.prepopulate_from]), field.maxlength)) 551 552 class BoundField(object): 553 def __init__(self, index, field, original, form_prefix, name_prefix, rel, form_fields): 554 self.index = index 555 self.field = field 556 self.hack_prefix = 'form.' + form_prefix 557 self.label_name = 'id_%s%s' % (form_prefix, field.get_manipulator_field_names('')[0]) 558 self.has_label_first = not isinstance(self.field, meta.BooleanField) 559 self.original = original 560 self.raw_id_admin = use_raw_id_admin(field) 561 self.name_prefix = name_prefix 562 self.rel = rel 563 self.is_date_time = isinstance(field, meta.DateTimeField) 564 self.is_file_field = isinstance(field, meta.FileField) 565 self.needs_add_label = field.rel and isinstance(field.rel, meta.ManyToOne) and field.rel.to.admin 566 self.not_in_table = isinstance(self.field, meta.AutoField) 567 self.form_fields = form_fields 568 569 classes = [] 570 if(self.raw_id_admin): 571 classes.append('nowrap') 572 # if(self.field.errors()): 573 # classes.append('error') 574 self.cell_class_attribute = ' '.join(classes) 575 self._repr_filled = False 576 577 def as_field_list(self): 578 return [self.field] 579 580 def original_value(self): 581 return self.original.__dict__[self.field.name] 582 583 def _fetch_existing_repr(self, func_name): 584 class_dict = self.original.__class__.__dict__ 585 func = class_dict.get(func_name) 586 return func(self.original) 587 588 def _fill_existing_repr(self): 589 if self._repr_filled: 590 return 591 #HACK 592 if isinstance(self.field.rel, meta.ManyToOne): 593 func_name = 'get_%s' % self.field.name 594 self._repr = self._fetch_existing_repr(func_name) 595 elif isinstance(self.field.rel, meta.ManyToMany): 596 func_name = 'get_%s_list' % self.field.name 597 self._repr = ",".join(self._fetch_existing_repr(func_name)) 598 self._repr_filled = True 599 600 def existing_repr(self): 601 self._fill_existing_repr() 602 return self._repr 603 604 def __repr__(self): 605 return repr(self.__dict__) 606 607 608 609 610 class AdminFieldSet(object): 611 def __init__(self, fieldset_name, options, bound_field_sets): 612 self.name = fieldset_name 613 self.options = options 614 self.bound_field_sets = bound_field_sets 615 self.classes = options.get('classes', '') 616 # self.fields = options['fields'] 617 618 def __repr__(self): 619 return "Fieldset:(%s,%s)" % (self.name, self.bound_field_sets) 620 621 class InlineRelatedObject(object): 622 def __init__(self,obj, field): 623 self.obj = obj 624 self.field = field 625 626 627 def bound_field_sets(opts, context, name_prefix): 628 original = template.resolve_variable('original', context); 629 form_prefix = '' 630 fields = opts['fields'] 631 #raise repr(fields) 632 bound_field_sets = [ [BoundField(i, f, original, form_prefix, name_prefix, False, 633 resolve_form_fields(f,name_prefix, context)) for i,f in enumerate(field) ] for field in fields] 634 635 return bound_field_sets 636 637 def resolve_form_fields(field, name_prefix, context): 638 # raise repr(field.get_manipulator_field_names(name_prefix)) + "\n!!\n" + repr(context) 639 return [template.resolve_variable(name, context) for name in field.get_manipulator_field_names(name_prefix)] 640 641 642 def fill_extra_context(opts, app_label, context, add=False, change=False, show_delete=False, form_url=''): 643 admin_field_objs = opts.admin.get_field_objs(opts) 644 ordered_objects = opts.get_ordered_objects()[:] 645 auto_populated_fields = [f for f in opts.fields if f.prepopulate_from] 646 647 javascript_imports = get_javascript_imports(opts,auto_populated_fields, ordered_objects, admin_field_objs); 648 649 if ordered_objects: 650 coltype = 'colMS' 651 else: 652 coltype = 'colM' 653 654 has_absolute_url = hasattr(opts.get_model_module().Klass, 'get_absolute_url') 655 656 form_enc_attrib = opts.has_field_type(meta.FileField) and 'enctype="multipart/form-data" ' or '' 657 658 admin_fieldsets = [AdminFieldSet(f, o, bound_field_sets(o, context, 'form.')) for f, o in admin_field_objs] 659 inline_related_objects = [InlineRelatedObject(obj, field) for obj, field in opts.get_inline_related_objects()] 660 661 ordered_object_names = ' '.join(['object.%s' % o.pk.name for o in ordered_objects]) 662 663 extra_context = { 664 'add': add, 665 'change': change, 666 'admin_field_objs' : admin_field_objs, 667 'ordered_objects' : ordered_objects, 668 'auto_populated_fields' : auto_populated_fields, 669 'javascript_imports' : javascript_imports, 670 'coltype' : coltype, 671 'has_absolute_url': has_absolute_url, 672 'form_enc_attrib': form_enc_attrib, 673 'form_url' : form_url, 674 'admin_fieldsets' : admin_fieldsets, 675 'inline_related_objects': inline_related_objects, 676 'ordered_object_names' : ordered_object_names, 677 'content_type_id' : opts.get_content_type_id(), 678 'save_on_top' : opts.admin.save_on_top, 679 'verbose_name_plural': opts.verbose_name_plural, 680 'save_as': opts.admin.save_as, 681 'app_label': app_label, 682 'object_name': opts.object_name, 683 'has_delete_permission' : context['perms'][app_label][opts.get_delete_permission()] 684 } 685 686 context.update(extra_context) 687 688 689 def add_stage_new(request, app_label, module_name, show_delete=False, form_url='', post_url='../', post_url_continue='../%s/', object_id_override=None): 690 mod, opts = _get_mod_opts(app_label, module_name) 691 if not request.user.has_perm(app_label + '.' + opts.get_add_permission()): 692 raise PermissionDenied 693 manipulator = mod.AddManipulator() 694 if request.POST: 695 new_data = request.POST.copy() 696 if opts.has_field_type(meta.FileField): 697 new_data.update(request.FILES) 698 errors = manipulator.get_validation_errors(new_data) 699 if not errors and not request.POST.has_key("_preview"): 700 for f in opts.many_to_many: 701 if f.rel.raw_id_admin: 702 new_data.setlist(f.name, new_data[f.name].split(",")) 703 manipulator.do_html2python(new_data) 704 new_object = manipulator.save(new_data) 705 pk_value = getattr(new_object, opts.pk.column) 706 log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.ADDITION) 707 msg = 'The %s "%s" was added successfully.' % (opts.verbose_name, new_object) 708 # Here, we distinguish between different save types by checking for 709 # the presence of keys in request.POST. 710 if request.POST.has_key("_continue"): 711 request.user.add_message("%s You may edit it again below." % msg) 712 if request.POST.has_key("_popup"): 713 post_url_continue += "?_popup=1" 714 return HttpResponseRedirect(post_url_continue % pk_value) 715 if request.POST.has_key("_popup"): 716 return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, %s, "%s");</script>' % \ 717 (pk_value, repr(new_object).replace('"', '\\"')), mimetype='text/html; charset=utf-8') 718 elif request.POST.has_key("_addanother"): 719 request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name)) 720 return HttpResponseRedirect(request.path) 721 else: 722 request.user.add_message(msg) 723 return HttpResponseRedirect(post_url) 724 if request.POST.has_key("_preview"): 725 manipulator.do_html2python(new_data) 726 else: 727 new_data = {} 728 # Add default data. 729 for f in opts.fields: 730 if f.has_default(): 731 new_data.update( f.flatten_data(f.get_default()) ) 732 # In required many-to-one fields with only one available choice, 733 # select that one available choice. Note: We have to check that 734 # the length of choices is *2*, not 1, because SelectFields always 735 # have an initial "blank" value. 736 elif not f.blank and ((isinstance(f.rel, meta.ManyToOne) and not f.rel.raw_id_admin) or f.choices) and len(manipulator[f.name].choices) == 2: 737 new_data[f.name] = manipulator[f.name].choices[1][0] 738 # In required many-to-many fields with only one available choice, 739 # select that one available choice. 740 for f in opts.many_to_many: 741 if not f.blank and not f.rel.edit_inline and not f.rel.raw_id_admin and len(manipulator[f.name].choices) == 1: 742 new_data[f.name] = [manipulator[f.name].choices[0][0]] 743 # Add default data for related objects. 744 for rel_opts, rel_field in opts.get_inline_related_objects(): 745 var_name = rel_opts.object_name.lower() 746 for i in range(rel_field.rel.num_in_admin): 747 for f in rel_opts.fields + rel_opts.many_to_many: 748 if f.has_default(): 749 for field_name in f.get_manipulator_field_names(''): 750 new_data['%s.%d.%s' % (var_name, i, field_name)] = f.get_default() 751 # Override the defaults with request.GET, if it exists. 752 new_data.update(request.GET) 753 errors = {} 754 755 # Populate the FormWrapper. 756 form = formfields.FormWrapper(manipulator, new_data, errors) 757 for rel_opts, rel_field in opts.get_inline_related_objects(): 758 var_name = rel_opts.object_name.lower() 759 wrapper = [] 760 for i in range(rel_field.rel.num_in_admin): 761 collection = {} 762 for f in rel_opts.fields + rel_opts.many_to_many: 763 if f.editable and f != rel_field and not isinstance(f, meta.AutoField): 764 for field_name in f.get_manipulator_field_names(''): 765 full_field_name = '%s.%d.%s' % (var_name, i, field_name) 766 field = manipulator[full_field_name] 767 data = field.extract_data(new_data) 768 collection[field_name] = formfields.FormFieldWrapper(field, data, errors.get(full_field_name, [])) 769 wrapper.append(formfields.FormFieldCollection(collection)) 770 setattr(form, rel_opts.module_name, wrapper) 771 772 c = Context(request, { 773 'title': 'Add %s' % opts.verbose_name, 774 "form": form, 775 "is_popup": request.REQUEST.has_key("_popup"), 776 }) 777 if object_id_override is not None: 778 c['object_id'] = object_id_override 779 #raw_template = _get_template(opts, app_label, add=True, show_delete=show_delete, form_url=form_url) 780 # return HttpResponse(raw_template, mimetype='text/plain') 781 # t = template_loader.get_template_from_string(raw_template) 782 783 fill_extra_context(opts, app_label, c, change=True) 784 #raw_template = _get_template(opts, app_label, change=True) 785 # return HttpResponse(raw_template, mimetype='text/plain') 786 #t = template_loader.get_template_from_string(raw_template) 787 t = template_loader.get_template("admin_change_form"); 788 789 return HttpResponse(t.render(c), mimetype='text/html; charset=utf-8') 790 791 792 793 def change_stage_new(request, app_label, module_name, object_id): 794 mod, opts = _get_mod_opts(app_label, module_name) 795 if not request.user.has_perm(app_label + '.' + opts.get_change_permission()): 796 raise PermissionDenied 797 if request.POST and request.POST.has_key("_saveasnew"): 798 return add_stage_new(request, app_label, module_name, form_url='../add/') 799 try: 800 manipulator = mod.ChangeManipulator(object_id) 801 except ObjectDoesNotExist: 802 raise Http404 803 804 inline_related_objects = opts.get_inline_related_objects() 805 if request.POST: 806 new_data = request.POST.copy() 807 if opts.has_field_type(meta.FileField): 808 new_data.update(request.FILES) 809 810 errors = manipulator.get_validation_errors(new_data) 811 if not errors and not request.POST.has_key("_preview"): 812 for f in opts.many_to_many: 813 if f.rel.raw_id_admin: 814 new_data.setlist(f.name, new_data[f.name].split(",")) 815 manipulator.do_html2python(new_data) 816 new_object = manipulator.save(new_data) 817 pk_value = getattr(new_object, opts.pk.column) 818 819 # Construct the change message. 820 change_message = [] 821 if manipulator.fields_added: 822 change_message.append('Added %s.' % get_text_list(manipulator.fields_added, 'and')) 823 if manipulator.fields_changed: 824 change_message.append('Changed %s.' % get_text_list(manipulator.fields_changed, 'and')) 825 if manipulator.fields_deleted: 826 change_message.append('Deleted %s.' % get_text_list(manipulator.fields_deleted, 'and')) 827 change_message = ' '.join(change_message) 828 if not change_message: 829 change_message = 'No fields changed.' 830 831 log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.CHANGE, change_message) 832 msg = 'The %s "%s" was changed successfully.' % (opts.verbose_name, new_object) 833 if request.POST.has_key("_continue"): 834 request.user.add_message("%s You may edit it again below." % msg) 835 if request.REQUEST.has_key('_popup'): 836 return HttpResponseRedirect(request.path + "?_popup=1") 837 else: 838 return HttpResponseRedirect(request.path) 839 elif request.POST.has_key("_saveasnew"): 840 request.user.add_message('The %s "%s" was added successfully. You may edit it again below.' % (opts.verbose_name, new_object)) 841 return HttpResponseRedirect("../%s/" % pk_value) 842 elif request.POST.has_key("_addanother"): 843 request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name)) 844 return HttpResponseRedirect("../add/") 845 else: 846 request.user.add_message(msg) 847 return HttpResponseRedirect("../") 848 if request.POST.has_key("_preview"): 849 manipulator.do_html2python(new_data) 850 else: 851 # Populate new_data with a "flattened" version of the current data. 852 new_data = {} 853 obj = manipulator.original_object 854 for f in opts.fields: 855 new_data.update(f.flatten_data(getattr(obj, f.column))) 856 857 for f in opts.many_to_many: 858 get_list_func = getattr(obj, 'get_%s_list' % f.rel.singular) 859 if f.rel.raw_id_admin: 860 new_data[f.name] = ",".join([str(getattr(i, f.rel.to.pk.column)) for i in get_list_func()]) 861 elif not f.rel.edit_inline: 862 new_data[f.name] = [getattr(i, f.rel.to.pk.column) for i in get_list_func()] 863 864 for rel_obj, rel_field in inline_related_objects: 865 var_name = rel_obj.object_name.lower() 866 for i, rel_instance in enumerate(getattr(obj, 'get_%s_list' % opts.get_rel_object_method_name(rel_obj, rel_field))()): 867 for f in rel_obj.fields: 868 if f.editable and f != rel_field: 869 for k, v in f.flatten_data(getattr(rel_instance, f.column)).items(): 870 new_data['%s.%d.%s' % (var_name, i, k)] = v 871 for f in rel_obj.many_to_many: 872 new_data['%s.%d.%s' % (var_name, i, f.column)] = [j.id for j in getattr(rel_instance, 'get_%s_list' % f.rel.singular)()] 873 874 # If the object has ordered objects on its admin page, get the existing 875 # order and flatten it into a comma-separated list of IDs. 876 id_order_list = [] 877 for rel_obj in opts.get_ordered_objects(): 878 id_order_list.extend(getattr(obj, 'get_%s_order' % rel_obj.object_name.lower())()) 879 if id_order_list: 880 new_data['order_'] = ','.join(map(str, id_order_list)) 881 errors = {} 882 883 # Populate the FormWrapper. 884 form = formfields.FormWrapper(manipulator, new_data, errors) 885 form.original = manipulator.original_object 886 form.order_objects = [] 887 for rel_opts, rel_field in inline_related_objects: 888 var_name = rel_opts.object_name.lower() 889 wrapper = [] 890 orig_list = getattr(manipulator.original_object, 'get_%s_list' % opts.get_rel_object_method_name(rel_opts, rel_field))() 891 count = len(orig_list) + rel_field.rel.num_extra_on_change 892 if rel_field.rel.min_num_in_admin: 893 count = max(count, rel_field.rel.min_num_in_admin) 894 if rel_field.rel.max_num_in_admin: 895 count = min(count, rel_field.rel.max_num_in_admin) 896 for i in range(count): 897 collection = {'original': (i < len(orig_list) and orig_list[i] or None)} 898 for f in rel_opts.fields + rel_opts.many_to_many: 899 if f.editable and f != rel_field: 900 for field_name in f.get_manipulator_field_names(''): 901 full_field_name = '%s.%d.%s' % (var_name, i, field_name) 902 field = manipulator[full_field_name] 903 data = field.extract_data(new_data) 904 collection[field_name] = formfields.FormFieldWrapper(field, data, errors.get(full_field_name, [])) 905 wrapper.append(formfields.FormFieldCollection(collection)) 906 setattr(form, rel_opts.module_name, wrapper) 907 if rel_opts.order_with_respect_to and rel_opts.order_with_respect_to.rel and rel_opts.order_with_respect_to.rel.to == opts: 908 form.order_objects.extend(orig_list) 909 910 c = Context(request, { 911 'title': 'Change %s' % opts.verbose_name, 912 "form": form, 913 'object_id': object_id, 914 'original': manipulator.original_object, 915 'is_popup' : request.REQUEST.has_key('_popup') 916 }) 917 918 fill_extra_context(opts, app_label, c, change=True) 919 920 #raw_template = _get_template(opts, app_label, change=True) 921 # return HttpResponse(raw_template, mimetype='text/plain') 922 #t = template_loader.get_template_from_string(raw_template) 923 t = template_loader.get_template("admin_change_form"); 924 return HttpResponse(t.render(c), mimetype='text/html; charset=utf-8') 925 926 533 927 def _get_template(opts, app_label, add=False, change=False, show_delete=False, form_url=''): 534 928 admin_field_objs = opts.admin.get_field_objs(opts) 535 929 ordered_objects = opts.get_ordered_objects()[:] … … 802 1196 # Add default data. 803 1197 for f in opts.fields: 804 1198 if f.has_default(): 805 new_data.update( _get_flattened_data(f, f.get_default()))1199 new_data.update( f.flatten_data(f.get_default()) ) 806 1200 # In required many-to-one fields with only one available choice, 807 1201 # select that one available choice. Note: We have to check that 808 1202 # the length of choices is *2*, not 1, because SelectFields always … … 837 1231 if f.editable and f != rel_field and not isinstance(f, meta.AutoField): 838 1232 for field_name in f.get_manipulator_field_names(''): 839 1233 full_field_name = '%s.%d.%s' % (var_name, i, field_name) 840 collection[field_name] = formfields.FormFieldWrapper(manipulator[full_field_name], new_data.get(full_field_name, ''), errors.get(full_field_name, [])) 1234 field = manipulator[full_field_name] 1235 data = field.extract_data(new_data) 1236 collection[field_name] = formfields.FormFieldWrapper(field, data, errors.get(full_field_name, [])) 841 1237 wrapper.append(formfields.FormFieldCollection(collection)) 842 1238 setattr(form, rel_opts.module_name, wrapper) 843 1239 … … 915 1311 new_data = {} 916 1312 obj = manipulator.original_object 917 1313 for f in opts.fields: 918 new_data.update( _get_flattened_data(f,getattr(obj, f.column)))1314 new_data.update(f.flatten_data(getattr(obj, f.column))) 919 1315 for f in opts.many_to_many: 920 1316 get_list_func = getattr(obj, 'get_%s_list' % f.rel.singular) 921 1317 if f.rel.raw_id_admin: … … 927 1323 for i, rel_instance in enumerate(getattr(obj, 'get_%s_list' % opts.get_rel_object_method_name(rel_obj, rel_field))()): 928 1324 for f in rel_obj.fields: 929 1325 if f.editable and f != rel_field: 930 for k, v in _get_flattened_data(f,getattr(rel_instance, f.column)).items():1326 for k, v in f.flatten_data(getattr(rel_instance, f.column)).items(): 931 1327 new_data['%s.%d.%s' % (var_name, i, k)] = v 932 1328 for f in rel_obj.many_to_many: 933 1329 new_data['%s.%d.%s' % (var_name, i, f.column)] = [j.id for j in getattr(rel_instance, 'get_%s_list' % f.rel.singular)()] … … 960 1356 if f.editable and f != rel_field: 961 1357 for field_name in f.get_manipulator_field_names(''): 962 1358 full_field_name = '%s.%d.%s' % (var_name, i, field_name) 963 collection[field_name] = formfields.FormFieldWrapper(manipulator[full_field_name], new_data.get(full_field_name, f.get_default()), errors.get(full_field_name, [])) 1359 field = manipulator[full_field_name] 1360 data = field.extract_data(new_data) 1361 collection[field_name] = formfields.FormFieldWrapper(field, data, errors.get(full_field_name, [])) 964 1362 wrapper.append(formfields.FormFieldCollection(collection)) 965 1363 setattr(form, rel_opts.module_name, wrapper) 966 1364 if rel_opts.order_with_respect_to and rel_opts.order_with_respect_to.rel and rel_opts.order_with_respect_to.rel.to == opts: