Django

Code

Changeset 2409

Show
Ignore:
Timestamp:
02/27/06 08:23:52 (3 years ago)
Author:
russellm
Message:

magic-removal: Modified behavior of add() for ForeignKey? descriptors to take a list of objects, rather than creating a new object. Added create() methods to ForeignKey? and m2m descriptors to allow for new instance creation.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/magic-removal/django/db/models/fields/related.py

    r2385 r2409  
    9898            other_field = self._field.rel.get_related_field() 
    9999            if other_field.rel: 
    100                 params = {'%s__%s__exact' % (self._field.rel.field_name, other_field.rel.field_name): val} 
     100                params = {'%s__pk' % self._field.rel.field_name: val} 
    101101            else: 
    102102                params = {'%s__exact' % self._field.rel.field_name: val} 
     
    105105            return rel_obj 
    106106 
     107class ForeignRelatedObjectsDescriptor(object): 
     108    # This class provides the functionality that makes the related-object 
     109    # managers available as attributes on a model class, for fields that have 
     110    # multiple "remote" values and have a ForeignKey pointed at them by 
     111    # some other model. In the example "poll.choice_set", the choice_set  
     112    # attribute is a ForeignRelatedObjectsDescriptor instance. 
     113    def __init__(self, related): 
     114        self.related = related   # RelatedObject instance 
     115 
     116    def __get__(self, instance, instance_type=None): 
     117        if instance is None: 
     118            raise AttributeError, "Manager must be accessed via instance" 
     119 
     120        rel_field = self.related.field 
     121        rel_model = self.related.model 
     122 
     123        # Dynamically create a class that subclasses the related 
     124        # model's default manager. 
     125        superclass = self.related.model._default_manager.__class__ 
     126 
     127        class RelatedManager(superclass): 
     128            def get_query_set(self): 
     129                return superclass.get_query_set(self).filter(**(self.core_filters)) 
     130 
     131            def add(self, *objs): 
     132                for obj in objs: 
     133                    val = getattr(instance, rel_field.rel.get_related_field().attname) 
     134                    setattr(obj, rel_field.attname, val) 
     135                    obj.save() 
     136            add.alters_data = True 
     137 
     138            def create(self, **kwargs): 
     139                new_obj = self.model(**kwargs) 
     140                self.add(new_obj) 
     141                return new_obj 
     142            create.alters_data = True 
     143 
     144        manager = RelatedManager() 
     145        manager.core_filters = {'%s__pk' % rel_field.name: getattr(instance, rel_field.rel.get_related_field().attname)} 
     146        manager.model = self.related.model 
     147 
     148        return manager 
     149 
    107150def _add_m2m_items(rel_manager_inst, managerclass, rel_model, join_table, source_col_name, 
    108         target_col_name, source_pk_val, *objs, **kwargs): 
     151        target_col_name, source_pk_val, *objs): 
    109152    # Utility function used by the ManyRelatedObjectsDescriptors 
    110153    # to do addition to a many-to-many field. 
     
    116159    # target_col_name: the PK colname in join_table for the target object 
    117160    # source_pk_val: the primary key for the source object 
    118     # *objs - objects to add, or **kwargs to create new objects 
     161    # *objs - objects to add 
    119162 
    120163    from django.db import connection 
    121164    rel_opts = rel_model._meta 
    122     # Create the related object. 
    123     if kwargs: 
    124         assert len(objs) == 0, "add() can't be passed both positional and keyword arguments" 
    125         objs = [managerclass.add(rel_manager_inst, **kwargs)] 
    126     else: 
    127         assert len(objs) > 0, "add() must be passed either positional or keyword arguments" 
    128         for obj in objs: 
    129             if not isinstance(obj, rel_model): 
    130                 raise ValueError, "positional arguments to add() must be %s instances" % rel_opts.object_name 
    131165 
    132166    # Add the newly created or already existing objects to the join table. 
     
    192226    # multiple "remote" values and have a ManyToManyField pointed at them by 
    193227    # some other model (rather than having a ManyToManyField themselves). 
    194     # In the example "poll.choice_set", the choice_set attribute is a 
     228    # In the example "publication.article_set", the article_set attribute is a 
    195229    # ManyRelatedObjectsDescriptor instance. 
    196     def __init__(self, related, rel_type): 
     230    def __init__(self, related): 
    197231        self.related = related   # RelatedObject instance 
    198         self.rel_type = rel_type # Either 'o2m' or 'm2m' 
    199232 
    200233    def __get__(self, instance, instance_type=None): 
     
    203236 
    204237        rel_field = self.related.field 
    205         rel_type = self.rel_type 
    206238        rel_model = self.related.model 
    207239 
    208         if rel_type == "m2m": 
    209             qn = backend.quote_name 
    210             this_opts = instance.__class__._meta 
    211             rel_opts = rel_model._meta 
    212             join_table = qn(self.related.field.m2m_db_table()) 
    213             source_col_name = qn(self.related.field.m2m_reverse_name()) 
    214             target_col_name = qn(self.related.field.m2m_column_name()) 
     240        qn = backend.quote_name 
     241        this_opts = instance.__class__._meta 
     242        rel_opts = rel_model._meta 
     243        join_table = qn(self.related.field.m2m_db_table()) 
     244        source_col_name = qn(self.related.field.m2m_reverse_name()) 
     245        target_col_name = qn(self.related.field.m2m_column_name()) 
    215246 
    216247        # Dynamically create a class that subclasses the related 
     
    222253                return superclass.get_query_set(self).filter(**(self.core_filters)) 
    223254 
    224             if rel_type == "o2m": 
    225                 def add(self, **kwargs): 
    226                     kwargs.update({rel_field.name: instance}) 
    227                     return superclass.add(self, **kwargs) 
    228             else: 
    229                 def add(self, *objs, **kwargs): 
    230                     _add_m2m_items(self, superclass, rel_model, join_table, source_col_name, 
    231                         target_col_name, instance._get_pk_val(), *objs, **kwargs) 
     255            def add(self, *objs): 
     256                _add_m2m_items(self, superclass, rel_model, join_table, source_col_name, 
     257                    target_col_name, instance._get_pk_val(), *objs) 
    232258            add.alters_data = True 
    233259 
    234             if rel_type == "o2m": 
    235                 def remove(self, *objs): 
    236                     pass # TODO 
    237             else: 
    238                 def remove(self, *objs): 
    239                     _remove_m2m_items(rel_model, join_table, source_col_name, 
    240                         target_col_name, instance._get_pk_val(), *objs) 
     260            def remove(self, *objs): 
     261                _remove_m2m_items(rel_model, join_table, source_col_name, 
     262                    target_col_name, instance._get_pk_val(), *objs) 
    241263            remove.alters_data = True 
    242264 
    243             if rel_type == "o2m": 
    244                 def clear(self): 
    245                     pass # TODO 
    246             else: 
    247                 def clear(self): 
    248                     _clear_m2m_items(join_table, source_col_name, instance._get_pk_val()) 
     265            def clear(self): 
     266                _clear_m2m_items(join_table, source_col_name, instance._get_pk_val()) 
    249267            clear.alters_data = True 
    250268 
     269            def create(self, **kwargs): 
     270                new_obj = self.model(**kwargs) 
     271                new_obj.save() 
     272                self.add(new_obj) 
     273                return new_obj 
     274            create.alters_data = True 
     275             
    251276        manager = RelatedManager() 
    252  
    253         if self.rel_type == 'o2m': 
    254             manager.core_filters = {'%s__pk' % rel_field.name: getattr(instance, rel_field.rel.get_related_field().attname)} 
    255         else: 
    256             manager.core_filters = {'%s__pk' % rel_field.name: instance._get_pk_val()} 
    257  
     277        manager.core_filters = {'%s__pk' % rel_field.name: instance._get_pk_val()} 
    258278        manager.model = self.related.model 
    259279 
     
    265285    # multiple "remote" values and have a ManyToManyField defined in their 
    266286    # model (rather than having another model pointed *at* them). 
    267     # In the example "poll.sites", the sites attribute is a 
     287    # In the example "article.publications", the publications attribute is a 
    268288    # ReverseManyRelatedObjectsDescriptor instance. 
    269289    def __init__(self, m2m_field): 
     
    291311                return superclass.get_query_set(self).filter(**(self.core_filters)) 
    292312 
    293             def add(self, *objs, **kwargs): 
     313            def add(self, *objs): 
    294314                _add_m2m_items(self, superclass, rel_model, join_table, source_col_name, 
    295                     target_col_name, instance._get_pk_val(), *objs, **kwargs
     315                    target_col_name, instance._get_pk_val(), *objs
    296316 
    297317                # If this is a symmetrical m2m relation to self, add the mirror entry in the m2m table 
    298318                if instance.__class__ == rel_model and symmetrical: 
    299319                    _add_m2m_items(self, superclass, rel_model, join_table, target_col_name, 
    300                         source_col_name, instance._get_pk_val(), *objs, **kwargs)                     
    301  
     320                        source_col_name, instance._get_pk_val(), *objs) 
    302321            add.alters_data = True 
    303322 
     
    305324                _remove_m2m_items(rel_model, join_table, source_col_name, 
    306325                    target_col_name, instance._get_pk_val(), *objs) 
    307                      
     326 
    308327                # If this is a symmetrical m2m relation to self, remove the mirror entry in the m2m table 
    309328                if instance.__class__ == rel_model and symmetrical: 
    310329                    _remove_m2m_items(rel_model, join_table, target_col_name, 
    311                         source_col_name, instance._get_pk_val(), *objs) 
    312                      
     330                        source_col_name, instance._get_pk_val(), *objs)   
    313331            remove.alters_data = True 
    314332 
     
    319337                if instance.__class__ == rel_model and symmetrical: 
    320338                    _clear_m2m_items(join_table, target_col_name, instance._get_pk_val()) 
    321                  
    322339            clear.alters_data = True 
    323340 
     341            def create(self, **kwargs): 
     342                new_obj = self.model(**kwargs) 
     343                new_obj.save() 
     344                self.add(new_obj) 
     345                return new_obj 
     346            create.alters_data = True 
     347 
    324348        manager = RelatedManager() 
    325          
    326349        manager.core_filters = {'%s__pk' % self.field.related_query_name() : instance._get_pk_val()} 
    327          
    328350        manager.model = rel_model 
    329351 
     
    417439 
    418440    def contribute_to_related_class(self, cls, related): 
    419         setattr(cls, related.get_accessor_name(), ManyRelatedObjectsDescriptor(related, 'o2m')) 
     441        setattr(cls, related.get_accessor_name(), ForeignRelatedObjectsDescriptor(related)) 
    420442 
    421443class OneToOneField(RelatedField, IntegerField): 
     
    558580        if related.model != related.parent_model or not self.rel.symmetrical: 
    559581            # Add the descriptor for the m2m relation 
    560             setattr(cls, related.get_accessor_name(), ManyRelatedObjectsDescriptor(related, 'm2m')) 
     582            setattr(cls, related.get_accessor_name(), ManyRelatedObjectsDescriptor(related)) 
    561583             
    562584        self.rel.singular = self.rel.singular or self.rel.to._meta.object_name.lower() 
  • django/branches/magic-removal/django/db/models/manager.py

    r2307 r2409  
    8888        return self.get_query_set().values(*args, **kwargs) 
    8989 
    90     ################# 
    91     # OTHER METHODS # 
    92     ################# 
    93  
    94     def add(self, **kwargs): 
    95         new_obj = self.model(**kwargs) 
    96         new_obj.save() 
    97         return new_obj 
    98     add.alters_data = True 
    99  
    10090class ManagerDescriptor(object): 
    10191    # This class ensures managers aren't accessible via model instances. 
  • django/branches/magic-removal/tests/modeltests/many_to_many/models.py

    r2366 r2409  
    5555 
    5656# Add a Publication directly via publications.add by using keyword arguments. 
    57 >>> a2.publications.add(title='Highlights for Children') 
     57>>> new_publication = a2.publications.create(title='Highlights for Children') 
    5858 
    5959# Article objects have access to their related Publication objects. 
     
    124124 
    125125# Adding via the other end using keywords 
    126 >>> p2.article_set.add(headline='Oxygen-free diet works wonders') 
     126>>> new_article = p2.article_set.create(headline='Oxygen-free diet works wonders') 
    127127>>> p2.article_set.all() 
    128128[NASA finds intelligent life on Earth, Oxygen-free diet works wonders] 
  • django/branches/magic-removal/tests/modeltests/many_to_one/models.py

    r2307 r2409  
    4949 
    5050# Create an Article via the Reporter object. 
    51 >>> new_article = r.article_set.add(headline="John's second story", pub_date=datetime(2005, 7, 29)) 
     51>>> new_article = r.article_set.create(headline="John's second story", pub_date=datetime(2005, 7, 29)) 
    5252>>> new_article 
    5353John's second story 
     
    55551 
    5656 
    57 >>> new_article2 = r2.article_set.add(headline="Paul's story", pub_date=datetime(2006, 1, 17)) 
     57# Create a new article, and add it to the article set. 
     58>>> new_article2 = Article(headline="Paul's story", pub_date=datetime(2006, 1, 17)) 
     59>>> r.article_set.add(new_article2) 
     60>>> new_article2.reporter.id 
     61
     62>>> r.article_set.all() 
     63[This is a test, John's second story, Paul's story] 
     64 
     65# Add the same article to a different article set - check that it moves. 
     66>>> r2.article_set.add(new_article2) 
    5867>>> new_article2.reporter.id 
    59682 
     69>>> r.article_set.all() 
     70[This is a test, John's second story] 
     71>>> r2.article_set.all() 
     72[Paul's story] 
    6073 
    6174# Reporter objects have access to their related Article objects. 
  • django/branches/magic-removal/tests/modeltests/many_to_one_null/models.py

    r2259 r2409  
    4040 
    4141# Create an Article via the Reporter object. 
    42 >>> a2 = r.article_set.add(headline="Second") 
     42>>> a2 = r.article_set.create(headline="Second") 
    4343>>> a2 
    4444Second 
  • django/branches/magic-removal/tests/modeltests/mutually_referential/models.py

    r2157 r2409  
    2121 
    2222# Create some children 
    23 >>> c = q.child_set.add(name='Charles') 
    24 >>> e = q.child_set.add(name='Edward') 
     23>>> c = q.child_set.create(name='Charles') 
     24>>> e = q.child_set.create(name='Edward') 
    2525 
    2626# Set the best child 
  • django/branches/magic-removal/tests/modeltests/one_to_one/models.py

    r2241 r2409  
    8686 
    8787# Add a Waiter to the Restaurant. 
    88 >>> w = r.waiter_set.add(name='Joe') 
     88>>> w = r.waiter_set.create(name='Joe') 
    8989>>> w.save() 
    9090>>> w