Changeset 740
- Timestamp:
- 09/30/05 07:16:43 (3 years ago)
- Files:
-
- django/branches/new-admin/django/conf/admin_media/js/urlify.js (modified) (1 diff)
- django/branches/new-admin/django/conf/admin_templates/admin_change_form.html (added)
- django/branches/new-admin/django/conf/admin_templates/admin_edit_inline_stacked.html (added)
- django/branches/new-admin/django/conf/admin_templates/admin_edit_inline_tabular.html (added)
- django/branches/new-admin/django/conf/admin_templates/admin_field.html (added)
- django/branches/new-admin/django/conf/admin_templates/admin_field_widget.html (added)
- django/branches/new-admin/django/conf/urls/admin.py (modified) (1 diff)
- django/branches/new-admin/django/core/defaulttags.py (modified) (4 diffs)
- django/branches/new-admin/django/core/formfields.py (modified) (19 diffs)
- django/branches/new-admin/django/core/meta/fields.py (modified) (15 diffs)
- django/branches/new-admin/django/core/meta/__init__.py (modified) (8 diffs)
- django/branches/new-admin/django/core/validators.py (modified) (2 diffs)
- django/branches/new-admin/django/templatetags/admin_modify.py (added)
- django/branches/new-admin/django/views/admin/main.py (modified) (11 diffs)
- django/branches/new-admin/tests/runtests.py (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
django/branches/new-admin/django/conf/admin_media/js/urlify.js
r96 r740 11 11 s = s.replace(/[^\w\s]/g, ''); // remove unneeded chars 12 12 s = s.replace(/^\s+|\s+$/g, ''); // trim leading/trailing spaces 13 s = s.replace(/\s+/g, ' _'); // convert spaces to underscores13 s = s.replace(/\s+/g, '-'); // convert spaces to dashes 14 14 s = s.toLowerCase(); // convert to lowercase 15 15 return s.substring(0, num_chars);// trim to first num_chars chars django/branches/new-admin/django/conf/urls/admin.py
r469 r740 49 49 urlpatterns += ( 50 50 # Metasystem admin pages 51 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/add_old/$', 'django.views.admin.main.add_stage'), 52 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)_old/$', 'django.views.admin.main.change_stage'), 51 53 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/$', 'django.views.admin.main.change_list'), 52 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/add/$', 'django.views.admin.main.add_stage '),54 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/add/$', 'django.views.admin.main.add_stage_new'), 53 55 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/jsvalidation/$', 'django.views.admin.jsvalidation.jsvalidation'), 54 56 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)/history/$', 'django.views.admin.main.history'), 55 57 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)/delete/$', 'django.views.admin.main.delete_stage'), 56 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)/$', 'django.views.admin.main.change_stage '),58 ('^(?P<app_label>[^/]+)/(?P<module_name>[^/]+)/(?P<object_id>.+)/$', 'django.views.admin.main.change_stage_new'), 57 59 ) 58 60 urlpatterns = patterns('', *urlpatterns) django/branches/new-admin/django/core/defaulttags.py
r574 r740 3 3 import sys 4 4 import template 5 import template_loader 5 6 6 7 class CommentNode(template.Node): … … 224 225 return output 225 226 227 class IncludeNode(template.Node): 228 def __init__(self, template_path): 229 self.template_path_var = template_path_var 230 231 def render(self, context): 232 try: 233 template_path = template.resolve(self.template_path_var, context) 234 t = template_loader.get_template(template_path) 235 return t.render(context) 236 except: 237 return '' # Fail silently for invalid included templates. 238 239 226 240 class LoadNode(template.Node): 227 241 def __init__(self, taglib): … … 601 615 return SsiNode(bits[1], parsed) 602 616 617 def do_include(parser, token): 618 """ 619 Loads a template using standard resolution mechanisms, and renders it in the current context. 620 """ 621 bits = token.contents.split() 622 parsed = False 623 if len(bits) != 2: 624 raise template.TemplateSyntaxError, "'include' tag takes one argument: the path to the template to be included" 625 return IncludeNode(bits[1]) 626 603 627 def do_load(parser, token): 604 628 """ … … 756 780 template.register_tag('if', do_if) 757 781 template.register_tag('ifchanged', do_ifchanged) 782 template.register_tag('include', do_include) 758 783 template.register_tag('regroup', do_regroup) 759 784 template.register_tag('ssi', do_ssi) django/branches/new-admin/django/core/formfields.py
r702 r740 23 23 if field.field_name == field_name: 24 24 return field 25 raise KeyError, "Field %s not found " % field_name25 raise KeyError, "Field %s not found\n%s" % (field_name, repr(self.fields)) 26 26 27 27 def __delitem__(self, field_name): … … 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 """ 93 94 for field in self.fields: 95 field.convert_post_data(new_data) 100 96 101 97 class FormWrapper: … … 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 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 125 136 126 137 def has_errors(self): … … 136 147 "Renders the field" 137 148 return str(self.formfield.render(self.data)) 149 138 150 139 151 def __repr__(self): … … 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" … … 175 190 errors = [] 176 191 for field in self.formfield_dict.values(): 177 errors.extend(field.errors()) 192 if(hasattr(field, 'errors') ): 193 errors.extend(field.errors()) 178 194 return errors 195 196 def has_errors(self): 197 return bool(len(self.errors())) 198 199 def html_combined_error_list(self): 200 return ''.join( [ field.html_error_list() for field in self.formfield_dict.values() if hasattr(field, 'errors')]) 201 202 class InlineObjectCollection: 203 "An object that acts like a list of form field collections." 204 def __init__(self, parent_manipulator, rel_obj, data, errors): 205 self.parent_manipulator = parent_manipulator 206 self.rel_obj = rel_obj 207 self.data = data 208 self.errors = errors 209 self._collections = None 210 self.name = rel_obj.name 211 212 def __len__(self): 213 self.fill() 214 return self._collections.__len__() 215 216 def __getitem__(self, k): 217 self.fill() 218 return self._collections.__getitem__(k) 219 220 def __setitem__(self, k, v): 221 self.fill() 222 return self._collections.__setitem__(k,v) 223 224 def __delitem__(self, k): 225 self.fill() 226 return self._collections.__delitem__(k) 227 228 def __iter__(self): 229 self.fill() 230 return self._collections.__iter__() 231 232 def fill(self): 233 if self._collections: 234 return 235 else: 236 var_name = self.rel_obj.opts.object_name.lower() 237 wrapper = [] 238 orig = hasattr(self.parent_manipulator, 'original_object') and self.parent_manipulator.original_object or None 239 orig_list = self.rel_obj.get_list(orig) 240 for i, instance in enumerate(orig_list): 241 collection = {'original': instance } 242 for f in self.rel_obj.editable_fields(): 243 for field_name in f.get_manipulator_field_names(''): 244 full_field_name = '%s.%d.%s' % (var_name, i, field_name) 245 field = self.parent_manipulator[full_field_name] 246 data = field.extract_data(self.data) 247 errors = self.errors.get(full_field_name, []) 248 # if(errors):raise full_field_name + " " + repr(errors) 249 collection[field_name] = FormFieldWrapper(field, data, errors) 250 wrapper.append(FormFieldCollection(collection)) 251 self._collections = wrapper 179 252 180 253 class FormField: … … 210 283 raise NotImplementedError 211 284 285 def get_member_name(self): 286 if hasattr(self, 'member_name'): 287 return self.member_name 288 else: 289 return self.field_name 290 291 def extract_data(self, data_dict): 292 if hasattr(self, 'requires_data_list') and hasattr(data_dict, 'getlist'): 293 data = data_dict.getlist(self.get_member_name()) 294 else: 295 data = data_dict.get(self.get_member_name(), None) 296 if data is None: 297 data = '' 298 self.data_dict = data_dict 299 return data 300 301 def convert_post_data(self, new_data): 302 name = self.get_member_name() 303 if new_data.has_key(name): 304 d = new_data.getlist(name) 305 #del new_data[self.field_name] 306 new_data.setlist(name, 307 [self.__class__.html2python(data) 308 for data in d]) 309 else: 310 try: 311 # individual fields deal with None values themselves 312 new_data.setlist(name, [self.__class__.html2python(None)]) 313 except EmptyValue: 314 new_data.setlist(name, []) 315 316 def get_id(self): 317 return FORM_FIELD_ID_PREFIX + self.field_name 212 318 #################### 213 319 # GENERIC WIDGETS # … … 238 344 data = data.encode('utf-8') 239 345 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 '',346 (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 241 347 self.field_name, self.length, escape(data), maxlength) 242 348 … … 249 355 # value is always blank because we never want to redisplay it 250 356 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 '',357 (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 252 358 self.field_name) 253 359 … … 267 373 data = data.encode('utf-8') 268 374 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 '',375 (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 270 376 self.field_name, self.rows, self.cols, escape(data)) 271 377 … … 277 383 def render(self, data): 278 384 return '<input type="hidden" id="%s" name="%s" value="%s" />' % \ 279 ( FORM_FIELD_ID_PREFIX + self.field_name, self.field_name, escape(data))385 (self.get_id(), self.field_name, escape(data)) 280 386 281 387 class CheckboxField(FormField): … … 290 396 checked_html = ' checked="checked"' 291 397 return '<input type="checkbox" id="%s" class="v%s" name="%s"%s />' % \ 292 ( FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__,398 (self.get_id(), self.__class__.__name__, 293 399 self.field_name, checked_html) 294 400 … … 300 406 html2python = staticmethod(html2python) 301 407 408 302 409 class SelectField(FormField): 303 def __init__(self, field_name, choices=[], size=1, is_required=False, validator_list=[] ):410 def __init__(self, field_name, choices=[], size=1, is_required=False, validator_list=[], member_name=None): 304 411 self.field_name = field_name 305 412 # choices is a list of (value, human-readable key) tuples because order matters 306 413 self.choices, self.size, self.is_required = choices, size, is_required 307 414 self.validator_list = [self.isValidChoice] + validator_list 308 309 def render(self, data): 415 if member_name != None: 416 self.member_name = member_name 417 418 def render(self, data): 419 str_data = str(data) # normalize to string 310 420 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 421 (self.get_id(), self.__class__.__name__, 422 self.is_required and ' required' or '', self.field_name, self.size)] 314 423 for value, display_name in self.choices: 315 424 selected_html = '' … … 335 444 336 445 class RadioSelectField(FormField): 337 def __init__(self, field_name, choices=[], ul_class='', is_required=False, validator_list=[] ):446 def __init__(self, field_name, choices=[], ul_class='', is_required=False, validator_list=[], member_name=None): 338 447 self.field_name = field_name 339 448 # choices is a list of (value, human-readable key) tuples because order matters … … 341 450 self.validator_list = [self.isValidChoice] + validator_list 342 451 self.ul_class = ul_class 452 if member_name != None: 453 self.member_name = member_name 343 454 344 455 def render(self, data): … … 383 494 'name': display_name, 384 495 '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),496 (self.get_id() + '_' + str(i), self.field_name, value, selected_html), 386 497 'label': '<label for="%s">%s</label>' % \ 387 ( FORM_FIELD_ID_PREFIX + self.field_name+ '_' + str(i), display_name),498 (self.get_id() + '_' + str(i), display_name), 388 499 }) 389 500 return RadioFieldRenderer(datalist, self.ul_class) … … 415 526 def render(self, data): 416 527 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 '',528 (self.get_id(), self.__class__.__name__, self.is_required and ' required' or '', 418 529 self.field_name, self.size)] 419 530 str_data_list = map(str, data) # normalize to strings … … 470 581 checked_html = ' checked="checked"' 471 582 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))583 output.append('<li><input type="checkbox" id="%s" class="v%s" name="%s"%s /> <label for="%s">%s</label></li>' % \ 584 (get_id() + value , self.__class__.__name__, field_name, checked_html, 585 get_id() + value, choice)) 475 586 output.append('</ul>') 476 587 return '\n'.join(output) … … 491 602 def render(self, data): 492 603 return '<input type="file" id="%s" class="v%s" name="%s" />' % \ 493 ( FORM_FIELD_ID_PREFIX + self.field_name, self.__class__.__name__,604 (self.get_id(), self.__class__.__name__, 494 605 self.field_name) 495 606 django/branches/new-admin/django/core/meta/fields.py
r713 r740 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' … … 156 156 return self.default.__get_value__() 157 157 return self.default 158 if self.null:158 if not self.empty_strings_allowed or self.null: 159 159 return None 160 160 return "" … … 164 164 Returns a list of field names that this object adds to the manipulator. 165 165 """ 166 return [name_prefix + self. name]166 return [name_prefix + self.column] 167 167 168 168 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False): … … 178 178 params['maxlength'] = self.maxlength 179 179 if isinstance(self.rel, ManyToOne): 180 params['member_name'] = name_prefix + self.get_db_column() 180 181 if self.rel.raw_id_admin: 181 182 field_objs = self.get_manipulator_field_objs() … … 184 185 if self.radio_admin: 185 186 field_objs = [formfields.RadioSelectField] 186 params['choices'] = self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE)187 187 params['ul_class'] = get_ul_class(self.radio_admin) 188 188 else: … … 191 191 else: 192 192 field_objs = [formfields.SelectField] 193 params['choices'] = self.get_choices()193 params['choices'] = self.get_choices_default() 194 194 elif self.choices: 195 195 if self.radio_admin: 196 196 field_objs = [formfields.RadioSelectField] 197 params['choices'] = self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE)198 197 params['ul_class'] = get_ul_class(self.radio_admin) 199 198 else: 200 199 field_objs = [formfields.SelectField] 201 params['choices'] = self.get_choices() 200 201 params['choices'] = self.get_choices_default() 202 202 else: 203 203 field_objs = self.get_manipulator_field_objs() … … 259 259 return val 260 260 261 261 262 def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH): 262 263 "Returns a list of tuples used as SelectField choices for this field." 264 263 265 first_choice = include_blank and blank_choice or [] 264 266 if self.choices: 265 267 return first_choice + list(self.choices) 266 268 rel_obj = self.rel.to 267 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)] 268 269 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)] 270 271 return choices 272 273 274 def get_choices_default(self): 275 if(self.radio_admin): 276 return self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE) 277 else: 278 return self.get_choices() 279 280 def _get_val_from_obj(self, obj): 281 if obj: 282 return getattr(obj, self.column) 283 else: 284 return self.get_default() 285 286 def flatten_data(self, obj = None): 287 """ 288 Returns a dictionary mapping the field's manipulator field names to its 289 "flattened" string values for the admin view. Obj is the instance to extract the 290 values from. 291 """ 292 return { self.get_db_column(): self._get_val_from_obj(obj)} 293 294 269 295 class AutoField(Field): 270 296 empty_strings_allowed = False … … 275 301 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False): 276 302 if not rel: 277 return [] # Don't add a FormField unless it's in a related c ontext.303 return [] # Don't add a FormField unless it's in a related change context. 278 304 return Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel) 279 305 … … 330 356 def get_manipulator_field_objs(self): 331 357 return [formfields.DateField] 358 359 def flatten_data(self, obj = None): 360 val = self._get_val_from_obj(obj) 361 return {self.get_db_column(): (val is not None and val.strftime("%Y-%m-%d") or '')} 332 362 333 363 class DateTimeField(DateField): … … 359 389 return datetime.datetime.combine(d, t) 360 390 return self.get_default() 391 392 def flatten_data(self,obj = None): 393 val = self._get_val_from_obj(obj) 394 date_field, time_field = self.get_manipulator_field_names('') 395 return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''), 396 time_field: (val is not None and val.strftime("%H:%M:%S") or '')} 361 397 362 398 class EmailField(Field): … … 543 579 return [formfields.TimeField] 544 580 581 def flatten_data(self,obj = None): 582 val = self._get_val_from_obj(obj) 583 return {self.get_db_column(): (val is not None and val.strftime("%H:%M:%S") or '')} 584 545 585 class URLField(Field): 546 586 def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs): … … 598 638 def get_manipulator_field_objs(self): 599 639 return [formfields.IntegerField] 640 641 def flatten_data(self, obj = None): 642 if not obj: 643 # In required many-to-one fields with only one available choice, 644 # select that one available choice. Note: We have to check that 645 # the length of choices is *2*, not 1, because SelectFields always 646 # have an initial "blank" value. 647 if not self.blank and not self.rel.raw_id_admin and self.choices: 648 choice_list = self.get_choices_default() 649 if len(choice_list) == 2: 650 return { self.name : choice_list[1][0] } 651 return Field.flatten_data(self, obj) 600 652 601 653 class ManyToManyField(Field): … … 616 668 return [formfields.CommaSeparatedIntegerField] 617 669 else: 618 choices = self.get_choices (include_blank=False)670 choices = self.get_choices_default() 619 671 return [curry(formfields.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)] 672 673 def get_choices_default(self): 674 return Field.get_choices(self, include_blank=False) 620 675 621 676 def get_m2m_db_table(self, original_opts): … … 638 693 len(badkeys) == 1 and badkeys[0] or tuple(badkeys), 639 694 len(badkeys) == 1 and "is" or "are") 695 696 def flatten_data(self, obj = None): 697 new_data = {} 698 if obj: 699 get_list_func = getattr(obj, 'get_%s_list' % self.rel.singular) 700 instance_ids = [getattr(instance, self.rel.to.pk.column) for instance in get_list_func()] 701 if self.rel.raw_id_admin: 702 new_data[self.name] = ",".join([str(id) for id in instance_ids]) 703 elif not self.rel.edit_inline: 704 new_data[self.name] = instance_ids 705 else: 706 # In required many-to-many fields with only one available choice, 707 # select that one available choice. 708 if not self.blank and not self.rel.edit_inline and not self.rel.raw_id_admin and self.choices: 709 choice_list = self.get_choices_default() 710 if len(choice_list) == 1: 711 new_data[self.name] = [choices_list[0][0]] 712 return new_data 713 640 714 641 715 class OneToOneField(IntegerField): … … 721 795 field names. If self.fields is None, defaults to putting every 722 796 non-AutoField field with editable=True in a single fieldset. 797 798 returns a list of lists of name, dict 799 the dict has attribs 'fields' and maybe 'classes'. 800 fields is a list of subclasses of Field. 801 802 Return value needs to be encapsulated. 723 803 """ 724 804 if self.fields is None: django/branches/new-admin/django/core/meta/__init__.py
r698 r740 146 146 class BadKeywordArguments(Exception): 147 147 pass 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, wrapping_func = lambda x: x): 202 """Get the fields in this class that should be edited inline. 203 Pass a callable, eg a class, as the second argument to wrap the fields. 204 This can be useful to add extra attributes for use in templates.""" 205 206 return [wrapping_func(f) for f in self.opts.fields + self.opts.many_to_many if f.editable and f != self.field ] 207 208 def __repr__(self): 209 return "<InlineRelatedObject: %s related to %s>" % ( self.name, self.field.name) 210 211 148 212 149 213 class Options: … … 317 381 def get_inline_related_objects(self): 318 382 return [(a, b) for a, b in self.get_all_related_objects() if b.rel.edit_inline] 383 384 def get_inline_related_objects_wrapped(self): 385 return [InlineRelatedObject(self, opts, field) for opts, field in self.get_all_related_objects() if field.rel.edit_inline] 386 387 def get_data_holders(self): 388 return self.fields + self.many_to_many + self.get_inline_related_objects_wrapped() 319 389 320 390 def get_all_related_many_to_many_objects(self): … … 595 665 596 666 for f in opts.fields: 667 #TODO : change this into a virtual function so that user defined fields will be able to add methods to module or class. 597 668 if f.choices: 598 669 # Add "get_thingie_display" method to get human-readable value. … … 789 860 if cursor.fetchone(): 790 861 db_values = [f.get_db_prep_save(f.pre_save(getattr(self, f.column), False)) for f in non_pks] 791 cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % (opts.db_table, 792 ','.join(['%s=%%s' % f.column for f in non_pks]), opts.pk.column), 862 while 1: 863 try: 864 idx = db_values.index('') 865 non_pks[idx:idx+1] = [] 866 db_values[idx:idx +1] = [] 867 except: break 868 cursor.execute("UPDATE %s SET %s WHERE %s=%%s" % (opts.db_table, 869 ','.join(['%s=%%s' % f.column for f in non_pks]), opts.pk.column), 793 870 db_values + [pk_val]) 794 871 else: … … 1333 1410 order_by.append(db.get_random_function_sql()) 1334 1411 else: 1412 if f.startswith('-'): 1413 col_name = f[1:] 1414 order = "DESC" 1415 else: 1416 col_name = f 1417 order = "ASC" 1335 1418 # Use the database table as a column prefix if it wasn't given, 1336 1419 # and if the requested column isn't a custom SELECT. 1337 if "." not in f and fnot in [k[0] for k in kwargs.get('select', [])]:1420 if "." not in col_name and col_name not in [k[0] for k in kwargs.get('select', [])]: 1338 1421 table_prefix = opts.db_table + '.' 1339 1422 else: 1340 1423 table_prefix = '' 1341 if f.startswith('-'): 1342 order_by.append('%s%s DESC' % (table_prefix, orderfield2column(f[1:], opts))) 1343 else: 1344 order_by.append('%s%s ASC' % (table_prefix, orderfield2column(f, opts))) 1424 1425 order_by.append('%s%s %s' % (table_prefix, orderfield2column(col_name, opts), order)) 1426 1345 1427 order_by = ", ".join(order_by) 1346 1428 … … 1398 1480 man.__init__ = curry(manipulator_init, opts, add, change) 1399 1481 man.save = curry(manipulator_save, opts, klass, add, change) 1482 man.get_inline_related_objects_wrapped = curry(manipulator_get_inline_related_objects_wrapped, opts, klass, add, change) 1483 man.flatten_data = curry(manipulator_flatten_data, opts, klass, add, change) 1400 1484 for field_name_list in opts.unique_together: 1401 1485 setattr(man, 'isUnique%s' % '_'.join(field_name_list), curry(manipulator_validator_unique_together, field_name_list, opts)) … … 1440 1524 1441 1525 # Add fields for related objects. 1442 for rel_opts, rel_field in opts.get_inline_related_objects():1526 for obj in opts.get_inline_related_objects_wrapped(): 1443 1527 if change: 1444 count = getattr(self.original_object, 'get_%s_count' % opts.get_rel_object_method_name( rel_opts, rel_field))()1445 count += rel_field.rel.num_extra_on_change1446 if rel_field.rel.min_num_in_admin:1447 count = max(count, rel_field.rel.min_num_in_admin)1448 if rel_field.rel.max_num_in_admin:1449 count = min(count, rel_field.rel.max_num_in_admin)1528 count = getattr(self.original_object, 'get_%s_count' % opts.get_rel_object_method_name(obj.opts, obj.field))() 1529 count += obj.field.rel.num_extra_on_change 1530 if obj.field.rel.min_num_in_admin: 1531 count = max(count, obj.field.rel.min_num_in_admin) 1532 if obj.field.rel.max_num_in_admin: 1533 count = min(count, obj.field.rel.max_num_in_admin) 1450 1534 else: 1451 count = rel_field.rel.num_in_admin1452 for f in rel_opts.fields + rel_opts.many_to_many:1453 if f.editable and f != rel_field and (not f.primary_key or (f.primary_key and change)):1535 count = obj.field.rel.num_in_admin 1536 for f in obj.opts.fields + obj.opts.many_to_many: 1537 if f.editable and f != obj.field : 1454 1538 for i in range(count): 1455 self.fields.extend(f.get_manipulator_fields( rel_opts, self, change, name_prefix='%s.%d.' % (rel_opts.object_name.lower(), i), rel=True))1539 self.fields.extend(f.get_manipulator_fields(obj.opts, self, change, name_prefix='%s.%d.' % (obj.opts.object_name.lower(), i), rel=True)) 1456 1540 1457 1541 # Add field for ordering. … … 1593 1677 getattr(new_object, 'set_%s_order' % rel_opts.object_name.lower())(order) 1594 1678 return new_object 1679 1680 def manipulator_get_inline_related_objects_wrapped(opts, klass, add, change, self): 1681 return opts.get_inline_related_objects_wrapped() 1682 1683 def manipulator_flatten_data(opts, klass, add, change, self): 1684 new_data = {} 1685 obj = change and self.original_object or None 1686 for f in opts.get_data_holders(): 1687 new_data.update(f.flatten_data(obj)) 1688 return new_data 1595 1689 1596 1690 def manipulator_validator_unique_together(field_name_list, opts, self, field_data, all_data): django/branches/new-admin/django/core/validators.py
r712 r740 13 13 _datere = r'\d{4}-((?:0?[1-9])|(?:1[0-2]))-((?:0?[1-9])|(?:[12][0-9])|(?:3[0-1]))' 14 14 _timere = r'(?:[01]?[0-9]|2[0-3]):[0-5][0-9](?::[0-5][0-9])?' 15 alnum_re = re.compile(r'^ \w+$')15 alnum_re = re.compile(r'^[\w-]+$') 16 16 alnumurl_re = re.compile(r'^[\w/]+$') 17 17 ansi_date_re = re.compile('^%s$' % _datere) … … 54 54 def isAlphaNumeric(field_data, all_data): 55 55 if not alnum_re.search(field_data): 56 raise ValidationError, "This value must contain only letters, numbers and underscores."56 raise ValidationError, "This value must contain only letters, numbers, dashes and underscores." 57 57 58 58 def isAlphaNumericURL(field_data, all_data): django/branches/new-admin/django/views/admin/main.py
r714 r740 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, ObjectDoesNot
