Ticket #5420: queryset_fields_trunk.diff

File queryset_fields_trunk.diff, 29.7 KB (added by dogada, 16 years ago)

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

  • django/db/models/manager.py

     
    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

     
    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

    Property changes on: tests/modeltests/fields/__init__.py
    ___________________________________________________________________
    Name: svn:keywords
       + Author Date Id
    Name: svn:eol-style
       + native
    
     
     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

    Property changes on: tests/modeltests/fields/models.py
    ___________________________________________________________________
    Name: svn:keywords
       + Author Date Id
    Name: svn:eol-style
       + native
    
     
    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

     
    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')
Back to Top