summaryrefslogtreecommitdiff
path: root/django/db/models/fields
diff options
context:
space:
mode:
authorRobin Munn <robin.munn@gmail.com>2007-01-31 23:43:09 +0000
committerRobin Munn <robin.munn@gmail.com>2007-01-31 23:43:09 +0000
commitfe361e678a46dc4c717c79c2f12b3ba32293b81a (patch)
tree8f42488e7d95244bab3db7b2bf934e006940521a /django/db/models/fields
parent122426e7453ed638a0c5be7e8b925adcddea3889 (diff)
Merged revisions 4186 to 4454 from trunk.
git-svn-id: http://code.djangoproject.com/svn/django/branches/sqlalchemy@4455 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django/db/models/fields')
-rw-r--r--django/db/models/fields/__init__.py128
-rw-r--r--django/db/models/fields/generic.py4
-rw-r--r--django/db/models/fields/related.py132
3 files changed, 184 insertions, 80 deletions
diff --git a/django/db/models/fields/__init__.py b/django/db/models/fields/__init__.py
index 8e8d68aad5..024fa95b8e 100644
--- a/django/db/models/fields/__init__.py
+++ b/django/db/models/fields/__init__.py
@@ -2,7 +2,8 @@ from django.db.models import signals
from django.dispatch import dispatcher
from django.conf import settings
from django.core import validators
-from django import forms
+from django import oldforms
+from django import newforms as forms
from django.core.exceptions import ObjectDoesNotExist
from django.utils.functional import curry
from django.utils.itercompat import tee
@@ -206,10 +207,10 @@ class Field(object):
if self.choices:
if self.radio_admin:
- field_objs = [forms.RadioSelectField]
+ field_objs = [oldforms.RadioSelectField]
params['ul_class'] = get_ul_class(self.radio_admin)
else:
- field_objs = [forms.SelectField]
+ field_objs = [oldforms.SelectField]
params['choices'] = self.get_choices_default()
else:
@@ -218,7 +219,7 @@ class Field(object):
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
"""
- Returns a list of forms.FormField instances for this field. It
+ Returns a list of oldforms.FormField instances for this field. It
calculates the choices at runtime, not at compile time.
name_prefix is a prefix to prepend to the "field_name" argument.
@@ -333,6 +334,16 @@ class Field(object):
return self._choices
choices = property(_get_choices)
+ def formfield(self, **kwargs):
+ "Returns a django.newforms.Field instance for this database Field."
+ defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
+ defaults.update(kwargs)
+ return forms.CharField(**defaults)
+
+ def value_from_object(self, obj):
+ "Returns the value of this field in the given model instance."
+ return getattr(obj, self.attname)
+
class AutoField(Field):
empty_strings_allowed = False
def __init__(self, *args, **kwargs):
@@ -354,7 +365,7 @@ class AutoField(Field):
return Field.get_manipulator_fields(self, opts, manipulator, change, name_prefix, rel, follow)
def get_manipulator_field_objs(self):
- return [forms.HiddenField]
+ return [oldforms.HiddenField]
def get_manipulator_new_data(self, new_data, rel=False):
# Never going to be called
@@ -369,6 +380,9 @@ class AutoField(Field):
super(AutoField, self).contribute_to_class(cls, name)
cls._meta.has_auto_field = True
+ def formfield(self, **kwargs):
+ return None
+
class BooleanField(Field):
def __init__(self, *args, **kwargs):
kwargs['blank'] = True
@@ -381,11 +395,16 @@ class BooleanField(Field):
raise validators.ValidationError, gettext("This value must be either True or False.")
def get_manipulator_field_objs(self):
- return [forms.CheckboxField]
+ return [oldforms.CheckboxField]
+
+ def formfield(self, **kwargs):
+ defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
+ defaults.update(kwargs)
+ return forms.BooleanField(**defaults)
class CharField(Field):
def get_manipulator_field_objs(self):
- return [forms.TextField]
+ return [oldforms.TextField]
def to_python(self, value):
if isinstance(value, basestring):
@@ -397,10 +416,15 @@ class CharField(Field):
raise validators.ValidationError, gettext_lazy("This field cannot be null.")
return str(value)
+ def formfield(self, **kwargs):
+ defaults = {'max_length': self.maxlength, 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
+ defaults.update(kwargs)
+ return forms.CharField(**defaults)
+
# TODO: Maybe move this into contrib, because it's specialized.
class CommaSeparatedIntegerField(CharField):
def get_manipulator_field_objs(self):
- return [forms.CommaSeparatedIntegerField]
+ return [oldforms.CommaSeparatedIntegerField]
class DateField(Field):
empty_strings_allowed = False
@@ -462,12 +486,17 @@ class DateField(Field):
return Field.get_db_prep_save(self, value)
def get_manipulator_field_objs(self):
- return [forms.DateField]
+ return [oldforms.DateField]
- def flatten_data(self, follow, obj = None):
+ def flatten_data(self, follow, obj=None):
val = self._get_val_from_obj(obj)
return {self.attname: (val is not None and val.strftime("%Y-%m-%d") or '')}
+ def formfield(self, **kwargs):
+ defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
+ defaults.update(kwargs)
+ return forms.DateField(**defaults)
+
class DateTimeField(DateField):
def to_python(self, value):
if isinstance(value, datetime.datetime):
@@ -503,7 +532,7 @@ class DateTimeField(DateField):
return Field.get_db_prep_lookup(self, lookup_type, value)
def get_manipulator_field_objs(self):
- return [forms.DateField, forms.TimeField]
+ return [oldforms.DateField, oldforms.TimeField]
def get_manipulator_field_names(self, name_prefix):
return [name_prefix + self.name + '_date', name_prefix + self.name + '_time']
@@ -526,6 +555,11 @@ class DateTimeField(DateField):
return {date_field: (val is not None and val.strftime("%Y-%m-%d") or ''),
time_field: (val is not None and val.strftime("%H:%M:%S") or '')}
+ def formfield(self, **kwargs):
+ defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
+ defaults.update(kwargs)
+ return forms.DateTimeField(**defaults)
+
class EmailField(CharField):
def __init__(self, *args, **kwargs):
kwargs['maxlength'] = 75
@@ -535,11 +569,16 @@ class EmailField(CharField):
return "CharField"
def get_manipulator_field_objs(self):
- return [forms.EmailField]
+ return [oldforms.EmailField]
def validate(self, field_data, all_data):
validators.isValidEmail(field_data, all_data)
+ def formfield(self, **kwargs):
+ defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
+ defaults.update(kwargs)
+ return forms.EmailField(**defaults)
+
class FileField(Field):
def __init__(self, verbose_name=None, name=None, upload_to='', **kwargs):
self.upload_to = upload_to
@@ -599,7 +638,7 @@ class FileField(Field):
os.remove(file_name)
def get_manipulator_field_objs(self):
- return [forms.FileUploadField, forms.HiddenField]
+ return [oldforms.FileUploadField, oldforms.HiddenField]
def get_manipulator_field_names(self, name_prefix):
return [name_prefix + self.name + '_file', name_prefix + self.name]
@@ -627,7 +666,7 @@ class FilePathField(Field):
Field.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self):
- return [curry(forms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)]
+ return [curry(oldforms.FilePathField, path=self.path, match=self.match, recursive=self.recursive)]
class FloatField(Field):
empty_strings_allowed = False
@@ -636,7 +675,7 @@ class FloatField(Field):
Field.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self):
- return [curry(forms.FloatField, max_digits=self.max_digits, decimal_places=self.decimal_places)]
+ return [curry(oldforms.FloatField, max_digits=self.max_digits, decimal_places=self.decimal_places)]
class ImageField(FileField):
def __init__(self, verbose_name=None, name=None, width_field=None, height_field=None, **kwargs):
@@ -644,7 +683,7 @@ class ImageField(FileField):
FileField.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self):
- return [forms.ImageUploadField, forms.HiddenField]
+ return [oldforms.ImageUploadField, oldforms.HiddenField]
def contribute_to_class(self, cls, name):
super(ImageField, self).contribute_to_class(cls, name)
@@ -670,7 +709,12 @@ class ImageField(FileField):
class IntegerField(Field):
empty_strings_allowed = False
def get_manipulator_field_objs(self):
- return [forms.IntegerField]
+ return [oldforms.IntegerField]
+
+ def formfield(self, **kwargs):
+ defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
+ defaults.update(kwargs)
+ return forms.IntegerField(**defaults)
class IPAddressField(Field):
def __init__(self, *args, **kwargs):
@@ -678,7 +722,7 @@ class IPAddressField(Field):
Field.__init__(self, *args, **kwargs)
def get_manipulator_field_objs(self):
- return [forms.IPAddressField]
+ return [oldforms.IPAddressField]
def validate(self, field_data, all_data):
validators.isValidIPAddress4(field_data, None)
@@ -689,22 +733,22 @@ class NullBooleanField(Field):
Field.__init__(self, *args, **kwargs)
def get_manipulator_field_objs(self):
- return [forms.NullBooleanField]
+ return [oldforms.NullBooleanField]
class PhoneNumberField(IntegerField):
def get_manipulator_field_objs(self):
- return [forms.PhoneNumberField]
+ return [oldforms.PhoneNumberField]
def validate(self, field_data, all_data):
validators.isValidPhone(field_data, all_data)
class PositiveIntegerField(IntegerField):
def get_manipulator_field_objs(self):
- return [forms.PositiveIntegerField]
+ return [oldforms.PositiveIntegerField]
class PositiveSmallIntegerField(IntegerField):
def get_manipulator_field_objs(self):
- return [forms.PositiveSmallIntegerField]
+ return [oldforms.PositiveSmallIntegerField]
class SlugField(Field):
def __init__(self, *args, **kwargs):
@@ -716,15 +760,20 @@ class SlugField(Field):
Field.__init__(self, *args, **kwargs)
def get_manipulator_field_objs(self):
- return [forms.TextField]
+ return [oldforms.TextField]
class SmallIntegerField(IntegerField):
def get_manipulator_field_objs(self):
- return [forms.SmallIntegerField]
+ return [oldforms.SmallIntegerField]
class TextField(Field):
def get_manipulator_field_objs(self):
- return [forms.LargeTextField]
+ return [oldforms.LargeTextField]
+
+ def formfield(self, **kwargs):
+ defaults = {'required': not self.blank, 'widget': forms.Textarea, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
+ defaults.update(kwargs)
+ return forms.CharField(**defaults)
class TimeField(Field):
empty_strings_allowed = False
@@ -760,24 +809,39 @@ class TimeField(Field):
return Field.get_db_prep_save(self, value)
def get_manipulator_field_objs(self):
- return [forms.TimeField]
+ return [oldforms.TimeField]
def flatten_data(self,follow, obj = None):
val = self._get_val_from_obj(obj)
return {self.attname: (val is not None and val.strftime("%H:%M:%S") or '')}
-class URLField(Field):
+ def formfield(self, **kwargs):
+ defaults = {'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
+ defaults.update(kwargs)
+ return forms.TimeField(**defaults)
+
+class URLField(CharField):
def __init__(self, verbose_name=None, name=None, verify_exists=True, **kwargs):
+ kwargs['maxlength'] = kwargs.get('maxlength', 200)
if verify_exists:
kwargs.setdefault('validator_list', []).append(validators.isExistingURL)
- Field.__init__(self, verbose_name, name, **kwargs)
+ self.verify_exists = verify_exists
+ CharField.__init__(self, verbose_name, name, **kwargs)
def get_manipulator_field_objs(self):
- return [forms.URLField]
+ return [oldforms.URLField]
+
+ def get_internal_type(self):
+ return "CharField"
+
+ def formfield(self, **kwargs):
+ defaults = {'required': not self.blank, 'verify_exists': self.verify_exists, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
+ defaults.update(kwargs)
+ return forms.URLField(**defaults)
class USStateField(Field):
def get_manipulator_field_objs(self):
- return [forms.USStateField]
+ return [oldforms.USStateField]
class XMLField(TextField):
def __init__(self, verbose_name=None, name=None, schema_path=None, **kwargs):
@@ -788,7 +852,7 @@ class XMLField(TextField):
return "TextField"
def get_manipulator_field_objs(self):
- return [curry(forms.XMLLargeTextField, schema_path=self.schema_path)]
+ return [curry(oldforms.XMLLargeTextField, schema_path=self.schema_path)]
class OrderingField(IntegerField):
empty_strings_allowed=False
@@ -801,4 +865,4 @@ class OrderingField(IntegerField):
return "IntegerField"
def get_manipulator_fields(self, opts, manipulator, change, name_prefix='', rel=False, follow=True):
- return [forms.HiddenField(name_prefix + self.name)]
+ return [oldforms.HiddenField(name_prefix + self.name)]
diff --git a/django/db/models/fields/generic.py b/django/db/models/fields/generic.py
index 7d7651029c..1ad8346e42 100644
--- a/django/db/models/fields/generic.py
+++ b/django/db/models/fields/generic.py
@@ -2,7 +2,7 @@
Classes allowing "generic" relations through ContentType and object-id fields.
"""
-from django import forms
+from django import oldforms
from django.core.exceptions import ObjectDoesNotExist
from django.db import backend
from django.db.models import signals
@@ -98,7 +98,7 @@ class GenericRelation(RelatedField, Field):
def get_manipulator_field_objs(self):
choices = self.get_choices_default()
- return [curry(forms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
+ return [curry(oldforms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
def get_choices_default(self):
return Field.get_choices(self, include_blank=False)
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py
index bd9262d55a..b517747735 100644
--- a/django/db/models/fields/related.py
+++ b/django/db/models/fields/related.py
@@ -2,10 +2,12 @@ from django.db import backend, transaction
from django.db.models import signals, get_model
from django.db.models.fields import AutoField, Field, IntegerField, get_ul_class
from django.db.models.related import RelatedObject
+from django.utils.text import capfirst
from django.utils.translation import gettext_lazy, string_concat, ngettext
from django.utils.functional import curry
from django.core import validators
-from django import forms
+from django import oldforms
+from django import newforms as forms
from django.dispatch import dispatcher
# For Python 2.3
@@ -256,8 +258,7 @@ class ForeignRelatedObjectsDescriptor(object):
# Otherwise, just move the named objects into the set.
if self.related.field.null:
manager.clear()
- for obj in value:
- manager.add(obj)
+ manager.add(*value)
def create_many_related_manager(superclass):
"""Creates a manager that subclasses 'superclass' (which is a Manager)
@@ -315,28 +316,36 @@ def create_many_related_manager(superclass):
# join_table: name of the m2m link table
# source_col_name: the PK colname in join_table for the source object
# target_col_name: the PK colname in join_table for the target object
- # *objs - objects to add
+ # *objs - objects to add. Either object instances, or primary keys of object instances.
from django.db import connection
- # Add the newly created or already existing objects to the join table.
- # First find out which items are already added, to avoid adding them twice
- new_ids = set([obj._get_pk_val() for obj in objs])
- cursor = connection.cursor()
- cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \
- (target_col_name, self.join_table, source_col_name,
- target_col_name, ",".join(['%s'] * len(new_ids))),
- [self._pk_val] + list(new_ids))
- if cursor.rowcount is not None and cursor.rowcount != 0:
- existing_ids = set([row[0] for row in cursor.fetchmany(cursor.rowcount)])
- else:
- existing_ids = set()
+ # If there aren't any objects, there is nothing to do.
+ if objs:
+ # Check that all the objects are of the right type
+ new_ids = set()
+ for obj in objs:
+ if isinstance(obj, self.model):
+ new_ids.add(obj._get_pk_val())
+ else:
+ new_ids.add(obj)
+ # Add the newly created or already existing objects to the join table.
+ # First find out which items are already added, to avoid adding them twice
+ cursor = connection.cursor()
+ cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \
+ (target_col_name, self.join_table, source_col_name,
+ target_col_name, ",".join(['%s'] * len(new_ids))),
+ [self._pk_val] + list(new_ids))
+ if cursor.rowcount is not None and cursor.rowcount != 0:
+ existing_ids = set([row[0] for row in cursor.fetchmany(cursor.rowcount)])
+ else:
+ existing_ids = set()
- # Add the ones that aren't there already
- for obj_id in (new_ids - existing_ids):
- cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
- (self.join_table, source_col_name, target_col_name),
- [self._pk_val, obj_id])
- transaction.commit_unless_managed()
+ # Add the ones that aren't there already
+ for obj_id in (new_ids - existing_ids):
+ cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
+ (self.join_table, source_col_name, target_col_name),
+ [self._pk_val, obj_id])
+ transaction.commit_unless_managed()
def _remove_items(self, source_col_name, target_col_name, *objs):
# source_col_name: the PK colname in join_table for the source object
@@ -344,16 +353,22 @@ def create_many_related_manager(superclass):
# *objs - objects to remove
from django.db import connection
- for obj in objs:
- if not isinstance(obj, self.model):
- raise ValueError, "objects to remove() must be %s instances" % self.model._meta.object_name
- # Remove the specified objects from the join table
- cursor = connection.cursor()
- for obj in objs:
- cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s = %%s" % \
- (self.join_table, source_col_name, target_col_name),
- [self._pk_val, obj._get_pk_val()])
- transaction.commit_unless_managed()
+ # If there aren't any objects, there is nothing to do.
+ if objs:
+ # Check that all the objects are of the right type
+ old_ids = set()
+ for obj in objs:
+ if isinstance(obj, self.model):
+ old_ids.add(obj._get_pk_val())
+ else:
+ old_ids.add(obj)
+ # Remove the specified objects from the join table
+ cursor = connection.cursor()
+ cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \
+ (self.join_table, source_col_name,
+ target_col_name, ",".join(['%s'] * len(old_ids))),
+ [self._pk_val] + list(old_ids))
+ transaction.commit_unless_managed()
def _clear_items(self, source_col_name):
# source_col_name: the PK colname in join_table for the source object
@@ -405,8 +420,7 @@ class ManyRelatedObjectsDescriptor(object):
manager = self.__get__(instance)
manager.clear()
- for obj in value:
- manager.add(obj)
+ manager.add(*value)
class ReverseManyRelatedObjectsDescriptor(object):
# This class provides the functionality that makes the related-object
@@ -447,8 +461,7 @@ class ReverseManyRelatedObjectsDescriptor(object):
manager = self.__get__(instance)
manager.clear()
- for obj in value:
- manager.add(obj)
+ manager.add(*value)
class ForeignKey(RelatedField, Field):
empty_strings_allowed = False
@@ -493,13 +506,13 @@ class ForeignKey(RelatedField, Field):
params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
else:
if self.radio_admin:
- field_objs = [forms.RadioSelectField]
+ field_objs = [oldforms.RadioSelectField]
params['ul_class'] = get_ul_class(self.radio_admin)
else:
if self.null:
- field_objs = [forms.NullSelectField]
+ field_objs = [oldforms.NullSelectField]
else:
- field_objs = [forms.SelectField]
+ field_objs = [oldforms.SelectField]
params['choices'] = self.get_choices_default()
return field_objs, params
@@ -508,7 +521,7 @@ class ForeignKey(RelatedField, Field):
if self.rel.raw_id_admin and not isinstance(rel_field, AutoField):
return rel_field.get_manipulator_field_objs()
else:
- return [forms.IntegerField]
+ return [oldforms.IntegerField]
def get_db_prep_save(self, value):
if value == '' or value == None:
@@ -539,6 +552,11 @@ class ForeignKey(RelatedField, Field):
def contribute_to_related_class(self, cls, related):
setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related))
+ def formfield(self, **kwargs):
+ defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
+ defaults.update(kwargs)
+ return forms.ChoiceField(**defaults)
+
class OneToOneField(RelatedField, IntegerField):
def __init__(self, to, to_field=None, **kwargs):
try:
@@ -581,13 +599,13 @@ class OneToOneField(RelatedField, IntegerField):
params['validator_list'].append(curry(manipulator_valid_rel_key, self, manipulator))
else:
if self.radio_admin:
- field_objs = [forms.RadioSelectField]
+ field_objs = [oldforms.RadioSelectField]
params['ul_class'] = get_ul_class(self.radio_admin)
else:
if self.null:
- field_objs = [forms.NullSelectField]
+ field_objs = [oldforms.NullSelectField]
else:
- field_objs = [forms.SelectField]
+ field_objs = [oldforms.SelectField]
params['choices'] = self.get_choices_default()
return field_objs, params
@@ -600,6 +618,11 @@ class OneToOneField(RelatedField, IntegerField):
if not cls._meta.one_to_one_field:
cls._meta.one_to_one_field = self
+ def formfield(self, **kwargs):
+ defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
+ defaults.update(kwargs)
+ return forms.ChoiceField(**kwargs)
+
class ManyToManyField(RelatedField, Field):
def __init__(self, to, **kwargs):
kwargs['verbose_name'] = kwargs.get('verbose_name', None)
@@ -610,6 +633,7 @@ class ManyToManyField(RelatedField, Field):
limit_choices_to=kwargs.pop('limit_choices_to', None),
raw_id_admin=kwargs.pop('raw_id_admin', False),
symmetrical=kwargs.pop('symmetrical', True))
+ self.db_table = kwargs.pop('db_table', None)
if kwargs["rel"].raw_id_admin:
kwargs.setdefault("validator_list", []).append(self.isValidIDList)
Field.__init__(self, **kwargs)
@@ -622,17 +646,20 @@ class ManyToManyField(RelatedField, Field):
def get_manipulator_field_objs(self):
if self.rel.raw_id_admin:
- return [forms.RawIdAdminField]
+ return [oldforms.RawIdAdminField]
else:
choices = self.get_choices_default()
- return [curry(forms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
+ return [curry(oldforms.SelectMultipleField, size=min(max(len(choices), 5), 15), choices=choices)]
def get_choices_default(self):
return Field.get_choices(self, include_blank=False)
def _get_m2m_db_table(self, opts):
"Function that can be curried to provide the m2m table name for this relation"
- return '%s_%s' % (opts.db_table, self.name)
+ if self.db_table:
+ return self.db_table
+ else:
+ return '%s_%s' % (opts.db_table, self.name)
def _get_m2m_column_name(self, related):
"Function that can be curried to provide the source column name for the m2m table"
@@ -706,6 +733,19 @@ class ManyToManyField(RelatedField, Field):
def set_attributes_from_rel(self):
pass
+ def value_from_object(self, obj):
+ "Returns the value of this field in the given model instance."
+ return getattr(obj, self.attname).all()
+
+ def formfield(self, **kwargs):
+ # If initial is passed in, it's a list of related objects, but the
+ # MultipleChoiceField takes a list of IDs.
+ if kwargs.get('initial') is not None:
+ kwargs['initial'] = [i._get_pk_val() for i in kwargs['initial']]
+ defaults = {'choices': self.get_choices_default(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text}
+ defaults.update(kwargs)
+ return forms.MultipleChoiceField(**defaults)
+
class ManyToOneRel(object):
def __init__(self, to, field_name, num_in_admin=3, min_num_in_admin=None,
max_num_in_admin=None, num_extra_on_change=1, edit_inline=False,