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:
parent
c9814b838f
commit
15d99ff525
|
@ -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):
|
||||||
|
|
|
@ -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()
|
||||||
|
|
Loading…
Reference in New Issue