Django

Code

Changeset 8185

Show
Ignore:
Timestamp:
08/01/08 18:16:59 (4 months ago)
Author:
gwilson
Message:

Fixed #8070 -- Cache related objects passed to Model init as keyword arguments. Also:

  • Model init no longer performs a database query to refetch the related objects it is passed.
  • Model init now caches unsaved related objects correctly, too. (Previously, accessing the field would raise DoesNotExist error for null=False fields.)
  • Added tests for assigning None to null=True ForeignKey fields (refs #6886).
Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/trunk/django/db/models/base.py

    r8046 r8185  
    201201 
    202202        for field in fields_iter: 
     203            rel_obj = None 
    203204            if kwargs: 
    204205                if isinstance(field.rel, ManyToOneRel): 
     
    217218                        if rel_obj is None and field.null: 
    218219                            val = None 
    219                         else: 
    220                             try: 
    221                                 val = getattr(rel_obj, field.rel.get_related_field().attname) 
    222                             except AttributeError: 
    223                                 raise TypeError("Invalid value: %r should be a %s instance, not a %s" % 
    224                                     (field.name, field.rel.to, type(rel_obj))) 
    225220                else: 
    226221                    val = kwargs.pop(field.attname, field.get_default()) 
    227222            else: 
    228223                val = field.get_default() 
    229             setattr(self, field.attname, val) 
     224            # If we got passed a related instance, set it using the field.name 
     225            # instead of field.attname (e.g. "user" instead of "user_id") so 
     226            # that the object gets properly cached (and type checked) by the 
     227            # RelatedObjectDescriptor. 
     228            if rel_obj: 
     229                setattr(self, field.name, rel_obj) 
     230            else: 
     231                setattr(self, field.attname, val) 
    230232 
    231233        if kwargs: 
  • django/trunk/tests/modeltests/many_to_one/models.py

    r7971 r8185  
    4747# Article objects have access to their related Reporter objects. 
    4848>>> r = a.reporter 
     49 
     50# These are strings instead of unicode strings because that's what was used in 
     51# the creation of this reporter (and we haven't refreshed the data from the 
     52# database, which always returns unicode strings). 
    4953>>> r.first_name, r.last_name 
    50 (u'John', u'Smith') 
     54('John', 'Smith') 
    5155 
    5256# Create an Article via the Reporter object. 
     
    177181 
    178182# You can also use a queryset instead of a literal list of instances. 
    179 # The queryset must be reduced to a list of values using values(),  
     183# The queryset must be reduced to a list of values using values(), 
    180184# then converted into a query 
    181185>>> Article.objects.filter(reporter__in=Reporter.objects.filter(first_name='John').values('pk').query).distinct() 
  • django/trunk/tests/regressiontests/many_to_one_regress/models.py

    r7778 r8185  
    5656 
    5757# 
    58 # Tests of ForeignKey assignment and the related-object cache (see #6886) 
     58# Tests of ForeignKey assignment and the related-object cache (see #6886). 
    5959# 
    6060>>> p = Parent.objects.create(name="Parent") 
    6161>>> c = Child.objects.create(name="Child", parent=p) 
    6262 
    63 # Look up the object again so that we get a "fresh" object 
     63# Look up the object again so that we get a "fresh" object. 
    6464>>> c = Child.objects.get(name="Child") 
    6565>>> p = c.parent 
    6666 
    67 # Accessing the related object again returns the exactly same object 
     67# Accessing the related object again returns the exactly same object. 
    6868>>> c.parent is p 
    6969True 
    7070 
    71 # But if we kill the cache, we get a new object 
     71# But if we kill the cache, we get a new object. 
    7272>>> del c._parent_cache 
    7373>>> c.parent is p 
    7474False 
    7575 
    76 # Assigning a new object results in that object getting cached immediately 
     76# Assigning a new object results in that object getting cached immediately. 
    7777>>> p2 = Parent.objects.create(name="Parent 2") 
    7878>>> c.parent = p2 
     
    8080True 
    8181 
    82 # Assigning None fails: Child.parent is null=False 
     82# Assigning None succeeds if field is null=True. 
     83>>> p.bestchild = None 
     84>>> p.bestchild is None 
     85True 
     86 
     87# Assigning None fails: Child.parent is null=False. 
    8388>>> c.parent = None 
    8489Traceback (most recent call last): 
     
    9297ValueError: Cannot assign "<First: First object>": "Child.parent" must be a "Parent" instance. 
    9398 
    94 # Test of multiple ForeignKeys to the same model (bug #7125) 
     99# Creation using keyword argument should cache the related object. 
     100>>> p = Parent.objects.get(name="Parent") 
     101>>> c = Child(parent=p) 
     102>>> c.parent is p 
     103True 
    95104 
     105# Creation using keyword argument and unsaved related instance (#8070). 
     106>>> p = Parent() 
     107>>> c = Child(parent=p) 
     108>>> c.parent is p 
     109True 
     110 
     111# Creation using attname keyword argument and an id will cause the related 
     112# object to be fetched. 
     113>>> p = Parent.objects.get(name="Parent") 
     114>>> c = Child(parent_id=p.id) 
     115>>> c.parent is p 
     116False 
     117>>> c.parent == p 
     118True 
     119 
     120 
     121# 
     122# Test of multiple ForeignKeys to the same model (bug #7125). 
     123# 
    96124>>> c1 = Category.objects.create(name='First') 
    97125>>> c2 = Category.objects.create(name='Second') 
  • django/trunk/tests/regressiontests/one_to_one_regress/models.py

    r7574 r8185  
    2323        return u"%s the bar" % self.place.name 
    2424 
     25class UndergroundBar(models.Model): 
     26    place = models.OneToOneField(Place, null=True) 
     27    serves_cocktails = models.BooleanField() 
     28 
    2529class Favorites(models.Model): 
    2630    name = models.CharField(max_length = 50) 
     
    4347[<Restaurant: Demon Dogs the restaurant>] 
    4448 
    45 # Regression test for #7173: Check that the name of the cache for the  
     49# Regression test for #7173: Check that the name of the cache for the 
    4650# reverse object is correct. 
    4751>>> b = Bar(place=p1, serves_cocktails=False) 
     
    5458# 
    5559# Regression test for #6886 (the related-object cache) 
    56 #  
     60# 
    5761 
    5862# Look up the objects again so that we get "fresh" objects 
     
    7781True 
    7882 
     83# Assigning None succeeds if field is null=True. 
     84>>> ug_bar = UndergroundBar.objects.create(place=p, serves_cocktails=False) 
     85>>> ug_bar.place = None 
     86>>> ug_bar.place is None 
     87True 
     88 
    7989# Assigning None fails: Place.restaurant is null=False 
    8090>>> p.restaurant = None 
     
    8999ValueError: Cannot assign "<Place: Demon Dogs the place>": "Place.restaurant" must be a "Restaurant" instance. 
    90100 
     101# Creation using keyword argument should cache the related object. 
     102>>> p = Place.objects.get(name="Demon Dogs") 
     103>>> r = Restaurant(place=p) 
     104>>> r.place is p 
     105True 
     106 
     107# Creation using keyword argument and unsaved related instance (#8070). 
     108>>> p = Place() 
     109>>> r = Restaurant(place=p) 
     110>>> r.place is p 
     111True 
     112 
     113# Creation using attname keyword argument and an id will cause the related 
     114# object to be fetched. 
     115>>> p = Place.objects.get(name="Demon Dogs") 
     116>>> r = Restaurant(place_id=p.id) 
     117>>> r.place is p 
     118False 
     119>>> r.place == p 
     120True 
     121 
    91122"""}