Django

Code

Changeset 3246

Show
Ignore:
Timestamp:
06/30/06 20:14:41 (2 years ago)
Author:
russellm
Message:

Fixed #2217 -- Allowed raw objects to be used in exact and in query terms. Existing use of primary keys in query terms is preserved.

Files:

Legend:

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

    r3197 r3246  
    7979        self.contribute_to_related_class(other, related) 
    8080 
     81    def get_db_prep_lookup(self, lookup_type, value): 
     82        # If we are doing a lookup on a Related Field, we must be  
     83        # comparing object instances. The value should be the PK of value,  
     84        # not value itself. 
     85        def pk_trace(value): 
     86            # Value may be a primary key, or an object held in a relation.  
     87            # If it is an object, then we need to get the primary key value for 
     88            # that object. In certain conditions (especially one-to-one relations), 
     89            # the primary key may itself be an object - so we need to keep drilling 
     90            # down until we hit a value that can be used for a comparison. 
     91            v = value 
     92            try: 
     93                while True: 
     94                    v = getattr(v, v._meta.pk.name) 
     95            except AttributeError: 
     96                pass 
     97            return v  
     98             
     99        if lookup_type == 'exact': 
     100            return [pk_trace(value)] 
     101        if lookup_type == 'in': 
     102            return [pk_trace(v) for v in value] 
     103        elif lookup_type == 'isnull': 
     104            return [] 
     105        raise TypeError, "Related Field has invalid lookup: %s" % lookup_type 
     106             
    81107    def _get_related_query_name(self, opts): 
    82108        # This method defines the name that can be used to identify this related object 
  • django/trunk/django/db/models/query.py

    r3217 r3246  
    842842 
    843843    if path: 
     844        # There are elements left in the path. More joins are required. 
    844845        if len(path) == 1 and path[0] in (new_opts.pk.name, None) \ 
    845846            and clause in ('exact', 'isnull') and not join_required: 
    846             # If the last name query is for a key, and the search is for 
    847             # isnull/exact, then the current (for N-1) or intermediate 
    848             # (for N-N) table can be used for the search - no need to join an 
    849             # extra table just to check the primary key. 
     847            # If the next and final name query is for a primary key,  
     848            # and the search is for isnull/exact, then the current  
     849            # (for N-1) or intermediate (for N-N) table can be used  
     850            # for the search - no need to join an extra table just  
     851            # to check the primary key. 
    850852            new_table = current_table 
    851853        else: 
     
    873875        params.extend(params2) 
    874876    else: 
    875         # Evaluate clause on current table. 
    876         if name in (current_opts.pk.name, None) and clause in ('exact', 'isnull') and current_column: 
    877             # If this is an exact/isnull key search, and the last pass 
    878             # found/introduced a current/intermediate table that we can use to 
    879             # optimize the query, then use that column name. 
     877        # No elements left in path. Current element is the element on which  
     878        # the search is being performed.  
     879         
     880        if join_required: 
     881            # Last query term is a RelatedObject  
     882            if field.field.rel.multiple:     
     883                # RelatedObject is from a 1-N relation. 
     884                # Join is required; query operates on joined table. 
     885                column = new_opts.pk.name             
     886                joins[backend.quote_name(new_table)] = ( 
     887                    backend.quote_name(new_opts.db_table), 
     888                    "INNER JOIN", 
     889                    "%s.%s = %s.%s" % 
     890                        (backend.quote_name(current_table), 
     891                        backend.quote_name(join_column), 
     892                        backend.quote_name(new_table), 
     893                        backend.quote_name(new_column)) 
     894                ) 
     895                current_table = new_table 
     896            else: 
     897                # RelatedObject is from a 1-1 relation,  
     898                # No need to join; get the pk value from the related object,  
     899                # and compare using that. 
     900                column = current_opts.pk.name 
     901        elif intermediate_table:  
     902            # Last query term is a related object from an N-N relation. 
     903            # Join from intermediate table is sufficient. 
     904            column = join_column 
     905        elif name == current_opts.pk.name and clause in ('exact', 'isnull') and current_column: 
     906            # Last query term is for a primary key. If previous iterations  
     907            # introduced a current/intermediate table that can be used to 
     908            # optimize the query, then use that table and column name. 
    880909            column = current_column 
    881910        else: 
     911            # Last query term was a normal field. 
    882912            column = field.column 
    883913 
  • django/trunk/django/db/models/related.py

    r2809 r3246  
    7171            return [None] * self.field.rel.num_in_admin 
    7272 
     73    def get_db_prep_lookup(self, lookup_type, value): 
     74        # Defer to the actual field definition for db prep 
     75        return self.field.get_db_prep_lookup(lookup_type, value) 
     76         
    7377    def editable_fields(self): 
    7478        "Get the fields in this class that should be edited inline." 
  • django/trunk/tests/modeltests/many_to_many/models.py

    r3075 r3246  
    7676>>> Article.objects.filter(publications__pk=1) 
    7777[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>] 
     78>>> Article.objects.filter(publications=1) 
     79[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>] 
     80>>> Article.objects.filter(publications=p1) 
     81[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>] 
    7882 
    7983>>> Article.objects.filter(publications__title__startswith="Science") 
     
    90941 
    9195 
     96>>> Article.objects.filter(publications__in=[1,2]).distinct() 
     97[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>] 
     98>>> Article.objects.filter(publications__in=[1,p2]).distinct() 
     99[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>] 
     100>>> Article.objects.filter(publications__in=[p1,p2]).distinct() 
     101[<Article: Django lets you build Web apps easily>, <Article: NASA uses Python>] 
     102 
    92103# Reverse m2m queries are supported (i.e., starting at the table that doesn't 
    93104# have a ManyToManyField). 
     
    102113>>> Publication.objects.filter(article__id__exact=1) 
    103114[<Publication: The Python Journal>] 
    104  
    105115>>> Publication.objects.filter(article__pk=1) 
    106116[<Publication: The Python Journal>] 
     117>>> Publication.objects.filter(article=1) 
     118[<Publication: The Python Journal>] 
     119>>> Publication.objects.filter(article=a1) 
     120[<Publication: The Python Journal>] 
     121 
     122>>> Publication.objects.filter(article__in=[1,2]).distinct() 
     123[<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>] 
     124>>> Publication.objects.filter(article__in=[1,a2]).distinct() 
     125[<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>] 
     126>>> Publication.objects.filter(article__in=[a1,a2]).distinct() 
     127[<Publication: Highlights for Children>, <Publication: Science News>, <Publication: Science Weekly>, <Publication: The Python Journal>] 
    107128 
    108129# If we delete a Publication, its Articles won't be able to access it. 
  • django/trunk/tests/modeltests/many_to_one/models.py

    r3075 r3246  
    152152 
    153153# Find all Articles for the Reporter whose ID is 1. 
     154# Use direct ID check, pk check, and object comparison  
    154155>>> Article.objects.filter(reporter__id__exact=1) 
    155156[<Article: John's second story>, <Article: This is a test>] 
    156157>>> Article.objects.filter(reporter__pk=1) 
    157158[<Article: John's second story>, <Article: This is a test>] 
     159>>> Article.objects.filter(reporter=1) 
     160[<Article: John's second story>, <Article: This is a test>] 
     161>>> Article.objects.filter(reporter=r) 
     162[<Article: John's second story>, <Article: This is a test>] 
     163 
     164>>> Article.objects.filter(reporter__in=[1,2]).distinct() 
     165[<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>] 
     166>>> Article.objects.filter(reporter__in=[r,r2]).distinct() 
     167[<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>] 
    158168 
    159169# You need two underscores between "reporter" and "id" -- not one. 
     
    169179TypeError: Cannot resolve keyword 'reporter_id' into field 
    170180 
    171 # "pk" shortcut syntax works in a related context, too. 
    172 >>> Article.objects.filter(reporter__pk=1) 
    173 [<Article: John's second story>, <Article: This is a test>] 
    174  
    175181# You can also instantiate an Article by passing 
    176182# the Reporter's ID instead of a Reporter object. 
     
    201207>>> Reporter.objects.filter(article__pk=1) 
    202208[<Reporter: John Smith>] 
     209>>> Reporter.objects.filter(article=1) 
     210[<Reporter: John Smith>] 
     211>>> Reporter.objects.filter(article=a) 
     212[<Reporter: John Smith>] 
     213 
     214>>> Reporter.objects.filter(article__in=[1,4]).distinct() 
     215[<Reporter: John Smith>] 
     216>>> Reporter.objects.filter(article__in=[1,a3]).distinct() 
     217[<Reporter: John Smith>] 
     218>>> Reporter.objects.filter(article__in=[a,a3]).distinct() 
     219[<Reporter: John Smith>] 
     220 
    203221>>> Reporter.objects.filter(article__headline__startswith='This') 
    204222[<Reporter: John Smith>, <Reporter: John Smith>, <Reporter: John Smith>] 
     
    216234[<Reporter: John Smith>, <Reporter: John Smith>, <Reporter: John Smith>, <Reporter: John Smith>] 
    217235>>> Reporter.objects.filter(article__reporter__first_name__startswith='John').distinct() 
     236[<Reporter: John Smith>] 
     237>>> Reporter.objects.filter(article__reporter__exact=r).distinct() 
    218238[<Reporter: John Smith>] 
    219239 
  • django/trunk/tests/modeltests/one_to_one/models.py

    r3075 r3246  
    9595>>> Restaurant.objects.get(place__exact=1) 
    9696<Restaurant: Demon Dogs the restaurant> 
     97>>> Restaurant.objects.get(place__exact=p1) 
     98<Restaurant: Demon Dogs the restaurant> 
     99>>> Restaurant.objects.get(place=1) 
     100<Restaurant: Demon Dogs the restaurant> 
     101>>> Restaurant.objects.get(place=p1) 
     102<Restaurant: Demon Dogs the restaurant> 
    97103>>> Restaurant.objects.get(place__pk=1) 
    98104<Restaurant: Demon Dogs the restaurant> 
     
    106112>>> Place.objects.get(restaurant__place__exact=1) 
    107113<Place: Demon Dogs the place> 
     114>>> Place.objects.get(restaurant__place__exact=p1) 
     115<Place: Demon Dogs the place> 
    108116>>> Place.objects.get(restaurant__pk=1) 
     117<Place: Demon Dogs the place> 
     118>>> Place.objects.get(restaurant=1) 
     119<Place: Demon Dogs the place> 
     120>>> Place.objects.get(restaurant=r) 
     121<Place: Demon Dogs the place> 
     122>>> Place.objects.get(restaurant__exact=1) 
     123<Place: Demon Dogs the place> 
     124>>> Place.objects.get(restaurant__exact=r) 
    109125<Place: Demon Dogs the place> 
    110126 
     
    116132 
    117133# Query the waiters 
     134>>> Waiter.objects.filter(restaurant__place__pk=1) 
     135[<Waiter: Joe the waiter at Demon Dogs the restaurant>] 
    118136>>> Waiter.objects.filter(restaurant__place__exact=1) 
     137[<Waiter: Joe the waiter at Demon Dogs the restaurant>] 
     138>>> Waiter.objects.filter(restaurant__place__exact=p1) 
    119139[<Waiter: Joe the waiter at Demon Dogs the restaurant>] 
    120140>>> Waiter.objects.filter(restaurant__pk=1) 
     
    124144>>> Waiter.objects.filter(pk=1) 
    125145[<Waiter: Joe the waiter at Demon Dogs the restaurant>] 
     146>>> Waiter.objects.filter(restaurant=1) 
     147[<Waiter: Joe the waiter at Demon Dogs the restaurant>] 
     148>>> Waiter.objects.filter(restaurant=r) 
     149[<Waiter: Joe the waiter at Demon Dogs the restaurant>] 
    126150 
    127151# Delete the restaurant; the waiter should also be removed