Django

Code

Ticket #5420: queryset_fields_trunk.diff

File queryset_fields_trunk.diff, 29.7 kB (added by dogada, 2 years ago)

QuerySet? patch: adds fields() and improves values)() methods

  • django/db/models/manager.py

    old new  
    101101    def values(self, *args, **kwargs): 
    102102        return self.get_query_set().values(*args, **kwargs) 
    103103 
     104    def fields(self, *args, **kwargs): 
     105        return self.get_query_set().fields(*args, **kwargs) 
     106 
    104107class ManagerDescriptor(object): 
    105108    # This class ensures managers aren't accessible via model instances. 
    106109    # For example, Poll.objects works, but poll_obj.objects raises AttributeError. 
  • django/db/models/query.py

    old new  
    9191        self._order_by = None        # Ordering, e.g. ('date', '-name'). If None, use model's ordering. 
    9292        self._select_related = False # Whether to fill cache for related objects. 
    9393        self._max_related_depth = 0  # Maximum "depth" for select_related 
     94        self._fields = {}            # Subset of fields of this and related models to select 
     95        self._modelize = True        # Convert rows to models or return dictionaries 
    9496        self._distinct = False       # Whether the query should use SELECT DISTINCT. 
    9597        self._select = {}            # Dictionary of attname -> SQL. 
    9698        self._where = []             # List of extra WHERE clauses to use. 
     
    189191        cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 
    190192 
    191193        fill_cache = self._select_related 
    192         fields = self.model._meta.fields 
     194        fields = _get_model_fields(self.model._meta, "", self._fields, self._select) 
    193195        index_end = len(fields) 
    194196        has_resolve_columns = hasattr(self, 'resolve_columns') 
    195197        while 1: 
     
    201203                    row = self.resolve_columns(row, fields) 
    202204                if fill_cache: 
    203205                    obj, index_end = get_cached_row(klass=self.model, row=row, 
    204                                                     index_start=0, max_depth=self._max_related_depth) 
     206                                                    index_start=0, 
     207                                                    max_depth=self._max_related_depth, 
     208                                                    fields=self._fields, modelize=self._modelize) 
    205209                else: 
    206                     obj = self.model(*row[:index_end]) 
     210                    obj = _init_model(self._modelize and self.model or None,\ 
     211                                      fields, row[:index_end]) 
    207212                for i, k in enumerate(extra_select): 
    208                     setattr(obj, k[0], row[index_end+i]) 
     213                    if self._modelize: 
     214                        setattr(obj, k[0], row[index_end+i]) 
     215                    else: 
     216                        obj[k[0]] = row[index_end+i] 
    209217                yield obj 
    210218 
    211219    def count(self): 
     
    357365    # PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS # 
    358366    ################################################## 
    359367 
    360     def values(self, *fields): 
    361         return self._clone(klass=ValuesQuerySet, _fields=fields) 
    362  
    363368    def dates(self, field_name, kind, order='ASC'): 
    364369        """ 
    365370        Returns a list of datetime objects representing all available dates 
     
    415420        "Returns a new QuerySet instance with '_select_related' modified." 
    416421        return self._clone(_select_related=true_or_false, _max_related_depth=depth) 
    417422 
     423    def fields(self, *fields, **related_fields): 
     424        """Returns a new QuerySet instance with '_fields' modified. 
     425        owner_fields is stored inside _fields with empty string key.""" 
     426        assert fields or related_fields, "fields() don't have sense without arguments" 
     427        _fields, _select_related = _merge_fields(self.model, fields, related_fields) 
     428        return self._clone(_modelize=True, _fields=_fields, 
     429                           _select_related=_select_related or self._select_related) 
     430 
     431    def values(self, *fields, **related_fields): 
     432        """Returns a new QuerySet instance with '_fields' and '_modelize'.""" 
     433        if not (fields or related_fields): 
     434            return self._clone(_modelize=False, _fields={}) # process all fields from master model 
     435        _fields, _select_related = _merge_fields(self.model, fields, related_fields) 
     436        return self._clone(_modelize=False, _fields=_fields, 
     437                           _select_related=_select_related or self._select_related) 
     438 
    418439    def order_by(self, *field_names): 
    419440        "Returns a new QuerySet instance with the ordering changed." 
    420441        assert self._limit is None and self._offset is None, \ 
     
    448469        c._order_by = self._order_by 
    449470        c._select_related = self._select_related 
    450471        c._max_related_depth = self._max_related_depth 
     472        c._fields = self._fields 
     473        c._modelize = self._modelize 
    451474        c._distinct = self._distinct 
    452475        c._select = self._select.copy() 
    453476        c._where = self._where[:] 
     
    488511        opts = self.model._meta 
    489512 
    490513        # Construct the fundamental parts of the query: SELECT X FROM Y WHERE Z. 
    491         select = ["%s.%s" % (qn(opts.db_table), qn(f.column)) for f in opts.fields] 
     514        select = ["%s.%s" % (qn(opts.db_table), qn(f.column)) for f in \ 
     515                  _get_model_fields(opts, "", self._fields, self._select)] 
    492516        tables = [quote_only_if_word(t) for t in self._tables] 
    493517        joins = SortedDict() 
    494518        where = self._where[:] 
     
    505529            fill_table_cache(opts, select, tables, where, 
    506530                             old_prefix=opts.db_table, 
    507531                             cache_tables_seen=[opts.db_table], 
    508                              max_depth=self._max_related_depth
     532                             max_depth=self._max_related_depth, fields=self._fields
    509533 
    510534        # Add any additional SELECTs. 
    511535        if self._select: 
     
    571595else: 
    572596    QuerySet = _QuerySet 
    573597 
    574 class ValuesQuerySet(QuerySet): 
    575     def __init__(self, *args, **kwargs): 
    576         super(ValuesQuerySet, self).__init__(*args, **kwargs) 
    577         # select_related isn't supported in values(). 
    578         self._select_related = False 
    579  
    580     def iterator(self): 
    581         try: 
    582             select, sql, params = self._get_sql_clause() 
    583         except EmptyResultSet: 
    584             raise StopIteration 
    585  
    586         qn = connection.ops.quote_name 
    587  
    588         # self._select is a dictionary, and dictionaries' key order is 
    589         # undefined, so we convert it to a list of tuples. 
    590         extra_select = self._select.items() 
    591  
    592         # Construct two objects -- fields and field_names. 
    593         # fields is a list of Field objects to fetch. 
    594         # field_names is a list of field names, which will be the keys in the 
    595         # resulting dictionaries. 
    596         if self._fields: 
    597             if not extra_select: 
    598                 fields = [self.model._meta.get_field(f, many_to_many=False) for f in self._fields] 
    599                 field_names = self._fields 
    600             else: 
    601                 fields = [] 
    602                 field_names = [] 
    603                 for f in self._fields: 
    604                     if f in [field.name for field in self.model._meta.fields]: 
    605                         fields.append(self.model._meta.get_field(f, many_to_many=False)) 
    606                         field_names.append(f) 
    607                     elif not self._select.has_key(f): 
    608                         raise FieldDoesNotExist('%s has no field named %r' % (self.model._meta.object_name, f)) 
    609         else: # Default to all fields. 
    610             fields = self.model._meta.fields 
    611             field_names = [f.attname for f in fields] 
    612  
    613         columns = [f.column for f in fields] 
    614         select = ['%s.%s' % (qn(self.model._meta.db_table), qn(c)) for c in columns] 
    615         if extra_select: 
    616             select.extend(['(%s) AS %s' % (quote_only_if_word(s[1]), qn(s[0])) for s in extra_select]) 
    617             field_names.extend([f[0] for f in extra_select]) 
    618  
    619         cursor = connection.cursor() 
    620         cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") + ",".join(select) + sql, params) 
    621  
    622         has_resolve_columns = hasattr(self, 'resolve_columns') 
    623         while 1: 
    624             rows = cursor.fetchmany(GET_ITERATOR_CHUNK_SIZE) 
    625             if not rows: 
    626                 raise StopIteration 
    627             for row in rows: 
    628                 if has_resolve_columns: 
    629                     row = self.resolve_columns(row, fields) 
    630                 yield dict(zip(field_names, row)) 
    631  
    632     def _clone(self, klass=None, **kwargs): 
    633         c = super(ValuesQuerySet, self)._clone(klass, **kwargs) 
    634         c._fields = self._fields[:] 
    635         return c 
    636  
    637598class DateQuerySet(QuerySet): 
    638599    def iterator(self): 
    639600        from django.db.backends.util import typecast_timestamp 
     
    829790            raise NotImplementedError 
    830791    raise TypeError, "Got invalid lookup_type: %s" % repr(lookup_type) 
    831792 
    832 def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0): 
     793def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0, depth_path="", fields={}, modelize=True): 
    833794    """Helper function that recursively returns an object with cache filled""" 
    834795 
    835796    # If we've got a max_depth set and we've exceeded that depth, bail now. 
    836797    if max_depth and cur_depth > max_depth: 
    837798        return None 
    838799 
    839     index_end = index_start + len(klass._meta.fields) 
    840     obj = klass(*row[index_start:index_end]) 
    841     for f in klass._meta.fields: 
    842         if f.rel and not f.null: 
    843             cached_row = get_cached_row(f.rel.to, row, index_end, max_depth, cur_depth+1) 
     800    cur_fields = _get_model_fields(klass._meta, depth_path, fields) 
     801    if not cur_fields: 
     802        return None 
     803 
     804    index_end = index_start + len(cur_fields) 
     805    obj = _init_model(modelize and klass or None, cur_fields, row[index_start:index_end]) 
     806    for f in cur_fields: 
     807        if f.rel and not f.null and _get_fields(f, depth_path, fields): 
     808            cached_row = get_cached_row(f.rel.to, row, index_end, max_depth, cur_depth+1,\ 
     809                                        _lookup(depth_path, f.name), fields, modelize) 
    844810            if cached_row: 
    845811                rel_obj, index_end = cached_row 
    846                 setattr(obj, f.get_cache_name(), rel_obj) 
     812                if modelize: 
     813                    setattr(obj, f.get_cache_name(), rel_obj) 
     814                else: 
     815                    obj[f.name] = rel_obj 
    847816    return obj, index_end 
    848817 
    849 def fill_table_cache(opts, select, tables, where, old_prefix, cache_tables_seen, max_depth=0, cur_depth=0): 
     818def fill_table_cache(opts, select, tables, where, old_prefix, cache_tables_seen,\ 
     819                     max_depth=0, cur_depth=0, depth_path="", fields={}): 
    850820    """ 
    851821    Helper function that recursively populates the select, tables and where (in 
    852822    place) for select_related queries. 
    853823    """ 
    854  
    855824    # If we've got a max_depth set and we've exceeded that depth, bail now. 
    856825    if max_depth and cur_depth > max_depth: 
    857826        return None 
    858827 
    859828    qn = connection.ops.quote_name 
    860     for f in opts.fields
    861         if f.rel and not f.null
     829    for f in _get_model_fields(opts, depth_path, fields)
     830        if f.rel and not f.null and _get_fields(f, depth_path, fields)
    862831            db_table = f.rel.to._meta.db_table 
    863832            if db_table not in cache_tables_seen: 
    864833                tables.append(qn(db_table)) 
     
    869838            cache_tables_seen.append(db_table) 
    870839            where.append('%s.%s = %s.%s' % \ 
    871840                (qn(old_prefix), qn(f.column), qn(db_table), qn(f.rel.get_related_field().column))) 
    872             select.extend(['%s.%s' % (qn(db_table), qn(f2.column)) for f2 in f.rel.to._meta.fields]) 
    873             fill_table_cache(f.rel.to._meta, select, tables, where, db_table, cache_tables_seen, max_depth, cur_depth+1) 
     841            select.extend(['%s.%s' % (qn(db_table), qn(f2.column)) for f2 in\ 
     842                           _get_fields(f, depth_path, fields)]) 
     843            fill_table_cache(f.rel.to._meta, select, tables, where, db_table, cache_tables_seen,\ 
     844                             max_depth, cur_depth+1, _lookup(depth_path, f.name), fields) 
    874845 
     846def _lookup(depth_path, fname): 
     847    return depth_path and "%s__%s" % (depth_path, fname) or fname 
     848 
     849def _split_lookup(lookup): 
     850    """Right split lookup on parent and child.""" 
     851    if LOOKUP_SEPARATOR in lookup: 
     852        return lookup.rsplit(LOOKUP_SEPARATOR, 1) 
     853    else: 
     854        return '', lookup 
     855 
     856def _merge_fields(klass, fields, related_fields): 
     857    """Resolves child-parent relations and checks is select_related required 
     858    or not.""" 
     859    _select_related = False 
     860    if related_fields or [f for f in klass._meta.fields if f.name in fields and f.rel]: 
     861        _select_related = True 
     862 
     863    related_fields[''] = fields 
     864    # check that each child primary key is in parent field subset 
     865    for lookup in related_fields.iterkeys(): 
     866        if lookup:                  # root lookup is empty string 
     867            parent, child = _split_lookup(lookup) 
     868            if not related_fields.has_key(parent): 
     869                continue            # a grandparent may have required field 
     870            parent_fields = related_fields[parent] 
     871            if child not in parent_fields: 
     872                parent_fields = list(parent_fields) + [child] 
     873                related_fields[parent] = parent_fields 
     874 
     875    return related_fields, _select_related 
     876 
     877def _get_fields(field, depth_path, fields): 
     878    """Returns fields of field.rel model that need to be selected from db.""" 
     879    assert field.rel and field.rel.to, "ForeignKey is required." 
     880    return _get_model_fields(field.rel.to._meta, _lookup(depth_path, field.name), fields) 
     881 
     882def _get_model_fields(meta, lookup, fields, extra_select=[]): 
     883    if fields:                    # was used fields(...), so return only specified fields 
     884        if fields.has_key(lookup):      # we have implicit instruction for the lookup 
     885            fnames = fields[lookup] 
     886            if fnames: 
     887                # check that each field name is valid field name for this model and extra select 
     888                # actually this is optional step, but tests/modeltests/lookup.py require it 
     889                valid_fnames = [f.name for f in meta.fields] 
     890                if extra_select: 
     891                    valid_fnames += extra_select.keys() 
     892                for fname in fnames: 
     893                    if not fname in valid_fnames: 
     894                        raise FieldDoesNotExist("%s has no field named '%s'" %\ 
     895                                                (meta.object_name, fname)) 
     896            return [f for f in meta.fields if f.name in fnames] 
     897        else:                           # checking parent 
     898            while lookup: 
     899                lookup, child = _split_lookup(lookup) 
     900                if fields.has_key(lookup): 
     901                    # we have child defined in parent fields instead of 
     902                    # dedicated lookup for this child, so return all fields 
     903                    # for example BlogPost.objects.fields(('headline', 'blog')) should 
     904                    # return only BlogPost.headline, all Blog fields and all Blog children 
     905                    # fields (limited to _max_related_depth of course) 
     906                    return child in fields[lookup] and meta.fields or () 
     907    else: 
     908        return meta.fields 
     909 
     910def _init_model(klass, fields, values): 
     911    """If klass, return model instance, else return dictionary.""" 
     912    obj = dict(zip([f.attname for f in fields], values)) 
     913    if klass: 
     914        obj = klass(**obj) 
     915    return obj 
     916 
    875917def parse_lookup(kwarg_items, opts): 
    876918    # Helper function that handles converting API kwargs 
    877919    # (e.g. "name__exact": "tom") to SQL. 
  • tests/modeltests/fields/models.py

    old new  
     1""" 
     247. Tests for fields() 
     3 
     4``fields()`` allows to select only fields subset from a model and its related 
     5models.  It change the behaviour of methods that return model 
     6instances. Affects querysets with or without select_related mode. If fields() 
     7isn't called for queryset, queryset behavior isn't chaged, i.e. all fields of 
     8all selected models are returned. 
     9""" 
     10 
     11from django.db import models 
     12 
     13class Author(models.Model): 
     14    name = models.CharField(max_length=50) 
     15    about = models.TextField() 
     16    def __unicode__(self): 
     17        """When self.about isn't loaded it will be shown as [].""" 
     18        return u"%s [%s]" % (self.name, self.about) 
     19 
     20class Blog(models.Model): 
     21    name = models.CharField(max_length=50) 
     22    about = models.TextField() 
     23    author = models.ForeignKey(Author) 
     24 
     25    def __unicode__(self): 
     26        return u"%s [%s]" % (self.name, self.about) 
     27 
     28class Entry(models.Model): 
     29    blog = models.ForeignKey(Blog) 
     30    headline = models.CharField(max_length=50) 
     31    content = models.TextField() 
     32 
     33    def __unicode__(self): 
     34        return u"%s [%s]" % (self.headline, self.content) 
     35 
     36def create_world(): 
     37    """Helper to create test models.""" 
     38    etch = Author.objects.create(name="Etch", about="About Etch") 
     39    lenny = Author.objects.create(name="Lenny", about="About Lenny") 
     40    etch_blog = Blog.objects.create(name="Etch's blog", about="All about Etch", author=etch) 
     41    lenny_blog = Blog.objects.create(name="Lenny's blog", about="All about Lenny", author=lenny) 
     42    for blog in Blog.objects.select_related(): 
     43        for i in xrange(5): 
     44            headline = "%s_%d" % (blog.author.name, i) 
     45            Entry.objects.create(headline=headline, content="huge content", blog=blog) 
     46 
     47 
     48__test__ = {'API_TESTS':""" 
     49 
     50>>> from django.conf import settings 
     51>>> old_debug = settings.DEBUG 
     52>>> settings.DEBUG = True 
     53>>> from django import db 
     54>>> create_world() 
     55>>> Author.objects.count(), Blog.objects.count(), Entry.objects.count() 
     56(2, 2, 10) 
     57 
     58# load only `id` and `headline` of a Entry 
     59>>> db.reset_queries() 
     60>>> p = Entry.objects.fields('id', 'headline').get(headline='Lenny_0') 
     61>>> p, p.id, p.content 
     62(<Entry: Lenny_0 []>, 6, '') 
     63>>> len(db.connection.queries) 
     641 
     65 
     66# load all fields of all related objects 
     67>>> db.reset_queries() 
     68>>> p = Entry.objects.select_related().get(headline="Etch_1") 
     69>>> p, p.blog, p.blog.author 
     70(<Entry: Etch_1 [huge content]>, <Blog: Etch's blog [All about Etch]>, <Author: Etch [About Etch]>) 
     71>>> len(db.connection.queries) 
     721 
     73 
     74# load only Entry.headline, Blog.name and Author.name and don't load 
     75huge TextFields for Lenny's blog posts (typical task when it's need to show an object list). 
     76>>> db.reset_queries() 
     77>>> posts = Entry.objects.fields('headline', blog=('name',), blog__author=('name',)).filter(blog__author__name="Lenny")[:1] 
     78>>> [(p, p.blog, p.blog.author) for p in posts] 
     79[(<Entry: Lenny_0 []>, <Blog: Lenny's blog []>, <Author: Lenny []>)] 
     80>>> p = posts[0] 
     81>>> p.content == p.blog.about == p.blog.author.about == '' 
     82True 
     83>>> len(db.connection.queries) 
     841 
     85 
     86# load all Entry fields and only Blog.headline and Author.name (don't load `about` fields) 
     87>>> db.reset_queries() 
     88>>> p = Entry.objects.fields('id','headline', 'content', blog=('name',), blog__author=('name',)).select_related().get(headline="Etch_1") 
     89>>> p, p.blog, p.blog.author 
     90(<Entry: Etch_1 [huge content]>, <Blog: Etch's blog []>, <Author: Etch []>) 
     91>>> print p.content 
     92huge content 
     93>>> (p.blog.about, p.blog.author.about) 
     94('', '') 
     95>>> len(db.connection.queries) 
     961 
     97 
     98# don't load any field from Blog, but load Blog.author.name 
     99>>> db.reset_queries() 
     100>>> p = Entry.objects.fields('id','headline', 'content', blog=(), blog__author=('name',)).get(headline="Lenny_1") 
     101>>> p, p.blog, p.blog.author 
     102(<Entry: Lenny_1 [huge content]>, <Blog:  []>, <Author: Lenny []>) 
     103>>> len(db.connection.queries) 
     1041 
     105 
     106# select Entry.headline, all fields from Blog and only Author.name 
     107>>> db.reset_queries() 
     108>>> p = Entry.objects.fields('headline', 'blog', blog__author=('name',)).get(headline="Lenny_2") 
     109>>> p, p.blog, p.blog.author 
     110(<Entry: Lenny_2 []>, <Blog: Lenny's blog [All about Lenny]>, <Author: Lenny []>) 
     111>>> len(db.connection.queries) 
     1121 
     113 
     114# check count() 
     115>>> db.reset_queries() 
     116>>> Entry.objects.fields('id', 'blog', blog__author=('name',)).select_related().filter(headline__contains='1').count() 
     1172 
     118>>> len(db.connection.queries) 
     1191 
     120 
     121# select all field from Entry, Blog and don't select Author fields because 
     122# select_related(depth=1) don't touch Author table and so field limitations from 
     123# fields() aren't used 
     124>>> db.reset_queries() 
     125>>> p = Entry.objects.fields('id','headline', 'content', 'blog', blog__author=('name',)).select_related(depth=1).get(headline="Lenny_3") 
     126>>> p, p.blog, p.blog.author 
     127(<Entry: Lenny_3 [huge content]>, <Blog: Lenny's blog [All about Lenny]>, <Author: Lenny [About Lenny]>) 
     128>>> len(db.connection.queries) 
     1292 
     130 
     131# select Blog.name and Author.name only and check extra() 
     132>>> db.reset_queries() 
     133>>> b = Blog.objects.fields('name', author=('name',)).extra(select={'post_count': "SELECT COUNT(*) FROM fields_entry t WHERE t.blog_id=fields_blog.id"}).get(name="Lenny\'s blog") 
     134>>> b, b.author, b.post_count 
     135(<Blog: Lenny's blog []>, <Author: Lenny []>, 5) 
     136>>> len(db.connection.queries) 
     1371 
     138 
     139# check extra() with collection 
     140>>> db.reset_queries() 
     141>>> blogs = Blog.objects.fields('id','name', author=('name',)).select_related().extra(select={'post_count': "SELECT COUNT(*) FROM fields_entry t WHERE t.blog_id=fields_blog.id"}) 
     142>>> [(b, b.author, b.post_count) for b in blogs] 
     143[(<Blog: Etch's blog []>, <Author: Etch []>, 5), (<Blog: Lenny's blog []>, <Author: Lenny []>, 5)] 
     144>>> len(db.connection.queries) 
     1451 
     146 
     147# load Blog.name, Author.name and check extra() 
     148>>> db.reset_queries() 
     149>>> b = Blog.objects.fields('name', author=('name',)).select_related().extra(select={'post_count': "SELECT COUNT(*) FROM fields_entry t WHERE t.blog_id=fields_blog.id"}).get(name="Lenny\'s blog") 
     150>>> b, b.author, b.post_count 
     151(<Blog: Lenny's blog []>, <Author: Lenny []>, 5) 
     152>>> len(db.connection.queries) 
     1531 
     154 
     155# load only Author.name without any fields from Blog 
     156>>> db.reset_queries() 
     157>>> b1 = Blog.objects.fields(author=('name',)).select_related().get(name="Etch\'s blog") 
     158>>> b1, b1.author 
     159(<Blog:  []>, <Author: Etch []>) 
     160>>> len(db.connection.queries) 
     1611 
     162 
     163# dive even deeper in absolute beauty, load only child fields without parent fields 
     164>>> db.reset_queries() 
     165>>> p3 = Entry.objects.fields(blog=(), blog__author=('id','name',)).select_related().get(headline="Etch_3") 
     166>>> p3, p3.blog, p3.blog.author, p3.blog.author.id 
     167(<Entry:  []>, <Blog:  []>, <Author: Etch []>, 1) 
     168>>> len(db.connection.queries) 
     1691 
     170 
     171# check old-style values() 
     172>>> d = Entry.objects.filter(blog__author__name='Lenny').values('id', 'headline')[0] 
     173>>> print d['id'], d['headline'] 
     1746 Lenny_0 
     175>>> l = Entry.objects.values('id', 'headline').extra(select={'id_plus_1': 'id + 1'})[:2] 
     176>>> [(d['id'], d['id_plus_1'], str(d['headline'])) for d in l] 
     177[(1, 2, 'Etch_0'), (2, 3, 'Etch_1')] 
     178 
     179# test models for docs/db-api.txt examples 
     180>>> joe = Author.objects.create(name="Joe") 
     181>>> blog = Blog.objects.create(id=1, name='Beatles Blog', about='All the latest Beatles news.', author=joe) 
     182>>> entry = Entry.objects.create(headline='Joe about Beatles', content='Beatles was ...', blog=blog) 
     183 
     184# load all fields from Entry and related models 
     185>>> e = Entry.objects.select_related().get(headline='Joe about Beatles') 
     186>>> e.headline, e.content, e.blog.name 
     187(u'Joe about Beatles', u'Beatles was ...', u'Beatles Blog') 
     188 
     189# don't load huge text field from Entry and only load name from related Blog 
     190>>> e = Entry.objects.fields('headline', blog=('name',)).get(headline='Joe about Beatles') 
     191>>> e.headline, e.content, e.blog.name 
     192(u'Joe about Beatles', '', u'Beatles Blog') 
     193 
     194>>> db.reset_queries() 
     195>>> Entry.objects.values('headline', blog=('name',)).filter(headline__contains='Beatles') 
     196[{'headline': u'Joe about Beatles', 'blog': {'name': u'Beatles Blog'}, 'blog_id': 1}] 
     197>>> len(db.connection.queries) 
     1981 
     199>>> Entry.objects.values('headline', blog=('id', 'name',)).filter(headline__contains='Beatles') 
     200[{'headline': u'Joe about Beatles', 'blog': {'id': 1, 'name': u'Beatles Blog'}, 'blog_id': 1}] 
     201 
     202# Reset DEBUG to where we found it. 
     203>>> settings.DEBUG = old_debug 
     204"""} 
  • AUTHORS

    old new  
    357357    ymasuda@ethercube.com 
    358358    Jarek Zgoda <jarek.zgoda@gmail.com> 
    359359    Cheng Zhang 
     360    Dima Dogadaylo <http://www.mysoftparade.com/> 
    360361 
    361362A big THANK YOU goes to: 
    362363 
  • docs/db-api.txt

    old new  
    536536However, if your query spans multiple tables, it's possible to get duplicate 
    537537results when a ``QuerySet`` is evaluated. That's when you'd use ``distinct()``. 
    538538 
    539 ``values(*fields)`` 
     539``fields(*fields, **related_fields)`` 
    540540~~~~~~~~~~~~~~~~~~~ 
    541541 
    542 Returns a ``ValuesQuerySet`` -- a ``QuerySet`` that evaluates to a list of 
    543 dictionaries instead of model-instance objects. 
     542**New in Django development version** 
    544543 
     544Returns a ``QuerySet`` that allows to load only some of fields from this and 
     545related models. 
     546 
     547This example demonstrates effect of particular field loading:: 
     548 
     549    # load all fields from Entry and related models 
     550    >>> e = Entry.objects.select_related().get(headline='Joe about Beatles') 
     551    >>> e.headline, e.body_text, e.blog.name 
     552    (u'Joe about Beatles', u'Beatles was ...', u'Beatles Blog') 
     553 
     554    # don't load huge text field from Entry and only load name from related Blog 
     555    >>> e = Entry.objects.fields('headline', blog=('name',)).get(headline='Joe about Beatles') 
     556    >>> e.headline, e.body_text, e.blog.name 
     557    (u'Joe about Beatles', '', u'Beatles Blog') 
     558 
     559``fields()`` takes optional positional arguments, ``*fields``, which specify 
     560field names of this model for to which the ``SELECT`` should be limited.  
     561 
     562``fields()`` also takes optional keyword arguments, ``**related_fields``, 
     563which specify field names of related models to which the ``SELECT`` should be 
     564limited. Each argument in ``**related_fields`` affects corresponding related 
     565model in the same way like ``*fields`` affects master model. 
     566 
     567You can reference related objects for any desired depth with standard Django 
     568lookup syntax, for example: 
     569 
     570    Entry.objects.fields('headline', author__address__country=('name')) 
     571 
     572The method ``fields()`` is extremelly usefull when you have models that 
     573contain large text or binary fields and you don't want to load these heavy 
     574fields. For example you need to show latest 10 blog posts and you only need 
     575headlines of these blog posts and their author names, with ``fields`` you can 
     576load only ``headline`` from ``Entry`` table and only ``name`` from ``Author`` 
     577table (other fields of these 2 models will not be loaded and will have default 
     578values).  
     579 
     580With ``fields()`` you have full control on what fields will be loaded from 
     581database, take this power with care. For example suppose ``Entry`` model 
     582references ``Author`` model, that references ``Address`` model that have 
     583foreign key to ``Country`` model. With call like 
     584    Entry.objects.fields('headline', author__address__country=('name')) 
     585, you can load ``Entry.headline``, don't load fields from ``Author`` and 
     586``Address`` and load only name from ``Country`` assotiated with blog entry's 
     587author. 
     588 
     589``values(*fields, **related_fields)`` 
     590~~~~~~~~~~~~~~~~~~~ 
     591 
     592**New in Django development version** 
     593 
     594Returns a ``QuerySet`` that evaluates to a list of dictionaries instead of 
     595model-instance objects. 
     596 
    545597Each of those dictionaries represents an object, with the keys corresponding to 
    546598the attribute names of model objects. 
    547599 
     
    569621    >>> Blog.objects.values('id', 'name') 
    570622    [{'id': 1, 'name': 'Beatles Blog'}] 
    571623 
    572 A ``ValuesQuerySet`` is useful when you know you're only going to need values 
     624``values()`` also takes optional keyword arguments, ``**related_fields``, 
     625which specify field names of related models to which the ``SELECT`` should be 
     626limited. Each argument in ``**related_fields`` affects corresponding related 
     627model in the same way like ``*fields`` affects master model. 
     628 
     629Example:: 
     630 
     631    >>> Entry.objects.values('headline', blog=('id', 'name',)).filter(headline__contains='Beatles') 
     632    [{'headline': u'Joe about Beatles', 'blog': {'name': u'Beatles Blog'}, 'blog_id': 1}] 
     633    >>> Entry.objects.values('headline', blog=('id', 'name',)).filter(headline__contains='Beatles') 
     634    [{'headline': u'Joe about Beatles', 'blog': {'id': 1, 'name': u'Beatles Blog'}, 'blog_id': 1}] 
     635 
     636Note, when fields from related models is selected, primary keys of related 
     637models also populated in parent model dictionary. 
     638 
     639It is useful when you know you're only going to need values 
    573640from a small number of the available fields and you won't need the 
    574641functionality of a model instance object. It's more efficient to select only 
    575642the fields you need to use. 
    576643 
    577 Finally, note a ``ValuesQuerySet`` is a subclass of ``QuerySet``, so it has all 
    578 methods of ``QuerySet``. You can call ``filter()`` on it, or ``order_by()``, or 
     644Finally, note ``values()`` and ``fields()`` use same code for fields selecting 
     645and only diferent format returned. All that you can do with ``values()`` you 
     646can do with ``fields()`` and vise versa. And for sure after ``values()`` and 
     647``fields()`` you can call call ``filter()``, or ``order_by()``, or 
    579648whatever. Yes, that means these two calls are identical:: 
    580649 
    581650    Blog.objects.values().order_by('id')