Ticket #1665: fields.py

File fields.py, 40.7 KB (added by robert.morris@…, 19 years ago)

Modified to take a "length" parameter, used in "size" attribute of HTML form fields

Line 
1from django.conf import settings
2from django.core import formfields, validators
3from django.core.exceptions import ObjectDoesNotExist
4from django.utils.functional import curry, lazy
5from django.utils.text import capfirst
6from django.utils.translation import gettext_lazy, ngettext
7import datetime, os
8
9class NOT_PROVIDED:
10 pass
11
12# Values for filter_interface.
13HORIZONTAL, VERTICAL = 1, 2
14
15# The values to use for "blank" in SelectFields. Will be appended to the start of most "choices" lists.
16BLANK_CHOICE_DASH = [("", "---------")]
17BLANK_CHOICE_NONE = [("", "None")]
18
19# Values for Relation.edit_inline.
20TABULAR, STACKED = 1, 2
21
22RECURSIVE_RELATIONSHIP_CONSTANT = 'self'
23
24# prepares a value for use in a LIKE query
25prep_for_like_query = lambda x: str(x).replace("%", "\%").replace("_", "\_")
26
27# returns the <ul> class for a given radio_admin value
28get_ul_class = lambda x: 'radiolist%s' % ((x == HORIZONTAL) and ' inline' or '')
29
30def string_concat(*strings):
31 """"
32 lazy variant of string concatenation, needed for translations that are
33 constructed from multiple parts. Handles lazy strings and non-strings by
34 first turning all arguments to strings, before joining them.
35 """
36 return ''.join([str(el) for el in strings])
37
38string_concat = lazy(string_concat, str)
39
40def manipulator_valid_rel_key(f, self, field_data, all_data):
41 "Validates that the value is a valid foreign key"
42 mod = f.rel.to.get_model_module()
43 try:
44 mod.get_object(pk=field_data)
45 except ObjectDoesNotExist:
46 raise validators.ValidationError, _("Please enter a valid %s.") % f.verbose_name
47
48def manipulator_validator_unique(f, opts, self, field_data, all_data):
49 "Validates that the value is unique for this field."
50 if f.rel and isinstance(f.rel, ManyToOneRel):
51 lookup_type = '%s__%s__exact' % (f.name, f.rel.get_related_field().name)
52 else:
53 lookup_type = '%s__exact' % f.name
54 try:
55 old_obj = opts.get_model_module().get_object(**{lookup_type: field_data})
56 except ObjectDoesNotExist:
57 return
58 if hasattr(self, 'original_object') and getattr(self.original_object, opts.pk.attname) == getattr(old_obj, opts.pk.attname):
59 return
60 raise validators.ValidationError, _("%(optname)s with this %(fieldname)s already exists.") % {'optname': capfirst(opts.verbose_name), 'fieldname': f.verbose_name}
61
62class BoundField(object):
63 def __init__(self, field, field_mapping, original):
64 self.field = field
65 self.original = original
66 self.form_fields = self.resolve_form_fields(field_mapping)
67
68 def resolve_form_fields(self, field_mapping):
69 return [field_mapping[name] for name in self.field.get_manipulator_field_names('')]
70
71 def as_field_list(self):
72 return [self.field]
73
74 def original_value(self):
75 if self.original:
76 return self.original.__dict__[self.field.column]
77
78 def __repr__(self):
79 return "BoundField:(%s, %s)" % (self.field.name, self.form_fields)
80
81# A guide to Field parameters:
82#
83# * name: The name of the field specified in the model.
84# * attname: The attribute to use on the model object. This is the same as
85# "name", except in the case of ForeignKeys, where "_id" is
86# appended.
87# * db_column: The db_column specified in the model (or None).
88# * column: The database column for this field. This is the same as
89# "attname", except if db_column is specified.
90#
91# Code that introspects values, or does other dynamic things, should use
92# attname. For example, this gets the primary key value of object "obj":
93#
94# getattr(obj, opts.pk.attname)
95
96class Field(object):
97
98 # Designates whether empty strings fundamentally are allowed at the
99 # database level.
100 empty_strings_allowed = True
101
102 # Tracks each time a Field instance is created. Used to retain order.
103 creation_counter = 0
104
105 def __init__(self, verbose_name=None, name=None, primary_key=False,
106 maxlength=None, unique=False, blank=False, null=False, db_index=None,
107 core=False, rel=None, default=NOT_PROVIDED, editable=True,
108 prepopulate_from=None, unique_for_date=None, unique_for_month=None,
109 unique_for_year=None, validator_list=None, choices=None, radio_admin=None,
110 help_text='', db_column=None, length=None):
111 self.name = name
112 self.verbose_name = verbose_name or (name and name.replace('_', ' '))
113 self.primary_key = primary_key
114 self.maxlength, self.unique = maxlength, unique
115 self.length = length
116 self.blank, self.null = blank, null
117 self.core, self.rel, self.default = core, rel, default
118 self.editable = editable
119 self.validator_list = validator_list or []
120 self.prepopulate_from = prepopulate_from
121 self.unique_for_date, self.unique_for_month = unique_for_date, unique_for_month
122 self.unique_for_year = unique_for_year
123 self.choices = choices or []
124 self.radio_admin = radio_admin
125 self.help_text = help_text
126 self.db_column = db_column
127 if rel and isinstance(rel, ManyToManyRel):
128 if rel.raw_id_admin:
129 self.help_text = string_concat(self.help_text,
130 gettext_lazy(' Separate multiple IDs with commas.'))
131 else:
132 self.help_text = string_concat(self.help_text,
133 gettext_lazy(' Hold down "Control", or "Command" on a Mac, to select more than one.'))
134
135 # Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
136 if db_index is None:
137 if isinstance(rel, OneToOneRel) or isinstance(rel, ManyToOneRel):
138 self.db_index = True
139 else:
140 self.db_index = False
141 else:
142 self.db_index = db_index
143
144 # Increase the creation counter, and save our local copy.
145 self.creation_counter = Field.creation_counter
146 Field.creation_counter += 1
147
148 self.attname, self.column = self.get_attname_column()
149
150 def set_name(self, name):
151 self.name = name
152 self.verbose_name = self.verbose_name or name.replace('_', ' ')
153 self.attname, self.column = self.get_attname_column()
154
155 def get_attname_column(self):
156 if isinstance(self.rel, ManyToOneRel):
157 attname = '%s_id' % self.name
158 else:
159 attname = self.name
160 column = self.db_column or attname
161 return attname, column
162
163 def get_cache_name(self):
164 return '_%s_cache' % self.name
165
166 def get_internal_type(self):
167 return self.__class__.__name__
168
169 def pre_save(self, value, add):
170 "Returns field's value just before saving."
171 return value
172
173 def get_db_prep_save(self, value):
174 "Returns field's value prepared for saving into a database."
175 return value
176
177 def get_db_prep_lookup(self, lookup_type, value):
178 "Returns field's value prepared for database lookup."
179 if lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'ne', 'year', 'month', 'day'):
180 return [value]
181 elif lookup_type in ('range', 'in'):
182 return value
183 elif lookup_type in ('contains', 'icontains'):
184 return ["%%%s%%" % prep_for_like_query(value)]
185 elif lookup_type == 'iexact':
186 return [prep_for_like_query(value)]
187 elif lookup_type in ('startswith', 'istartswith'):
188 return ["%s%%" % prep_for_like_query(value)]
189 elif lookup_type in ('endswith', 'iendswith'):
190 return ["%%%s" % prep_for_like_query(value)]
191 elif lookup_type == 'isnull':
192 return []
193 raise TypeError, "Field has invalid lookup: %s" % lookup_type
194
195 def has_default(self):
196 "Returns a boolean of whether this field has a default value."
197 return self.default is not NOT_PROVIDED
198
199 def get_default(self):
200 "Returns the default value for this field."
201 if self.default is not NOT_PROVIDED:
202 if hasattr(self.default, '__get_value__'):
203 return self.default.__get_value__()
204 return self.default
205 if not self.empty_strings_allowed or self.null:
206 return None
207 return ""
208
209 def get_manipulator_field_names(self, name_prefix):
210 """
211 Returns a list of field names that this object adds to the manipulator.
212 """
213 return [name_prefix + self.name]
214
215 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False):
216 """
217 Returns a list of formfields.FormField instances for this field. It
218 calculates the choices at runtime, not at compile time.
219
220 name_prefix is a prefix to prepend to the "field_name" argument.
221 rel is a boolean specifying whether this field is in a related context.
222 """
223 params = {'validator_list': self.validator_list[:]}
224 if self.maxlength and not self.choices: # Don't give SelectFields a maxlength parameter.
225 params['maxlength'] = self.maxlength
226 if self.length:
227 params['length'] = self.length
228 if isinstance(self.rel, ManyToOneRel):
229 params['member_name'] = name_prefix + self.attname
230 if self.rel.raw_id_admin:
231 field_objs = self.get_manipulator_field_objs()
232 params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
233 else:
234 if self.radio_admin:
235 field_objs = [formfields.RadioSelectField]
236 params['ul_class'] = get_ul_class(self.radio_admin)
237 else:
238 if self.null:
239 field_objs = [formfields.NullSelectField]
240 else:
241 field_objs = [formfields.SelectField]
242 params['choices'] = self.get_choices_default()
243 elif self.choices:
244 if self.radio_admin:
245 field_objs = [formfields.RadioSelectField]
246 params['ul_class'] = get_ul_class(self.radio_admin)
247 else:
248 field_objs = [formfields.SelectField]
249
250 params['choices'] = self.get_choices_default()
251 else:
252 field_objs = self.get_manipulator_field_objs()
253
254 # Add the "unique" validator(s).
255 for field_name_list in opts.unique_together:
256 if field_name_list[0] == self.name:
257 params['validator_list'].append(getattr(manipulator, 'isUnique%s' % '_'.join(field_name_list)))
258
259 # Add the "unique for..." validator(s).
260 if self.unique_for_date:
261 params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_date)))
262 if self.unique_for_month:
263 params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_month)))
264 if self.unique_for_year:
265 params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_year)))
266 if self.unique or (self.primary_key and not rel):
267 params['validator_list'].append(curry(manipulator_validator_unique, self, opts, manipulator))
268
269 # Only add is_required=True if the field cannot be blank. Primary keys
270 # are a special case, and fields in a related context should set this
271 # as False, because they'll be caught by a separate validator --
272 # RequiredIfOtherFieldGiven.
273 params['is_required'] = not self.blank and not self.primary_key and not rel
274
275 # If this field is in a related context, check whether any other fields
276 # in the related object have core=True. If so, add a validator --
277 # RequiredIfOtherFieldsGiven -- to this FormField.
278 if rel and not self.blank and not isinstance(self, AutoField) and not isinstance(self, FileField):
279 # First, get the core fields, if any.
280 core_field_names = []
281 for f in opts.fields:
282 if f.core and f != self:
283 core_field_names.extend(f.get_manipulator_field_names(name_prefix))
284 # Now, if there are any, add the validator to this FormField.
285 if core_field_names:
286 params['validator_list'].append(validators.RequiredIfOtherFieldsGiven(core_field_names, gettext_lazy("This field is required.")))
287
288 # BooleanFields (CheckboxFields) are a special case. They don't take
289 # is_required or validator_list.
290 if isinstance(self, BooleanField):
291 del params['validator_list'], params['is_required']
292
293 # Finally, add the field_names.
294 field_names = self.get_manipulator_field_names(name_prefix)
295 return [man(field_name=field_names[i], **params) for i, man in enumerate(field_objs)]
296
297 def get_manipulator_new_data(self, new_data, rel=False):
298 """
299 Given the full new_data dictionary (from the manipulator), returns this
300 field's data.
301 """
302 if rel:
303 return new_data.get(self.name, [self.get_default()])[0]
304 else:
305 val = new_data.get(self.name, self.get_default())
306 if not self.empty_strings_allowed and val == '' and self.null:
307 val = None
308 return val
309
310 def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH):
311 "Returns a list of tuples used as SelectField choices for this field."
312 first_choice = include_blank and blank_choice or []
313 if self.choices:
314 return first_choice + list(self.choices)
315 rel_obj = self.rel.to
316 return first_choice + [(getattr(x, rel_obj.pk.attname), str(x))
317 for x in rel_obj.get_model_module().get_list(**self.rel.limit_choices_to)]
318
319 def get_choices_default(self):
320 if(self.radio_admin):
321 return self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE)
322 else:
323 return self.get_choices()
324
325 def _get_val_from_obj(self, obj):
326 if obj:
327 return getattr(obj, self.attname)
328 else:
329 return self.get_default()
330
331 def flatten_data(self, follow, obj=None):
332 """
333 Returns a dictionary mapping the field's manipulator field names to its
334 "flattened" string values for the admin view. obj is the instance to
335 extract the values from.
336 """
337 return {self.attname: self._get_val_from_obj(obj)}
338
339 def get_follow(self, override=None):
340 if override != None:
341 return override
342 else:
343 return self.editable
344
345 def bind(self, fieldmapping, original, bound_field_class=BoundField):
346 return bound_field_class(self, fieldmapping, original)
347
348class AutoField(Field):
349 empty_strings_allowed = False
350 def __init__(self, *args, **kwargs):
351 assert kwargs.get('primary_key', False) is True, "%ss must have primary_key=True." % self.__class__.__name__
352 Field.__init__(self, *args, **kwargs)
353
354 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False):
355 if not rel:
356 return [] # Don't add a FormField unless it's in a related context.
357 return Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel)
358
359 def get_manipulator_field_objs(self):
360 return [formfields.HiddenField]
361
362 def get_manipulator_new_data(self, new_data, rel=False):
363 if not rel:
364 return None
365 return Field.get_manipulator_new_data(self, new_data, rel)
366
367class BooleanField(Field):
368 def __init__(self, *args, **kwargs):
369 kwargs['blank'] = True
370 Field.__init__(self, *args, **kwargs)
371
372 def get_manipulator_field_objs(self):
373 return [formfields.CheckboxField]
374
375class CharField(Field):
376 def get_manipulator_field_objs(self):
377 return [formfields.TextField]
378
379class CommaSeparatedIntegerField(CharField):
380 def get_manipulator_field_objs(self):
381 return [formfields.CommaSeparatedIntegerField]
382
383class DateField(Field):
384 empty_strings_allowed = False
385 def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
386 self.auto_now, self.auto_now_add = auto_now, auto_now_add
387 #HACKs : auto_now_add/auto_now should be done as a default or a pre_save...
388 if auto_now or auto_now_add:
389 kwargs['editable'] = False
390 kwargs['blank'] = True
391 Field.__init__(self, verbose_name, name, **kwargs)
392
393 def get_db_prep_lookup(self, lookup_type, value):
394 if lookup_type == 'range':
395 value = [str(v) for v in value]
396 else:
397 value = str(value)
398 return Field.get_db_prep_lookup(self, lookup_type, value)
399
400 def pre_save(self, value, add):
401 if self.auto_now or (self.auto_now_add and add):
402 return datetime.datetime.now()
403 return value
404
405 # Needed because of horrible auto_now[_add] behaviour wrt. editable
406 def get_follow(self, override=None):
407 if override != None:
408 return override
409 else:
410 return self.editable or self.auto_now or self.auto_now_add
411
412 def get_db_prep_save(self, value):
413 # Casts dates into string format for entry into database.
414 if value is not None:
415 value = value.strftime('%Y-%m-%d')
416 return Field.get_db_prep_save(self, value)
417
418 def get_manipulator_field_objs(self):
419 return [formfields.DateField]
420
421 def flatten_data(self, follow, obj = None):
422 val = self._get_val_from_obj(obj)
423 return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')}
424
425class DateTimeField(DateField):
426 def get_db_prep_save(self, value):
427 # Casts dates into string format for entry into database.
428 if value is not None:
429 # MySQL will throw a warning if microseconds are given, because it
430 # doesn't support microseconds.
431 if settings.DATABASE_ENGINE == 'mysql':
432 value = value.replace(microsecond=0)
433 value = str(value)
434 return Field.get_db_prep_save(self, value)
435
436 def get_manipulator_field_objs(self):
437 return [formfields.DateField, formfields.TimeField]
438
439 def get_manipulator_field_names(self, name_prefix):
440 return [name_prefix + self.name + '_date', name_prefix + self.name + '_time']
441
442 def get_manipulator_new_data(self, new_data, rel=False):
443 date_field, time_field = self.get_manipulator_field_names('')
444 if rel:
445 d = new_data.get(date_field, [None])[0]
446 t = new_data.get(time_field, [None])[0]
447 else:
448 d = new_data.get(date_field, None)
449 t = new_data.get(time_field, None)
450 if d is not None and t is not None:
451 return datetime.datetime.combine(d, t)
452 return self.get_default()
453
454 def flatten_data(self,follow, obj = None):
455 val = self._get_val_from_obj(obj)
456 date_field, time_field = self.get_manipulator_field_names('')
457 return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''),
458 time_field: (val is not None and val.strftime("%H:%M:%S") or '')}
459
460class EmailField(Field):
461 def __init__(self, *args, **kwargs):
462 kwargs['maxlength'] = 75
463 Field.__init__(self, *args, **kwargs)
464
465 def get_internal_type(self):
466 return "CharField"
467
468 def get_manipulator_field_objs(self):
469 return [formfields.EmailField]
470
471class FileField(Field):
472 def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
473 self.upload_to = upload_to
474 Field.__init__(self, verbose_name, name, **kwargs)
475
476 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False):
477 field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel)
478
479 if not self.blank:
480 if rel:
481 # This validator makes sure FileFields work in a related context.
482 class RequiredFileField:
483 def __init__(self, other_field_names, other_file_field_name):
484 self.other_field_names = other_field_names
485 self.other_file_field_name = other_file_field_name
486 self.always_test = True
487 def __call__(self, field_data, all_data):
488 if not all_data.get(self.other_file_field_name, False):
489 c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, gettext_lazy("This field is required."))
490 c(field_data, all_data)
491 # First, get the core fields, if any.
492 core_field_names = []
493 for f in opts.fields:
494 if f.core and f != self:
495 core_field_names.extend(f.get_manipulator_field_names(name_prefix))
496 # Now, if there are any, add the validator to this FormField.
497 if core_field_names:
498 field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name))
499 else:
500 v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, gettext_lazy("This field is required."))
501 v.always_test = True
502 field_list[0].validator_list.append(v)
503 field_list[0].is_required = field_list[1].is_required = False
504
505 # If the raw path is passed in, validate it's under the MEDIA_ROOT.
506 def isWithinMediaRoot(field_data, all_data):
507 f = os.path.abspath(os.path.join(settings.MEDIA_ROOT, field_data))
508 if not f.startswith(os.path.normpath(settings.MEDIA_ROOT)):
509 raise validators.ValidationError, _("Enter a valid filename.")
510 field_list[1].validator_list.append(isWithinMediaRoot)
511 return field_list
512
513 def get_manipulator_field_objs(self):
514 return [formfields.FileUploadField, formfields.HiddenField]
515
516 def get_manipulator_field_names(self, name_prefix):
517 return [name_prefix + self.name + '_file', name_prefix + self.name]
518
519 def save_file(self, new_data, new_object, original_object, change, rel):
520 upload_field_name = self.get_manipulator_field_names('')[0]
521 if new_data.get(upload_field_name, False):
522 func = getattr(new_object, 'save_%s_file' % self.name)
523 if rel:
524 func(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"])
525 else:
526 func(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"])
527
528 def get_directory_name(self):
529 return os.path.normpath(datetime.datetime.now().strftime(self.upload_to))
530
531 def get_filename(self, filename):
532 from django.utils.text import get_valid_filename
533 f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename)))
534 return os.path.normpath(f)
535
536class FilePathField(Field):
537 def __init__(self, verbose_name=None, name=None, path='', match=None, recursive=False, **kwargs):
538 self.path, self.match, self.recursive = path, match, recursive
539 Field.__init__(self, verbose_name, name, **kwargs)
540
541 def get_manipulator_field_objs(self):
542 return [curry(formfields.FilePathField, path=self.path, match=self.match, recursive=self.recursive)]
543
544class FloatField(Field):
545 empty_strings_allowed = False
546 def __init__(self, verbose_name=None, name=None, max_digits=None, decimal_places=None, **kwargs):
547 self.max_digits, self.decimal_places = max_digits, decimal_places
548 Field.__init__(self, verbose_name, name, **kwargs)
549
550 def get_manipulator_field_objs(self):
551 return [curry(formfields.FloatField, max_digits=self.max_digits, decimal_places=self.decimal_places)]
552
553class ImageField(FileField):
554 def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
555 self.width_field, self.height_field = width_field, height_field
556 FileField.__init__(self, verbose_name, name, **kwargs)
557
558 def get_manipulator_field_objs(self):
559 return [formfields.ImageUploadField, formfields.HiddenField]
560
561 def save_file(self, new_data, new_object, original_object, change, rel):
562 FileField.save_file(self, new_data, new_object, original_object, change, rel)
563 # If the image has height and/or width field(s) and they haven't
564 # changed, set the width and/or height field(s) back to their original
565 # values.
566 if change and (self.width_field or self.height_field):
567 if self.width_field:
568 setattr(new_object, self.width_field, getattr(original_object, self.width_field))
569 if self.height_field:
570 setattr(new_object, self.height_field, getattr(original_object, self.height_field))
571 new_object.save()
572
573class IntegerField(Field):
574 empty_strings_allowed = False
575 def get_manipulator_field_objs(self):
576 return [formfields.IntegerField]
577
578class IPAddressField(Field):
579 def __init__(self, *args, **kwargs):
580 kwargs['maxlength'] = 15
581 Field.__init__(self, *args, **kwargs)
582
583 def get_manipulator_field_objs(self):
584 return [formfields.IPAddressField]
585
586class NullBooleanField(Field):
587 def __init__(self, *args, **kwargs):
588 kwargs['null'] = True
589 Field.__init__(self, *args, **kwargs)
590
591 def get_manipulator_field_objs(self):
592 return [formfields.NullBooleanField]
593
594class PhoneNumberField(IntegerField):
595 def get_manipulator_field_objs(self):
596 return [formfields.PhoneNumberField]
597
598class PositiveIntegerField(IntegerField):
599 def get_manipulator_field_objs(self):
600 return [formfields.PositiveIntegerField]
601
602class PositiveSmallIntegerField(IntegerField):
603 def get_manipulator_field_objs(self):
604 return [formfields.PositiveSmallIntegerField]
605
606class SlugField(Field):
607 def __init__(self, *args, **kwargs):
608 # Default to a maxlength of 50 but allow overrides.
609 kwargs['maxlength'] = kwargs.get('maxlength', 50)
610 kwargs.setdefault('validator_list', []).append(validators.isSlug)
611 # Set db_index=True unless it's been set manually.
612 if not kwargs.has_key('db_index'):
613 kwargs['db_index'] = True
614 Field.__init__(self, *args, **kwargs)
615
616 def get_manipulator_field_objs(self):
617 return [formfields.TextField]
618
619class SmallIntegerField(IntegerField):
620 def get_manipulator_field_objs(self):
621 return [formfields.SmallIntegerField]
622
623class TextField(Field):
624 def get_manipulator_field_objs(self):
625 return [formfields.LargeTextField]
626
627class TimeField(Field):
628 empty_strings_allowed = False
629 def __init__(self, verbose_name=None, name=None, auto_now=False, auto_now_add=False, **kwargs):
630 self.auto_now, self.auto_now_add = auto_now, auto_now_add
631 if auto_now or auto_now_add:
632 kwargs['editable'] = False
633 Field.__init__(self, verbose_name, name, **kwargs)
634
635 def get_db_prep_lookup(self, lookup_type, value):
636 if lookup_type == 'range':
637 value = [str(v) for v in value]
638 else:
639 value = str(value)
640 return Field.get_db_prep_lookup(self, lookup_type, value)
641
642 def pre_save(self, value, add):
643 if self.auto_now or (self.auto_now_add and add):
644 return datetime.datetime.now().time()
645 return value
646
647 def get_db_prep_save(self, value):
648 # Casts dates into string format for entry into database.
649 if value is not None:
650 # MySQL will throw a warning if microseconds are given, because it
651 # doesn't support microseconds.
652 if settings.DATABASE_ENGINE == 'mysql':
653 value = value.replace(microsecond=0)
654 value = str(value)
655 return Field.get_db_prep_save(self, value)
656
657 def get_manipulator_field_objs(self):
658 return [formfields.TimeField]
659
660 def flatten_data(self,follow, obj = None):
661 val = self._get_val_from_obj(obj)
662 return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')}
663
664class URLField(Field):
665 def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
666 if verify_exists:
667 kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
668 Field.__init__(self, verbose_name, name, **kwargs)
669
670 def get_manipulator_field_objs(self):
671 return [formfields.URLField]
672
673class USStateField(Field):
674 def get_manipulator_field_objs(self):
675 return [formfields.USStateField]
676
677class XMLField(TextField):
678 def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
679 self.schema_path = schema_path
680 Field.__init__(self, verbose_name, name, **kwargs)
681
682 def get_internal_type(self):
683 return "TextField"
684
685 def get_manipulator_field_objs(self):
686 return [curry(formfields.XMLLargeTextField, schema_path=self.schema_path)]
687
688class ForeignKey(Field):
689 empty_strings_allowed = False
690 def __init__(self, to, to_field=None, **kwargs):
691 try:
692 to_name = to._meta.object_name.lower()
693 except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
694 assert to == 'self', "ForeignKey(%r) is invalid. First parameter to ForeignKey must be either a model or the string %r" % (to, RECURSIVE_RELATIONSHIP_CONSTANT)
695 else:
696 to_field = to_field or to._meta.pk.name
697 kwargs['verbose_name'] = kwargs.get('verbose_name', '')
698
699 if kwargs.has_key('edit_inline_type'):
700 import warnings
701 warnings.warn("edit_inline_type is deprecated. Use edit_inline instead.")
702 kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
703
704 kwargs['rel'] = ManyToOneRel(to, to_field,
705 num_in_admin=kwargs.pop('num_in_admin', 3),
706 min_num_in_admin=kwargs.pop('min_num_in_admin', None),
707 max_num_in_admin=kwargs.pop('max_num_in_admin', None),
708 num_extra_on_change=kwargs.pop('num_extra_on_change', 1),
709 edit_inline=kwargs.pop('edit_inline', False),
710 related_name=kwargs.pop('related_name', None),
711 limit_choices_to=kwargs.pop('limit_choices_to', None),
712 lookup_overrides=kwargs.pop('lookup_overrides', None),
713 raw_id_admin=kwargs.pop('raw_id_admin', False))
714 Field.__init__(self, **kwargs)
715
716 def get_manipulator_field_objs(self):
717 rel_field = self.rel.get_related_field()
718 if self.rel.raw_id_admin and not isinstance(rel_field, AutoField):
719 return rel_field.get_manipulator_field_objs()
720 else:
721 return [formfields.IntegerField]
722
723 def get_db_prep_save(self,value):
724 if value == '' or value == None:
725 return None
726 else:
727 return self.rel.get_related_field().get_db_prep_save(value)
728
729 def flatten_data(self, follow, obj=None):
730 if not obj:
731 # In required many-to-one fields with only one available choice,
732 # select that one available choice. Note: For SelectFields
733 # (radio_admin=False), we have to check that the length of choices
734 # is *2*, not 1, because SelectFields always have an initial
735 # "blank" value. Otherwise (radio_admin=True), we check that the
736 # length is 1.
737 if not self.blank and (not self.rel.raw_id_admin or self.choices):
738 choice_list = self.get_choices_default()
739 if self.radio_admin and len(choice_list) == 1:
740 return {self.attname: choice_list[0][0]}
741 if not self.radio_admin and len(choice_list) == 2:
742 return {self.attname: choice_list[1][0]}
743 return Field.flatten_data(self, follow, obj)
744
745class ManyToManyField(Field):
746 def __init__(self, to, **kwargs):
747 kwargs['verbose_name'] = kwargs.get('verbose_name', to._meta.verbose_name_plural)
748 kwargs['rel'] = ManyToManyRel(to, kwargs.pop('singular', None),
749 num_in_admin=kwargs.pop('num_in_admin', 0),
750 related_name=kwargs.pop('related_name', None),
751 filter_interface=kwargs.pop('filter_interface', None),
752 limit_choices_to=kwargs.pop('limit_choices_to', None),
753 raw_id_admin=kwargs.pop('raw_id_admin', False))
754 if kwargs["rel"].raw_id_admin:
755 kwargs.setdefault("validator_list", []).append(self.isValidIDList)
756 Field.__init__(self, **kwargs)
757
758 def get_manipulator_field_objs(self):
759 if self.rel.raw_id_admin:
760 return [formfields.RawIdAdminField]
761 else:
762 choices = self.get_choices_default()
763 return [curry(formfields.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
764
765 def get_choices_default(self):
766 return Field.get_choices(self, include_blank=False)
767
768 def get_m2m_db_table(self, original_opts):
769 "Returns the name of the many-to-many 'join' table."
770 return '%s_%s' % (original_opts.db_table, self.name)
771
772 def isValidIDList(self, field_data, all_data):
773 "Validates that the value is a valid list of foreign keys"
774 mod = self.rel.to.get_model_module()
775 try:
776 pks = map(int, field_data.split(','))
777 except ValueError:
778 # the CommaSeparatedIntegerField validator will catch this error
779 return
780 objects = mod.get_in_bulk(pks)
781 if len(objects) != len(pks):
782 badkeys = [k for k in pks if k not in objects]
783 raise validators.ValidationError, ngettext("Please enter valid %(self)s IDs. The value %(value)r is invalid.",
784 "Please enter valid %(self)s IDs. The values %(value)r are invalid.", len(badkeys)) % {
785 'self': self.verbose_name,
786 'value': len(badkeys) == 1 and badkeys[0] or tuple(badkeys),
787 }
788
789 def flatten_data(self, follow, obj = None):
790 new_data = {}
791 if obj:
792 get_list_func = getattr(obj, 'get_%s_list' % self.rel.singular)
793 instance_ids = [getattr(instance, self.rel.to.pk.attname) for instance in get_list_func()]
794 if self.rel.raw_id_admin:
795 new_data[self.name] = ",".join([str(id) for id in instance_ids])
796 else:
797 new_data[self.name] = instance_ids
798 else:
799 # In required many-to-many fields with only one available choice,
800 # select that one available choice.
801 if not self.blank and not self.rel.edit_inline and not self.rel.raw_id_admin:
802 choices_list = self.get_choices_default()
803 if len(choices_list) == 1:
804 new_data[self.name] = [choices_list[0][0]]
805 return new_data
806
807class OneToOneField(IntegerField):
808 def __init__(self, to, to_field=None, **kwargs):
809 kwargs['verbose_name'] = kwargs.get('verbose_name', 'ID')
810 to_field = to_field or to._meta.pk.name
811
812 if kwargs.has_key('edit_inline_type'):
813 import warnings
814 warnings.warn("edit_inline_type is deprecated. Use edit_inline instead.")
815 kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
816
817 kwargs['rel'] = OneToOneRel(to, to_field,
818 num_in_admin=kwargs.pop('num_in_admin', 0),
819 edit_inline=kwargs.pop('edit_inline', False),
820 related_name=kwargs.pop('related_name', None),
821 limit_choices_to=kwargs.pop('limit_choices_to', None),
822 lookup_overrides=kwargs.pop('lookup_overrides', None),
823 raw_id_admin=kwargs.pop('raw_id_admin', False))
824 kwargs['primary_key'] = True
825 IntegerField.__init__(self, **kwargs)
826
827class ManyToOneRel:
828 def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
829 max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,
830 related_name=None, limit_choices_to=None, lookup_overrides=None, raw_id_admin=False):
831 try:
832 self.to = to._meta
833 except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
834 assert to == RECURSIVE_RELATIONSHIP_CONSTANT, "'to' must be either a model or the string '%s'" % RECURSIVE_RELATIONSHIP_CONSTANT
835 self.to = to
836 self.field_name = field_name
837 self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
838 self.min_num_in_admin, self.max_num_in_admin = min_num_in_admin, max_num_in_admin
839 self.num_extra_on_change, self.related_name = num_extra_on_change, related_name
840 self.limit_choices_to = limit_choices_to or {}
841 self.lookup_overrides = lookup_overrides or {}
842 self.raw_id_admin = raw_id_admin
843
844 def get_related_field(self):
845 "Returns the Field in the 'to' object to which this relationship is tied."
846 return self.to.get_field(self.field_name)
847
848class ManyToManyRel:
849 def __init__(self, to, singular=None, num_in_admin=0, related_name=None,
850 filter_interface=None, limit_choices_to=None, raw_id_admin=False):
851 self.to = to._meta
852 self.singular = singular or to._meta.object_name.lower()
853 self.num_in_admin = num_in_admin
854 self.related_name = related_name
855 self.filter_interface = filter_interface
856 self.limit_choices_to = limit_choices_to or {}
857 self.edit_inline = False
858 self.raw_id_admin = raw_id_admin
859 assert not (self.raw_id_admin and self.filter_interface), "ManyToManyRels may not use both raw_id_admin and filter_interface"
860
861class OneToOneRel(ManyToOneRel):
862 def __init__(self, to, field_name, num_in_admin=0, edit_inline=False,
863 related_name=None, limit_choices_to=None, lookup_overrides=None,
864 raw_id_admin=False):
865 self.to, self.field_name = to._meta, field_name
866 self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
867 self.related_name = related_name
868 self.limit_choices_to = limit_choices_to or {}
869 self.lookup_overrides = lookup_overrides or {}
870 self.raw_id_admin = raw_id_admin
871
872class BoundFieldLine(object):
873 def __init__(self, field_line, field_mapping, original, bound_field_class=BoundField):
874 self.bound_fields = [field.bind(field_mapping, original, bound_field_class) for field in field_line]
875
876 def __iter__(self):
877 for bound_field in self.bound_fields:
878 yield bound_field
879
880 def __len__(self):
881 return len(self.bound_fields)
882
883class FieldLine(object):
884 def __init__(self, field_locator_func, linespec):
885 if isinstance(linespec, basestring):
886 self.fields = [field_locator_func(linespec)]
887 else:
888 self.fields = [field_locator_func(field_name) for field_name in linespec]
889
890 def bind(self, field_mapping, original, bound_field_line_class=BoundFieldLine):
891 return bound_field_line_class(self, field_mapping, original)
892
893 def __iter__(self):
894 for field in self.fields:
895 yield field
896
897 def __len__(self):
898 return len(self.fields)
899
900class BoundFieldSet(object):
901 def __init__(self, field_set, field_mapping, original, bound_field_line_class=BoundFieldLine):
902 self.name = field_set.name
903 self.classes = field_set.classes
904 self.bound_field_lines = [field_line.bind(field_mapping,original, bound_field_line_class) for field_line in field_set]
905
906 def __iter__(self):
907 for bound_field_line in self.bound_field_lines:
908 yield bound_field_line
909
910 def __len__(self):
911 return len(self.bound_field_lines)
912
913class FieldSet(object):
914 def __init__(self, name, classes, field_locator_func, line_specs):
915 self.name = name
916 self.field_lines = [FieldLine(field_locator_func, line_spec) for line_spec in line_specs]
917 self.classes = classes
918
919 def __repr__(self):
920 return "FieldSet:(%s,%s)" % (self.name, self.field_lines)
921
922 def bind(self, field_mapping, original, bound_field_set_class=BoundFieldSet):
923 return bound_field_set_class(self, field_mapping, original)
924
925 def __iter__(self):
926 for field_line in self.field_lines:
927 yield field_line
928
929 def __len__(self):
930 return len(self.field_lines)
931
932class Admin:
933 def __init__(self, fields=None, js=None, list_display=None, list_filter=None, date_hierarchy=None,
934 save_as=False, ordering=None, search_fields=None, save_on_top=False, list_select_related=False):
935 self.fields = fields
936 self.js = js or []
937 self.list_display = list_display or ['__repr__']
938 self.list_filter = list_filter or []
939 self.date_hierarchy = date_hierarchy
940 self.save_as, self.ordering = save_as, ordering
941 self.search_fields = search_fields or []
942 self.save_on_top = save_on_top
943 self.list_select_related = list_select_related
944
945 def get_field_sets(self, opts):
946 if self.fields is None:
947 field_struct = ((None, {
948 'fields': [f.name for f in opts.fields + opts.many_to_many if f.editable and not isinstance(f, AutoField)]
949 }),)
950 else:
951 field_struct = self.fields
952 new_fieldset_list = []
953 for fieldset in field_struct:
954 name = fieldset[0]
955 fs_options = fieldset[1]
956 classes = fs_options.get('classes', ())
957 line_specs = fs_options['fields']
958 new_fieldset_list.append(FieldSet(name, classes, opts.get_field, line_specs))
959 return new_fieldset_list
Back to Top