Ticket #10109: patch_django_10109.20090123.diff

File patch_django_10109.20090123.diff, 14.3 KB (added by David Larlet, 13 years ago)

Initial patch, without any documentation nor tests, design discussion for now

  • django/db/models/sql/query.py

     
    1313from django.utils.datastructures import SortedDict
    1414from django.utils.encoding import force_unicode
    1515from django.db.backends.util import truncate_name
    16 from django.db import connection
     16from django.db import connection, transaction
    1717from django.db.models import signals
    1818from django.db.models.fields import FieldDoesNotExist
    1919from django.db.models.query_utils import select_related_descend
     
    19211921            select_col = join_info[RHS_JOIN_COL]
    19221922        self.select = [(select_alias, select_col)]
    19231923
     1924    def _add_items(self, source_col_name, target_col_name, join_table,
     1925                   pk_val, instance, field, *objs):
     1926        # join_table: name of the m2m link table
     1927        # source_col_name: the PK colname in join_table for the source object
     1928        # target_col_name: the PK colname in join_table for the target object
     1929        # *objs - objects to add. Either object instances, or primary keys of object instances.
     1930   
     1931        # If there aren't any objects, there is nothing to do.
     1932        if objs:
     1933            # Check that all the objects are of the right type
     1934            new_ids = set()
     1935            for obj in objs:
     1936                if isinstance(obj, self.model):
     1937                    new_ids.add(obj._get_pk_val())
     1938                else:
     1939                    new_ids.add(obj)
     1940            # Add the newly created or already existing objects to the join table.
     1941            # First find out which items are already added, to avoid adding them twice
     1942            cursor = connection.cursor()
     1943            cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \
     1944                (target_col_name, join_table, source_col_name,
     1945                target_col_name, ",".join(['%s'] * len(new_ids))),
     1946                [pk_val] + list(new_ids))
     1947            existing_ids = set([row[0] for row in cursor.fetchall()])
     1948   
     1949            # Add the ones that aren't there already
     1950            for obj_id in (new_ids - existing_ids):
     1951                cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
     1952                    (join_table, source_col_name, target_col_name),
     1953                    [pk_val, obj_id])
     1954            transaction.commit_unless_managed()
     1955
     1956    def _remove_items(self, source_col_name, target_col_name, join_table,
     1957                      pk_val, instance, field, *objs):
     1958        # source_col_name: the PK colname in join_table for the source object
     1959        # target_col_name: the PK colname in join_table for the target object
     1960        # *objs - objects to remove
     1961   
     1962        # If there aren't any objects, there is nothing to do.
     1963        if objs:
     1964            # Check that all the objects are of the right type
     1965            old_ids = set()
     1966            for obj in objs:
     1967                if isinstance(obj, self.model):
     1968                    old_ids.add(obj._get_pk_val())
     1969                else:
     1970                    old_ids.add(obj)
     1971            # Remove the specified objects from the join table
     1972            cursor = connection.cursor()
     1973            cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \
     1974                (join_table, source_col_name,
     1975                target_col_name, ",".join(['%s'] * len(old_ids))),
     1976                [pk_val] + list(old_ids))
     1977            transaction.commit_unless_managed()
     1978   
     1979    def _clear_items(self, source_col_name, join_table, pk_val, instance,
     1980                     field):
     1981        # source_col_name: the PK colname in join_table for the source object
     1982        cursor = connection.cursor()
     1983        cursor.execute("DELETE FROM %s WHERE %s = %%s" % \
     1984            (join_table, source_col_name),
     1985            [pk_val])
     1986        transaction.commit_unless_managed()
     1987
    19241988    def execute_sql(self, result_type=MULTI):
    19251989        """
    19261990        Run the query against the database and returns the result(s). The
  • django/db/models/fields/related.py

     
    1 from django.db import connection, transaction
     1from django.db import connection
    22from django.db.backends import util
    33from django.db.models import signals, get_model
    44from django.db.models.fields import AutoField, Field, IntegerField, PositiveIntegerField, PositiveSmallIntegerField, FieldDoesNotExist
     
    361361    and adds behavior for many-to-many related objects."""
    362362    class ManyRelatedManager(superclass):
    363363        def __init__(self, model=None, core_filters=None, instance=None, symmetrical=None,
    364                 join_table=None, source_col_name=None, target_col_name=None):
     364                join_table=None, source_col_name=None, target_col_name=None, field=None):
    365365            super(ManyRelatedManager, self).__init__()
    366366            self.core_filters = core_filters
    367367            self.model = model
     
    370370            self.join_table = join_table
    371371            self.source_col_name = source_col_name
    372372            self.target_col_name = target_col_name
     373            self.field = field
    373374            self.through = through
    374375            self._pk_val = self.instance._get_pk_val()
    375376            if self._pk_val is None:
     
    382383        # the add and remove methods do not exist.
    383384        if through is None:
    384385            def add(self, *objs):
    385                 self._add_items(self.source_col_name, self.target_col_name, *objs)
     386                self.get_query_set()._add_items(self.source_col_name,
     387                                                self.target_col_name,
     388                                                self.join_table,
     389                                                self._pk_val,
     390                                                self.instance,
     391                                                self.field,
     392                                                *objs)
    386393
    387394                # If this is a symmetrical m2m relation to self, add the mirror entry in the m2m table
    388395                if self.symmetrical:
    389                     self._add_items(self.target_col_name, self.source_col_name, *objs)
     396                    self.get_query_set()._add_items(self.target_col_name,
     397                                                    self.source_col_name,
     398                                                    self.join_table,
     399                                                    self._pk_val,
     400                                                    self.instance,
     401                                                    self.field,
     402                                                    *objs)
    390403            add.alters_data = True
    391404
    392405            def remove(self, *objs):
    393                 self._remove_items(self.source_col_name, self.target_col_name, *objs)
     406                self.get_query_set()._remove_items(self.source_col_name,
     407                                                   self.target_col_name,
     408                                                   self.join_table,
     409                                                   self._pk_val,
     410                                                   self.instance,
     411                                                   self.field,
     412                                                   *objs)
    394413
    395414                # If this is a symmetrical m2m relation to self, remove the mirror entry in the m2m table
    396415                if self.symmetrical:
    397                     self._remove_items(self.target_col_name, self.source_col_name, *objs)
     416                    self.get_query_set()._remove_items(self.target_col_name,
     417                                                       self.source_col_name,
     418                                                       self.join_table,
     419                                                       self._pk_val,
     420                                                       self.instance,
     421                                                       self.field,
     422                                                       *objs)
    398423            remove.alters_data = True
    399424
    400425        def clear(self):
    401             self._clear_items(self.source_col_name)
     426            self.get_query_set()._clear_items(self.source_col_name,
     427                                              self.join_table,
     428                                              self._pk_val,
     429                                              self.instance,
     430                                              self.field)
    402431
    403432            # If this is a symmetrical m2m relation to self, clear the mirror entry in the m2m table
    404433            if self.symmetrical:
    405                 self._clear_items(self.target_col_name)
     434                self.get_query_set()._clear_items(self.target_col_name,
     435                                                  self.join_table,
     436                                                  self._pk_val,
     437                                                  self.instance,
     438                                                  self.field)
    406439        clear.alters_data = True
    407440
    408441        def create(self, **kwargs):
     
    425458            return obj, created
    426459        get_or_create.alters_data = True
    427460
    428         def _add_items(self, source_col_name, target_col_name, *objs):
    429             # join_table: name of the m2m link table
    430             # source_col_name: the PK colname in join_table for the source object
    431             # target_col_name: the PK colname in join_table for the target object
    432             # *objs - objects to add. Either object instances, or primary keys of object instances.
    433 
    434             # If there aren't any objects, there is nothing to do.
    435             if objs:
    436                 # Check that all the objects are of the right type
    437                 new_ids = set()
    438                 for obj in objs:
    439                     if isinstance(obj, self.model):
    440                         new_ids.add(obj._get_pk_val())
    441                     else:
    442                         new_ids.add(obj)
    443                 # Add the newly created or already existing objects to the join table.
    444                 # First find out which items are already added, to avoid adding them twice
    445                 cursor = connection.cursor()
    446                 cursor.execute("SELECT %s FROM %s WHERE %s = %%s AND %s IN (%s)" % \
    447                     (target_col_name, self.join_table, source_col_name,
    448                     target_col_name, ",".join(['%s'] * len(new_ids))),
    449                     [self._pk_val] + list(new_ids))
    450                 existing_ids = set([row[0] for row in cursor.fetchall()])
    451 
    452                 # Add the ones that aren't there already
    453                 for obj_id in (new_ids - existing_ids):
    454                     cursor.execute("INSERT INTO %s (%s, %s) VALUES (%%s, %%s)" % \
    455                         (self.join_table, source_col_name, target_col_name),
    456                         [self._pk_val, obj_id])
    457                 transaction.commit_unless_managed()
    458 
    459         def _remove_items(self, source_col_name, target_col_name, *objs):
    460             # source_col_name: the PK colname in join_table for the source object
    461             # target_col_name: the PK colname in join_table for the target object
    462             # *objs - objects to remove
    463 
    464             # If there aren't any objects, there is nothing to do.
    465             if objs:
    466                 # Check that all the objects are of the right type
    467                 old_ids = set()
    468                 for obj in objs:
    469                     if isinstance(obj, self.model):
    470                         old_ids.add(obj._get_pk_val())
    471                     else:
    472                         old_ids.add(obj)
    473                 # Remove the specified objects from the join table
    474                 cursor = connection.cursor()
    475                 cursor.execute("DELETE FROM %s WHERE %s = %%s AND %s IN (%s)" % \
    476                     (self.join_table, source_col_name,
    477                     target_col_name, ",".join(['%s'] * len(old_ids))),
    478                     [self._pk_val] + list(old_ids))
    479                 transaction.commit_unless_managed()
    480 
    481         def _clear_items(self, source_col_name):
    482             # source_col_name: the PK colname in join_table for the source object
    483             cursor = connection.cursor()
    484             cursor.execute("DELETE FROM %s WHERE %s = %%s" % \
    485                 (self.join_table, source_col_name),
    486                 [self._pk_val])
    487             transaction.commit_unless_managed()
    488 
    489461    return ManyRelatedManager
    490462
    491463class ManyRelatedObjectsDescriptor(object):
     
    561533            symmetrical=(self.field.rel.symmetrical and instance.__class__ == rel_model),
    562534            join_table=qn(self.field.m2m_db_table()),
    563535            source_col_name=qn(self.field.m2m_column_name()),
    564             target_col_name=qn(self.field.m2m_reverse_name())
     536            target_col_name=qn(self.field.m2m_reverse_name()),
     537            field=self.field
    565538        )
    566539
    567540        return manager
  • django/db/models/query.py

     
    710710        obj = self.values("pk")
    711711        return obj.query.as_nested_sql()
    712712
     713    ################################################
     714    # PRIVATE METHODS THAT DEAL WITH M2M RELATIONS #
     715    ################################################
     716   
     717    def _add_items(self, source_col_name=None, target_col_name=None,
     718                   join_table=None, pk_val=None,
     719                   instance=None, field=None, *objs):
     720        self.query._add_items(source_col_name, target_col_name,
     721                              join_table, pk_val,instance, field, *objs)
     722
     723    def _remove_items(self, source_col_name=None, target_col_name=None,
     724                      join_table=None, pk_val=None,
     725                      instance=None, field=None, *objs):
     726        self.query._remove_items(source_col_name, target_col_name,
     727                                 join_table, pk_val, instance, field, *objs)
     728
     729    def _clear_items(self, source_col_name=None, join_table=None, pk_val=None,
     730                     instance=None, field=None):
     731        self.query._clear_items(source_col_name, join_table, pk_val, instance,
     732                                field)
     733
    713734    # When used as part of a nested query, a queryset will never be an "always
    714735    # empty" result.
    715736    value_annotation = True
Back to Top