Django

Code

Ticket #10109: patch_django_10109.20090123.diff

File patch_django_10109.20090123.diff, 14.3 kB (added by david, 1 year ago)

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

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

    old new  
    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

    old new  
    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

    old new  
    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