Ticket #535: django-admin-refactor-6.patch
| File django-admin-refactor-6.patch, 75.8 kB (added by robert@wittams.com, 3 years ago) |
|---|
-
django/conf/urls/admin.py
old new 48 48 49 49 urlpatterns += ( 50 50 # Metasystem admin pages 51 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/add_old/$', 'django.views.admin.main.add_stage'), 52 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)_old/$', 'django.views.admin.main.change_stage'), 51 53 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/$', 'django.views.admin.main.change_list'), 52 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/add/$', 'django.views.admin.main.add_stage '),54 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/add/$', 'django.views.admin.main.add_stage_new'), 53 55 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/jsvalidation/$', 'django.views.admin.jsvalidation.jsvalidation'), 54 56 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)/history/$', 'django.views.admin.main.history'), 55 57 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)/delete/$', 'django.views.admin.main.delete_stage'), 56 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)/$', 'django.views.admin.main.change_stage '),58 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)/$', 'django.views.admin.main.change_stage_new'), 57 59 ) 58 60 urlpatterns = patterns('', *urlpatterns) -
django/conf/admin_templates/admin_change_form.html
old new 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 {% filter_interface_script_maybe bound_field %} 62 {% endfor %} 63 {% endfor %} 64 </fieldset> 65 {% endfor %} 66 67 {% if change %} 68 {% if ordered_objects %} 69 <fieldset class="module"><h2>Ordering</h2> 70 <div class="form-row{% if form.order_.errors %} error{% endif %} "> 71 {% if form.order_.errors %}{{ form.order_.html_error_list }}{% endif %} 72 <p><label for="id_order_">Order:</label> {{ form.order_ }}</p> 73 </div></fieldset> 74 {% endif %} 75 {% endif %} 76 77 78 {% for relation in inline_related_objects %} 79 {% edit_inline relation %} 80 {% endfor %} 81 82 {% submit_row %} 83 84 {% if add %} 85 <script type="text/javascript">document.getElementById("id_{{first_field}}").focus();</script>' 86 {% endif %} 87 88 {% if auto_populated_fields %} 89 <script type="text/javascript"> 90 {% auto_populated_field_script auto_populated_fields %} 91 </script> 92 {% endif %} 93 94 {% if change %} 95 {% if ordered_objects %} 96 {% if form.order_objects %}<ul id="orderthese"> 97 {% for object in form.order_objects %} 98 <li id="p{% firstof ordered_object_names %}"> 99 <span id="handlep{% firstof ordered_object_names %}">{{ object|truncatewords:"5" }}</span> 100 </li> 101 {% endfor%} 102 {% endif %} 103 {% endif %} 104 {% endif%} 105 </form> 106 107 {% endblock %} -
django/conf/admin_templates/admin_edit_inline_stacked.html
old new 1 <fieldset class="module aligned"> 2 {% for ow in form_object_wrapper_list %} 3 <h2>{{relation.opts.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
old new 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
old new 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.element_id}}" 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.element_id}}" onclick="return showAddAnotherPopup(this);"> <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
old new 1 <fieldset class="module"> 2 <h2>{{relation.opts.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/conf/settings.py
old new 26 26 if not me.SETTINGS_MODULE: # If it's set but is an empty string. 27 27 raise KeyError 28 28 except KeyError: 29 raise EnvironmentError, "Environment variable %s is undefined." % ENVIRONMENT_VARIABLE29 raise EnvironmentError, "Environment variable %s is undefined." % (ENVIRONMENT_VARIABLE) 30 30 31 31 try: 32 32 mod = __import__(me.SETTINGS_MODULE, '', '', ['']) -
django/core/formfields.py
old new 22 22 for field in self.fields: 23 23 if field.field_name == field_name: 24 24 return field 25 raise KeyError, "Field %s not found " % field_name25 raise KeyError, "Field %s not found\n%s" % (field_name, repr(self.fields)) 26 26 27 27 def __delitem__(self, field_name): 28 28 "Deletes the field with the given field name; raises KeyError on failure" … … 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. 104 100 This allows dictionary-style lookups of formfields. It also handles feeding 105 101 prepopulated data and validation error messages to the formfield objects. 106 102 """ 107 def __init__(self, manipulator, data, error_dict ):103 def __init__(self, manipulator, data, error_dict, edit_inline = False): 108 104 self.manipulator, self.data = manipulator, data 109 105 self.error_dict = error_dict 106 self._inline_collections = None 107 self.edit_inline = edit_inline 110 108 111 109 def __repr__(self): 112 return repr(self. data)110 return repr(self.__dict__) 113 111 114 112 def __getitem__(self, key): 115 113 for field in self.manipulator.fields: 116 114 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, [])) 115 data = field.extract_data(self.data) 116 return FormFieldWrapper(field, data, self.error_dict.get(field.field_name, [])) 117 if self.edit_inline: 118 self.fill_inline_collections() 119 for inline_collection in self._inline_collections: 120 if inline_collection.name == key: 121 return inline_collection 122 124 123 raise KeyError 125 124 125 def fill_inline_collections(self): 126 if not self._inline_collections: 127 ic = [] 128 related_objects = self.manipulator.get_inline_related_objects_wrapped() 129 for rel_obj in related_objects: 130 data = rel_obj.extract_data(self.data) 131 inline_collection = InlineObjectCollection(self.manipulator, rel_obj, data, self.error_dict) 132 ic.append(inline_collection) 133 self._inline_collections = ic 134 135 136 126 137 def has_errors(self): 127 138 return self.error_dict != {} 128 139 … … 135 146 def __str__(self): 136 147 "Renders the field" 137 148 return str(self.formfield.render(self.data)) 149 138 150 139 151 def __repr__(self): 140 152 return '<FormFieldWrapper for "%s">' % self.formfield.field_name … … 155 167 else: 156 168 return '' 157 169 170 def get_id(self): 171 return self.formfield.get_id() 172 158 173 class FormFieldCollection(FormFieldWrapper): 159 174 "A utility class that gives the template access to a dict of FormFieldWrappers" 160 175 def __init__(self, formfield_dict): … … 177 192 errors.extend(field.errors()) 178 193 return errors 179 194 195 196 197 198 199 class InlineObjectCollection: 200 "An object that acts like a list of form field collections." 201 def __init__(self, parent_manipulator, rel_obj, data, errors): 202 self.parent_manipulator = parent_manipulator 203 self.rel_obj = rel_obj 204 self.data = data 205 self.errors = errors 206 self._collections = None 207 self.name = rel_obj.name 208 209 def __len__(self): 210 self.fill() 211 return self._collections.__len__() 212 213 def __getitem__(self, k): 214 self.fill() 215 return self._collections.__getitem__(k) 216 217 def __setitem__(self, k, v): 218 self.fill() 219 return self._collections.__setitem__(k,v) 220 221 def __delitem__(self, k): 222 self.fill() 223 return self._collections.__delitem__(k) 224 225 def __iter__(self): 226 self.fill() 227 return self._collections.__iter__() 228 229 def fill(self): 230 if self._collections: 231 return 232 else: 233 var_name = self.rel_obj.opts.object_name.lower() 234 wrapper = [] 235 orig = hasattr(self.parent_manipulator, 'original_object') and self.parent_manipulator.original_object or None 236 orig_list = self.rel_obj.get_list(orig) 237 for i, instance in enumerate(orig_list): 238 collection = {'original': instance } 239 for f in self.rel_obj.editable_fields(): 240 for field_name in f.get_manipulator_field_names(''): 241 full_field_name = '%s.%d.%s' % (var_name, i, field_name) 242 field = self.parent_manipulator[full_field_name] 243 data = field.extract_data(self.data) 244 collection[field_name] = FormFieldWrapper(field, data, self.errors.get(full_field_name, [])) 245 wrapper.append(FormFieldCollection(collection)) 246 self._collections = wrapper 247 180 248 class FormField: 181 249 """Abstract class representing a form field. 182 250 … … 209 277 def render(self, data): 210 278 raise NotImplementedError 211 279 280 def get_member_name(self): 281 if hasattr(self, 'member_name'): 282 return self.member_name 283 else: 284 return self.field_name 285 286 def extract_data(self, data_dict): 287 if hasattr(self, 'requires_data_list') and hasattr(data_dict, 'getlist'): 288 data = data_dict.getlist(self.get_member_name()) 289 else: 290 data = data_dict.get(self.get_member_name(), None) 291 if data is None: 292 data = '' 293 self.data_dict = data_dict 294 return data 295 296 def convert_post_data(self, new_data): 297 name = self.get_member_name() 298 if new_data.has_key(self.field_name): 299 d = new_data.getlist(self.field_name) 300 #del new_data[self.field_name] 301 new_data.setlist(name, 302 [self.__class__.html2python(data) 303 for data in d]) 304 else: 305 try: 306 # individual fields deal with None values themselves 307 new_data.setlist(name, [self.__class__.html2python(None)]) 308 except EmptyValue: 309 new_data.setlist(name, []) 310 311 def get_id(self): 312 return FORM_FIELD_ID_PREFIX + self.field_name 212 313 #################### 213 314 # GENERIC WIDGETS # 214 315 #################### … … 237 338 if isinstance(data, unicode): 238 339 data = data.encode('utf-8') 239 340 return '<input type="text" id="%s" class="v%s%s" name="%s" size="%s" value="%s" %s/>' % \ 240 ( FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__, self.is_required and ' required' or '',341 (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 241 342 self.field_name, self.length, escape(data), maxlength) 242 343 243 344 def html2python(data): … … 248 349 def render(self, data): 249 350 # value is always blank because we never want to redisplay it 250 351 return '<input type="password" id="%s" class="v%s%s" name="%s" value="" />' % \ 251 ( FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__, self.is_required and ' required' or '',352 (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 252 353 self.field_name) 253 354 254 355 class LargeTextField(TextField): … … 266 367 if isinstance(data, unicode): 267 368 data = data.encode('utf-8') 268 369 return '<textarea id="%s" class="v%s%s" name="%s" rows="%s" cols="%s">%s</textarea>' % \ 269 ( FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__, self.is_required and ' required' or '',370 (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 270 371 self.field_name, self.rows, self.cols, escape(data)) 271 372 272 373 class HiddenField(FormField): … … 276 377 277 378 def render(self, data): 278 379 return '<input type="hidden" id="%s" name="%s" value="%s" />' % \ 279 ( FORM_FIELD_ID_PREFIX + self.field_name, self.field_name, escape(data))380 (self.get_id(), self.field_name, escape(data)) 280 381 281 382 class CheckboxField(FormField): 282 383 def __init__(self, field_name, checked_by_default=False): … … 289 390 if data or (data is '' and self.checked_by_default): 290 391 checked_html = ' checked="checked"' 291 392 return '<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \ 292 ( FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__,393 (self.get_id(), self.__class__.__name__, 293 394 self.field_name, checked_html) 294 395 295 396 def html2python(data): … … 299 400 return False 300 401 html2python = staticmethod(html2python) 301 402 403 302 404 class SelectField(FormField): 303 def __init__(self, field_name, choices=[], size=1, is_required=False, validator_list=[] ):405 def __init__(self, field_name, choices=[], size=1, is_required=False, validator_list=[], member_name=None): 304 406 self.field_name = field_name 305 407 # choices is a list of (value, human-readable key) tuples because order matters 306 408 self.choices, self.size, self.is_required = choices, size, is_required 307 409 self.validator_list = [self.isValidChoice] + validator_list 410 if member_name != None: 411 self.member_name = member_name 308 412 309 413 def render(self, data): 414 str_data = str(data) # normalize to string 310 415 output = ['<select id="%s" class="v%s%s" name="%s" size="%s">' % \ 311 (FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__, self.is_required and ' required' or '', 312 self.field_name, self.size)] 313 str_data = str(data) # normalize to string 416 (self.get_id(), self.__class__.__name__, 417 self.is_required and ' required' or '', self.field_name, self.size)] 314 418 for value, display_name in self.choices: 315 419 selected_html = '' 316 420 if str(value) == str_data: … … 334 438 html2python = staticmethod(html2python) 335 439 336 440 class RadioSelectField(FormField): 337 def __init__(self, field_name, choices=[], ul_class='', is_required=False, validator_list=[] ):441 def __init__(self, field_name, choices=[], ul_class='', is_required=False, validator_list=[], member_name=None): 338 442 self.field_name = field_name 339 443 # choices is a list of (value, human-readable key) tuples because order matters 340 444 self.choices, self.is_required = choices, is_required 341 445 self.validator_list = [self.isValidChoice] + validator_list 342 446 self.ul_class = ul_class 447 if member_name != None: 448 self.member_name = member_name 343 449 344 450 def render(self, data): 345 451 """ … … 382 488 'value': value, 383 489 'name': display_name, 384 490 'field': '<input type="radio" id="%s" name="%s" value="%s"%s/>' % \ 385 ( FORM_FIELD_ID_PREFIX + self.field_name+ '_' + str(i), self.field_name, value, selected_html),491 (self.get_id() + '_' + str(i), self.field_name, value, selected_html), 386 492 'label': '<label for="%s">%s</label>' % \ 387 ( FORM_FIELD_ID_PREFIX + self.field_name+ '_' + str(i), display_name),493 (self.get_id() + '_' + str(i), display_name), 388 494 }) 389 495 return RadioFieldRenderer(datalist, self.ul_class) 390 496 … … 414 520 requires_data_list = True 415 521 def render(self, data): 416 522 output = ['<select id="%s" class="v%s%s" name="%s" size="%s" multiple="multiple">' % \ 417 ( FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__, self.is_required and ' required' or '',523 (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 418 524 self.field_name, self.size)] 419 525 str_data_list = map(str, data) # normalize to strings 420 526 for value, choice in self.choices: … … 469 575 if str(value) in str_data_list: 470 576 checked_html = ' checked="checked"' 471 577 field_name = '%s%s' % (self.field_name, value) 472 output.append('<li><input type="checkbox" id="%s %s" class="v%s" name="%s"%s /> <label for="%s%s">%s</label></li>' % \473 ( FORM_FIELD_ID_PREFIX, field_name, self.__class__.__name__, field_name, checked_html,474 FORM_FIELD_ID_PREFIX, field_name, choice))578 output.append('<li><input type="checkbox" id="%s" class="v%s" name="%s"%s /> <label for="%s">%s</label></li>' % \ 579 (get_id() + value , self.__class__.__name__, field_name, checked_html, 580 get_id() + value, choice)) 475 581 output.append('</ul>') 476 582 return '\n'.join(output) 477 583 … … 490 596 491 597 def render(self, data): 492 598 return '<input type="file" id="%s" class="v%s" name="%s" />' % \ 493 ( FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__,599 (self.get_id(), self.__class__.__name__, 494 600 self.field_name) 495 601 496 602 def html2python(data): -
django/core/meta/__init__.py
old new 146 146 class BadKeywordArguments(Exception): 147 147 pass 148 148 149 150 class InlineRelatedObject(object): 151 def __init__(self,parent_opts, opts, field): 152 self.parent_opts = parent_opts 153 self.opts = opts 154 self.field = field 155 self.name = opts.module_name 156 157 def flatten_data(self,obj = None): 158 var_name = self.opts.object_name.lower() 159 new_data = {} 160 rel_instances = self.get_list(obj) 161 162 for i, rel_instance in enumerate(rel_instances): 163 instance_data = {} 164 for f in self.opts.fields + self.opts.many_to_many: 165 field_data = f.flatten_data(rel_instance) 166 #if hasattr(f, 'editable') and f.editable and f != self.field: 167 for name, value in field_data.items(): 168 instance_data['%s.%d.%s' % (var_name, i, name)] = value 169 new_data.update(instance_data) 170 171 return new_data 172 173 def extract_data(self, data): 174 "Pull out the data meant for inline objects of this class, ie anything starting with our module name" 175 return data # TODO 176 177 def get_list(self, parent_instance = None): 178 "Get the list of this type of object from an instance of the parent class" 179 if parent_instance != None: 180 func_name = 'get_%s_list' % self.parent_opts.get_rel_object_method_name(self.opts, self.field) 181 func = getattr(parent_instance, func_name) 182 list = func() 183 184 count = len(list) + self.field.rel.num_extra_on_change 185 if self.field.rel.min_num_in_admin: 186 count = max(count, self.field.rel.min_num_in_admin) 187 if self.field.rel.max_num_in_admin: 188 count = min(count, self.field.rel.max_num_in_admin) 189 190 change = count - len(list) 191 if change > 0: 192 return list + [None for _ in range(change)] 193 if change < 0: 194 return list[:change] 195 else: # Just right 196 return list 197 else: 198 return [None for _ in range(self.field.rel.num_in_admin)] 199 200 201 def editable_fields(self): 202 "Get the fields in this class that should be edited inline" 203 return [f for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field ] 204 205 def __repr__(self): 206 return "<InlineRelatedObject: %s related to %s>" % ( self.name, self.field.name) 207 208 209 149 210 class Options: 150 211 def __init__(self, module_name='', verbose_name='', verbose_name_plural='', db_table='', 151 212 fields=None, ordering=None, unique_together=None, admin=None, has_related_links=False, … … 317 378 def get_inline_related_objects(self): 318 379 return [(a, b) for a, b in self.get_all_related_objects() if b.rel.edit_inline] 319 380 381 def get_inline_related_objects_wrapped(self): 382 return [InlineRelatedObject(self, opts, field) for opts, field in self.get_all_related_objects() if field.rel.edit_inline] 383 384 def get_data_holders(self): 385 return self.fields + self.many_to_many + self.get_inline_related_objects_wrapped() 386 320 387 def get_all_related_many_to_many_objects(self): 321 388 module_list = get_installed_model_modules() 322 389 rel_objs = [] … … 594 661 new_mod.get_latest = curry(function_get_latest, opts, new_class, does_not_exist_exception) 595 662 596 663 for f in opts.fields: 664 #TODO : change this into a virtual function so that user defined fields will be able to add methods to module or class. 597 665 if f.choices: 598 666 # Add "get_thingie_display" method to get human-readable value. 599 667 func = curry(method_get_display_value, f) … … 788 856 # If it does already exist, do an UPDATE. 789 857 if cursor.fetchone(): 790 858 db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.column), False)) for f in non_pks] 791 cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % (opts.db_table, 792 ','.join(['%s=%%s' % f.column for f in non_pks]), opts.pk.column), 859 while 1: 860 try: 861 idx = db_values.index('') 862 non_pks[idx:idx+1] = [] 863 db_values[idx:idx +1] = [] 864 except: break 865 cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % (opts.db_table, 866 ','.join(['%s=%%s' % f.column for f in non_pks]), opts.pk.column), 793 867 db_values + [pk_val]) 794 868 else: 795 869 record_exists = False … … 1332 1406 if f == '?': # Special case. 1333 1407 order_by.append(db.get_random_function_sql()) 1334 1408 else: 1409 if f.startswith('-'): 1410 col_name = f[1:] 1411 order = "DESC" 1412 else: 1413 col_name = f 1414 order = "ASC" 1335 1415 # Use the database table as a column prefix if it wasn't given, 1336 1416 # and if the requested column isn't a custom SELECT. 1337 if "." not in f and fnot in [k[0] for k in kwargs.get('select', [])]:1417 if "." not in col_name and col_name not in [k[0] for k in kwargs.get('select', [])]: 1338 1418 table_prefix = opts.db_table + '.' 1339 1419 else: 1340 1420 table_prefix = '' 1341 if f.startswith('-'): 1342 order_by.append('%s%s DESC' % (table_prefix, orderfield2column(f[1:], opts))) 1343 else: 1344 order_by.append('%s%s ASC' % (table_prefix, orderfield2column(f, opts))) 1421 1422 order_by.append('%s%s %s' % (table_prefix, orderfield2column(col_name, opts), order)) 1423 1345 1424 order_by = ", ".join(order_by) 1346 1425 1347 1426 # LIMIT and OFFSET clauses … … 1397 1476 man.__module__ = MODEL_PREFIX + '.' + opts.module_name # Set this explicitly, as above. 1398 1477 man.__init__ = curry(manipulator_init, opts, add, change) 1399 1478 man.save = curry(manipulator_save, opts, klass, add, change) 1479 man.get_inline_related_objects_wrapped = curry(manipulator_get_inline_related_objects_wrapped, opts, klass, add, change) 1480 man.flatten_data = curry(manipulator_flatten_data, opts, klass, add, change) 1400 1481 for field_name_list in opts.unique_together: 1401 1482 setattr(man, 'isUnique%s' % '_'.join(field_name_list), curry(manipulator_validator_unique_together, field_name_list, opts)) 1402 1483 for f in opts.fields: … … 1439 1520 self.fields.extend(f.get_manipulator_fields(opts, self, change)) 1440 1521 1441 1522 # Add fields for related objects. 1442 for rel_opts, rel_field in opts.get_inline_related_objects():1523 for obj in opts.get_inline_related_objects_wrapped(): 1443 1524 if change: 1444 count = getattr(self.original_object, 'get_%s_count' % opts.get_rel_object_method_name( rel_opts, rel_field))()1445 count += rel_field.rel.num_extra_on_change1446 if rel_field.rel.min_num_in_admin:1447 count = max(count, rel_field.rel.min_num_in_admin)1448 if rel_field.rel.max_num_in_admin:1449 count = min(count, rel_field.rel.max_num_in_admin)1525 count = getattr(self.original_object, 'get_%s_count' % opts.get_rel_object_method_name(obj.opts, obj.field))() 1526 count += obj.field.rel.num_extra_on_change 1527 if obj.field.rel.min_num_in_admin: 1528 count = max(count, obj.field.rel.min_num_in_admin) 1529 if obj.field.rel.max_num_in_admin: 1530 count = min(count, obj.field.rel.max_num_in_admin) 1450 1531 else: 1451 count = rel_field.rel.num_in_admin1452 for f in rel_opts.fields + rel_opts.many_to_many:1453 if f.editable and f != rel_field and (not f.primary_key or (f.primary_key and change)):1532 count = obj.field.rel.num_in_admin 1533 for f in obj.opts.fields + obj.opts.many_to_many: 1534 if f.editable and f != obj.field : 1454 1535 for i in range(count): 1455 self.fields.extend(f.get_manipulator_fields( rel_opts, self, change, name_prefix='%s.%d.' % (rel_opts.object_name.lower(), i), rel=True))1536 self.fields.extend(f.get_manipulator_fields(obj.opts, self, change, name_prefix='%s.%d.' % (obj.opts.object_name.lower(), i), rel=True)) 1456 1537 1457 1538 # Add field for ordering. 1458 1539 if change and opts.get_ordered_objects(): … … 1593 1674 getattr(new_object, 'set_%s_order' % rel_opts.object_name.lower())(order) 1594 1675 return new_object 1595 1676 1677 def manipulator_get_inline_related_objects_wrapped(opts, klass, add, change, self): 1678 return opts.get_inline_related_objects_wrapped() 1679 1680 def manipulator_flatten_data(opts, klass, add, change, self): 1681 new_data = {} 1682 obj = change and self.original_object or None 1683 for f in opts.get_data_holders(): 1684 new_data.update(f.flatten_data(obj)) 1685 return new_data 1686 1596 1687 def manipulator_validator_unique_together(field_name_list, opts, self, field_data, all_data): 1597 1688 from django.utils.text import get_text_list 1598 1689 field_list = [opts.get_field(field_name) for field_name in field_name_list] -
django/core/meta/fields.py
old new 16 16 BLANK_CHOICE_NONE = [("", "None")] 17 17 18 18 # Values for Relation.edit_inline. 19 TABULAR, STACKED = 1, 219 TABULAR, STACKED = "admin_edit_inline_tabular", "admin_edit_inline_stacked" 20 20 21 21 RECURSIVE_RELATIONSHIP_CONSTANT = 'self' 22 22 … … 152 152 if hasattr(self.default, '__get_value__'): 153 153 return self.default.__get_value__() 154 154 return self.default 155 if self.null:155 if not self.empty_strings_allowed or self.null: 156 156 return None 157 157 return "" 158 158 … … 174 174 if self.maxlength and not self.choices: # Don't give SelectFields a maxlength parameter. 175 175 params['maxlength'] = self.maxlength 176 176 if isinstance(self.rel, ManyToOne): 177 params['member_name'] = name_prefix + self.get_db_column() 177 178 if self.rel.raw_id_admin: 178 179 field_objs = self.get_manipulator_field_objs() 179 180 params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator)) 180 181 else: 181 182 if self.radio_admin: 182 183 field_objs = [formfields.RadioSelectField] 183 params['choices'] = self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE)184 184 params['ul_class'] = get_ul_class(self.radio_admin) 185 185 else: 186 186 if self.null: 187 187 field_objs = [formfields.NullSelectField] 188 188 else: 189 189 field_objs = [formfields.SelectField] 190 params['choices'] = self.get_choices()190 params['choices'] = self.get_choices_default() 191 191 elif self.choices: 192 192 if self.radio_admin: 193 193 field_objs = [formfields.RadioSelectField] 194 params['choices'] = self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE)195 194 params['ul_class'] = get_ul_class(self.radio_admin) 196 195 else: 197 196 field_objs = [formfields.SelectField] 198 params['choices'] = self.get_choices() 197 198 params['choices'] = self.get_choices_default() 199 199 else: 200 200 field_objs = self.get_manipulator_field_objs() 201 201 … … 255 255 val = None 256 256 return val 257 257 258 258 259 def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH): 259 260 "Returns a list of tuples used as SelectField choices for this field." 261 260 262 first_choice = include_blank and blank_choice or [] 261 263 if self.choices: 262 264 return first_choice + list(self.choices) 263 265 rel_obj = self.rel.to 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)] 266 choices = 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)] 267 268 return choices 265 269 270 271 def get_choices_default(self): 272 if(self.radio_admin): 273 return self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE) 274 else: 275 return self.get_choices() 276 277 def _get_val_from_obj(self, obj): 278 if obj: 279 return getattr(obj, self.column) 280 else: 281 return self.get_default() 282 283 def flatten_data(self, obj = None): 284 """ 285 Returns a dictionary mapping the field's manipulator field names to its 286 "flattened" string values for the admin view. Obj is the instance to extract the 287 values from. 288 """ 289 return { self.get_db_column(): self._get_val_from_obj(obj)} 290 291 266 292 class AutoField(Field): 267 293 empty_strings_allowed = False 268 294 def __init__(self, *args, **kwargs): … …
