Django

Code

Changeset 4645

Show
Ignore:
Timestamp:
02/28/07 09:24:05 (2 years ago)
Author:
jacob
Message:

Added a "depth" argument to select_related() to control how many "levels" of relations select_related() is willing to follow (refs #3275).

Also added unit tests for select_related().

Files:

Legend:

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

    r4563 r4645  
    8585        self._order_by = None        # Ordering, e.g. ('date', '-name'). If None, use model's ordering. 
    8686        self._select_related = False # Whether to fill cache for related objects. 
     87        self._max_related_depth = 0  # Maximum "depth" for select_related 
    8788        self._distinct = False       # Whether the query should use SELECT DISTINCT. 
    8889        self._select = {}            # Dictionary of attname -> SQL. 
     
    187188            for row in rows: 
    188189                if fill_cache: 
    189                     obj, index_end = get_cached_row(self.model, row, 0) 
     190                    obj, index_end = get_cached_row(klass=self.model, row=row,  
     191                                                    index_start=0, max_depth=self._max_related_depth) 
    190192                else: 
    191193                    obj = self.model(*row[:index_end]) 
     
    395397            return self._filter_or_exclude(None, **filter_obj) 
    396398 
    397     def select_related(self, true_or_false=True): 
     399    def select_related(self, true_or_false=True, depth=0): 
    398400        "Returns a new QuerySet instance with '_select_related' modified." 
    399         return self._clone(_select_related=true_or_false
     401        return self._clone(_select_related=true_or_false, _max_related_depth=depth
    400402 
    401403    def order_by(self, *field_names): 
     
    431433        c._order_by = self._order_by 
    432434        c._select_related = self._select_related 
     435        c._max_related_depth = self._max_related_depth 
    433436        c._distinct = self._distinct 
    434437        c._select = self._select.copy() 
     
    484487        # Add additional tables and WHERE clauses based on select_related. 
    485488        if self._select_related: 
    486             fill_table_cache(opts, select, tables, where, opts.db_table, [opts.db_table]) 
     489            fill_table_cache(opts, select, tables, where,  
     490                             old_prefix=opts.db_table,  
     491                             cache_tables_seen=[opts.db_table],  
     492                             max_depth=self._max_related_depth) 
    487493 
    488494        # Add any additional SELECTs. 
     
    729735    raise TypeError, "Got invalid lookup_type: %s" % repr(lookup_type) 
    730736 
    731 def get_cached_row(klass, row, index_start): 
    732     "Helper function that recursively returns an object with cache filled" 
     737def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0): 
     738    """Helper function that recursively returns an object with cache filled""" 
     739     
     740    # If we've got a max_depth set and we've exceeded that depth, bail now. 
     741    if max_depth and cur_depth > max_depth: 
     742        return None 
     743     
    733744    index_end = index_start + len(klass._meta.fields) 
    734745    obj = klass(*row[index_start:index_end]) 
    735746    for f in klass._meta.fields: 
    736747        if f.rel and not f.null: 
    737             rel_obj, index_end = get_cached_row(f.rel.to, row, index_end) 
    738             setattr(obj, f.get_cache_name(), rel_obj) 
     748            cached_row = get_cached_row(f.rel.to, row, index_end, max_depth, cur_depth+1) 
     749            if cached_row: 
     750                rel_obj, index_end = cached_row 
     751                setattr(obj, f.get_cache_name(), rel_obj) 
    739752    return obj, index_end 
    740753 
    741 def fill_table_cache(opts, select, tables, where, old_prefix, cache_tables_seen): 
     754def fill_table_cache(opts, select, tables, where, old_prefix, cache_tables_seen, max_depth=0, cur_depth=0): 
    742755    """ 
    743756    Helper function that recursively populates the select, tables and where (in 
    744757    place) for select_related queries. 
    745758    """ 
     759     
     760    # If we've got a max_depth set and we've exceeded that depth, bail now. 
     761    if max_depth and cur_depth > max_depth: 
     762        return None 
     763     
    746764    qn = backend.quote_name 
    747765    for f in opts.fields: 
     
    758776                (qn(old_prefix), qn(f.column), qn(db_table), qn(f.rel.get_related_field().column))) 
    759777            select.extend(['%s.%s' % (qn(db_table), qn(f2.column)) for f2 in f.rel.to._meta.fields]) 
    760             fill_table_cache(f.rel.to._meta, select, tables, where, db_table, cache_tables_seen
     778            fill_table_cache(f.rel.to._meta, select, tables, where, db_table, cache_tables_seen, max_depth, cur_depth+1
    761779 
    762780def parse_lookup(kwarg_items, opts): 
  • django/trunk/docs/db-api.txt

    r4636 r4645  
    597597``null=True``. 
    598598 
     599Usually, using ``select_related()`` can vastly improve performance since your 
     600app can avoid many database calls. However, in situations with deeply nested 
     601sets of relationships ``select_related()`` can sometimes end up following "too 
     602many" relations, and can generate queries so large that they end up being slow. 
     603 
     604In these situations, you can use the ``depth`` argument to ``select_related()`` 
     605to control how many "levels" of relations ``select_related()`` will actually 
     606follow:: 
     607 
     608    b = Book.objects.select_related(depth=1).get(id=4) 
     609    p = b.author         # Doesn't hit the database. 
     610    c = p.hometown       # Requires a database call. 
     611     
    599612``extra(select=None, where=None, params=None, tables=None)`` 
    600613~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~