Ticket #535: django-admin-refactor-5.patch
File django-admin-refactor-5.patch, 71.6 KB (added by , 19 years ago) |
---|
-
django/conf/urls/admin.py
48 48 49 49 urlpatterns += ( 50 50 # Metasystem admin pages 51 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/add_new/$', 'django.views.admin.main.add_stage_new'), 52 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)_new/$', 'django.views.admin.main.change_stage_new'), 51 53 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/$', 'django.views.admin.main.change_list'), 52 54 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/add/$', 'django.views.admin.main.add_stage'), 53 55 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/jsvalidation/$', 'django.views.admin.jsvalidation.jsvalidation'), -
django/conf/admin_templates/admin_change_form.html
1 {% extends "base_site" %} 2 {% load admin_modify %} 3 {% load adminmedia %} 4 {% block extrahead %} 5 6 {% for js in javascript_imports %} 7 {% include_admin_script js %} 8 {% endfor %} 9 10 {% endblock %} 11 12 {% block coltype %}{{ coltype }}{% endblock %} 13 14 {% block bodyclass %}{{app_label}}-{{object_name.lower}} change-form{% endblock %} 15 16 {% block breadcrumbs %}{% if not is_popup %} 17 <div class="breadcrumbs"> 18 <a href="../../../">Home</a> › 19 <a href="../">{{verbose_name_plural|capfirst}}</a> › 20 {% if add %} 21 Add {{verbose_name}} 22 {% else %} 23 {{original|striptags|truncatewords:"18"}} 24 {% endif %} 25 </div> 26 {% endif %} 27 {% endblock %} 28 29 {% block content %}<div id="content-main"> 30 {% if change %} 31 {% if not is_popup %} 32 <ul class="object-tools"><li><a href="history/" class="historylink">History</a></li> 33 {% if has_absolute_url %} 34 <li><a href="/r/{{ content_type_id }}/{{ object_id }}/" class="viewsitelink">View on site</a></li> 35 {% endif%} 36 </ul> 37 {% endif %} 38 {% endif %} 39 40 <form {{ form_enc_attrib }} action='{{ form_url }}' method="post"> 41 42 {% if is_popup %}<input type="hidden" name="_popup" value="1">{% endif %} 43 44 {% if save_on_top %} 45 {% submit_row %} 46 {% endif %} 47 48 {% if form.error_dict %} 49 <p class="errornote">Please correct the error{{ form.error_dict.items|pluralize }} below.</p> 50 {% endif %} 51 <b> 52 </b> 53 {% for fieldset in admin_fieldsets %} 54 <fieldset class="module aligned {{ fieldset.classes }}"> 55 {% if fieldset.name %} 56 <h2>{{fieldset.name }}</h2> 57 {% endif %} 58 {% for bound_field_set in fieldset.bound_field_sets %} 59 {% for bound_field in bound_field_set %} 60 {% admin_field_bound bound_field %} 61 {% 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}}"> <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
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 = [] … … 592 659 new_mod.get_latest = curry(function_get_latest, opts, new_class, does_not_exist_exception) 593 660 594 661 for f in opts.fields: 662 if len(f.choices) != 0: 663 # Add an accessor method to get to the human readable value 664 func = curry(method_get_display_value, f) 665 setattr(new_class, 'get_%s_display' % f.name , func) 595 666 if isinstance(f, DateField) or isinstance(f, DateTimeField): 596 667 # Add "get_next_by_thingie" and "get_previous_by_thingie" methods 597 668 # for all DateFields and DateTimeFields that cannot be null. … … 782 853 # If it does already exist, do an UPDATE. 783 854 if cursor.fetchone(): 784 855 db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.column), False)) for f in non_pks] 785 cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % (opts.db_table, 786 ','.join(['%s=%%s' % f.column for f in non_pks]), opts.pk.column), 856 while 1: 857 try: 858 idx = db_values.index('') 859 non_pks[idx:idx+1] = [] 860 db_values[idx:idx +1] = [] 861 except: break 862 cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % (opts.db_table, 863 ','.join(['%s=%%s' % f.column for f in non_pks]), opts.pk.column), 787 864 db_values + [pk_val]) 788 865 else: 789 866 record_exists = False … … 991 1068 kwargs['limit'] = 1 992 1069 return get_object_func(**kwargs) 993 1070 1071 # CHOICE-RELATED METHODS ################### 1072 1073 def method_get_display_value(field, self): 1074 value = getattr(self, field.name) 1075 for (v, d) in field.choices: 1076 if v==value: 1077 return d 1078 # Couldn't find it in the list 1079 return value 1080 994 1081 # FILE-RELATED METHODS ##################### 995 1082 996 1083 def method_get_file_filename(field, self): … … 1386 1473 man.__module__ = MODEL_PREFIX + '.' + opts.module_name # Set this explicitly, as above. 1387 1474 man.__init__ = curry(manipulator_init, opts, add, change) 1388 1475 man.save = curry(manipulator_save, opts, klass, add, change) 1476 man.get_inline_related_objects_wrapped = curry(manipulator_get_inline_related_objects_wrapped, opts, klass, add, change) 1477 man.flatten_data = curry(manipulator_flatten_data, opts, klass, add, change) 1389 1478 for field_name_list in opts.unique_together: 1390 1479 setattr(man, 'isUnique%s' % '_'.join(field_name_list), curry(manipulator_validator_unique_together, field_name_list, opts)) 1391 1480 for f in opts.fields: … … 1582 1671 getattr(new_object, 'set_%s_order' % rel_opts.object_name.lower())(order) 1583 1672 return new_object 1584 1673 1674 def manipulator_get_inline_related_objects_wrapped(opts, klass, add, change, self): 1675 return opts.get_inline_related_objects_wrapped() 1676 1677 def manipulator_flatten_data(opts, klass, add, change, self): 1678 new_data = {} 1679 obj = change and self.original_object or None 1680 for f in opts.get_data_holders(): 1681 new_data.update(f.flatten_data(obj)) 1682 return new_data 1683 1585 1684 def manipulator_validator_unique_together(field_name_list, opts, self, field_data, all_data): 1586 1685 from django.utils.text import get_text_list 1587 1686 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 … … 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): … … 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 def has_errors(self): 130 return max([ bool( len( self.obj[fw.field.name].errors())) for fw in self.field_wrappers]) 131 132 def html_combined_error_list(self): 133 return ''.join( [ self.obj[fw.field.name].html_error_list() for fw in self.field_wrappers]) 134 135 136 137 138 class EditInlineNode(template.Node): 139 def __init__(self, rel_var): 140 self.rel_var = rel_var 141 142 def render(self, context): 143 relation = template.resolve_variable(self.rel_var, context) 144 add, change = context['add'], context['change'] 145 146 context.push() 147 148 self.fill_context(relation, add, change, context) 149 150 t = template_loader.get_template(relation.field.rel.edit_inline) 151 152 output = t.render(context) 153 154 context.pop() 155 return output 156 157 158 def fill_context(self, relation, add, change, context): 159 field_wrapper_list = [FieldWrapper(f) for f in relation.editable_fields()] 160 161 var_name = relation.opts.object_name.lower() 162 163 form = template.resolve_variable('form', context) 164 form_objects = form[relation.opts.module_name] 165 form_object_wrapper_list = [FormObjectWrapper(o,field_wrapper_list) for o in form_objects] 166 167 context['field_wrapper_list'] = field_wrapper_list 168 context['form_object_wrapper_list'] = form_object_wrapper_list 169 context['num_headers'] = len(field_wrapper_list) 170 context['original_row_needed'] = max([fw.use_raw_id_admin() for fw in field_wrapper_list]) 171 context['name_prefix'] = "%s." % (var_name,) 172 173 class FieldLabelNode(template.Node): 174 def __init__(self, bound_field_var): 175 self.bound_field_var = bound_field_var 176 177 def render(self, context): 178 bound_field = template.resolve_variable(self.bound_field_var, context) 179 class_names = [] 180 if isinstance(bound_field.field, meta.BooleanField): 181 class_names.append("vCheckboxLabel") 182 else: 183 if not bound_field.field.blank: 184 class_names.append('required') 185 if not bound_field.first: 186 class_names.append('inline') 187 188 class_str = class_names and ' class="%s"' % ' '.join(class_names) or '' 189 return '<label for="%s"%s>%s:</label> ' % (bound_field.element_id, class_str, capfirst(bound_field.field.verbose_name) ) 190 191 class OutputAllNode(template.Node): 192 def __init__(self, form_fields_var): 193 self.form_fields_var = form_fields_var 194 195 def render(self, context): 196 form_fields = template.resolve_variable(self.form_fields_var, context) 197 return ''.join([str(f) for f in form_fields]) 198 199 class AutoPopulatedFieldScriptNode(template.Node): 200 def __init__(self, auto_pop_var): 201 self.auto_pop_var = auto_pop_var 202 203 def render(self,context): 204 auto_pop_fields = template.resolve_variable(self.auto_pop_var, context) 205 change = context['change'] 206 for field in auto_pop_fields: 207 t = [] 208 if change: 209 t.append('document.getElementById("id_%s")._changed = true;' % field.name ) 210 else: 211 t.append('document.getElementById("id_%s").onchange = function() { this._changed = true; };' % field.name) 212 213 add_values = ' + " " + '.join(['document.getElementById("id_%s").value' % g for g in field.prepopulate_from]) 214 for f in field.prepopulate_from: 215 t.append(""" 216 document.getElementById("id_%s").onkeyup = function() { 217 var e = document.getElementById("id_%s"); 218 if(e._changed) { e.value = URLify(%s, %s);} 219 } 220 """ % (f, field.name, add_values, field.maxlength) ) 221 222 return ''.join(t) 223 224 class FilterInterfaceScriptMaybeNode(template.Node): 225 def __init__(self, bound_field_var): 226 self.bound_field_var = bound_field_var 227 228 def render(self, context): 229 bound_field = template.resolve_variable(self.bound_field_var, context) 230 f = bound_field.field 231 if f.rel and isinstance(f.rel, meta.ManyToMany) and f.rel.filter_interface: 232 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) 233 else: 234 return '' 235 236 237 238 239 def do_submit_row(parser, token): 240 return SubmitRowNode() 241 242 243 def do_one_arg_tag(node_factory, parser,token): 244 tokens = token.contents.split() 245 if len(tokens) != 2: 246 raise template.TemplateSyntaxError("%s takes 1 argument" % tokens[0]) 247 return node_factory(tokens[1]) 248 249 250 one_arg_tag_nodes = [ 251 IncludeAdminScriptNode, 252 AdminFieldBoundNode, 253 FieldLabelNode, 254 FieldWidgetNode, 255 OutputAllNode, 256 EditInlineNode, 257 AutoPopulatedFieldScriptNode, 258 FilterInterfaceScriptMaybeNode, 259 ] 260 261 word = re.compile('[A-Z][a-z]+') 262 for node in one_arg_tag_nodes: 263 tag_name = '_'.join([ s.lower() for s in word.findall(node.__name__)[:-1] ]) 264 265 parse_func = curry(do_one_arg_tag, node) 266 template.register_tag(tag_name, parse_func) 267 268 #template.register_tag('include_admin_script', do_include_admin_script) 269 template.register_tag('submit_row', do_submit_row ) 270 #template.register_tag('admin_field_bound', do_admin_field_bound) 271 #template.register_tag('edit_inline', do_edit_inline) 272 #template.register_tag('auto_populated_field_script', do_auto_populated_field_script) 273 #template.register_tag('field_label', do_field_label) 274 #template.register_tag('field_widget', do_field_widget) 275 #template.register_tag('output_all', do_output_all) 276 #template.register_tag('filter_interface_script_maybe', curry(do_one_arg_tag, FilterInterfaceScriptMaybeNode) ) -
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 … … 492 492 }) 493 493 return HttpResponse(t.render(c)) 494 494 495 def _get_flattened_data(field, val):496 """497 Returns a dictionary mapping the field's manipulator field names to its498 "flattened" string values for the admin view. "val" is an instance of the499 field's value.500 """501 if isinstance(field, meta.DateTimeField):502 date_field, time_field = field.get_manipulator_field_names('')503 return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''),504 time_field: (val is not None and val.strftime("%H:%M:%S") or '')}505 elif isinstance(field, meta.DateField):506 return {field.name: (val is not None and val.strftime("%Y-%m-%d") or '')}507 elif isinstance(field, meta.TimeField):508 return {field.name: (val is not None and val.strftime("%H:%M:%S") or '')}509 else:510 return {field.name: val}511 512 495 use_raw_id_admin = lambda field: isinstance(field.rel, (meta.ManyToOne, meta.ManyToMany)) and field.rel.raw_id_admin 513 496 514 497 def _get_submit_row_template(opts, app_label, add, change, show_delete, ordered_objects): … … 529 512 t.append('</div>\n') 530 513 return t 531 514 515 def get_javascript_imports(opts,auto_populated_fields, ordered_objects, admin_field_objs): 516 # Put in any necessary JavaScript imports. 517 js = ['js/core.js', 'js/admin/RelatedObjectLookups.js'] 518 if auto_populated_fields: 519 js.append('js/urlify.js') 520 if opts.has_field_type(meta.DateTimeField) or opts.has_field_type(meta.TimeField) or opts.has_field_type(meta.DateField): 521 js.extend(['js/calendar.js', 'js/admin/DateTimeShortcuts.js']) 522 if ordered_objects: 523 js.extend(['js/getElementsBySelector.js', 'js/dom-drag.js' , 'js/admin/ordering.js']) 524 if opts.admin.js: 525 js.extend(opts.admin.js) 526 seen_collapse = False 527 for _, options in admin_field_objs: 528 if not seen_collapse and 'collapse' in options.get('classes', ''): 529 seen_collapse = True 530 js.append('js/admin/CollapsedFieldsets.js' ) 531 try: 532 for field_list in options['fields']: 533 for f in field_list: 534 if f.rel and isinstance(f, meta.ManyToManyField) and f.rel.filter_interface: 535 js.extend(['js/SelectBox.js' , 'js/SelectFilter2.js']) 536 raise StopIteration 537 except StopIteration: 538 break 539 return js 540 541 class BoundField(object): 542 def __init__(self, field, original, rel, field_mapping): 543 self.field = field 544 545 self.form_fields = self.resolve_form_fields(field_mapping) 546 547 self.element_id = self.form_fields[0].get_id() 548 self.has_label_first = not isinstance(self.field, meta.BooleanField) 549 self.original = original 550 self.raw_id_admin = use_raw_id_admin(field) 551 self.rel = rel 552 self.is_date_time = isinstance(field, meta.DateTimeField) 553 self.is_file_field = isinstance(field, meta.FileField) 554 self.needs_add_label = field.rel and isinstance(field.rel, meta.ManyToOne) and field.rel.to.admin 555 self.not_in_table = isinstance(self.field, meta.AutoField) 556 self.first = False 557 558 classes = [] 559 if(self.raw_id_admin): 560 classes.append('nowrap') 561 if max([bool(f.errors()) for f in self.form_fields]): 562 classes.append('error') 563 self.cell_class_attribute = ' '.join(classes) 564 self._repr_filled = False 565 566 def resolve_form_fields(self, field_mapping): 567 return [field_mapping[name] for name in self.field.get_manipulator_field_names('')] 568 569 570 def as_field_list(self): 571 return [self.field] 572 573 def original_value(self): 574 return self.original.__dict__[self.field.name] 575 576 def _fetch_existing_repr(self, func_name): 577 class_dict = self.original.__class__.__dict__ 578 func = class_dict.get(func_name) 579 return func(self.original) 580 581 def _fill_existing_repr(self): 582 if self._repr_filled: 583 return 584 #HACK 585 if isinstance(self.field.rel, meta.ManyToOne): 586 func_name = 'get_%s' % self.field.name 587 self._repr = self._fetch_existing_repr(func_name) 588 elif isinstance(self.field.rel, meta.ManyToMany): 589 func_name = 'get_%s_list' % self.field.name 590 self._repr = ",".join(self._fetch_existing_repr(func_name)) 591 self._repr_filled = True 592 593 def existing_repr(self): 594 self._fill_existing_repr() 595 return self._repr 596 597 def __repr__(self): 598 return repr(self.__dict__) 599 600 601 602 603 class AdminFieldSet(object): 604 def __init__(self, fieldset_name, options, form, original): 605 self.name = fieldset_name 606 self.options = options 607 self.bound_field_sets = self.get_bound_field_sets(form, original) 608 self.classes = options.get('classes', '') 609 610 def __repr__(self): 611 return "Fieldset:(%s,%s)" % (self.name, self.bound_field_sets) 612 613 def get_bound_field_sets(self, form, original): 614 fields = self.options['fields'] 615 bound_field_sets = [ [BoundField(f, original, False, form) for f in field ] for field in fields] 616 for set in bound_field_sets: 617 set[0].first = True 618 619 return bound_field_sets 620 621 def fill_extra_context(opts, app_label, context, add=False, change=False, show_delete=False, form_url=''): 622 admin_field_objs = opts.admin.get_field_objs(opts) 623 ordered_objects = opts.get_ordered_objects()[:] 624 auto_populated_fields = [f for f in opts.fields if f.prepopulate_from] 625 626 javascript_imports = get_javascript_imports(opts,auto_populated_fields, ordered_objects, admin_field_objs); 627 628 if ordered_objects: 629 coltype = 'colMS' 630 else: 631 coltype = 'colM' 632 633 has_absolute_url = hasattr(opts.get_model_module().Klass, 'get_absolute_url') 634 635 form_enc_attrib = opts.has_field_type(meta.FileField) and 'enctype="multipart/form-data" ' or '' 636 637 form = context['form'] 638 original = context['original'] 639 admin_fieldsets = [AdminFieldSet(name, options, form, original) for name, options in admin_field_objs] 640 inline_related_objects = opts.get_inline_related_objects_wrapped() 641 642 ordered_object_names = ' '.join(['object.%s' % o.pk.name for o in ordered_objects]) 643 644 extra_context = { 645 'add': add, 646 'change': change, 647 'admin_field_objs' : admin_field_objs, 648 'ordered_objects' : ordered_objects, 649 'auto_populated_fields' : auto_populated_fields, 650 'javascript_imports' : javascript_imports, 651 'coltype' : coltype, 652 'has_absolute_url': has_absolute_url, 653 'form_enc_attrib': form_enc_attrib, 654 'form_url' : form_url, 655 'admin_fieldsets' : admin_fieldsets, 656 'inline_related_objects': inline_related_objects, 657 'ordered_object_names' : ordered_object_names, 658 'content_type_id' : opts.get_content_type_id(), 659 'save_on_top' : opts.admin.save_on_top, 660 'verbose_name_plural': opts.verbose_name_plural, 661 'save_as': opts.admin.save_as, 662 'app_label': app_label, 663 'object_name': opts.object_name, 664 'has_delete_permission' : context['perms'][app_label][opts.get_delete_permission()] 665 } 666 667 context.update(extra_context) 668 669 670 def add_stage_new(request, app_label, module_name, show_delete=False, form_url='', post_url='../', post_url_continue='../%s/', object_id_override=None): 671 mod, opts = _get_mod_opts(app_label, module_name) 672 if not request.user.has_perm(app_label + '.' + opts.get_add_permission()): 673 raise PermissionDenied 674 manipulator = mod.AddManipulator() 675 if request.POST: 676 new_data = request.POST.copy() 677 if opts.has_field_type(meta.FileField): 678 new_data.update(request.FILES) 679 errors = manipulator.get_validation_errors(new_data) 680 if not errors and not request.POST.has_key("_preview"): 681 for f in opts.many_to_many: 682 if f.rel.raw_id_admin: 683 new_data.setlist(f.name, new_data[f.name].split(",")) 684 manipulator.do_html2python(new_data) 685 new_object = manipulator.save(new_data) 686 pk_value = getattr(new_object, opts.pk.column) 687 log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.ADDITION) 688 msg = 'The %s "%s" was added successfully.' % (opts.verbose_name, new_object) 689 # Here, we distinguish between different save types by checking for 690 # the presence of keys in request.POST. 691 if request.POST.has_key("_continue"): 692 request.user.add_message("%s You may edit it again below." % msg) 693 if request.POST.has_key("_popup"): 694 post_url_continue += "?_popup=1" 695 return HttpResponseRedirect(post_url_continue % pk_value) 696 if request.POST.has_key("_popup"): 697 return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, %s, "%s");</script>' % \ 698 (pk_value, repr(new_object).replace('"', '\\"'))) 699 elif request.POST.has_key("_addanother"): 700 request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name)) 701 return HttpResponseRedirect(request.path) 702 else: 703 request.user.add_message(msg) 704 return HttpResponseRedirect(post_url) 705 if request.POST.has_key("_preview"): 706 manipulator.do_html2python(new_data) 707 else: 708 # Add default data. 709 new_data = manipulator.flatten_data() 710 711 # Override the defaults with request.GET, if it exists. 712 new_data.update(request.GET) 713 errors = {} 714 715 # Populate the FormWrapper. 716 form = formfields.FormWrapper(manipulator, new_data, errors) 717 718 c = Context(request, { 719 'title': 'Add %s' % opts.verbose_name, 720 'form': form, 721 'is_popup': request.REQUEST.has_key('_popup'), 722 }) 723 if object_id_override is not None: 724 c['object_id'] = object_id_override 725 726 727 fill_extra_context(opts, app_label, c, change=True) 728 729 return render_to_response("admin_change_form", context_instance=c) 730 731 732 733 def change_stage_new(request, app_label, module_name, object_id): 734 mod, opts = _get_mod_opts(app_label, module_name) 735 if not request.user.has_perm(app_label + '.' + opts.get_change_permission()): 736 raise PermissionDenied 737 if request.POST and request.POST.has_key("_saveasnew"): 738 return add_stage_new(request, app_label, module_name, form_url='../add/') 739 try: 740 manipulator = mod.ChangeManipulator(object_id) 741 except ObjectDoesNotExist: 742 raise Http404 743 744 inline_related_objects = opts.get_inline_related_objects() 745 if request.POST: 746 new_data = request.POST.copy() 747 if opts.has_field_type(meta.FileField): 748 new_data.update(request.FILES) 749 750 errors = manipulator.get_validation_errors(new_data) 751 if not errors and not request.POST.has_key("_preview"): 752 for f in opts.many_to_many: 753 if f.rel.raw_id_admin: 754 new_data.setlist(f.name, new_data[f.name].split(",")) 755 manipulator.do_html2python(new_data) 756 new_object = manipulator.save(new_data) 757 pk_value = getattr(new_object, opts.pk.column) 758 759 # Construct the change message. 760 change_message = [] 761 if manipulator.fields_added: 762 change_message.append('Added %s.' % get_text_list(manipulator.fields_added, 'and')) 763 if manipulator.fields_changed: 764 change_message.append('Changed %s.' % get_text_list(manipulator.fields_changed, 'and')) 765 if manipulator.fields_deleted: 766 change_message.append('Deleted %s.' % get_text_list(manipulator.fields_deleted, 'and')) 767 change_message = ' '.join(change_message) 768 if not change_message: 769 change_message = 'No fields changed.' 770 771 log.log_action(request.user.id, opts.get_content_type_id(), pk_value, repr(new_object), log.CHANGE, change_message) 772 msg = 'The %s "%s" was changed successfully.' % (opts.verbose_name, new_object) 773 if request.POST.has_key("_continue"): 774 request.user.add_message("%s You may edit it again below." % msg) 775 if request.REQUEST.has_key('_popup'): 776 return HttpResponseRedirect(request.path + "?_popup=1") 777 else: 778 return HttpResponseRedirect(request.path) 779 elif request.POST.has_key("_saveasnew"): 780 request.user.add_message('The %s "%s" was added successfully. You may edit it again below.' % (opts.verbose_name, new_object)) 781 return HttpResponseRedirect("../%s/" % pk_value) 782 elif request.POST.has_key("_addanother"): 783 request.user.add_message("%s You may add another %s below." % (msg, opts.verbose_name)) 784 return HttpResponseRedirect("../add/") 785 else: 786 request.user.add_message(msg) 787 return HttpResponseRedirect("../") 788 if request.POST.has_key("_preview"): 789 manipulator.do_html2python(new_data) 790 else: 791 # Populate new_data with a "flattened" version of the current data. 792 new_data = manipulator.flatten_data() 793 794 795 # If the object has ordered objects on its admin page, get the existing 796 # order and flatten it into a comma-separated list of IDs. 797 id_order_list = [] 798 for rel_obj in opts.get_ordered_objects(): 799 id_order_list.extend(getattr(obj, 'get_%s_order' % rel_obj.object_name.lower())()) 800 if id_order_list: 801 new_data['order_'] = ','.join(map(str, id_order_list)) 802 errors = {} 803 804 # Populate the FormWrapper. 805 form = formfields.FormWrapper(manipulator, new_data, errors, edit_inline = True) 806 form.original = manipulator.original_object 807 form.order_objects = [] 808 809 for rel_opts, rel_field in inline_related_objects: 810 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: 811 form.order_objects.extend(orig_list) 812 813 c = Context(request, { 814 'title': 'Change %s' % opts.verbose_name, 815 'form': form, 816 'object_id': object_id, 817 'original': manipulator.original_object, 818 'is_popup' : request.REQUEST.has_key('_popup') 819 }) 820 821 fill_extra_context(opts, app_label, c, change=True) 822 823 #t = template_loader.get_template_from_string(raw_template) 824 825 return render_to_response('admin_change_form', context_instance=c); 826 827 532 828 def _get_template(opts, app_label, add=False, change=False, show_delete=False, form_url=''): 533 829 admin_field_objs = opts.admin.get_field_objs(opts) 534 830 ordered_objects = opts.get_ordered_objects()[:] … … 679 975 else: 680 976 t.append('document.getElementById("id_%s").onchange = function() { this._changed = true; };' % field.name) 681 977 for f in field.prepopulate_from: 682 t.append('document.getElementById("id_%s").onkeyup = function() { var e = document.getElementById("id_%s"); if ( !e._changed) { e.value = URLify(%s, %s);}};' % \978 t.append('document.getElementById("id_%s").onkeyup = function() { var e = document.getElementById("id_%s"); if (e._changed) { e.value = URLify(%s, %s);}};' % \ 683 979 (f, field.name, ' + " " + '.join(['document.getElementById("id_%s").value' % g for g in field.prepopulate_from]), field.maxlength)) 684 980 t.append('</script>\n') 685 981 if change and ordered_objects: … … 801 1097 # Add default data. 802 1098 for f in opts.fields: 803 1099 if f.has_default(): 804 new_data.update( _get_flattened_data(f, f.get_default()))1100 new_data.update( f.flatten_data() ) 805 1101 # In required many-to-one fields with only one available choice, 806 1102 # select that one available choice. Note: We have to check that 807 1103 # the length of choices is *2*, not 1, because SelectFields always … … 836 1132 if f.editable and f != rel_field and not isinstance(f, meta.AutoField): 837 1133 for field_name in f.get_manipulator_field_names(''): 838 1134 full_field_name = '%s.%d.%s' % (var_name, i, field_name) 839 collection[field_name] = formfields.FormFieldWrapper(manipulator[full_field_name], new_data.get(full_field_name, ''), errors.get(full_field_name, [])) 1135 field = manipulator[full_field_name] 1136 data = field.extract_data(new_data) 1137 collection[field_name] = formfields.FormFieldWrapper(field, data, errors.get(full_field_name, [])) 840 1138 wrapper.append(formfields.FormFieldCollection(collection)) 841 1139 setattr(form, rel_opts.module_name, wrapper) 842 1140 … … 848 1146 if object_id_override is not None: 849 1147 c['object_id'] = object_id_override 850 1148 raw_template = _get_template(opts, app_label, add=True, show_delete=show_delete, form_url=form_url) 851 # return HttpResponse(raw_template, mimetype='text/plain')852 1149 t = template_loader.get_template_from_string(raw_template) 853 1150 return HttpResponse(t.render(c)) 854 1151 … … 914 1211 new_data = {} 915 1212 obj = manipulator.original_object 916 1213 for f in opts.fields: 917 new_data.update( _get_flattened_data(f, getattr(obj, f.column)))1214 new_data.update(f.flatten_data(obj)) 918 1215 for f in opts.many_to_many: 919 1216 get_list_func = getattr(obj, 'get_%s_list' % f.rel.singular) 920 1217 if f.rel.raw_id_admin: … … 926 1223 for i, rel_instance in enumerate(getattr(obj, 'get_%s_list' % opts.get_rel_object_method_name(rel_obj, rel_field))()): 927 1224 for f in rel_obj.fields: 928 1225 if f.editable and f != rel_field: 929 for k, v in _get_flattened_data(f, getattr(rel_instance, f.column)).items():1226 for k, v in f.flatten_data(rel_instance).items(): 930 1227 new_data['%s.%d.%s' % (var_name, i, k)] = v 931 1228 for f in rel_obj.many_to_many: 932 1229 new_data['%s.%d.%s' % (var_name, i, f.column)] = [j.id for j in getattr(rel_instance, 'get_%s_list' % f.rel.singular)()] … … 959 1256 if f.editable and f != rel_field: 960 1257 for field_name in f.get_manipulator_field_names(''): 961 1258 full_field_name = '%s.%d.%s' % (var_name, i, field_name) 962 collection[field_name] = formfields.FormFieldWrapper(manipulator[full_field_name], new_data.get(full_field_name, f.get_default()), errors.get(full_field_name, [])) 1259 field = manipulator[full_field_name] 1260 data = field.extract_data(new_data) 1261 collection[field_name] = formfields.FormFieldWrapper(field, data, errors.get(full_field_name, [])) 963 1262 wrapper.append(formfields.FormFieldCollection(collection)) 964 1263 setattr(form, rel_opts.module_name, wrapper) 965 1264 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)