Ticket #87: fields.py

File fields.py, 30.9 KB (added by Jason Huggins, 19 years ago)
Line 
1from django.conf import settings
2from django.core import formfields, validators
3from django.core import db
4from django.core.exceptions import ObjectDoesNotExist
5from django.utils.functional import curry
6from django.utils.text import capfirst
7import datetime, os
8
9# Random entropy string used by "default" param.
10NOT_PROVIDED = 'oijpwojefiojpanv'
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 manipulator_valid_rel_key(f, self, field_data, all_data):
31 "Validates that the value is a valid foreign key"
32 mod = f.rel.to.get_model_module()
33 try:
34 mod.get_object(**{'id__iexact': field_data})
35 except ObjectDoesNotExist:
36 raise validators.ValidationError, "Please enter a valid %s." % f.verbose_name
37
38def manipulator_validator_unique(f, opts, self, field_data, all_data):
39 "Validates that the value is unique for this field."
40 try:
41 old_obj = opts.get_model_module().get_object(**{'%s__exact' % f.name: field_data})
42 except ObjectDoesNotExist:
43 return
44 if hasattr(self, 'original_object') and getattr(self.original_object, opts.pk.name) == getattr(old_obj, opts.pk.name):
45 return
46 raise validators.ValidationError, "%s with this %s already exists." % (capfirst(opts.verbose_name), f.verbose_name)
47
48class Field(object):
49
50 # Designates whether empty strings fundamentally are allowed at the
51 # database level.
52 empty_strings_allowed = True
53
54 def __init__(self, name, verbose_name=None, primary_key=False,
55 maxlength=None, unique=False, blank=False, null=False, db_index=None,
56 core=False, rel=None, default=NOT_PROVIDED, editable=True,
57 prepopulate_from=None, unique_for_date=None, unique_for_month=None,
58 unique_for_year=None, validator_list=None, choices=None, radio_admin=None,
59 help_text=''):
60 self.name = name
61 self.verbose_name = verbose_name or name.replace('_', ' ')
62 self.primary_key = primary_key
63 self.maxlength, self.unique = maxlength, unique
64 self.blank, self.null = blank, null
65 self.core, self.rel, self.default = core, rel, default
66 self.editable = editable
67 self.validator_list = validator_list or []
68 self.prepopulate_from = prepopulate_from
69 self.unique_for_date, self.unique_for_month = unique_for_date, unique_for_month
70 self.unique_for_year = unique_for_year
71 self.choices = choices or []
72 self.radio_admin = radio_admin
73 self.help_text = help_text
74 if rel and isinstance(rel, ManyToMany):
75 self.help_text += ' Hold down "Control", or "Command" on a Mac, to select more than one.'
76
77 # Set db_index to True if the field has a relationship and doesn't explicitly set db_index.
78 if db_index is None:
79 if isinstance(rel, OneToOne) or isinstance(rel, ManyToOne):
80 self.db_index = True
81 else:
82 self.db_index = False
83 else:
84 self.db_index = db_index
85
86 def pre_save(self, value, add):
87 "Returns field's value just before saving."
88 return value
89
90 def get_db_prep_save(self, value):
91 "Returns field's value prepared for saving into a database."
92
93 # Oracle treats empty strings ('') the same as NULLs.
94 # To get around this wart, we need to change it to something else...
95 if value == '':
96 string_replacement = getattr(db,'EMPTY_STR_EQUIV','')
97 value = string_replacement
98 return value
99
100 def get_db_prep_lookup(self, lookup_type, value):
101 "Returns field's value prepared for database lookup."
102 if lookup_type in ('exact', 'gt', 'gte', 'lt', 'lte', 'ne', 'month', 'day'):
103 return [value]
104 elif lookup_type in ('range', 'in'):
105 return value
106 elif lookup_type == 'year':
107 return ['%s-01-01' % value, '%s-12-31' % value]
108 elif lookup_type in ('contains', 'icontains'):
109 return ["%%%s%%" % prep_for_like_query(value)]
110 elif lookup_type == 'iexact':
111 return [prep_for_like_query(value)]
112 elif lookup_type in ('startswith', 'istartswith'):
113 return ["%s%%" % prep_for_like_query(value)]
114 elif lookup_type in ('endswith', 'iendswith'):
115 return ["%%%s" % prep_for_like_query(value)]
116 elif lookup_type == 'isnull':
117 return []
118 raise TypeError, "Field has invalid lookup: %s" % lookup_type
119
120 def has_default(self):
121 "Returns a boolean of whether this field has a default value."
122 return self.default != NOT_PROVIDED
123
124 def get_default(self):
125 "Returns the default value for this field."
126 if self.default != NOT_PROVIDED:
127 if hasattr(self.default, '__get_value__'):
128 return self.default.__get_value__()
129 return self.default
130 if self.null:
131 return None
132 return ""
133
134 def get_manipulator_field_names(self, name_prefix):
135 """
136 Returns a list of field names that this object adds to the manipulator.
137 """
138 return [name_prefix + self.name]
139
140 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False):
141 """
142 Returns a list of formfields.FormField instances for this field. It
143 calculates the choices at runtime, not at compile time.
144
145 name_prefix is a prefix to prepend to the "field_name" argument.
146 rel is a boolean specifying whether this field is in a related context.
147 """
148 params = {'validator_list': self.validator_list[:]}
149 if self.maxlength and not self.choices: # Don't give SelectFields a maxlength parameter.
150 params['maxlength'] = self.maxlength
151 if isinstance(self.rel, ManyToOne):
152 if self.rel.raw_id_admin:
153 field_objs = self.get_manipulator_field_objs()
154 params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
155 else:
156 if self.radio_admin:
157 field_objs = [formfields.RadioSelectField]
158 params['choices'] = self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE)
159 params['ul_class'] = get_ul_class(self.radio_admin)
160 else:
161 if self.null:
162 field_objs = [formfields.NullSelectField]
163 else:
164 field_objs = [formfields.SelectField]
165 params['choices'] = self.get_choices()
166 elif self.choices:
167 if self.radio_admin:
168 field_objs = [formfields.RadioSelectField]
169 params['choices'] = self.get_choices(include_blank=self.blank, blank_choice=BLANK_CHOICE_NONE)
170 params['ul_class'] = get_ul_class(self.radio_admin)
171 else:
172 field_objs = [formfields.SelectField]
173 params['choices'] = self.get_choices()
174 else:
175 field_objs = self.get_manipulator_field_objs()
176
177 # Add the "unique" validator(s).
178 for field_name_list in opts.unique_together:
179 if field_name_list[0] == self.name:
180 params['validator_list'].append(getattr(manipulator, 'isUnique%s' % '_'.join(field_name_list)))
181
182 # Add the "unique for..." validator(s).
183 if self.unique_for_date:
184 params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_date)))
185 if self.unique_for_month:
186 params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_month)))
187 if self.unique_for_year:
188 params['validator_list'].append(getattr(manipulator, 'isUnique%sFor%s' % (self.name, self.unique_for_year)))
189 if self.unique or (self.primary_key and not rel):
190 params['validator_list'].append(curry(manipulator_validator_unique, self, opts, manipulator))
191
192 # Only add is_required=True if the field cannot be blank. Primary keys
193 # are a special case, and fields in a related context should set this
194 # as False, because they'll be caught by a separate validator --
195 # RequiredIfOtherFieldGiven.
196 params['is_required'] = not self.blank and not self.primary_key and not rel
197
198 # If this field is in a related context, check whether any other fields
199 # in the related object have core=True. If so, add a validator --
200 # RequiredIfOtherFieldsGiven -- to this FormField.
201 if rel and not self.blank and not isinstance(self, AutoField) and not isinstance(self, FileField):
202 # First, get the core fields, if any.
203 core_field_names = []
204 for f in opts.fields:
205 if f.core and f != self:
206 core_field_names.extend(f.get_manipulator_field_names(name_prefix))
207 # Now, if there are any, add the validator to this FormField.
208 if core_field_names:
209 params['validator_list'].append(validators.RequiredIfOtherFieldsGiven(core_field_names, "This field is required."))
210
211 # BooleanFields (CheckboxFields) are a special case. They don't take
212 # is_required or validator_list.
213 if isinstance(self, BooleanField):
214 del params['validator_list'], params['is_required']
215
216 # Finally, add the field_names.
217 field_names = self.get_manipulator_field_names(name_prefix)
218 return [man(field_name=field_names[i], **params) for i, man in enumerate(field_objs)]
219
220 def get_manipulator_new_data(self, new_data, rel=False):
221 """
222 Given the full new_data dictionary (from the manipulator), returns this
223 field's data.
224 """
225 if rel:
226 return new_data.get(self.name, [self.get_default()])[0]
227 else:
228 val = new_data.get(self.name, self.get_default())
229 if not self.empty_strings_allowed and val == '' and self.null:
230 val = None
231 return val
232
233 def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH):
234 "Returns a list of tuples used as SelectField choices for this field."
235 first_choice = include_blank and blank_choice or []
236 if self.choices:
237 return first_choice + list(self.choices)
238 rel_obj = self.rel.to
239 return first_choice + [(getattr(x, rel_obj.pk.name), repr(x)) for x in rel_obj.get_model_module().get_list(**self.rel.limit_choices_to)]
240
241class AutoField(Field):
242 empty_strings_allowed = False
243 def __init__(self, *args, **kwargs):
244 assert kwargs.get('primary_key', False) is True, "%ss must have primary_key=True." % self.__class__.__name__
245 Field.__init__(self, *args, **kwargs)
246
247 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False):
248 if not rel:
249 return [] # Don't add a FormField unless it's in a related context.
250 return Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel)
251
252 def get_manipulator_field_objs(self):
253 return [formfields.HiddenField]
254
255 def get_manipulator_new_data(self, new_data, rel=False):
256 if not rel:
257 return None
258 return Field.get_manipulator_new_data(self, new_data, rel)
259
260class BooleanField(Field):
261 def __init__(self, *args, **kwargs):
262 kwargs['blank'] = True
263 Field.__init__(self, *args, **kwargs)
264
265 def get_db_prep_lookup(self, lookup_type, value):
266 if db.DATABASE_ENGINE == 'oracle':
267 if value == 'True':
268 value = 1
269 elif value == 'False':
270 value = 0
271 return Field.get_db_prep_lookup(self, lookup_type, value)
272
273 def get_manipulator_field_objs(self):
274 return [formfields.CheckboxField]
275
276class CharField(Field):
277 def get_manipulator_field_objs(self):
278 return [formfields.TextField]
279
280class CommaSeparatedIntegerField(CharField):
281 def get_manipulator_field_objs(self):
282 return [formfields.CommaSeparatedIntegerField]
283
284class DateField(Field):
285 empty_strings_allowed = False
286 def __init__(self, name, verbose_name=None, auto_now=False, auto_now_add=False, **kwargs):
287 self.auto_now, self.auto_now_add = auto_now, auto_now_add
288 if auto_now or auto_now_add:
289 kwargs['editable'] = False
290 Field.__init__(self, name, verbose_name, **kwargs)
291
292 def get_db_prep_lookup(self, lookup_type, value):
293 if lookup_type == 'range':
294 value = [str(v) for v in value]
295 else:
296 value = str(value)
297 return Field.get_db_prep_lookup(self, lookup_type, value)
298
299 def pre_save(self, value, add):
300 if self.auto_now or (self.auto_now_add and add):
301 return datetime.datetime.now()
302 return value
303
304 def get_db_prep_save(self, value):
305 # Casts dates into string format for entry into database.
306 if value is not None:
307 if db.DATABASE_ENGINE != 'oracle':
308 #Oracle does not need a string conversion
309 value = value.strftime('%Y-%m-%d')
310 return Field.get_db_prep_save(self, value)
311
312 def get_manipulator_field_objs(self):
313 return [formfields.DateField]
314
315class DateTimeField(DateField):
316 def get_db_prep_save(self, value):
317 # Casts dates into string format for entry into database.
318 if value is not None:
319 if db.DATABASE_ENGINE != 'oracle':
320 #Oracle does not need a string conversion
321 value = value.strftime('%Y-%m-%d %H:%M:%S')
322 return Field.get_db_prep_save(self, value)
323
324 def get_manipulator_field_objs(self):
325 return [formfields.DateField, formfields.TimeField]
326
327 def get_manipulator_field_names(self, name_prefix):
328 return [name_prefix + self.name + '_date', name_prefix + self.name + '_time']
329
330 def get_manipulator_new_data(self, new_data, rel=False):
331 date_field, time_field = self.get_manipulator_field_names('')
332 if rel:
333 d = new_data.get(date_field, [None])[0]
334 t = new_data.get(time_field, [None])[0]
335 else:
336 d = new_data.get(date_field, None)
337 t = new_data.get(time_field, None)
338 if d is not None and t is not None:
339 return datetime.datetime.combine(d, t)
340 return self.get_default()
341
342class EmailField(Field):
343 def get_manipulator_field_objs(self):
344 return [formfields.EmailField]
345
346class FileField(Field):
347 def __init__(self, name, verbose_name=None, upload_to='', **kwargs):
348 self.upload_to = upload_to
349 Field.__init__(self, name, verbose_name, **kwargs)
350
351 def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False):
352 field_list = Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel)
353
354 if not self.blank:
355 if rel:
356 # This validator makes sure FileFields work in a related context.
357 class RequiredFileField:
358 def __init__(self, other_field_names, other_file_field_name):
359 self.other_field_names = other_field_names
360 self.other_file_field_name = other_file_field_name
361 self.always_test = True
362 def __call__(self, field_data, all_data):
363 if not all_data.get(self.other_file_field_name, False):
364 c = validators.RequiredIfOtherFieldsGiven(self.other_field_names, "This field is required.")
365 c(field_data, all_data)
366 # First, get the core fields, if any.
367 core_field_names = []
368 for f in opts.fields:
369 if f.core and f != self:
370 core_field_names.extend(f.get_manipulator_field_names(name_prefix))
371 # Now, if there are any, add the validator to this FormField.
372 if core_field_names:
373 field_list[0].validator_list.append(RequiredFileField(core_field_names, field_list[1].field_name))
374 else:
375 v = validators.RequiredIfOtherFieldNotGiven(field_list[1].field_name, "This field is required.")
376 v.always_test = True
377 field_list[0].validator_list.append(v)
378 field_list[0].is_required = field_list[1].is_required = False
379
380 # If the raw path is passed in, validate it's under the MEDIA_ROOT.
381 def isWithinMediaRoot(field_data, all_data):
382 f = os.path.abspath(os.path.join(settings.MEDIA_ROOT, field_data))
383 if not f.startswith(os.path.normpath(settings.MEDIA_ROOT)):
384 raise validators.ValidationError, "Enter a valid filename."
385 field_list[1].validator_list.append(isWithinMediaRoot)
386 return field_list
387
388 def get_manipulator_field_objs(self):
389 return [formfields.FileUploadField, formfields.HiddenField]
390
391 def get_manipulator_field_names(self, name_prefix):
392 return [name_prefix + self.name + '_file', name_prefix + self.name]
393
394 def save_file(self, new_data, new_object, original_object, change, rel):
395 upload_field_name = self.get_manipulator_field_names('')[0]
396 if new_data.get(upload_field_name, False):
397 if rel:
398 getattr(new_object, 'save_%s_file' % self.name)(new_data[upload_field_name][0]["filename"], new_data[upload_field_name][0]["content"])
399 else:
400 getattr(new_object, 'save_%s_file' % self.name)(new_data[upload_field_name]["filename"], new_data[upload_field_name]["content"])
401
402 def get_directory_name(self):
403 return os.path.normpath(datetime.datetime.now().strftime(self.upload_to))
404
405 def get_filename(self, filename):
406 from django.utils.text import get_valid_filename
407 f = os.path.join(self.get_directory_name(), get_valid_filename(os.path.basename(filename)))
408 return os.path.normpath(f)
409
410class FloatField(Field):
411 empty_strings_allowed = False
412 def __init__(self, name, verbose_name=None, max_digits=None, decimal_places=None, **kwargs):
413 self.max_digits, self.decimal_places = max_digits, decimal_places
414 Field.__init__(self, name, verbose_name, **kwargs)
415
416 def get_manipulator_field_objs(self):
417 return [curry(formfields.FloatField, max_digits=self.max_digits, decimal_places=self.decimal_places)]
418
419class ImageField(FileField):
420 def __init__(self, name, verbose_name=None, width_field=None, height_field=None, **kwargs):
421 self.width_field, self.height_field = width_field, height_field
422 FileField.__init__(self, name, verbose_name, **kwargs)
423
424 def get_manipulator_field_objs(self):
425 return [formfields.ImageUploadField, formfields.HiddenField]
426
427 def save_file(self, new_data, new_object, original_object, change, rel):
428 FileField.save_file(self, new_data, new_object, original_object, change, rel)
429 # If the image has height and/or width field(s) and they haven't
430 # changed, set the width and/or height field(s) back to their original
431 # values.
432 if change and (self.width_field or self.height_field):
433 if self.width_field:
434 setattr(new_object, self.width_field, getattr(original_object, self.width_field))
435 if self.height_field:
436 setattr(new_object, self.height_field, getattr(original_object, self.height_field))
437 new_object.save()
438
439class IntegerField(Field):
440 empty_strings_allowed = False
441 def get_manipulator_field_objs(self):
442 return [formfields.IntegerField]
443
444class IPAddressField(Field):
445 def __init__(self, *args, **kwargs):
446 kwargs['maxlength'] = 15
447 Field.__init__(self, *args, **kwargs)
448
449 def get_manipulator_field_objs(self):
450 return [formfields.IPAddressField]
451
452class NullBooleanField(Field):
453 def __init__(self, *args, **kwargs):
454 kwargs['null'] = True
455 Field.__init__(self, *args, **kwargs)
456
457 def get_manipulator_field_objs(self):
458 return [formfields.NullBooleanField]
459
460class PhoneNumberField(IntegerField):
461 def get_manipulator_field_objs(self):
462 return [formfields.PhoneNumberField]
463
464class PositiveIntegerField(IntegerField):
465 def get_manipulator_field_objs(self):
466 return [formfields.PositiveIntegerField]
467
468class PositiveSmallIntegerField(IntegerField):
469 def get_manipulator_field_objs(self):
470 return [formfields.PositiveSmallIntegerField]
471
472class SlugField(Field):
473 def __init__(self, *args, **kwargs):
474 kwargs['maxlength'] = 50
475 kwargs.setdefault('validator_list', []).append(validators.isAlphaNumeric)
476 # Set db_index=True unless it's been set manually.
477 if not kwargs.has_key('db_index'):
478 kwargs['db_index'] = True
479 Field.__init__(self, *args, **kwargs)
480
481 def get_manipulator_field_objs(self):
482 return [formfields.TextField]
483
484class SmallIntegerField(IntegerField):
485 def get_manipulator_field_objs(self):
486 return [formfields.SmallIntegerField]
487
488class TextField(Field):
489 def get_manipulator_field_objs(self):
490 return [formfields.LargeTextField]
491
492class TimeField(Field):
493 empty_strings_allowed = False
494 def __init__(self, name, verbose_name=None, auto_now=False, auto_now_add=False, **kwargs):
495 self.auto_now, self.auto_now_add = auto_now, auto_now_add
496 if auto_now or auto_now_add:
497 kwargs['editable'] = False
498 Field.__init__(self, name, verbose_name, **kwargs)
499
500 def get_db_prep_lookup(self, lookup_type, value):
501 if lookup_type == 'range':
502 value = [str(v) for v in value]
503 else:
504 value = str(value)
505 return Field.get_db_prep_lookup(self, lookup_type, value)
506
507 def pre_save(self, value, add):
508 if self.auto_now or (self.auto_now_add and add):
509 return datetime.datetime.now().time()
510 return value
511
512 def get_db_prep_save(self, value):
513 # Casts dates into string format for entry into database.
514 if value is not None:
515 if db.DATABASE_ENGINE != 'oracle':
516 #Oracle does not need a string conversion
517 value = value.strftime('%H:%M:%S')
518 return Field.get_db_prep_save(self, value)
519
520 def get_manipulator_field_objs(self):
521 return [formfields.TimeField]
522
523class URLField(Field):
524 def __init__(self, name, verbose_name=None, verify_exists=True, **kwargs):
525 if verify_exists:
526 kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
527 Field.__init__(self, name, verbose_name, **kwargs)
528
529 def get_manipulator_field_objs(self):
530 return [formfields.URLField]
531
532class USStateField(Field):
533 def get_manipulator_field_objs(self):
534 return [formfields.USStateField]
535
536class XMLField(Field):
537 def __init__(self, name, verbose_name=None, schema_path=None, **kwargs):
538 self.schema_path = schema_path
539 Field.__init__(self, name, verbose_name, **kwargs)
540
541 def get_manipulator_field_objs(self):
542 return [curry(formfields.XMLLargeTextField, schema_path=self.schema_path)]
543
544class ForeignKey(Field):
545 empty_strings_allowed = False
546 def __init__(self, to, to_field=None, rel_name=None, **kwargs):
547 try:
548 to_name = to._meta.object_name.lower()
549 except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
550 assert to == 'self', "ForeignKey(%r) is invalid. First parameter to ForeignKey must be either a model or the string %r" % (to, RECURSIVE_RELATIONSHIP_CONSTANT)
551 kwargs['name'] = kwargs.get('name', '')
552 kwargs['verbose_name'] = kwargs.get('verbose_name', '')
553 else:
554 to_field = to_field or to._meta.pk.name
555 kwargs['name'] = kwargs.get('name', to_name + '_id')
556 kwargs['verbose_name'] = kwargs.get('verbose_name', to._meta.verbose_name)
557 rel_name = rel_name or to_name
558
559 if kwargs.has_key('edit_inline_type'):
560 import warnings
561 warnings.warn("edit_inline_type is deprecated. Use edit_inline instead.")
562 kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
563
564 kwargs['rel'] = ManyToOne(to, rel_name, to_field,
565 num_in_admin=kwargs.pop('num_in_admin', 3),
566 min_num_in_admin=kwargs.pop('min_num_in_admin', None),
567 max_num_in_admin=kwargs.pop('max_num_in_admin', None),
568 num_extra_on_change=kwargs.pop('num_extra_on_change', 1),
569 edit_inline=kwargs.pop('edit_inline', False),
570 related_name=kwargs.pop('related_name', None),
571 limit_choices_to=kwargs.pop('limit_choices_to', None),
572 lookup_overrides=kwargs.pop('lookup_overrides', None),
573 raw_id_admin=kwargs.pop('raw_id_admin', False))
574 Field.__init__(self, **kwargs)
575
576 def get_manipulator_field_objs(self):
577 return [formfields.IntegerField]
578
579class ManyToManyField(Field):
580 def __init__(self, to, rel_name=None, **kwargs):
581 kwargs['name'] = kwargs.get('name', to._meta.module_name)
582 kwargs['verbose_name'] = kwargs.get('verbose_name', to._meta.verbose_name_plural)
583 rel_name = rel_name or to._meta.object_name.lower()
584 kwargs['rel'] = ManyToMany(to, rel_name,
585 num_in_admin=kwargs.pop('num_in_admin', 0),
586 related_name=kwargs.pop('related_name', None),
587 filter_interface=kwargs.pop('filter_interface', None),
588 limit_choices_to=kwargs.pop('limit_choices_to', None))
589 Field.__init__(self, **kwargs)
590
591 def get_manipulator_field_objs(self):
592 choices = self.get_choices(include_blank=False)
593 return [curry(formfields.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
594
595 def get_m2m_db_table(self, original_opts):
596 "Returns the name of the many-to-many 'join' table."
597 return '%s_%s' % (original_opts.db_table, self.name)
598
599class OneToOneField(IntegerField):
600 def __init__(self, to, to_field=None, rel_name=None, **kwargs):
601 kwargs['name'] = kwargs.get('name', 'id')
602 kwargs['verbose_name'] = kwargs.get('verbose_name', 'ID')
603 to_field = to_field or to._meta.pk.name
604 rel_name = rel_name or to._meta.object_name.lower()
605
606 if kwargs.has_key('edit_inline_type'):
607 import warnings
608 warnings.warn("edit_inline_type is deprecated. Use edit_inline instead.")
609 kwargs['edit_inline'] = kwargs.pop('edit_inline_type')
610
611 kwargs['rel'] = OneToOne(to, rel_name, to_field,
612 num_in_admin=kwargs.pop('num_in_admin', 0),
613 edit_inline=kwargs.pop('edit_inline', False),
614 related_name=kwargs.pop('related_name', None),
615 limit_choices_to=kwargs.pop('limit_choices_to', None),
616 lookup_overrides=kwargs.pop('lookup_overrides', None),
617 raw_id_admin=kwargs.pop('raw_id_admin', False))
618 kwargs['primary_key'] = True
619 IntegerField.__init__(self, **kwargs)
620
621class ManyToOne:
622 def __init__(self, to, name, field_name, num_in_admin=3, min_num_in_admin=None,
623 max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,
624 related_name=None, limit_choices_to=None, lookup_overrides=None, raw_id_admin=False):
625 try:
626 self.to = to._meta
627 except AttributeError: # to._meta doesn't exist, so it must be RECURSIVE_RELATIONSHIP_CONSTANT
628 assert to == RECURSIVE_RELATIONSHIP_CONSTANT, "'to' must be either a model or the string '%s'" % RECURSIVE_RELATIONSHIP_CONSTANT
629 self.to = to
630 self.name, self.field_name = name, field_name
631 self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
632 self.min_num_in_admin, self.max_num_in_admin = min_num_in_admin, max_num_in_admin
633 self.num_extra_on_change, self.related_name = num_extra_on_change, related_name
634 self.limit_choices_to = limit_choices_to or {}
635 self.lookup_overrides = lookup_overrides or {}
636 self.raw_id_admin = raw_id_admin
637
638 def get_cache_name(self):
639 return '_%s_cache' % self.name
640
641 def get_related_field(self):
642 "Returns the Field in the 'to' object to which this relationship is tied."
643 return self.to.get_field(self.field_name)
644
645class ManyToMany:
646 def __init__(self, to, name, num_in_admin=0, related_name=None,
647 filter_interface=None, limit_choices_to=None):
648 self.to, self.name = to._meta, name
649 self.num_in_admin = num_in_admin
650 self.related_name = related_name
651 self.filter_interface = filter_interface
652 self.limit_choices_to = limit_choices_to or {}
653 self.edit_inline = False
654
655class OneToOne(ManyToOne):
656 def __init__(self, to, name, field_name, num_in_admin=0, edit_inline=False,
657 related_name=None, limit_choices_to=None, lookup_overrides=None,
658 raw_id_admin=False):
659 self.to, self.name, self.field_name = to._meta, name, field_name
660 self.num_in_admin, self.edit_inline = num_in_admin, edit_inline
661 self.related_name = related_name
662 self.limit_choices_to = limit_choices_to or {}
663 self.lookup_overrides = lookup_overrides or {}
664 self.raw_id_admin = raw_id_admin
665
666class Admin:
667 def __init__(self, fields=None, js=None, list_display=None, list_filter=None, date_hierarchy=None,
668 save_as=False, ordering=None, search_fields=None, save_on_top=False):
669 self.fields = fields
670 self.js = js or []
671 self.list_display = list_display or ['__repr__']
672 self.list_filter = list_filter or []
673 self.date_hierarchy = date_hierarchy
674 self.save_as, self.ordering = save_as, ordering
675 self.search_fields = search_fields or []
676 self.save_on_top = save_on_top
677
678 def get_field_objs(self, opts):
679 """
680 Returns self.fields, except with fields as Field objects instead of
681 field names. If self.fields is None, defaults to putting every
682 non-AutoField field with editable=True in a single fieldset.
683 """
684 if self.fields is None:
685 field_struct = ((None, {'fields': [f.name for f in opts.fields + opts.many_to_many if f.editable and not isinstance(f, AutoField)]}),)
686 else:
687 field_struct = self.fields
688 new_fieldset_list = []
689 for fieldset in field_struct:
690 new_fieldset = [fieldset[0], {}]
691 new_fieldset[1].update(fieldset[1])
692 admin_fields = []
693 for field_name_or_list in fieldset[1]['fields']:
694 if isinstance(field_name_or_list, basestring):
695 admin_fields.append([opts.get_field(field_name_or_list)])
696 else:
697 admin_fields.append([opts.get_field(field_name) for field_name in field_name_or_list])
698 new_fieldset[1]['fields'] = admin_fields
699 new_fieldset_list.append(new_fieldset)
700 return new_fieldset_list
Back to Top