Ticket #535: django-admin-refactor-6.patch
File django-admin-refactor-6.patch, 75.8 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_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
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
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
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.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
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
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
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
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
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): … … 271 297 272 298 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False): 273 299 if not rel: 274 return [] # Don't add a FormField unless it's in a related c ontext.300 return [] # Don't add a FormField unless it's in a related change context. 275 301 return Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel) 276 302 277 303 def get_manipulator_field_objs(self): … … 327 353 def get_manipulator_field_objs(self): 328 354 return [formfields.DateField] 329 355 356 def flatten_data(self, obj = None): 357 val = self._get_val_from_obj(obj) 358 return {self.get_db_column(): (val is not None and val.strftime("%Y-%m-%d") or '')} 359 330 360 class DateTimeField(DateField): 331 361 def get_db_prep_save(self, value): 332 362 # Casts dates into string format for entry into database. … … 356 386 return datetime.datetime.combine(d, t) 357 387 return self.get_default() 358 388 389 def flatten_data(self,obj = None): 390 val = self._get_val_from_obj(obj) 391 date_field, time_field = self.get_manipulator_field_names('') 392 return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''), 393 time_field: (val is not None and val.strftime("%H:%M:%S") or '')} 394 359 395 class EmailField(Field): 360 396 def get_manipulator_field_objs(self): 361 397 return [formfields.EmailField] … … 539 575 def get_manipulator_field_objs(self): 540 576 return [formfields.TimeField] 541 577 578 def flatten_data(self,obj = None): 579 val = self._get_val_from_obj(obj) 580 return {self.get_db_column(): (val is not None and val.strftime("%H:%M:%S") or '')} 581 542 582 class URLField(Field): 543 583 def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs): 544 584 if verify_exists: … … 592 632 def get_manipulator_field_objs(self): 593 633 return [formfields.IntegerField] 594 634 635 def flatten_data(self, obj = None): 636 if not obj: 637 # In required many-to-one fields with only one available choice, 638 # select that one available choice. Note: We have to check that 639 # the length of choices is *2*, not 1, because SelectFields always 640 # have an initial "blank" value. 641 if not self.blank and not self.rel.raw_id_admin and self.choices: 642 choice_list = self.get_choices_default() 643 if len(choice_list) == 2: 644 return { self.name : choice_list[1][0] } 645 return Field.flatten_data(self, obj) 646 595 647 class ManyToManyField(Field): 596 648 def __init__(self, to, **kwargs): 597 649 kwargs['verbose_name'] = kwargs.get('verbose_name', to._meta.verbose_name_plural) … … 609 661 if self.rel.raw_id_admin: 610 662 return [formfields.CommaSeparatedIntegerField] 611 663 else: 612 choices = self.get_choices (include_blank=False)664 choices = self.get_choices_default() 613 665 return [curry(formfields.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)] 614 666 667 def get_choices_default(self): 668 return Field.get_choices(self, include_blank=False) 669 615 670 def get_m2m_db_table(self, original_opts): 616 671 "Returns the name of the many-to-many 'join' table." 617 672 return '%s_%s' % (original_opts.db_table, self.name) … … 632 687 len(badkeys) == 1 and badkeys[0] or tuple(badkeys), 633 688 len(badkeys) == 1 and "is" or "are") 634 689 690 def flatten_data(self, obj = None): 691 new_data = {} 692 if obj: 693 get_list_func = getattr(obj, 'get_%s_list' % self.rel.singular) 694 instance_ids = [getattr(instance, self.rel.to.pk.column) for instance in get_list_func()] 695 if self.rel.raw_id_admin: 696 new_data[self.name] = ",".join([str(id) for id in instance_ids]) 697 elif not self.rel.edit_inline: 698 new_data[self.name] = instance_ids 699 else: 700 # In required many-to-many fields with only one available choice, 701 # select that one available choice. 702 if not self.blank and not self.rel.edit_inline and not self.rel.raw_id_admin and self.choices: 703 choice_list = self.get_choices_default() 704 if len(choice_list) == 1: 705 new_data[self.name] = [choices_list[0][0]] 706 return new_data 707 708 635 709 class OneToOneField(IntegerField): 636 710 def __init__(self, to, to_field=None, **kwargs): 637 711 kwargs['verbose_name'] = kwargs.get('verbose_name', 'ID') … … 714 788 Returns self.fields, except with fields as Field objects instead of 715 789 field names. If self.fields is None, defaults to putting every 716 790 non-AutoField field with editable=True in a single fieldset. 791 792 returns a list of lists of name, dict 793 the dict has attribs 'fields' and maybe 'classes'. 794 fields is a list of subclasses of Field. 795 796 Return value needs to be encapsulated. 717 797 """ 718 798 if self.fields is None: 719 799 field_struct = ((None, {'fields': [f.name for f in opts.fields + opts.many_to_many if f.editable and not isinstance(f, AutoField)]}),) -
django/core/defaulttags.py
2 2 3 3 import sys 4 4 import template 5 import template_loader 5 6 6 7 class CommentNode(template.Node): 7 8 def render(self, context): … … 223 224 return '' # Fail silently for invalid included templates. 224 225 return output 225 226 227 class IncludeNode(template.Node): 228 def __init__(self, template_path): 229 self.template_path = template_path 230 231 def render(self, context): 232 try: 233 t = template_loader.get_template(self.template_path) 234 return t.render(context) 235 except: 236 return '' # Fail silently for invalid included templates. 237 238 226 239 class LoadNode(template.Node): 227 240 def __init__(self, taglib): 228 241 self.taglib = taglib … … 600 613 raise template.TemplateSyntaxError, "Second (optional) argument to %s tag must be 'parsed'" % bits[0] 601 614 return SsiNode(bits[1], parsed) 602 615 616 def do_include(parser, token): 617 """ 618 Loads a template using standard resolution mechanisms, and renders it in the current context. 619 """ 620 bits = token.contents.split() 621 parsed = False 622 if len(bits) != 2: 623 raise template.TemplateSyntaxError, "'include' tag takes one argument: the path to the template to be included" 624 return IncludeNode(bits[1]) 625 603 626 def do_load(parser, token): 604 627 """ 605 628 Load a custom template tag set. … … 755 778 template.register_tag('ifnotequal', lambda parser, token: do_ifequal(parser, token, True)) 756 779 template.register_tag('if', do_if) 757 780 template.register_tag('ifchanged', do_ifchanged) 781 template.register_tag('include', do_include) 758 782 template.register_tag('regroup', do_regroup) 759 783 template.register_tag('ssi', do_ssi) 760 784 template.register_tag('load', do_load) -
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 from django.utils.functional import curry 6 7 from django.views.admin.main import BoundField 8 import re 9 10 class IncludeAdminScriptNode(template.Node): 11 def __init__(self, var): 12 self.var = var 13 14 def render(self, context): 15 resolved = template.resolve_variable(self.var, context) 16 return '<script type="text/javascript" src="%s%s"></script>' % \ 17 (ADMIN_MEDIA_PREFIX, resolved) 18 19 class SubmitRowNode(template.Node): 20 def __init__(self): 21 pass 22 23 def render(self, context): 24 change = context['change'] 25 add = context['add'] 26 show_delete = context['show_delete'] 27 ordered_objects = context['ordered_objects'] 28 save_as = context['save_as'] 29 has_delete_permission = context['has_delete_permission'] 30 is_popup = context['is_popup'] 31 32 t = ['<div class="submit-row">'] 33 onclick_attrib = ordered_objects and change and 'onclick="submitOrderForm();"' or '' 34 35 if not is_popup: 36 if has_delete_permission and (change or show_delete): 37 t.append('<p class="float-left"><a href="delete/" class="deletelink">Delete</a></p>') 38 if change and save_as: 39 t.append('<input type="submit" value="Save as new" name="_saveasnew" %s/>' % onclick_attrib) 40 if (not save_as or add): 41 t.append('<input type="submit" value="Save and add another" name="_addanother" %s/>' % onclick_attrib) 42 t.append('<input type="submit" value="Save and continue editing" name="_continue" %s/>' % onclick_attrib ) 43 t.append('<input type="submit" value="Save" class="default" %s/>' % onclick_attrib) 44 t.append('</div>\n') 45 46 return ''.join(t) 47 48 49 50 51 class AdminFieldBoundNode(template.Node): 52 def __init__(self, argument): 53 self.argument = argument 54 55 def render(self, context): 56 argument_val = template.resolve_variable(self.argument, context) 57 if (isinstance(argument_val, list)): 58 bound_fields = argument_val 59 else: 60 bound_fields = [argument_val] 61 add = context['add'] 62 change = context['change'] 63 64 context.push() 65 context['bound_fields'] = bound_fields 66 context['class_names'] = " ".join(self.get_class_names(bound_fields)) 67 t = template_loader.get_template("admin_field") 68 output = t.render(context) 69 context.pop() 70 71 return output 72 73 def get_class_names(self, bound_fields): 74 75 class_names = ['form-row'] 76 for bound_field in bound_fields: 77 for f in bound_field.form_fields: 78 if f.errors(): 79 class_names.append('errors') 80 break 81 82 # Assumes BooleanFields won't be stacked next to each other! 83 if isinstance(bound_fields[0].field, meta.BooleanField): 84 class_names.append('checkbox-row') 85 86 return class_names 87 88 class FieldWidgetNode(template.Node): 89 def __init__(self, bound_field_var): 90 self.bound_field_var = bound_field_var 91 92 def render(self, context): 93 bound_field = template.resolve_variable(self.bound_field_var, context) 94 add = context['add'] 95 change = context['change'] 96 97 context.push() 98 context['bound_field'] = bound_field 99 t = template_loader.get_template("admin_field_widget") 100 output = t.render(context) 101 context.pop() 102 103 return output 104 105 106 107 class FieldWrapper(object): 108 def __init__(self, field ): 109 self.field = field 110 111 def needs_header(self): 112 return not isinstance(self.field, meta.AutoField) 113 114 def header_class_attribute(self): 115 return self.field.blank and ' class="optional"' or '' 116 117 def use_raw_id_admin(self): 118 return isinstance(self.field.rel, (meta.ManyToOne, meta.ManyToMany)) \ 119 and self.field.rel.raw_id_admin 120 121 class FormObjectWrapper(object): 122 def __init__(self, obj, field_wrappers): 123 self.obj = obj 124 self.field_wrappers = field_wrappers 125 126 self.bound_fields = [ BoundField(fw.field, obj['original'], True, self.obj) for fw in self.field_wrappers ] 127 128 129 130 def has_errors(self): 131 return max([ bool( len( self.obj[fw.field.name].errors())) for fw in self.field_wrappers]) 132 133 def html_combined_error_list(self): 134 return ''.join( [ self.obj[fw.field.name].html_error_list() for fw in self.field_wrappers]) 135 136 137 138 139 class EditInlineNode(template.Node): 140 def __init__(self, rel_var): 141 self.rel_var = rel_var 142 143 def render(self, context): 144 relation = template.resolve_variable(self.rel_var, context) 145 add, change = context['add'], context['change'] 146 147 context.push() 148 149 self.fill_context(relation, add, change, context) 150 151 t = template_loader.get_template(relation.field.rel.edit_inline) 152 153 output = t.render(context) 154 155 context.pop() 156 return output 157 158 159 def fill_context(self, relation, add, change, context): 160 field_wrapper_list = [FieldWrapper(f) for f in relation.editable_fields()] 161 162 var_name = relation.opts.object_name.lower() 163 164 form = template.resolve_variable('form', context) 165 form_objects = form[relation.opts.module_name] 166 form_object_wrapper_list = [FormObjectWrapper(o,field_wrapper_list) for o in form_objects] 167 168 context['field_wrapper_list'] = field_wrapper_list 169 context['form_object_wrapper_list'] = form_object_wrapper_list 170 context['num_headers'] = len(field_wrapper_list) 171 context['original_row_needed'] = max([fw.use_raw_id_admin() for fw in field_wrapper_list]) 172 context['name_prefix'] = "%s." % (var_name,) 173 174 class FieldLabelNode(template.Node): 175 def __init__(self, bound_field_var): 176 self.bound_field_var = bound_field_var 177 178 def render(self, context): 179 bound_field = template.resolve_variable(self.bound_field_var, context) 180 class_names = [] 181 if isinstance(bound_field.field, meta.BooleanField): 182 class_names.append("vCheckboxLabel") 183 else: 184 if not bound_field.field.blank: 185 class_names.append('required') 186 if not bound_field.first: 187 class_names.append('inline') 188 189 class_str = class_names and ' class="%s"' % ' '.join(class_names) or '' 190 return '<label for="%s"%s>%s:</label> ' % (bound_field.element_id, class_str, capfirst(bound_field.field.verbose_name) ) 191 192 class OutputAllNode(template.Node): 193 def __init__(self, form_fields_var): 194 self.form_fields_var = form_fields_var 195 196 def render(self, context): 197 form_fields = template.resolve_variable(self.form_fields_var, context) 198 return ''.join([str(f) for f in form_fields]) 199 200 class AutoPopulatedFieldScriptNode(template.Node): 201 def __init__(self, auto_pop_var): 202 self.auto_pop_var = auto_pop_var 203 204 def render(self,context): 205 auto_pop_fields = template.resolve_variable(self.auto_pop_var, context) 206 change = context['change'] 207 for field in auto_pop_fields: 208 t = [] 209 if change: 210 t.append('document.getElementById("id_%s")._changed = true;' % field.name ) 211 else: 212 t.append('document.getElementById("id_%s").onchange = function() { this._changed = true; };' % field.name) 213 214 add_values = ' + " " + '.join(['document.getElementById("id_%s").value' % g for g in field.prepopulate_from]) 215 for f in field.prepopulate_from: 216 t.append(""" 217 document.getElementById("id_%s").onkeyup = function() { 218 var e = document.getElementById("id_%s"); 219 if(e._changed) { e.value = URLify(%s, %s);} 220 } 221 """ % (f, field.name, add_values, field.maxlength) ) 222 223 return ''.join(t) 224 225 class FilterInterfaceScriptMaybeNode(template.Node): 226 def __init__(self, bound_field_var): 227 self.bound_field_var = bound_field_var 228 229 def render(self, context): 230 bound_field = template.resolve_variable(self.bound_field_var, context) 231 f = bound_field.field 232 if f.rel and isinstance(f.rel, meta.ManyToMany) and f.rel.filter_interface: 233 return '<script type="text/javascript">addEvent(window, "load", function(e) { SelectFilter.init("id_%s", "%s", %s, %r); });</script>\n' % (f.name, f.verbose_name, f.rel.filter_interface-1, ADMIN_MEDIA_PREFIX) 234 else: 235 return '' 236 237 238 239 240 def do_submit_row(parser, token): 241 return SubmitRowNode() 242 243 244 def do_one_arg_tag(node_factory, parser,token): 245 tokens = token.contents.split() 246 if len(tokens) != 2: 247 raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 248 return node_factory(tokens[1]) 249 250 251 one_arg_tag_nodes = [ 252 IncludeAdminScriptNode, 253 AdminFieldBoundNode, 254 FieldLabelNode, 255 FieldWidgetNode, 256 OutputAllNode, 257 EditInlineNode, 258 AutoPopulatedFieldScriptNode, 259 FilterInterfaceScriptMaybeNode, 260 ] 261 262 word = re.compile('[A-Z][a-z]+') 263 def register_one_arg_tag(node): 264 tag_name = '_'.join([ s.lower() for s in word.findall(node.__name__)[:-1] ]) 265 parse_func = curry(do_one_arg_tag, node) 266 template.register_tag(tag_name, parse_func) 267 268 269 270 for node in one_arg_tag_nodes: 271 register_one_arg_tag(node) 272 273 template.register_tag('submit_row', do_submit_row ) -
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.core.extensions import get_object_or_404, render_to_response … … 493 493 }) 494 494 return HttpResponse(t.render(c)) 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 class BoundField(object): 543 def __init__(self, field, original, rel, field_mapping): 544 self.field = field 545 546 self.form_fields = self.resolve_form_fields(field_mapping) 547 548 self.element_id = self.form_fields[0].get_id() 549 self.has_label_first = not isinstance(self.field, meta.BooleanField) 550 self.original = original 551 self.raw_id_admin = use_raw_id_admin(field) 552 self.rel = rel 553 self.is_date_time = isinstance(field, meta.DateTimeField) 554 self.is_file_field = isinstance(field, meta.FileField) 555 self.needs_add_label = field.rel and isinstance(field.rel, meta.ManyToOne) and field.rel.to.admin 556 self.not_in_table = isinstance(self.field, meta.AutoField) 557 self.first = True 558 559 classes = [] 560 if(self.raw_id_admin): 561 classes.append('nowrap') 562 if max([bool(f.errors()) for f in self.form_fields]): 563 classes.append('error') 564 self.cell_class_attribute = ' '.join(classes) 565 self._repr_filled = False 566 567 def resolve_form_fields(self, field_mapping): 568 return [field_mapping[name] for name in self.field.get_manipulator_field_names('')] 569 570 571 def as_field_list(self): 572 return [self.field] 573 574 def original_value(self): 575 return self.original.__dict__[self.field.name] 576 577 def _fetch_existing_repr(self, func_name): 578 class_dict = self.original.__class__.__dict__ 579 func = class_dict.get(func_name) 580 return func(self.original) 581 582 def _fill_existing_repr(self): 583 if self._repr_filled: 584 return 585 #HACK 586 if isinstance(self.field.rel, meta.ManyToOne): 587 func_name = 'get_%s' % self.field.name 588 self._repr = self._fetch_existing_repr(func_name) 589 elif isinstance(self.field.rel, meta.ManyToMany): 590 func_name = 'get_%s_list' % self.field.name 591 self._repr = ",".join(self._fetch_existing_repr(func_name)) 592 self._repr_filled = True 593 594 def existing_repr(self): 595 self._fill_existing_repr() 596 return self._repr 597 598 def __repr__(self): 599 return repr(self.__dict__) 600 601 602 603 604 class AdminFieldSet(object): 605 def __init__(self, fieldset_name, options, form, original): 606 self.name = fieldset_name 607 self.options = options 608 self.bound_field_sets = self.get_bound_field_sets(form, original) 609 self.classes = options.get('classes', '') 610 611 def __repr__(self): 612 return "Fieldset:(%s,%s)" % (self.name, self.bound_field_sets) 613 614 def get_bound_field_sets(self, form, original): 615 fields = self.options['fields'] 616 bound_field_sets = [ [BoundField(f, original, False, form) for f in field ] for field in fields] 617 for set in bound_field_sets: 618 first = True 619 for bound_field in set: 620 bound_field.first = first 621 first = False 622 623 return bound_field_sets 624 625 def fill_extra_context(opts, app_label, context, add=False, change=False, show_delete=False, form_url=''): 626 admin_field_objs = opts.admin.get_field_objs(opts) 627 ordered_objects = opts.get_ordered_objects()[:] 628 auto_populated_fields = [f for f in opts.fields if f.prepopulate_from] 629 630 javascript_imports = get_javascript_imports(opts,auto_populated_fields, ordered_objects, admin_field_objs); 631 632 if ordered_objects: 633 coltype = 'colMS' 634 else: 635 coltype = 'colM' 636 637 has_absolute_url = hasattr(opts.get_model_module().Klass, 'get_absolute_url') 638 639 form_enc_attrib = opts.has_field_type(meta.FileField) and 'enctype="multipart/form-data" ' or '' 640 641 form = context['form'] 642 original = context['original'] 643 admin_fieldsets = [AdminFieldSet(name, options, form, original) for name, options in admin_field_objs] 644 inline_related_objects = opts.get_inline_related_objects_wrapped() 645 646 ordered_object_names = ' '.join(['object.%s' % o.pk.name for o in ordered_objects]) 647 648 extra_context = { 649 'add': add, 650 'change': change, 651 'admin_field_objs' : admin_field_objs, 652 'ordered_objects' : ordered_objects, 653 'auto_populated_fields' : auto_populated_fields, 654 'javascript_imports' : javascript_imports, 655 'coltype' : coltype, 656 'has_absolute_url': has_absolute_url, 657 'form_enc_attrib': form_enc_attrib, 658 'form_url' : form_url, 659 'admin_fieldsets' : admin_fieldsets, 660 'inline_related_objects': inline_related_objects, 661 'ordered_object_names' : ordered_object_names, 662 'content_type_id' : opts.get_content_type_id(), 663 'save_on_top' : opts.admin.save_on_top, 664 'verbose_name_plural': opts.verbose_name_plural, 665 'save_as': opts.admin.save_as, 666 'app_label': app_label, 667 'object_name': opts.object_name, 668 'has_delete_permission' : context['perms'][app_label][opts.get_delete_permission()] 669 } 670 671 context.update(extra_context) 672 673 674 def add_stage_new(request, app_label, module_name, show_delete=False, form_url='', post_url='../', post_url_continue='../%s/', object_id_override=None): 675 mod, opts = _get_mod_opts(app_label, module_name) 676 if not request.user.has_perm(app_label + '.' + opts.get_add_permission()): 677 raise PermissionDenied 678 manipulator = mod.AddManipulator() 679 if request.POST: 680 new_data = request.POST.copy() 681 if opts.has_field_type(meta.FileField): 682 new_data.update(request.FILES) 683 errors = manipulator.get_validation_errors(new_data) 684 if not errors and not request.POST.has_key("_preview"): 685 for f in opts.many_to_many: 686 if f.rel.raw_id_admin: 687 new_data.setlist(f.name, new_data[f.name].split(",")) 688 manipulator.do_html2python(new_data) 689 new_object = manipulator.save(new_data) 690 pk_value = getattr(new_object, opts.pk.column) 691 log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.ADDITION) 692 msg = 'The %s "%s" was added successfully.' % (opts.verbose_name, new_object) 693 # Here, we distinguish between different save types by checking for 694 # the presence of keys in request.POST. 695 if request.POST.has_key("_continue"): 696 request.user.add_message("%s You may edit it again below." % msg) 697 if request.POST.has_key("_popup"): 698 post_url_continue += "?_popup=1" 699 return HttpResponseRedirect(post_url_continue % pk_value) 700 if request.POST.has_key("_popup"): 701 return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, %s, "%s");</script>' % \ 702 (pk_value, repr(new_object).replace('"', '\\"'))) 703 elif request.POST.has_key("_addanother"): 704 request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name)) 705 return HttpResponseRedirect(request.path) 706 else: 707 request.user.add_message(msg) 708 return HttpResponseRedirect(post_url) 709 if request.POST.has_key("_preview"): 710 manipulator.do_html2python(new_data) 711 else: 712 # Add default data. 713 new_data = manipulator.flatten_data() 714 715 # Override the defaults with request.GET, if it exists. 716 new_data.update(request.GET) 717 errors = {} 718 719 # Populate the FormWrapper. 720 form = formfields.FormWrapper(manipulator, new_data, errors, edit_inline=True) 721 722 c = Context(request, { 723 'title': 'Add %s' % opts.verbose_name, 724 'form': form, 725 'is_popup': request.REQUEST.has_key('_popup'), 726 }) 727 if object_id_override is not None: 728 c['object_id'] = object_id_override 729 730 731 fill_extra_context(opts, app_label, c, change=True) 732 733 return render_to_response("admin_change_form", context_instance=c) 734 735 736 737 def change_stage_new(request, app_label, module_name, object_id): 738 mod, opts = _get_mod_opts(app_label, module_name) 739 if not request.user.has_perm(app_label + '.' + opts.get_change_permission()): 740 raise PermissionDenied 741 if request.POST and request.POST.has_key("_saveasnew"): 742 return add_stage_new(request, app_label, module_name, form_url='../add/') 743 try: 744 manipulator = mod.ChangeManipulator(object_id) 745 except ObjectDoesNotExist: 746 raise Http404 747 748 inline_related_objects = opts.get_inline_related_objects() 749 if request.POST: 750 new_data = request.POST.copy() 751 if opts.has_field_type(meta.FileField): 752 new_data.update(request.FILES) 753 754 errors = manipulator.get_validation_errors(new_data) 755 if not errors and not request.POST.has_key("_preview"): 756 for f in opts.many_to_many: 757 if f.rel.raw_id_admin: 758 new_data.setlist(f.name, new_data[f.name].split(",")) 759 manipulator.do_html2python(new_data) 760 new_object = manipulator.save(new_data) 761 pk_value = getattr(new_object, opts.pk.column) 762 763 # Construct the change message. 764 change_message = [] 765 if manipulator.fields_added: 766 change_message.append('Added %s.' % get_text_list(manipulator.fields_added, 'and')) 767 if manipulator.fields_changed: 768 change_message.append('Changed %s.' % get_text_list(manipulator.fields_changed, 'and')) 769 if manipulator.fields_deleted: 770 change_message.append('Deleted %s.' % get_text_list(manipulator.fields_deleted, 'and')) 771 change_message = ' '.join(change_message) 772 if not change_message: 773 change_message = 'No fields changed.' 774 775 log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.CHANGE, change_message) 776 msg = 'The %s "%s" was changed successfully.' % (opts.verbose_name, new_object) 777 if request.POST.has_key("_continue"): 778 request.user.add_message("%s You may edit it again below." % msg) 779 if request.REQUEST.has_key('_popup'): 780 return HttpResponseRedirect(request.path + "?_popup=1") 781 else: 782 return HttpResponseRedirect(request.path) 783 elif request.POST.has_key("_saveasnew"): 784 request.user.add_message('The %s "%s" was added successfully. You may edit it again below.' % (opts.verbose_name, new_object)) 785 return HttpResponseRedirect("../%s/" % pk_value) 786 elif request.POST.has_key("_addanother"): 787 request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name)) 788 return HttpResponseRedirect("../add/") 789 else: 790 request.user.add_message(msg) 791 return HttpResponseRedirect("../") 792 if request.POST.has_key("_preview"): 793 manipulator.do_html2python(new_data) 794 else: 795 # Populate new_data with a "flattened" version of the current data. 796 new_data = manipulator.flatten_data() 797 798 799 # If the object has ordered objects on its admin page, get the existing 800 # order and flatten it into a comma-separated list of IDs. 801 id_order_list = [] 802 for rel_obj in opts.get_ordered_objects(): 803 id_order_list.extend(getattr(obj, 'get_%s_order' % rel_obj.object_name.lower())()) 804 if id_order_list: 805 new_data['order_'] = ','.join(map(str, id_order_list)) 806 errors = {} 807 808 # Populate the FormWrapper. 809 form = formfields.FormWrapper(manipulator, new_data, errors, edit_inline = True) 810 form.original = manipulator.original_object 811 form.order_objects = [] 812 813 for rel_opts, rel_field in inline_related_objects: 814 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: 815 form.order_objects.extend(orig_list) 816 817 c = Context(request, { 818 'title': 'Change %s' % opts.verbose_name, 819 'form': form, 820 'object_id': object_id, 821 'original': manipulator.original_object, 822 'is_popup' : request.REQUEST.has_key('_popup') 823 }) 824 825 fill_extra_context(opts, app_label, c, change=True) 826 827 #t = template_loader.get_template_from_string(raw_template) 828 829 return render_to_response('admin_change_form', context_instance=c); 830 831 533 832 def _get_template(opts, app_label, add=False, change=False, show_delete=False, form_url=''): 534 833 admin_field_objs = opts.admin.get_field_objs(opts) 535 834 ordered_objects = opts.get_ordered_objects()[:] … … 608 907 for rel_obj, rel_field in opts.get_inline_related_objects(): 609 908 var_name = rel_obj.object_name.lower() 610 909 field_list = [f for f in rel_obj.fields + rel_obj.many_to_many if f.editable and f != rel_field] 910 611 911 t.append('<fieldset class="module%s">\n' % ((rel_field.rel.edit_inline != meta.TABULAR) and ' aligned' or '')) 612 912 view_on_site = '' 613 913 if change and hasattr(rel_obj, 'get_absolute_url'): … … 680 980 else: 681 981 t.append('document.getElementById("id_%s").onchange = function() { this._changed = true; };' % field.name) 682 982 for f in field.prepopulate_from: 683 t.append('document.getElementById("id_%s").onkeyup = function() { var e = document.getElementById("id_%s"); if ( !e._changed) { e.value = URLify(%s, %s);}};' % \983 t.append('document.getElementById("id_%s").onkeyup = function() { var e = document.getElementById("id_%s"); if (e._changed) { e.value = URLify(%s, %s);}};' % \ 684 984 (f, field.name, ' + " " + '.join(['document.getElementById("id_%s").value' % g for g in field.prepopulate_from]), field.maxlength)) 685 985 t.append('</script>\n') 686 986 if change and ordered_objects: … … 802 1102 # Add default data. 803 1103 for f in opts.fields: 804 1104 if f.has_default(): 805 new_data.update( _get_flattened_data(f, f.get_default()))1105 new_data.update( f.flatten_data() ) 806 1106 # In required many-to-one fields with only one available choice, 807 1107 # select that one available choice. Note: We have to check that 808 1108 # the length of choices is *2*, not 1, because SelectFields always … … 837 1137 if f.editable and f != rel_field and not isinstance(f, meta.AutoField): 838 1138 for field_name in f.get_manipulator_field_names(''): 839 1139 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, [])) 1140 field = manipulator[full_field_name] 1141 data = field.extract_data(new_data) 1142 collection[field_name] = formfields.FormFieldWrapper(field, data, errors.get(full_field_name, [])) 841 1143 wrapper.append(formfields.FormFieldCollection(collection)) 842 1144 setattr(form, rel_opts.module_name, wrapper) 843 1145 … … 849 1151 if object_id_override is not None: 850 1152 c['object_id'] = object_id_override 851 1153 raw_template = _get_template(opts, app_label, add=True, show_delete=show_delete, form_url=form_url) 852 # return HttpResponse(raw_template, mimetype='text/plain')853 1154 t = template_loader.get_template_from_string(raw_template) 854 1155 return HttpResponse(t.render(c)) 855 1156 … … 915 1216 new_data = {} 916 1217 obj = manipulator.original_object 917 1218 for f in opts.fields: 918 new_data.update( _get_flattened_data(f, getattr(obj, f.column)))1219 new_data.update(f.flatten_data(obj)) 919 1220 for f in opts.many_to_many: 920 1221 get_list_func = getattr(obj, 'get_%s_list' % f.rel.singular) 921 1222 if f.rel.raw_id_admin: … … 927 1228 for i, rel_instance in enumerate(getattr(obj, 'get_%s_list' % opts.get_rel_object_method_name(rel_obj, rel_field))()): 928 1229 for f in rel_obj.fields: 929 1230 if f.editable and f != rel_field: 930 for k, v in _get_flattened_data(f, getattr(rel_instance, f.column)).items():1231 for k, v in f.flatten_data(rel_instance).items(): 931 1232 new_data['%s.%d.%s' % (var_name, i, k)] = v 932 1233 for f in rel_obj.many_to_many: 933 1234 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 1261 if f.editable and f != rel_field: 961 1262 for field_name in f.get_manipulator_field_names(''): 962 1263 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, [])) 1264 field = manipulator[full_field_name] 1265 data = field.extract_data(new_data) 1266 collection[field_name] = formfields.FormFieldWrapper(field, data, errors.get(full_field_name, [])) 964 1267 wrapper.append(formfields.FormFieldCollection(collection)) 965 1268 setattr(form, rel_opts.module_name, wrapper) 966 1269 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: -
tests/runtests.py
100 100 self.output(1, "Creating test database") 101 101 try: 102 102 cursor.execute("CREATE DATABASE %s" % TEST_DATABASE_NAME) 103 except: 103 except Exception, e: 104 self.output(0, "There was an error creating the test database:%s " % str(e)) 104 105 confirm = raw_input("The test database, %s, already exists. Type 'yes' to delete it, or 'no' to cancel: " % TEST_DATABASE_NAME) 105 106 if confirm == 'yes': 106 107 cursor.execute("DROP DATABASE %s" % TEST_DATABASE_NAME)