Ticket #535: django-admin-refactor.patch
File django-admin-refactor.patch, 53.9 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.has_existing %} 26 <strong>{{ bound_field.existing | 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);"> 18 {% endif %} 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/" 21 class="add-another" id="add_{{ bound_field.label_name}}"> 22 <img src="{% admin_media_prefix %}img/admin/icon_addlink.gif" width="10" height="10" alt="Add Another"/> 23 </a> 24 {% endif %} 25 {% endif %} 26 {% endif %} 27 28 29 -
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 use_raw_id_admin = lambda field: isinstance(field.rel, (meta.ManyToOne, meta.ManyToMany)) and field.rel.raw_id_admin 7 8 9 from django.views.admin.main import BoundField, resolve_form_fields 10 11 class IncludeAdminScriptNode(template.Node): 12 def __init__(self, var): 13 self.var = var 14 15 def render(self, context): 16 resolved = template.resolve_variable(self.var, context) 17 return '<script type="text/javascript" src="%s%s"></script>' % \ 18 (ADMIN_MEDIA_PREFIX, resolved) 19 20 class DummyNode(template.Node): 21 def __init__(self, var): 22 self.var = var 23 24 def render(self, context): 25 #resolved = template.resolve_variable(self.var, context) 26 return "<b> Dummy: %s</b>" % self.var 27 28 def template_hack(t, context): 29 raw = ''.join(t) 30 template = template_loader.get_template_from_string(raw) 31 return template.render(context) 32 33 34 class SubmitRowNode(template.Node): 35 def __init__(self): 36 pass 37 38 def render(self, context): 39 change = context['change'] 40 add = context['add'] 41 show_delete = context['show_delete'] 42 ordered_objects = context['ordered_objects'] 43 save_as = context['save_as'] 44 has_delete_permission = context['has_delete_permission'] 45 is_popup = context['is_popup'] 46 47 t = ['<div class="submit-row">'] 48 onclick_attrib = ordered_objects and change and 'onclick="submitOrderForm();"' or '' 49 50 if not is_popup: 51 if has_delete_permission and (change or show_delete): 52 t.append('<p class="float-left"><a href="delete/" class="deletelink">Delete</a></p>') 53 if change and save_as: 54 t.append('<input type="submit" value="Save as new" name="_saveasnew" %s/>' % onclick_attrib) 55 if (not save_as or add): 56 t.append('<input type="submit" value="Save and add another" name="_addanother" %s/>' % onclick_attrib) 57 t.append('<input type="submit" value="Save and continue editing" name="_continue" %s/>' % onclick_attrib ) 58 t.append('<input type="submit" value="Save" class="default" %s/>' % onclick_attrib) 59 t.append('</div>\n') 60 61 return ''.join(t) 62 63 64 65 66 class AdminFieldBoundNode(template.Node): 67 def __init__(self, argument): 68 self.argument = argument 69 70 def render(self, context): 71 argument_val = template.resolve_variable(self.argument, context) 72 if (isinstance(argument_val, list)): 73 bound_fields = argument_val 74 else: 75 bound_fields = [argument_val] 76 add = context['add'] 77 change = context['change'] 78 79 context.push() 80 context['bound_fields'] = bound_fields 81 context['class_names'] = " ".join(self.get_class_names(bound_fields)) 82 t = template_loader.get_template("admin_field") 83 output = t.render(context) 84 context.pop() 85 86 return output 87 88 def get_class_names(self, bound_fields): 89 90 class_names = ['form-row'] 91 for bound_field in bound_fields: 92 for f in bound_field.form_fields: 93 if f.errors(): 94 class_names.append('errors') 95 break 96 97 # Assumes BooleanFields won't be stacked next to each other! 98 if isinstance(bound_fields[0].field, meta.BooleanField): 99 class_names.append('checkbox-row') 100 101 return class_names 102 103 class FieldWidgetNode(template.Node): 104 def __init__(self, bound_field_var): 105 self.bound_field_var = bound_field_var 106 107 def render(self, context): 108 bound_field = template.resolve_variable(self.bound_field_var, context) 109 add = context['add'] 110 change = context['change'] 111 112 context.push() 113 context['bound_field'] = bound_field 114 t = template_loader.get_template("admin_field_widget") 115 output = t.render(context) 116 context.pop() 117 118 return output 119 120 121 122 class FieldWrapper(object): 123 def __init__(self, field ): 124 self.field = field 125 126 def needs_header(self): 127 return not isinstance(self.field, meta.AutoField) 128 129 def header_class_attribute(self): 130 return self.field.blank and ' class="optional"' or '' 131 132 133 134 class FormObjectWrapper(object): 135 def __init__(self, obj, field_wrappers, i, object_name): 136 self.obj = obj 137 self.field_wrappers = field_wrappers 138 139 form_prefix = '%s.%s.' % ( object_name, i) 140 141 name_prefix = '' 142 143 self.bound_fields = [ BoundField(0, fw.field, obj['original'], form_prefix, 144 name_prefix, True, self.resolve_form_fields(fw.field, name_prefix) ) \ 145 for fw in self.field_wrappers ] 146 147 148 def resolve_form_fields(self,field, name_prefix): 149 return [self.obj[name] for name in field.get_manipulator_field_names(name_prefix)] 150 151 #HACK 152 def has_errors(self): 153 return max([ bool( len( self.obj[fw.field.name].errors() ) ) for fw in self.field_wrappers]) 154 155 def html_combined_error_list(self): 156 return ''.join( [ self.obj[fw.field.name].html_error_list() for fw in self.field_wrappers]) 157 158 159 160 161 class EditInlineNode(template.Node): 162 def __init__(self, rel_var): 163 self.rel_var = rel_var 164 165 def render(self, context): 166 relation = template.resolve_variable(self.rel_var, context) 167 add, change = context['add'], context['change'] 168 169 context.push() 170 171 self.fill_context_tabular(relation, add, change, context) 172 173 if relation.field.rel.edit_inline == meta.TABULAR: 174 t = template_loader.get_template("admin_edit_inline_tabular") 175 else: # meta.STACKED 176 t = template_loader.get_template("admin_edit_inline_stacked") 177 178 output = t.render(context) 179 # t = self.get_template(relation, add, change, context) 180 # return '<pre>%s</pre>' % escape(''.join(t)) 181 # return template_hack(t, context) 182 183 context.pop() 184 return output 185 186 187 def fill_context_tabular(self, relation, add, change, context): 188 field_wrapper_list = [FieldWrapper(f) for f in relation.obj.fields + relation.obj.many_to_many if f.editable and f != relation.field] 189 190 var_name = relation.obj.object_name.lower() 191 192 form_objects = template.resolve_variable('form.%s' % relation.obj.module_name , context) 193 form_object_wrapper_list = [FormObjectWrapper(o,field_wrapper_list, i, var_name) for i,o in enumerate(form_objects)] 194 195 context['field_wrapper_list'] = field_wrapper_list 196 context['form_object_wrapper_list'] = form_object_wrapper_list 197 context['num_headers'] = len(field_wrapper_list) 198 context['original_row_needed'] = max([use_raw_id_admin(fw.field) for fw in field_wrapper_list]) 199 context['name_prefix'] = "%s." % (var_name,) 200 201 class FieldLabelNode(template.Node): 202 def __init__(self, bound_field_var): 203 self.bound_field_var = bound_field_var 204 205 def render(self, context): 206 bound_field = template.resolve_variable(self.bound_field_var, context) 207 class_names = [] 208 if isinstance(bound_field.field, meta.BooleanField): 209 class_names.append("vCheckboxLabel") 210 else: 211 if not bound_field.field.blank: 212 class_names.append('required') 213 if bound_field.index > 0: 214 class_names.append('inline') 215 216 class_str = class_names and ' class="%s"' % ' '.join(class_names) or '' 217 return '<label for="%s"%s>%s:</label> ' % (bound_field.label_name, class_str, capfirst(bound_field.field.verbose_name) ) 218 219 class OutputAllNode(template.Node): 220 def __init__(self, form_fields_var): 221 self.form_fields_var = form_fields_var 222 223 def render(self, context): 224 form_fields = template.resolve_variable(self.form_fields_var, context) 225 return ''.join([str(f) for f in form_fields]) 226 227 class AutoPopulatedFieldScript(template.Node): 228 def __init__(self, auto_pop_var): 229 self.auto_pop_var = auto_pop_var 230 231 def render(self,context): 232 auto_pop_fields = template.resolve_variable(self, context) 233 change = context['change'] 234 for field in auto_populated_fields: 235 t = [] 236 if change: 237 t.append('document.getElementById("id_%s")._changed = true;' % field.name ) 238 else: 239 t.append('document.getElementById("id_%s").onchange = function() { this._changed = true; };' % field.name 240 241 add_values = ' + " " + ' join(['document.getElementById("id_%s").value' % g for g in field.prepopulate_from]) 242 for f in field.prepopulate_from: 243 t.append(""" 244 document.getElementById("id_%s").onkeyup = function() { 245 var e = document.getElementById("id_%s"); 246 if(!e._changed) { e.value = URLify(%s, %s);} 247 } 248 """ % (f, field.name, add_values, field.maxlength)) 249 ) 250 251 return ''.join(t) 252 253 def do_include_admin_script(parser, token): 254 tokens = token.contents.split() 255 if len(tokens) != 2: 256 raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 257 258 return IncludeAdminScriptNode(tokens[1]) 259 260 def do_dummy(parser, token): 261 return DummyNode(token.contents) 262 263 def do_submit_row(parser, token): 264 return SubmitRowNode() 265 266 def do_admin_field_bound(parser, token): 267 tokens = token.contents.split() 268 if len(tokens) != 2: 269 raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 270 return AdminFieldBoundNode(tokens[1]) 271 272 273 def do_field_label(parser, token): 274 tokens = token.contents.split() 275 if len(tokens) != 2: 276 raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 277 return FieldLabelNode(tokens[1]) 278 279 def do_field_widget(parser, token): 280 tokens = token.contents.split() 281 if len(tokens) != 2: 282 raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 283 return FieldWidgetNode(tokens[1]) 284 285 def do_output_all(parser, token): 286 tokens = token.contents.split() 287 if len(tokens) != 2: 288 raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 289 return OutputAllNode(tokens[1]) 290 291 292 def do_edit_inline(parser, token): 293 tokens = token.contents.split() 294 if len(tokens) != 2: 295 raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 296 return EditInlineNode(tokens[1]) 297 298 def do_auto_populated_field_script(parser, token): 299 tokens = token.contents.split() 300 if len(tokens) != 2: 301 raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 302 return AutoPopulatedFieldScriptNode(tokens[1]) 303 304 305 306 template.register_tag('include_admin_script', do_include_admin_script) 307 template.register_tag('submit_row', do_submit_row ) 308 template.register_tag('admin_field_bound', do_admin_field_bound) 309 template.register_tag('edit_inline', do_edit_inline) 310 template.register_tag('auto_populated_field_script', do_auto_populated_field_script) 311 template.register_tag('field_label', do_field_label) 312 template.register_tag('field_widget', do_field_widget) 313 template.register_tag('output_all', do_output_all) 314 315 -
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 576 def as_field_list(self): 577 return [self.field] 578 579 def original_value(self): 580 return self.original.__dict__[self.field.name] 581 582 def fill_existing_repr(self): 583 if self._repr_filled: 584 return 585 #HACK 586 if isinstance(field.rel, meta.ManyToOne): 587 self._obj = original.__class__.__dict__['get_%s' % self.field.name](original) 588 self._repr = obj 589 elif isinstance(field.rel, meta.ManyToMany): 590 self._obj = original.__class__.__dict__['get_%s_list' % self.field.name ](original) 591 self._repr = "".join(obj) 592 self._repr_filled = True 593 594 def has_existing_repr(self): 595 self.fill_existing_repr() 596 return self._obj and True or False 597 598 def existing_repr(self): 599 self.fill_existing_repr() 600 return self._repr 601 602 def __repr__(self): 603 return repr(self.__dict__) 604 605 606 607 608 class AdminFieldSet(object): 609 def __init__(self, fieldset_name, options, bound_field_sets): 610 self.name = fieldset_name 611 self.options = options 612 self.bound_field_sets = bound_field_sets 613 self.classes = [] + options['classes'] 614 # self.fields = options['fields'] 615 616 def __repr__(self): 617 return "Fieldset:(%s,%s)" % (self.name, self.bound_field_sets) 618 619 class InlineRelatedObject(object): 620 def __init__(self,obj, field): 621 self.obj = obj 622 self.field = field 623 624 625 def bound_field_sets(opts, context, name_prefix): 626 original = template.resolve_variable('original', context); 627 form_prefix = '' 628 fields = opts['fields'] 629 #raise repr(fields) 630 bound_field_sets = [ [BoundField(i, f, original, form_prefix, name_prefix, False, 631 resolve_form_fields(f,name_prefix, context)) for i,f in enumerate(field) ] for field in fields] 632 633 return bound_field_sets 634 635 def resolve_form_fields(field, name_prefix, context): 636 # raise repr(field.get_manipulator_field_names(name_prefix)) + "\n!!\n" + repr(context) 637 return [template.resolve_variable(name, context) for name in field.get_manipulator_field_names(name_prefix)] 638 639 640 def fill_extra_context(opts, app_label, context, add=False, change=False, show_delete=False, form_url=''): 641 admin_field_objs = opts.admin.get_field_objs(opts) 642 ordered_objects = opts.get_ordered_objects()[:] 643 auto_populated_fields = [f for f in opts.fields if f.prepopulate_from] 644 645 javascript_imports = get_javascript_imports(opts,auto_populated_fields, ordered_objects, admin_field_objs); 646 647 if ordered_objects: 648 coltype = 'colMS' 649 else: 650 coltype = 'colM' 651 652 has_absolute_url = hasattr(opts.get_model_module().Klass, 'get_absolute_url') 653 654 form_enc_attrib = opts.has_field_type(meta.FileField) and 'enctype="multipart/form-data" ' or '' 655 656 admin_fieldsets = [AdminFieldSet(f, o, bound_field_sets(o, context, 'form.')) for f, o in admin_field_objs] 657 inline_related_objects = [InlineRelatedObject(obj, field) for obj, field in opts.get_inline_related_objects()] 658 659 ordered_object_names = ' '.join(['object.%s' % o.pk.name for o in ordered_objects]) 660 661 extra_context = { 662 'add': add, 663 'change': change, 664 'admin_field_objs' : admin_field_objs, 665 'ordered_objects' : ordered_objects, 666 'auto_populated_fields' : auto_populated_fields, 667 'javascript_imports' : javascript_imports, 668 'coltype' : coltype, 669 'has_absolute_url': has_absolute_url, 670 'form_enc_attrib': form_enc_attrib, 671 'form_url' : form_url, 672 'admin_fieldsets' : admin_fieldsets, 673 'inline_related_objects': inline_related_objects, 674 'ordered_object_names' : ordered_object_names, 675 'content_type_id' : opts.get_content_type_id(), 676 'save_on_top' : opts.admin.save_on_top, 677 'verbose_name_plural': opts.verbose_name_plural, 678 'save_as': opts.admin.save_as, 679 'app_label': app_label, 680 'object_name': opts.object_name, 681 'has_delete_permission' = context['perms'][app_label][opts.get_delete_permission() 682 } 683 684 context.update(extra_context) 685 686 687 def add_stage_new(request, app_label, module_name, show_delete=False, form_url='', post_url='../', post_url_continue='../%s/', object_id_override=None): 688 mod, opts = _get_mod_opts(app_label, module_name) 689 if not request.user.has_perm(app_label + '.' + opts.get_add_permission()): 690 raise PermissionDenied 691 manipulator = mod.AddManipulator() 692 if request.POST: 693 new_data = request.POST.copy() 694 if opts.has_field_type(meta.FileField): 695 new_data.update(request.FILES) 696 errors = manipulator.get_validation_errors(new_data) 697 if not errors and not request.POST.has_key("_preview"): 698 for f in opts.many_to_many: 699 if f.rel.raw_id_admin: 700 new_data.setlist(f.name, new_data[f.name].split(",")) 701 manipulator.do_html2python(new_data) 702 new_object = manipulator.save(new_data) 703 pk_value = getattr(new_object, opts.pk.column) 704 log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.ADDITION) 705 msg = 'The %s "%s" was added successfully.' % (opts.verbose_name, new_object) 706 # Here, we distinguish between different save types by checking for 707 # the presence of keys in request.POST. 708 if request.POST.has_key("_continue"): 709 request.user.add_message("%s You may edit it again below." % msg) 710 if request.POST.has_key("_popup"): 711 post_url_continue += "?_popup=1" 712 return HttpResponseRedirect(post_url_continue % pk_value) 713 if request.POST.has_key("_popup"): 714 return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, %s, "%s");</script>' % \ 715 (pk_value, repr(new_object).replace('"', '\\"')), mimetype='text/html; charset=utf-8') 716 elif request.POST.has_key("_addanother"): 717 request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name)) 718 return HttpResponseRedirect(request.path) 719 else: 720 request.user.add_message(msg) 721 return HttpResponseRedirect(post_url) 722 if request.POST.has_key("_preview"): 723 manipulator.do_html2python(new_data) 724 else: 725 new_data = {} 726 # Add default data. 727 for f in opts.fields: 728 if f.has_default(): 729 new_data.update( f.flatten_data(f.get_default()) ) 730 # In required many-to-one fields with only one available choice, 731 # select that one available choice. Note: We have to check that 732 # the length of choices is *2*, not 1, because SelectFields always 733 # have an initial "blank" value. 734 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: 735 new_data[f.name] = manipulator[f.name].choices[1][0] 736 # In required many-to-many fields with only one available choice, 737 # select that one available choice. 738 for f in opts.many_to_many: 739 if not f.blank and not f.rel.edit_inline and not f.rel.raw_id_admin and len(manipulator[f.name].choices) == 1: 740 new_data[f.name] = [manipulator[f.name].choices[0][0]] 741 # Add default data for related objects. 742 for rel_opts, rel_field in opts.get_inline_related_objects(): 743 var_name = rel_opts.object_name.lower() 744 for i in range(rel_field.rel.num_in_admin): 745 for f in rel_opts.fields + rel_opts.many_to_many: 746 if f.has_default(): 747 for field_name in f.get_manipulator_field_names(''): 748 new_data['%s.%d.%s' % (var_name, i, field_name)] = f.get_default() 749 # Override the defaults with request.GET, if it exists. 750 new_data.update(request.GET) 751 errors = {} 752 753 # Populate the FormWrapper. 754 form = formfields.FormWrapper(manipulator, new_data, errors) 755 for rel_opts, rel_field in opts.get_inline_related_objects(): 756 var_name = rel_opts.object_name.lower() 757 wrapper = [] 758 for i in range(rel_field.rel.num_in_admin): 759 collection = {} 760 for f in rel_opts.fields + rel_opts.many_to_many: 761 if f.editable and f != rel_field and not isinstance(f, meta.AutoField): 762 for field_name in f.get_manipulator_field_names(''): 763 full_field_name = '%s.%d.%s' % (var_name, i, field_name) 764 field = manipulator[full_field_name] 765 data = field.extract_data(new_data) 766 collection[field_name] = formfields.FormFieldWrapper(field, data, errors.get(full_field_name, [])) 767 wrapper.append(formfields.FormFieldCollection(collection)) 768 setattr(form, rel_opts.module_name, wrapper) 769 770 c = Context(request, { 771 'title': 'Add %s' % opts.verbose_name, 772 "form": form, 773 "is_popup": request.REQUEST.has_key("_popup"), 774 }) 775 if object_id_override is not None: 776 c['object_id'] = object_id_override 777 #raw_template = _get_template(opts, app_label, add=True, show_delete=show_delete, form_url=form_url) 778 # return HttpResponse(raw_template, mimetype='text/plain') 779 # t = template_loader.get_template_from_string(raw_template) 780 781 fill_extra_context(opts, app_label, c, change=True) 782 #raw_template = _get_template(opts, app_label, change=True) 783 # return HttpResponse(raw_template, mimetype='text/plain') 784 #t = template_loader.get_template_from_string(raw_template) 785 t = template_loader.get_template("admin_change_form"); 786 787 return HttpResponse(t.render(c), mimetype='text/html; charset=utf-8') 788 789 790 791 def change_stage_new(request, app_label, module_name, object_id): 792 mod, opts = _get_mod_opts(app_label, module_name) 793 if not request.user.has_perm(app_label + '.' + opts.get_change_permission()): 794 raise PermissionDenied 795 if request.POST and request.POST.has_key("_saveasnew"): 796 return add_stage_new(request, app_label, module_name, form_url='../add/') 797 try: 798 manipulator = mod.ChangeManipulator(object_id) 799 except ObjectDoesNotExist: 800 raise Http404 801 802 inline_related_objects = opts.get_inline_related_objects() 803 if request.POST: 804 new_data = request.POST.copy() 805 if opts.has_field_type(meta.FileField): 806 new_data.update(request.FILES) 807 808 errors = manipulator.get_validation_errors(new_data) 809 if not errors and not request.POST.has_key("_preview"): 810 for f in opts.many_to_many: 811 if f.rel.raw_id_admin: 812 new_data.setlist(f.name, new_data[f.name].split(",")) 813 manipulator.do_html2python(new_data) 814 new_object = manipulator.save(new_data) 815 pk_value = getattr(new_object, opts.pk.column) 816 817 # Construct the change message. 818 change_message = [] 819 if manipulator.fields_added: 820 change_message.append('Added %s.' % get_text_list(manipulator.fields_added, 'and')) 821 if manipulator.fields_changed: 822 change_message.append('Changed %s.' % get_text_list(manipulator.fields_changed, 'and')) 823 if manipulator.fields_deleted: 824 change_message.append('Deleted %s.' % get_text_list(manipulator.fields_deleted, 'and')) 825 change_message = ' '.join(change_message) 826 if not change_message: 827 change_message = 'No fields changed.' 828 829 log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.CHANGE, change_message) 830 msg = 'The %s "%s" was changed successfully.' % (opts.verbose_name, new_object) 831 if request.POST.has_key("_continue"): 832 request.user.add_message("%s You may edit it again below." % msg) 833 if request.REQUEST.has_key('_popup'): 834 return HttpResponseRedirect(request.path + "?_popup=1") 835 else: 836 return HttpResponseRedirect(request.path) 837 elif request.POST.has_key("_saveasnew"): 838 request.user.add_message('The %s "%s" was added successfully. You may edit it again below.' % (opts.verbose_name, new_object)) 839 return HttpResponseRedirect("../%s/" % pk_value) 840 elif request.POST.has_key("_addanother"): 841 request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name)) 842 return HttpResponseRedirect("../add/") 843 else: 844 request.user.add_message(msg) 845 return HttpResponseRedirect("../") 846 if request.POST.has_key("_preview"): 847 manipulator.do_html2python(new_data) 848 else: 849 # Populate new_data with a "flattened" version of the current data. 850 new_data = {} 851 obj = manipulator.original_object 852 for f in opts.fields: 853 new_data.update(f.flatten_data(getattr(obj, f.column))) 854 855 for f in opts.many_to_many: 856 get_list_func = getattr(obj, 'get_%s_list' % f.rel.singular) 857 if f.rel.raw_id_admin: 858 new_data[f.name] = ",".join([str(getattr(i, f.rel.to.pk.column)) for i in get_list_func()]) 859 elif not f.rel.edit_inline: 860 new_data[f.name] = [getattr(i, f.rel.to.pk.column) for i in get_list_func()] 861 862 for rel_obj, rel_field in inline_related_objects: 863 var_name = rel_obj.object_name.lower() 864 for i, rel_instance in enumerate(getattr(obj, 'get_%s_list' % opts.get_rel_object_method_name(rel_obj, rel_field))()): 865 for f in rel_obj.fields: 866 if f.editable and f != rel_field: 867 for k, v in f.flatten_data(getattr(rel_instance, f.column)).items(): 868 new_data['%s.%d.%s' % (var_name, i, k)] = v 869 for f in rel_obj.many_to_many: 870 new_data['%s.%d.%s' % (var_name, i, f.column)] = [j.id for j in getattr(rel_instance, 'get_%s_list' % f.rel.singular)()] 871 872 # If the object has ordered objects on its admin page, get the existing 873 # order and flatten it into a comma-separated list of IDs. 874 id_order_list = [] 875 for rel_obj in opts.get_ordered_objects(): 876 id_order_list.extend(getattr(obj, 'get_%s_order' % rel_obj.object_name.lower())()) 877 if id_order_list: 878 new_data['order_'] = ','.join(map(str, id_order_list)) 879 errors = {} 880 881 # Populate the FormWrapper. 882 form = formfields.FormWrapper(manipulator, new_data, errors) 883 form.original = manipulator.original_object 884 form.order_objects = [] 885 for rel_opts, rel_field in inline_related_objects: 886 var_name = rel_opts.object_name.lower() 887 wrapper = [] 888 orig_list = getattr(manipulator.original_object, 'get_%s_list' % opts.get_rel_object_method_name(rel_opts, rel_field))() 889 count = len(orig_list) + rel_field.rel.num_extra_on_change 890 if rel_field.rel.min_num_in_admin: 891 count = max(count, rel_field.rel.min_num_in_admin) 892 if rel_field.rel.max_num_in_admin: 893 count = min(count, rel_field.rel.max_num_in_admin) 894 for i in range(count): 895 collection = {'original': (i < len(orig_list) and orig_list[i] or None)} 896 for f in rel_opts.fields + rel_opts.many_to_many: 897 if f.editable and f != rel_field: 898 for field_name in f.get_manipulator_field_names(''): 899 full_field_name = '%s.%d.%s' % (var_name, i, field_name) 900 field = manipulator[full_field_name] 901 data = field.extract_data(new_data) 902 collection[field_name] = formfields.FormFieldWrapper(field, data, errors.get(full_field_name, [])) 903 wrapper.append(formfields.FormFieldCollection(collection)) 904 setattr(form, rel_opts.module_name, wrapper) 905 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: 906 form.order_objects.extend(orig_list) 907 908 c = Context(request, { 909 'title': 'Change %s' % opts.verbose_name, 910 "form": form, 911 'object_id': object_id, 912 'original': manipulator.original_object, 913 'is_popup' : request.REQUEST.has_key('_popup') 914 }) 915 916 fill_extra_context(opts, app_label, c, change=True) 917 918 #raw_template = _get_template(opts, app_label, change=True) 919 # return HttpResponse(raw_template, mimetype='text/plain') 920 #t = template_loader.get_template_from_string(raw_template) 921 t = template_loader.get_template("admin_change_form"); 922 return HttpResponse(t.render(c), mimetype='text/html; charset=utf-8') 923 924 533 925 def _get_template(opts, app_label, add=False, change=False, show_delete=False, form_url=''): 534 926 admin_field_objs = opts.admin.get_field_objs(opts) 535 927 ordered_objects = opts.get_ordered_objects()[:] … … 802 1194 # Add default data. 803 1195 for f in opts.fields: 804 1196 if f.has_default(): 805 new_data.update( _get_flattened_data(f, f.get_default()))1197 new_data.update( f.flatten_data(f.get_default()) ) 806 1198 # In required many-to-one fields with only one available choice, 807 1199 # select that one available choice. Note: We have to check that 808 1200 # the length of choices is *2*, not 1, because SelectFields always … … 837 1229 if f.editable and f != rel_field and not isinstance(f, meta.AutoField): 838 1230 for field_name in f.get_manipulator_field_names(''): 839 1231 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, [])) 1232 field = manipulator[full_field_name] 1233 data = field.extract_data(new_data) 1234 collection[field_name] = formfields.FormFieldWrapper(field, data, errors.get(full_field_name, [])) 841 1235 wrapper.append(formfields.FormFieldCollection(collection)) 842 1236 setattr(form, rel_opts.module_name, wrapper) 843 1237 … … 915 1309 new_data = {} 916 1310 obj = manipulator.original_object 917 1311 for f in opts.fields: 918 new_data.update( _get_flattened_data(f,getattr(obj, f.column)))1312 new_data.update(f.flatten_data(getattr(obj, f.column))) 919 1313 for f in opts.many_to_many: 920 1314 get_list_func = getattr(obj, 'get_%s_list' % f.rel.singular) 921 1315 if f.rel.raw_id_admin: … … 927 1321 for i, rel_instance in enumerate(getattr(obj, 'get_%s_list' % opts.get_rel_object_method_name(rel_obj, rel_field))()): 928 1322 for f in rel_obj.fields: 929 1323 if f.editable and f != rel_field: 930 for k, v in _get_flattened_data(f,getattr(rel_instance, f.column)).items():1324 for k, v in f.flatten_data(getattr(rel_instance, f.column)).items(): 931 1325 new_data['%s.%d.%s' % (var_name, i, k)] = v 932 1326 for f in rel_obj.many_to_many: 933 1327 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 1354 if f.editable and f != rel_field: 961 1355 for field_name in f.get_manipulator_field_names(''): 962 1356 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, [])) 1357 field = manipulator[full_field_name] 1358 data = field.extract_data(new_data) 1359 collection[field_name] = formfields.FormFieldWrapper(field, data, errors.get(full_field_name, [])) 964 1360 wrapper.append(formfields.FormFieldCollection(collection)) 965 1361 setattr(form, rel_opts.module_name, wrapper) 966 1362 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: