Ticket #87: fields.py

File fields.py, 30.9 KB (added by Jason Huggins, 15 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