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