Ticket #3990: admin_readonly_fields_v1.1.diff
File admin_readonly_fields_v1.1.diff, 7.5 KB (added by , 18 years ago) |
---|
-
django/contrib/admin/options.py
1 1 from django import oldforms, template 2 2 from django import newforms as forms 3 3 from django.contrib.admin import widgets 4 from django.newforms.widgets import DisplayWidget 4 5 from django.core.exceptions import ImproperlyConfigured, PermissionDenied 5 6 from django.db import models 6 7 from django.http import Http404, HttpResponse, HttpResponseRedirect … … 111 112 fields = None 112 113 raw_id_fields = () 113 114 prepopulated_fields = {} 115 # this list contains fields that will only be changed when adding an object. They'll only be displayed in change forms 116 readonly_fields = () 114 117 filter_vertical = () 115 118 filter_horizontal = () 116 119 … … 201 204 for fs in self.fieldsets(request): 202 205 yield fs 203 206 204 def formfield_for_dbfield (self, db_field, **kwargs):207 def formfield_for_dbfield_add(self, db_field, override_readonly=True, **kwargs): 205 208 """ 209 call formfield_for_dbfield with override_readonly = True. Used for admin add object forms 210 """ 211 return self.formfield_for_dbfield(db_field, override_readonly, **kwargs) 212 213 def formfield_for_dbfield(self, db_field, override_readonly=False, **kwargs): 214 """ 206 215 Hook for specifying the form Field instance for a given database Field 207 216 instance. 208 217 209 218 If kwargs are given, they're passed to the form Field's constructor. 210 219 """ 220 # specify that this value cannot be edited by the user. The formfield will decide how to render this. 221 # The override is used by the create form to set the value. 222 this_field_is_readonly = False 223 if not override_readonly and db_field.name in self.readonly_fields: 224 this_field_is_readonly = True 225 kwargs['widget'] = DisplayWidget 226 211 227 # For ManyToManyFields with a filter interface, use a special Widget. 212 228 if isinstance(db_field, models.ManyToManyField) and db_field.name in (self.filter_vertical + self.filter_horizontal): 213 229 kwargs['widget'] = widgets.FilteredSelectMultiple(db_field.verbose_name, (db_field.name in self.filter_vertical)) 214 230 return db_field.formfield(**kwargs) 215 231 216 232 # For DateTimeFields, use a special field and widget. 217 if isinstance(db_field, models.DateTimeField) :233 if isinstance(db_field, models.DateTimeField) and not this_field_is_readonly: 218 234 return forms.SplitDateTimeField(required=not db_field.blank, 219 235 widget=widgets.AdminSplitDateTime(), label=capfirst(db_field.verbose_name), 220 236 help_text=db_field.help_text, **kwargs) 221 237 222 238 # For DateFields, add a custom CSS class. 223 if isinstance(db_field, models.DateField) :239 if isinstance(db_field, models.DateField) and not this_field_is_readonly: 224 240 kwargs['widget'] = forms.TextInput(attrs={'class': 'vDateField', 'size': '10'}) 225 241 return db_field.formfield(**kwargs) 226 242 227 243 # For TimeFields, add a custom CSS class. 228 if isinstance(db_field, models.TimeField) :244 if isinstance(db_field, models.TimeField) and not this_field_is_readonly: 229 245 kwargs['widget'] = forms.TextInput(attrs={'class': 'vTimeField', 'size': '8'}) 230 246 return db_field.formfield(**kwargs) 231 247 232 248 # For ForeignKey or ManyToManyFields, use a special widget. 233 249 if isinstance(db_field, (models.ForeignKey, models.ManyToManyField)): 234 if isinstance(db_field, models.ForeignKey) and db_field.name in self.raw_id_fields: 250 if this_field_is_readonly: 251 pass # the displaywidget will take care of it ! 252 elif isinstance(db_field, models.ForeignKey) and db_field.name in self.raw_id_fields: 235 253 kwargs['widget'] = widgets.ForeignKeyRawIdWidget(db_field.rel) 236 254 return db_field.formfield(**kwargs) 237 255 else: … … 392 410 # Object list will give 'Permission Denied', so go back to admin home 393 411 post_url = '../../../' 394 412 395 ModelForm = forms.form_for_model(model, formfield_callback=self.formfield_for_dbfield )413 ModelForm = forms.form_for_model(model, formfield_callback=self.formfield_for_dbfield_add) 396 414 397 415 if request.POST: 398 416 new_data = request.POST.copy() … … 443 461 new_data = request.POST.copy() 444 462 if opts.has_field_type(models.FileField): 445 463 new_data.update(request.FILES) 464 # get the values for readonly fields from the object, because they are displayed as strings instead of widgets and thus are not part of the POST request 465 for fieldname in self.readonly_fields: 466 fieldvalue = getattr(obj, fieldname) 467 new_data[fieldname] = fieldvalue 468 446 469 form = ModelForm(new_data) 447 470 448 471 if form.is_valid(): … … 507 530 "The 'delete' admin view for this model." 508 531 from django.contrib.contenttypes.models import ContentType 509 532 from django.contrib.admin.models import LogEntry, DELETION 533 from django.contrib.admin.views.main import _get_deleted_objects 510 534 opts = self.model._meta 511 535 app_label = opts.app_label 512 536 -
django/contrib/admin/filterspecs.py
158 158 def __init__(self, f, request, params, model): 159 159 super(AllValuesFilterSpec, self).__init__(f, request, params, model) 160 160 self.lookup_val = request.GET.get(f.name, None) 161 self.lookup_choices = model._ meta.admin.manager.distinct().order_by(f.name).values(f.name)161 self.lookup_choices = model._default_manager.distinct().order_by(f.name).values(f.name) 162 162 163 163 def title(self): 164 164 return self.field.verbose_name -
django/newforms/widgets.py
3 3 """ 4 4 5 5 __all__ = ( 6 'Widget', ' TextInput', 'PasswordInput', 'HiddenInput', 'MultipleHiddenInput',6 'Widget', 'DisplayWidget', 'TextInput', 'PasswordInput', 'HiddenInput', 'MultipleHiddenInput', 7 7 'FileInput', 'Textarea', 'CheckboxInput', 8 8 'Select', 'NullBooleanSelect', 'SelectMultiple', 'RadioSelect', 'CheckboxSelectMultiple', 9 9 'MultiWidget', 'SplitDateTimeWidget', … … 66 66 return id_ 67 67 id_for_label = classmethod(id_for_label) 68 68 69 class DisplayWidget(Widget): 70 """ 71 Base class for all read-only pseudo-widgets. Those are used by the admin interface to display read-only values 72 """ 73 def __init__(self, choices=None, attrs=None): 74 self.attrs = attrs or {} 75 self.choices = choices 76 77 def render(self, name, value, attrs=None): 78 # lookup choices values 79 if self.choices is not None: 80 for (primary_key, name) in self.choices: 81 if value == primary_key: 82 value = name 83 break 84 # and replace special values by strings to improve readability 85 if value is None: 86 value = '[Null]' 87 if value == '': 88 value = '[Blank]' 89 90 return smart_unicode(value) 91 92 69 93 class Input(Widget): 70 94 """ 71 95 Base class for all <input> widgets (except type='checkbox' and