1 from django
.conf
import settings
2 from django
.core
import formfields
, validators
3 from django
.core
.exceptions
import ObjectDoesNotExist
4 from django
.utils
.functional
import curry
, lazy
5 from django
.utils
.text
import capfirst
6 from django
.utils
.translation
import gettext_lazy
, ngettext
9 # Random entropy string used by "default" param.
10 NOT_PROVIDED
= 'oijpwojefiojpanv'
12 # Values for filter_interface.
13 HORIZONTAL
, VERTICAL
= 1, 2
15 # The values to use for "blank" in SelectFields. Will be appended to the start of most "choices" lists.
16 BLANK_CHOICE_DASH
= [("", "---------")]
17 BLANK_CHOICE_NONE
= [("", "None")]
19 # Values for Relation.edit_inline.
20 TABULAR
, STACKED
= 1, 2
22 RECURSIVE_RELATIONSHIP_CONSTANT
= 'self'
24 # prepares a value for use in a LIKE query
25 prep_for_like_query
= lambda x
: str(x
).replace("%", "\%").replace("_", "\_")
27 # returns the <ul> class for a given radio_admin value
28 get_ul_class
= lambda x
: 'radiolist%s' % ((x
== HORIZONTAL
) and ' inline' or '')
30 def string_concat(*strings
):
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.
36 return ''.join([str(el
) for el
in strings
])
38 string_concat
= lazy(string_concat
, str)
40 def 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()
44 mod
.get_object(pk
=field_data
)
45 except ObjectDoesNotExist
:
46 raise validators
.ValidationError
, _("Please enter a valid %s.") % f
.verbose_name
48 def 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
, ManyToOne
):
51 lookup_type
= '%s__%s__exact' % (f
.name
, f
.rel
.get_related_field().name
)
53 lookup_type
= '%s__exact' % f
.name
55 old_obj
= opts
.get_model_module().get_object(**{lookup_type
: field_data
})
56 except ObjectDoesNotExist
:
58 if hasattr(self
, 'original_object') and getattr(self
.original_object
, opts
.pk
.attname
) == getattr(old_obj
, opts
.pk
.attname
):
60 raise validators
.ValidationError
, _("%(optname)s with this %(fieldname)s already exists.") % {'optname': capfirst(opts
.verbose_name
), 'fieldname': f
.verbose_name
}
62 class BoundField(object):
63 def __init__(self
, field
, field_mapping
, original
):
65 self
.original
= original
66 self
.form_fields
= self
.resolve_form_fields(field_mapping
)
68 def resolve_form_fields(self
, field_mapping
):
69 return [field_mapping
[name
] for name
in self
.field
.get_manipulator_field_names('')]
71 def as_field_list(self
):
74 def original_value(self
):
76 return self
.original
.__dict
__[self
.field
.column
]
79 return "BoundField:(%s, %s)" % (self
.field
.name
, self
.form_fields
)
81 # A guide to Field parameters:
83 # * name: The name of the field specifed 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
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.
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":
94 # getattr(obj, opts.pk.attname)
98 # Designates whether empty strings fundamentally are allowed at the
100 empty_strings_allowed
= True
102 # Tracks each time a Field instance is created. Used to retain order.
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):
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
.blank
, self
.null
= blank
, null
116 self
.core
, self
.rel
, self
.default
= core
, rel
, default
117 self
.editable
= editable
118 self
.validator_list
= validator_list
or []
119 self
.prepopulate_from
= prepopulate_from
120 self
.unique_for_date
, self
.unique_for_month
= unique_for_date
, unique_for_month
121 self
.unique_for_year
= unique_for_year
122 self
.choices
= choices
or []
123 self
.radio_admin
= radio_admin
124 self
.help_text
= help_text
125 self
.db_column
= db_column
126 if rel
and isinstance(rel
, ManyToMany
):
128 self
.help_text
= string_concat(self
.help_text
,
129 gettext_lazy(' Separate multiple IDs with commas.'))
131 self
.help_text
= string_concat(self
.help_text
,
132 gettext_lazy(' Hold down "Control", or "Command" on a Mac, to select more than one.'))
134 # Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
136 if isinstance(rel
, OneToOne
) or isinstance(rel
, ManyToOne
):
139 self
.db_index
= False
141 self
.db_index
= db_index
143 # Increase the creation counter, and save our local copy.
144 self
.creation_counter
= Field
.creation_counter
145 Field
.creation_counter
+= 1
147 self
.attname
, self
.column
= self
.get_attname_column()
149 def set_name(self
, name
):
151 self
.verbose_name
= self
.verbose_name
or name
.replace('_', ' ')
152 self
.attname
, self
.column
= self
.get_attname_column()
154 def get_attname_column(self
):
155 if isinstance(self
.rel
, ManyToOne
):
156 attname
= '%s_id' % self
.name
159 column
= self
.db_column
or attname
160 return attname
, column
162 def get_cache_name(self
):
163 return '_%s_cache' % self
.name
165 def get_internal_type(self
):
166 return self
.__class
__.__name
__
168 def pre_save(self
, value
, add
):
169 "Returns field's value just before saving."
172 def get_db_prep_save(self
, value
):
173 "Returns field's value prepared for saving into a database."
176 def get_db_prep_lookup(self
, lookup_type
, value
):
177 "Returns field's value prepared for database lookup."
178 if lookup_type
in ('exact', 'gt', 'gte', 'lt', 'lte', 'ne', 'month', 'day'):
180 elif lookup_type
in ('range', 'in'):
182 elif lookup_type
== 'year':
183 return ['%s-01-01' % value
, '%s-12-31' % value
]
184 elif lookup_type
in ('contains', 'icontains'):
185 return ["%%%s%%" % prep_for_like_query(value
)]
186 elif lookup_type
== 'iexact':
187 return [prep_for_like_query(value
)]
188 elif lookup_type
in ('startswith', 'istartswith'):
189 return ["%s%%" % prep_for_like_query(value
)]
190 elif lookup_type
in ('endswith', 'iendswith'):
191 return ["%%%s" % prep_for_like_query(value
)]
192 elif lookup_type
== 'isnull':
194 raise TypeError, "Field has invalid lookup: %s" % lookup_type
196 def has_default(self
):
197 "Returns a boolean of whether this field has a default value."
198 return self
.default
!= NOT_PROVIDED
200 def get_default(self
):
201 "Returns the default value for this field."
202 if self
.default
!= NOT_PROVIDED
:
203 if hasattr(self
.default
, '__get_value__'):
204 return self
.default
.__get
_value
__()
206 if not self
.empty_strings_allowed
or self
.null
:
210 def get_manipulator_field_names(self
, name_prefix
):
212 Returns a list of field names that this object adds to the manipulator.
214 return [name_prefix
+ self
.name
]
216 def get_manipulator_fields(self
, opts
, manipulator
, change
, name_prefix
='', rel
=False):
218 Returns a list of formfields.FormField instances for this field. It
219 calculates the choices at runtime, not at compile time.
221 name_prefix is a prefix to prepend to the "field_name" argument.
222 rel is a boolean specifying whether this field is in a related context.
224 params
= {'validator_list': self
.validator_list
[:]}
225 if self
.maxlength
and not self
.choices
: # Don't give SelectFields a maxlength parameter.
226 params
['maxlength'] = self
.maxlength
227 if isinstance(self
.rel
, ManyToOne
):
228 params
['member_name'] = name_prefix
+ self
.attname
229 if self
.rel
.raw_id_admin
:
230 field_objs
= self
.get_manipulator_field_objs()
231 params
['validator_list'].append(curry(manipulator_valid_rel_key
, self
, manipulator
))
234 field_objs
= [formfields
.RadioSelectField
]
235 params
['ul_class'] = get_ul_class(self
.radio_admin
)
238 field_objs
= [formfields
.NullSelectField
]
240 field_objs
= [formfields
.SelectField
]
241 params
['choices'] = self
.get_choices_default()
244 field_objs
= [formfields
.RadioSelectField
]
245 params
['ul_class'] = get_ul_class(self
.radio_admin
)
247 field_objs
= [formfields
.SelectField
]
249 params
['choices'] = self
.get_choices_default()
251 field_objs
= self
.get_manipulator_field_objs()
253 # Add the "unique" validator(s).
254 for field_name_list
in opts
.unique_together
:
255 if field_name_list
[0] == self
.name
:
256 params
['validator_list'].append(getattr(manipulator
, 'isUnique%s' % '_'.join(field_name_list
)))
258 # Add the "unique for..." validator(s).
259 if self
.unique_for_date
:
260 params
['validator_list'].append(getattr(manipulator
, 'isUnique%sFor%s' % (self
.name
, self
.unique_for_date
)))
261 if self
.unique_for_month
:
262 params
['validator_list'].append(getattr(manipulator
, 'isUnique%sFor%s' % (self
.name
, self
.unique_for_month
)))
263 if self
.unique_for_year
:
264 params
['validator_list'].append(getattr(manipulator
, 'isUnique%sFor%s' % (self
.name
, self
.unique_for_year
)))
265 if self
.unique
or (self
.primary_key
and not rel
):
266 params
['validator_list'].append(curry(manipulator_validator_unique
, self
, opts
, manipulator
))
268 # Only add is_required=True if the field cannot be blank. Primary keys
269 # are a special case, and fields in a related context should set this
270 # as False, because they'll be caught by a separate validator --
271 # RequiredIfOtherFieldGiven.
272 params
['is_required'] = not self
.blank
and not self
.primary_key
and not rel
274 # If this field is in a related context, check whether any other fields
275 # in the related object have core=True. If so, add a validator --
276 # RequiredIfOtherFieldsGiven -- to this FormField.
277 if rel
and not self
.blank
and not isinstance(self
, AutoField
) and not isinstance(self
, FileField
):
278 # First, get the core fields, if any.
279 core_field_names
= []
280 for f
in opts
.fields
:
281 if f
.core
and f
!= self
:
282 core_field_names
.extend(f
.get_manipulator_field_names(name_prefix
))
283 # Now, if there are any, add the validator to this FormField.
285 params
['validator_list'].append(validators
.RequiredIfOtherFieldsGiven(core_field_names
, gettext_lazy("This field is required.")))
287 # BooleanFields (CheckboxFields) are a special case. They don't take
288 # is_required or validator_list.
289 if isinstance(self
, BooleanField
):
290 del params
['validator_list'], params
['is_required']
292 # Finally, add the field_names.
293 field_names
= self
.get_manipulator_field_names(name_prefix
)
294 return [man(field_name
=field_names
[i
], **params
) for i
, man
in enumerate(field_objs
)]
296 def get_manipulator_new_data(self
, new_data
, rel
=False):
298 Given the full new_data dictionary (from the manipulator), returns this
302 return new_data
.get(self
.name
, [self
.get_default()])[0]
304 val
= new_data
.get(self
.name
, self
.get_default())
305 if not self
.empty_strings_allowed
and val
== '' and self
.null
:
309 def get_choices(self
, include_blank
=True, blank_choice
=BLANK_CHOICE_DASH
):
310 "Returns a list of tuples used as SelectField choices for this field."
311 first_choice
= include_blank
and blank_choice
or []
313 return first_choice
+ list(self
.choices
)
314 rel_obj
= self
.rel
.to
315 return first_choice
+ [(getattr(x
, rel_obj
.pk
.attname
), str(x
))
316 for x
in rel_obj
.get_model_module().get_list(**self
.rel
.limit_choices_to
)]
318 def get_choices_default(self
):
319 if(self
.radio_admin
):
320 return self
.get_choices(include_blank
=self
.blank
, blank_choice
=BLANK_CHOICE_NONE
)
322 return self
.get_choices()
324 def _get_val_from_obj(self
, obj
):
326 return getattr(obj
, self
.attname
)
328 return self
.get_default()
330 def flatten_data(self
, follow
, obj
=None):
332 Returns a dictionary mapping the field's manipulator field names to its
333 "flattened" string values for the admin view. obj is the instance to
334 extract the values from.
336 return {self
.attname
: self
._get
_val
_from
_obj
(obj
)}
338 def get_follow(self
, override
=None):
344 def bind(self
, fieldmapping
, original
, bound_field_class
=BoundField
):
345 return bound_field_class(self
, fieldmapping
, original
)
347 class AutoField(Field
):
348 empty_strings_allowed
= False
349 def __init__(self
, *args
, **kwargs
):
350 assert kwargs
.get('primary_key', False) is True, "%ss must have primary_key=True." % self
.__class
__.__name
__
351 Field
.__init
__(self
, *args
, **kwargs
)
353 def get_manipulator_fields(self
, opts
, manipulator
, change
, name_prefix
='', rel
=False):
355 return [] # Don't add a FormField unless it's in a related context.
356 return Field
.get_manipulator_fields(self
, opts
, manipulator
, change
, name_prefix
, rel
)
358 def get_manipulator_field_objs(self
):
359 return [formfields
.HiddenField
]
361 def get_manipulator_new_data(self
, new_data
, rel
=False):
364 return Field
.get_manipulator_new_data(self
, new_data
, rel
)
366 class BooleanField(Field
):
367 def __init__(self
, *args
, **kwargs
):
368 kwargs
['blank'] = True
369 Field
.__init
__(self
, *args
, **kwargs
)
371 def get_manipulator_field_objs(self
):
372 return [formfields
.CheckboxField
]
374 class CharField(Field
):
375 def get_manipulator_field_objs(self
):
376 return [formfields
.TextField
]
378 class CommaSeparatedIntegerField(CharField
):
379 def get_manipulator_field_objs(self
):
380 return [formfields
.CommaSeparatedIntegerField
]
382 class DateField(Field
):
383 empty_strings_allowed
= False
384 def __init__(self
, verbose_name
=None, name
=None, auto_now
=False, auto_now_add
=False, **kwargs
):
385 self
.auto_now
, self
.auto_now_add
= auto_now
, auto_now_add
386 #HACKs : auto_now_add/auto_now should be done as a default or a pre_save...
387 if auto_now
or auto_now_add
:
388 kwargs
['editable'] = False
389 kwargs
['blank'] = True
390 Field
.__init
__(self
, verbose_name
, name
, **kwargs
)
392 def get_db_prep_lookup(self
, lookup_type
, value
):
393 if lookup_type
== 'range':
394 value
= [str(v
) for v
in value
]
397 return Field
.get_db_prep_lookup(self
, lookup_type
, value
)
399 def pre_save(self
, value
, add
):
400 if self
.auto_now
or (self
.auto_now_add
and add
):
401 return datetime
.datetime
.now()
404 # Needed because of horrible auto_now[_add] behaviour wrt. editable
405 def get_follow(self
, override
=None):
409 return self
.editable
or self
.auto_now
or self
.auto_now_add
411 def get_db_prep_save(self
, value
):
412 # Casts dates into string format for entry into database.
413 if value
is not None:
414 value
= value
.strftime('%Y-%m-%d')
415 return Field
.get_db_prep_save(self
, value
)
417 def get_manipulator_field_objs(self
):
418 return [formfields
.DateField
]
420 def flatten_data(self
, follow
, obj
= None):
421 val
= self
._get
_val
_from
_obj
(obj
)
422 return {self
.attname
: (val
is not None and val
.strftime("%Y-%m-%d") or '')}
424 class DateTimeField(DateField
):
425 def get_db_prep_save(self
, value
):
426 # Casts dates into string format for entry into database.
427 if value
is not None:
428 # MySQL will throw a warning if microseconds are given, because it
429 # doesn't support microseconds.
430 if settings
.DATABASE_ENGINE
== 'mysql':
431 value
= value
.replace(microsecond
=0)
433 return Field
.get_db_prep_save(self
, value
)
435 def get_manipulator_field_objs(self
):
436 return [formfields
.DateField
, formfields
.TimeField
]
438 def get_manipulator_field_names(self
, name_prefix
):
439 return [name_prefix
+ self
.name
+ '_date', name_prefix
+ self
.name
+ '_time']
441 def get_manipulator_new_data(self
, new_data
, rel
=False):
442 date_field
, time_field
= self
.get_manipulator_field_names('')
444 d
= new_data
.get(date_field
, [None])[0]
445 t
= new_data
.get(time_field
, [None])[0]
447 d
= new_data
.get(date_field
, None)
448 t
= new_data
.get(time_field
, None)
449 if d
is not None and t
is not None:
450 return datetime
.datetime
.combine(d
, t
)
451 return self
.get_default()
453 def flatten_data(self
,follow
, obj
= None):
454 val
= self
._get
_val
_from
_obj
(obj
)
455 date_field
, time_field
= self
.get_manipulator_field_names('')
456 return {date_field
: (val
is not None and val
.strftime("%Y-%m-%d") or ''),
457 time_field
: (val
is not None and val
.strftime("%H:%M:%S") or '')}
459 class EmailField(Field
):
460 def __init__(self
, *args
, **kwargs
):
461 kwargs
['maxlength'] = 75
462 Field
.__init
__(self
, *args
, **kwargs
)
464 def get_internal_type(self
):
467 def get_manipulator_field_objs(self
):
468 return [formfields
.EmailField
]
470 class FileField(Field
):
471 def __init__(self
, verbose_name
=None, name
=None, upload_to
='', **kwargs
):
472 self
.upload_to
= upload_to
473 Field
.__init
__(self
, verbose_name
, name
, **kwargs
)
475 def get_manipulator_fields(self
, opts
, manipulator
, change
, name_prefix
='', rel
=False):
476 field_list
= Field
.get_manipulator_fields(self
, opts
, manipulator
, change
, name_prefix
, rel
)
480 # This validator makes sure FileFields work in a related context.
481 class RequiredFileField
:
482 def __init__(self
, other_field_names
, other_file_field_name
):
483 self
.other_field_names
= other_field_names
484 self
.other_file_field_name
= other_file_field_name
485 self
.always_test
= True
486 def __call__(self
, field_data
, all_data
):
487 if not all_data
.get(self
.other_file_field_name
, False):
488 c
= validators
.RequiredIfOtherFieldsGiven(self
.other_field_names
, gettext_lazy("This field is required."))
489 c(field_data
, all_data
)
490 # First, get the core fields, if any.
491 core_field_names
= []
492 for f
in opts
.fields
:
493 if f
.core
and f
!= self
:
494 core_field_names
.extend(f
.get_manipulator_field_names(name_prefix
))
495 # Now, if there are any, add the validator to this FormField.
497 field_list
[0].validator_list
.append(RequiredFileField(core_field_names
, field_list
[1].field_name
))
499 v
= validators
.RequiredIfOtherFieldNotGiven(field_list
[1].field_name
, gettext_lazy("This field is required."))
501 field_list
[0].validator_list
.append(v
)
502 field_list
[0].is_required
= field_list
[1].is_required
= False
504 # If the raw path is passed in, validate it's under the MEDIA_ROOT.
505 def isWithinMediaRoot(field_data
, all_data
):
506 f
= os
.path
.abspath(os
.path
.join(settings
.MEDIA_ROOT
, field_data
))
507 if not f
.startswith(os
.path
.normpath(settings
.MEDIA_ROOT
)):
508 raise validators
.ValidationError
, _("Enter a valid filename.")
509 field_list
[1].validator_list
.append(isWithinMediaRoot
)
512 def get_manipulator_field_objs(self
):
513 return [formfields
.FileUploadField
, formfields
.HiddenField
]
515 def get_manipulator_field_names(self
, name_prefix
):
516 return [name_prefix
+ self
.name
+ '_file', name_prefix
+ self
.name
]
518 def save_file(self
, new_data
, new_object
, original_object
, change
, rel
):
519 upload_field_name
= self
.get_manipulator_field_names('')[0]
520 if new_data
.get(upload_field_name
, False):
521 func
= getattr(new_object
, 'save_%s_file' % self
.name
)
523 func(new_data
[upload_field_name
][0]["filename"], new_data
[upload_field_name
][0]["content"])
525 func(new_data
[upload_field_name
]["filename"], new_data
[upload_field_name
]["content"])
527 def get_directory_name(self
):
528 return os
.path
.normpath(datetime
.datetime
.now().strftime(self
.upload_to
))
530 def get_filename(self
, filename
):
531 from django
.utils
.text
import get_valid_filename
532 f
= os
.path
.join(self
.get_directory_name(), get_valid_filename(os
.path
.basename(filename
)))
533 return os
.path
.normpath(f
)
535 class FilePathField(Field
):
536 def __init__(self
, verbose_name
=None, name
=None, path
='', match
=None, recursive
=False, **kwargs
):
537 self
.path
, self
.match
, self
.recursive
= path
, match
, recursive
538 Field
.__init
__(self
, verbose_name
, name
, **kwargs
)
540 def get_manipulator_field_objs(self
):
541 return [curry(formfields
.FilePathField
, path
=self
.path
, match
=self
.match
, recursive
=self
.recursive
)]
543 class FloatField(Field
):
544 empty_strings_allowed
= False
545 def __init__(self
, verbose_name
=None, name
=None, max_digits
=None, decimal_places
=None, **kwargs
):
546 self
.max_digits
, self
.decimal_places
= max_digits
, decimal_places
547 Field
.__init
__(self
, verbose_name
, name
, **kwargs
)
549 def get_manipulator_field_objs(self
):
550 return [curry(formfields
.FloatField
, max_digits
=self
.max_digits
, decimal_places
=self
.decimal_places
)]
552 class ImageField(FileField
):
553 def __init__(self
, verbose_name
=None, name
=None, width_field
=None, height_field
=None, **kwargs
):
554 self
.width_field
, self
.height_field
= width_field
, height_field
555 FileField
.__init
__(self
, verbose_name
, name
, **kwargs
)
557 def get_manipulator_field_objs(self
):
558 return [formfields
.ImageUploadField
, formfields
.HiddenField
]
560 def save_file(self
, new_data
, new_object
, original_object
, change
, rel
):
561 FileField
.save_file(self
, new_data
, new_object
, original_object
, change
, rel
)
562 # If the image has height and/or width field(s) and they haven't
563 # changed, set the width and/or height field(s) back to their original
565 if change
and (self
.width_field
or self
.height_field
):
567 setattr(new_object
, self
.width_field
, getattr(original_object
, self
.width_field
))
568 if self
.height_field
:
569 setattr(new_object
, self
.height_field
, getattr(original_object
, self
.height_field
))
572 class IntegerField(Field
):
573 empty_strings_allowed
= False
574 def get_manipulator_field_objs(self
):
575 return [formfields
.IntegerField
]
577 class IPAddressField(Field
):
578 def __init__(self
, *args
, **kwargs
):
579 kwargs
['maxlength'] = 15
580 Field
.__init
__(self
, *args
, **kwargs
)
582 def get_manipulator_field_objs(self
):
583 return [formfields
.IPAddressField
]
585 class NullBooleanField(Field
):
586 def __init__(self
, *args
, **kwargs
):
587 kwargs
['null'] = True
588 Field
.__init
__(self
, *args
, **kwargs
)
590 def get_manipulator_field_objs(self
):
591 return [formfields
.NullBooleanField
]
593 class PhoneNumberField(IntegerField
):
594 def get_manipulator_field_objs(self
):
595 return [formfields
.PhoneNumberField
]
597 class PositiveIntegerField(IntegerField
):
598 def get_manipulator_field_objs(self
):
599 return [formfields
.PositiveIntegerField
]
601 class PositiveSmallIntegerField(IntegerField
):
602 def get_manipulator_field_objs(self
):
603 return [formfields
.PositiveSmallIntegerField
]
605 class SlugField(Field
):
606 def __init__(self
, *args
, **kwargs
):
607 kwargs
['maxlength'] = 50
608 kwargs
.setdefault('validator_list', []).append(validators
.isSlug
)
609 # Set db_index=True unless it's been set manually.
610 if not kwargs
.has_key('db_index'):
611 kwargs
['db_index'] = True
612 Field
.__init
__(self
, *args
, **kwargs
)
614 def get_manipulator_field_objs(self
):
615 return [formfields
.TextField
]
617 class SmallIntegerField(IntegerField
):
618 def get_manipulator_field_objs(self
):
619 return [formfields
.SmallIntegerField
]
621 class TextField(Field
):
622 def get_manipulator_field_objs(self
):
623 return [formfields
.LargeTextField
]
625 class TimeField(Field
):
626 empty_strings_allowed
= False
627 def __init__(self
, verbose_name
=None, name
=None, auto_now
=False, auto_now_add
=False, **kwargs
):
628 self
.auto_now
, self
.auto_now_add
= auto_now
, auto_now_add
629 if auto_now
or auto_now_add
:
630 kwargs
['editable'] = False
631 Field
.__init
__(self
, verbose_name
, name
, **kwargs
)
633 def get_db_prep_lookup(self
, lookup_type
, value
):
634 if lookup_type
== 'range':
635 value
= [str(v
) for v
in value
]
638 return Field
.get_db_prep_lookup(self
, lookup_type
, value
)
640 def pre_save(self
, value
, add
):
641 if self
.auto_now
or (self
.auto_now_add
and add
):
642 return datetime
.datetime
.now().time()
645 def get_db_prep_save(self
, value
):
646 # Casts dates into string format for entry into database.
647 if value
is not None:
648 # MySQL will throw a warning if microseconds are given, because it
649 # doesn't support microseconds.
650 if settings
.DATABASE_ENGINE
== 'mysql':
651 value
= value
.replace(microsecond
=0)
653 return Field
.get_db_prep_save(self
, value
)
655 def get_manipulator_field_objs(self
):
656 return [formfields
.TimeField
]
658 def flatten_data(self
,follow
, obj
= None):
659 val
= self
._get
_val
_from
_obj
(obj
)
660 return {self
.attname
: (val
is not None and val
.strftime("%H:%M:%S") or '')}
662 class URLField(Field
):
663 def __init__(self
, verbose_name
=None, name
=None, verify_exists
=True, **kwargs
):
665 kwargs
.setdefault('validator_list', []).append(validators
.isExistingURL
)
666 Field
.__init
__(self
, verbose_name
, name
, **kwargs
)
668 def get_manipulator_field_objs(self
):
669 return [formfields
.URLField
]
671 class USStateField(Field
):
672 def get_manipulator_field_objs(self
):
673 return [formfields
.USStateField
]
675 class XMLField(TextField
):
676 def __init__(self
, verbose_name
=None, name
=None, schema_path
=None, **kwargs
):
677 self
.schema_path
= schema_path
678 Field
.__init
__(self
, verbose_name
, name
, **kwargs
)
680 def get_internal_type(self
):
683 def get_manipulator_field_objs(self
):
684 return [curry(formfields
.XMLLargeTextField
, schema_path
=self
.schema_path
)]
686 class ForeignKey(Field
):
687 empty_strings_allowed
= False
688 def __init__(self
, to
, to_field
=None, **kwargs
):
690 to_name
= to
._meta
.object_name
.lower()
691 except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
692 assert to
== 'self', "ForeignKey(%r) is invalid. First parameter to ForeignKey must be either a model or the string %r" % (to
, RECURSIVE_RELATIONSHIP_CONSTANT
)
693 kwargs
['verbose_name'] = kwargs
.get('verbose_name', '')
695 to_field
= to_field
or to
._meta
.pk
.name
696 kwargs
['verbose_name'] = kwargs
.get('verbose_name', to
._meta
.verbose_name
)
698 if kwargs
.has_key('edit_inline_type'):
700 warnings
.warn("edit_inline_type is deprecated. Use edit_inline instead.")
701 kwargs
['edit_inline'] = kwargs
.pop('edit_inline_type')
703 kwargs
['rel'] = ManyToOne(to
, to_field
,
704 num_in_admin
=kwargs
.pop('num_in_admin', 3),
705 min_num_in_admin
=kwargs
.pop('min_num_in_admin', None),
706 max_num_in_admin
=kwargs
.pop('max_num_in_admin', None),
707 num_extra_on_change
=kwargs
.pop('num_extra_on_change', 1),
708 edit_inline
=kwargs
.pop('edit_inline', False),
709 related_name
=kwargs
.pop('related_name', None),
710 limit_choices_to
=kwargs
.pop('limit_choices_to', None),
711 lookup_overrides
=kwargs
.pop('lookup_overrides', None),
712 raw_id_admin
=kwargs
.pop('raw_id_admin', False))
713 Field
.__init
__(self
, **kwargs
)
715 def get_manipulator_field_objs(self
):
716 rel_field
= self
.rel
.get_related_field()
717 if self
.rel
.raw_id_admin
and not isinstance(rel_field
, AutoField
):
718 return rel_field
.get_manipulator_field_objs()
720 return [formfields
.IntegerField
]
722 def get_db_prep_save(self
,value
):
723 if value
== '' or value
== None:
726 return self
.rel
.get_related_field().get_db_prep_save(value
)
728 def flatten_data(self
, follow
, obj
=None):
730 # In required many-to-one fields with only one available choice,
731 # select that one available choice. Note: For SelectFields
732 # (radio_admin=False), we have to check that the length of choices
733 # is *2*, not 1, because SelectFields always have an initial
734 # "blank" value. Otherwise (radio_admin=True), we check that the
736 if not self
.blank
and (not self
.rel
.raw_id_admin
or self
.choices
):
737 choice_list
= self
.get_choices_default()
738 if self
.radio_admin
and len(choice_list
) == 1:
739 return {self
.attname
: choice_list
[0][0]}
740 if not self
.radio_admin
and len(choice_list
) == 2:
741 return {self
.attname
: choice_list
[1][0]}
742 return Field
.flatten_data(self
, follow
, obj
)
744 class ManyToManyField(Field
):
745 def __init__(self
, to
, **kwargs
):
746 kwargs
['verbose_name'] = kwargs
.get('verbose_name', to
._meta
.verbose_name_plural
)
747 kwargs
['rel'] = ManyToMany(to
, kwargs
.pop('singular', None),
748 num_in_admin
=kwargs
.pop('num_in_admin', 0),
749 related_name
=kwargs
.pop('related_name', None),
750 filter_interface
=kwargs
.pop('filter_interface', None),
751 limit_choices_to
=kwargs
.pop('limit_choices_to', None),
752 raw_id_admin
=kwargs
.pop('raw_id_admin', False))
753 if kwargs
["rel"].raw_id_admin
:
754 kwargs
.setdefault("validator_list", []).append(self
.isValidIDList
)
755 Field
.__init
__(self
, **kwargs
)
757 def get_manipulator_field_objs(self
):
758 if self
.rel
.raw_id_admin
:
759 return [formfields
.RawIdAdminField
]
761 choices
= self
.get_choices_default()
762 return [curry(formfields
.SelectMultipleField
, size
=min(max(len(choices
), 5), 15), choices
=choices
)]
764 def get_choices_default(self
):
765 return Field
.get_choices(self
, include_blank
=False)
767 def get_m2m_db_table(self
, original_opts
):
768 "Returns the name of the many-to-many 'join' table."
769 return '%s_%s' % (original_opts
.db_table
, self
.name
)
771 def isValidIDList(self
, field_data
, all_data
):
772 "Validates that the value is a valid list of foreign keys"
773 mod
= self
.rel
.to
.get_model_module()
775 pks
= map(int, field_data
.split(','))
777 # the CommaSeparatedIntegerField validator will catch this error
779 objects
= mod
.get_in_bulk(pks
)
780 if len(objects
) != len(pks
):
781 badkeys
= [k
for k
in pks
if k
not in objects
]
782 raise validators
.ValidationError
, ngettext("Please enter valid %(self)s IDs. The value %(value)r is invalid.",
783 "Please enter valid %(self)s IDs. The values %(value)r are invalid.", len(badkeys
)) % {
784 'self': self
.verbose_name
,
785 'value': len(badkeys
) == 1 and badkeys
[0] or tuple(badkeys
),
788 def flatten_data(self
, follow
, obj
= None):
791 get_list_func
= getattr(obj
, 'get_%s_list' % self
.rel
.singular
)
792 instance_ids
= [getattr(instance
, self
.rel
.to
.pk
.attname
) for instance
in get_list_func()]
793 if self
.rel
.raw_id_admin
:
794 new_data
[self
.name
] = ",".join([str(id) for id in instance_ids
])
796 new_data
[self
.name
] = instance_ids
798 # In required many-to-many fields with only one available choice,
799 # select that one available choice.
800 if not self
.blank
and not self
.rel
.edit_inline
and not self
.rel
.raw_id_admin
:
801 choices_list
= self
.get_choices_default()
802 if len(choices_list
) == 1:
803 print self
.name
, choices_list
[0][0]
804 new_data
[self
.name
] = [choices_list
[0][0]]
807 class 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
812 if kwargs
.has_key('edit_inline_type'):
814 warnings
.warn("edit_inline_type is deprecated. Use edit_inline instead.")
815 kwargs
['edit_inline'] = kwargs
.pop('edit_inline_type')
817 kwargs
['rel'] = OneToOne(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
)
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):
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
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
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
)
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):
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
), "ManyToMany relationships may not use both raw_id_admin and filter_interface"
861 class OneToOne(ManyToOne
):
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,
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
872 class 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
]
877 for bound_field
in self
.bound_fields
:
881 return len(self
.bound_fields
)
883 class FieldLine(object):
884 def __init__(self
, field_locator_func
, linespec
):
885 if isinstance(linespec
, basestring
):
886 self
.fields
= [field_locator_func(linespec
)]
888 self
.fields
= [field_locator_func(field_name
) for field_name
in linespec
]
890 def bind(self
, field_mapping
, original
, bound_field_line_class
=BoundFieldLine
):
891 return bound_field_line_class(self
, field_mapping
, original
)
894 for field
in self
.fields
:
898 return len(self
.fields
)
900 class 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
]
907 for bound_field_line
in self
.bound_field_lines
:
908 yield bound_field_line
911 return len(self
.bound_field_lines
)
913 class FieldSet(object):
914 def __init__(self
, name
, classes
, field_locator_func
, line_specs
):
916 self
.field_lines
= [FieldLine(field_locator_func
, line_spec
) for line_spec
in line_specs
]
917 self
.classes
= classes
920 return "FieldSet:(%s,%s)" % (self
.name
, self
.field_lines
)
922 def bind(self
, field_mapping
, original
, bound_field_set_class
=BoundFieldSet
):
923 return bound_field_set_class(self
, field_mapping
, original
)
926 for field_line
in self
.field_lines
:
930 return len(self
.field_lines
)
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):
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
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
)]
951 field_struct
= self
.fields
952 new_fieldset_list
= []
953 for fieldset
in field_struct
:
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