diff options
Diffstat (limited to 'django/db/models/fields/related.py')
-rw-r--r-- | django/db/models/fields/related.py | 136 |
1 files changed, 88 insertions, 48 deletions
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index aa232c07f3..fad9c164c1 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.translation import gettext_lazy, string_concat +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 = {'queryset': self.rel.to._default_manager.all(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text} + defaults.update(kwargs) + return forms.ModelChoiceField(**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 = {'queryset': self.rel.to._default_manager.all(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text} + defaults.update(kwargs) + return forms.ModelChoiceField(**defaults) + 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) @@ -618,21 +642,24 @@ class ManyToManyField(RelatedField, Field): msg = gettext_lazy('Separate multiple IDs with commas.') else: msg = gettext_lazy('Hold down "Control", or "Command" on a Mac, to select more than one.') - self.help_text = string_concat(self.help_text, msg) + self.help_text = string_concat(self.help_text, ' ', msg) 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 = {'queryset' : self.rel.to._default_manager.all(), 'required': not self.blank, 'label': capfirst(self.verbose_name), 'help_text': self.help_text} + defaults.update(kwargs) + return forms.ModelMultipleChoiceField(**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, |