magic-removal: Implemented adding many-to-many objects -- obj.sites.add() instead of obj.set_sites()

git-svn-id: http://code.djangoproject.com/svn/django/branches/magic-removal@2255 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Adrian Holovaty 2006-02-03 23:26:58 +00:00
parent c9814b838f
commit 15d99ff525
2 changed files with 27 additions and 44 deletions

View File

@ -357,43 +357,6 @@ class Model(object):
setattr(self, cachename, get_image_dimensions(filename)) setattr(self, cachename, get_image_dimensions(filename))
return getattr(self, cachename) return getattr(self, cachename)
def _set_many_to_many_objects(self, id_list, field_with_rel):
current_ids = [obj._get_pk_val() for obj in self._get_many_to_many_objects(field_with_rel)]
ids_to_add, ids_to_delete = dict([(i, 1) for i in id_list]), []
for current_id in current_ids:
if current_id in id_list:
del ids_to_add[current_id]
else:
ids_to_delete.append(current_id)
ids_to_add = ids_to_add.keys()
# Now ids_to_add is a list of IDs to add, and ids_to_delete is a list of IDs to delete.
if not ids_to_delete and not ids_to_add:
return False # No change
rel = field_with_rel.rel.to._meta
m2m_table = field_with_rel.get_m2m_db_table(self._meta)
cursor = connection.cursor()
this_id = self._get_pk_val()
if ids_to_delete:
sql = "DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \
(backend.quote_name(m2m_table),
backend.quote_name(self._meta.object_name.lower() + '_id'),
backend.quote_name(rel.object_name.lower() + '_id'), ','.join(map(str, ids_to_delete)))
cursor.execute(sql, [this_id])
if ids_to_add:
sql = "INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
(backend.quote_name(m2m_table),
backend.quote_name(self._meta.object_name.lower() + '_id'),
backend.quote_name(rel.object_name.lower() + '_id'))
cursor.executemany(sql, [(this_id, i) for i in ids_to_add])
connection.commit()
try:
delattr(self, '_%s_cache' % field_with_rel.name) # clear cache, if it exists
except AttributeError:
pass
return True
_set_many_to_many_objects.alters_data = True
# Handles setting many-to-many related objects. # Handles setting many-to-many related objects.
# Example: Album.set_songs() # Example: Album.set_songs()
def _set_related_many_to_many(self, rel_class, rel_field, id_list): def _set_related_many_to_many(self, rel_class, rel_field, id_list):

View File

@ -1,4 +1,4 @@
from django.db import backend from django.db import backend, connection
from django.db.models import signals from django.db.models import signals
from django.db.models.fields import AutoField, Field, IntegerField from django.db.models.fields import AutoField, Field, IntegerField
from django.db.models.related import RelatedObject from django.db.models.related import RelatedObject
@ -117,6 +117,7 @@ class ManyRelatedObjectsDescriptor(object):
def add(self, **kwargs): def add(self, **kwargs):
kwargs.update({rel_field.name: instance}) kwargs.update({rel_field.name: instance})
return superclass.add(self, **kwargs) return superclass.add(self, **kwargs)
add.alters_data = True
manager = RelatedManager() manager = RelatedManager()
@ -151,8 +152,11 @@ class ReverseManyRelatedObjectsDescriptor(object):
qn = backend.quote_name qn = backend.quote_name
this_opts = instance.__class__._meta this_opts = instance.__class__._meta
rel_model = self.rel_model
rel_opts = self.rel_model._meta rel_opts = self.rel_model._meta
join_table = backend.quote_name(self.field.get_m2m_db_table(this_opts)) join_table = backend.quote_name(self.field.get_m2m_db_table(this_opts))
this_col_name = this_opts.object_name.lower() + '_id'
rel_col_name = rel_opts.object_name.lower() + '_id'
# Dynamically create a class that subclasses the related # Dynamically create a class that subclasses the related
# model's default manager. # model's default manager.
@ -163,11 +167,31 @@ class ReverseManyRelatedObjectsDescriptor(object):
return superclass.get_query_set(self).extra( return superclass.get_query_set(self).extra(
tables=(join_table,), tables=(join_table,),
where=[ where=[
'%s.%s = %s.%s' % (qn(rel_opts.db_table), qn(rel_opts.pk.column), join_table, rel_opts.object_name.lower() + '_id'), '%s.%s = %s.%s' % (qn(rel_opts.db_table), qn(rel_opts.pk.column), join_table, rel_col_name),
'%s.%s = %%s' % (join_table, this_opts.object_name.lower() + '_id') '%s.%s = %%s' % (join_table, this_col_name)
], ],
params = [instance._get_pk_val()] params = [instance._get_pk_val()]
) )
def add(self, *objs, **kwargs):
from django.db import connection
# Create the related object.
if kwargs:
assert len(objs) == 0, "add() can't be passed both positional and keyword arguments"
objs = [superclass.add(self, **kwargs)]
else:
assert len(objs) > 0, "add() must be passed either positional or keyword arguments"
for obj in objs:
if not isinstance(obj, rel_model):
raise ValueError, "positional arguments to add() must be %s instances" % rel_opts.object_name
# Add the newly created object to the join table.
# TODO: Check to make sure the object isn't already in the join table.
cursor = connection.cursor()
for obj in objs:
cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
(join_table, this_col_name, rel_col_name),
[instance._get_pk_val(), obj._get_pk_val()])
connection.commit()
add.alters_data = True
manager = RelatedManager() manager = RelatedManager()
@ -384,10 +408,6 @@ class ManyToManyField(RelatedField, Field):
super(ManyToManyField, self).contribute_to_class(cls, name) super(ManyToManyField, self).contribute_to_class(cls, name)
setattr(cls, self.name, ReverseManyRelatedObjectsDescriptor(self)) setattr(cls, self.name, ReverseManyRelatedObjectsDescriptor(self))
# Add "set_thingie" methods for many-to-many related objects.
# EXAMPLES: Poll.set_sites(), Story.set_bylines()
setattr(cls, 'set_%s' % self.name, curry(cls._set_many_to_many_objects, field_with_rel=self))
def contribute_to_related_class(self, cls, related): def contribute_to_related_class(self, cls, related):
setattr(cls, related.get_accessor_name(), ManyRelatedObjectsDescriptor(related, 'm2m')) setattr(cls, related.get_accessor_name(), ManyRelatedObjectsDescriptor(related, 'm2m'))
self.rel.singular = self.rel.singular or self.rel.to._meta.object_name.lower() self.rel.singular = self.rel.singular or self.rel.to._meta.object_name.lower()