Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
gis: Merged revisions 7499,7501-7502,7504,7509-7510 via svnmerge from…
… trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@7511 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information
jbronn committed May 1, 2008
1 parent e5b52f9 commit 5922dab
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 38 deletions.
26 changes: 0 additions & 26 deletions django/db/models/base.py
Expand Up @@ -237,32 +237,6 @@ def __init__(self, *args, **kwargs):
raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0] raise TypeError, "'%s' is an invalid keyword argument for this function" % kwargs.keys()[0]
dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self) dispatcher.send(signal=signals.post_init, sender=self.__class__, instance=self)


def from_sequence(cls, values):
"""
An alternate class constructor, primarily for internal use.
Creates a model instance from a sequence of values (which corresponds
to all the non-many-to-many fields in creation order. If there are more
fields than values, the remaining (final) fields are given their
default values.
ForeignKey fields can only be initialised using id values, not
instances, in this method.
"""
dispatcher.send(signal=signals.pre_init, sender=cls, args=values,
kwargs={})
obj = Empty()
obj.__class__ = cls
field_iter = iter(obj._meta.fields)
for val, field in izip(values, field_iter):
setattr(obj, field.attname, val)
for field in field_iter:
setattr(obj, field.attname, field.get_default())
dispatcher.send(signal=signals.post_init, sender=cls, instance=obj)
return obj

from_sequence = classmethod(from_sequence)

def __repr__(self): def __repr__(self):
return smart_str(u'<%s: %s>' % (self.__class__.__name__, unicode(self))) return smart_str(u'<%s: %s>' % (self.__class__.__name__, unicode(self)))


Expand Down
17 changes: 14 additions & 3 deletions django/db/models/query.py
Expand Up @@ -28,6 +28,17 @@ def __init__(self, model=None, query=None):
# PYTHON MAGIC METHODS # # PYTHON MAGIC METHODS #
######################## ########################


def __getstate__(self):
"""
Allows the Queryset to be pickled.
"""
# Force the cache to be fully populated.
len(self)

obj_dict = self.__dict__.copy()
obj_dict['_iter'] = None
return obj_dict

def __repr__(self): def __repr__(self):
return repr(list(self)) return repr(list(self))


Expand All @@ -37,7 +48,7 @@ def __len__(self):
# whilst not messing up any existing iterators against the queryset. # whilst not messing up any existing iterators against the queryset.
if self._result_cache is None: if self._result_cache is None:
if self._iter: if self._iter:
self._result_cache = list(self._iter()) self._result_cache = list(self._iter)
else: else:
self._result_cache = list(self.iterator()) self._result_cache = list(self.iterator())
elif self._iter: elif self._iter:
Expand Down Expand Up @@ -153,7 +164,7 @@ def iterator(self):
obj, _ = get_cached_row(self.model, row, index_start, obj, _ = get_cached_row(self.model, row, index_start,
max_depth, requested=requested) max_depth, requested=requested)
else: else:
obj = self.model.from_sequence(row[index_start:]) obj = self.model(*row[index_start:])
for i, k in enumerate(extra_select): for i, k in enumerate(extra_select):
setattr(obj, k, row[i]) setattr(obj, k, row[i])
yield obj yield obj
Expand Down Expand Up @@ -644,7 +655,7 @@ def get_cached_row(klass, row, index_start, max_depth=0, cur_depth=0,


restricted = requested is not None restricted = requested is not None
index_end = index_start + len(klass._meta.fields) index_end = index_start + len(klass._meta.fields)
obj = klass.from_sequence(row[index_start:index_end]) obj = klass(*row[index_start:index_end])
for f in klass._meta.fields: for f in klass._meta.fields:
if (not f.rel or (not restricted and f.null) or if (not f.rel or (not restricted and f.null) or
(restricted and f.name not in requested) or f.rel.parent_link): (restricted and f.name not in requested) or f.rel.parent_link):
Expand Down
21 changes: 20 additions & 1 deletion django/db/models/sql/query.py
Expand Up @@ -99,6 +99,24 @@ def __deepcopy__(self, memo):
memo[id(self)] = result memo[id(self)] = result
return result return result


def __getstate__(self):
"""
Pickling support.
"""
obj_dict = self.__dict__.copy()
del obj_dict['connection']
return obj_dict

def __setstate__(self, obj_dict):
"""
Unpickling support.
"""
self.__dict__.update(obj_dict)
# XXX: Need a better solution for this when multi-db stuff is
# supported. It's the only class-reference to the module-level
# connection variable.
self.connection = connection

def get_meta(self): def get_meta(self):
""" """
Returns the Options instance (the model._meta) from which to start Returns the Options instance (the model._meta) from which to start
Expand Down Expand Up @@ -376,7 +394,8 @@ def get_columns(self, with_aliases=False):
some cases to avoid ambiguitity with nested queries. some cases to avoid ambiguitity with nested queries.
""" """
qn = self.quote_name_unless_alias qn = self.quote_name_unless_alias
result = ['(%s) AS %s' % (col, alias) for alias, col in self.extra_select.iteritems()] qn2 = self.connection.ops.quote_name
result = ['(%s) AS %s' % (col, qn2(alias)) for alias, col in self.extra_select.iteritems()]
aliases = set(self.extra_select.keys()) aliases = set(self.extra_select.keys())
if with_aliases: if with_aliases:
col_aliases = aliases.copy() col_aliases = aliases.copy()
Expand Down
6 changes: 3 additions & 3 deletions django/db/models/sql/where.py
Expand Up @@ -51,12 +51,12 @@ def as_sql(self, node=None, qn=None):
format = '(%s)' format = '(%s)'
elif isinstance(child, tree.Node): elif isinstance(child, tree.Node):
sql, params = self.as_sql(child, qn) sql, params = self.as_sql(child, qn)
if len(child.children) == 1: if child.negated:
format = 'NOT (%s)'
elif len(child.children) == 1:
format = '%s' format = '%s'
else: else:
format = '(%s)' format = '(%s)'
if child.negated:
format = 'NOT %s' % format
else: else:
sql, params = self.make_atom(child, qn) sql, params = self.make_atom(child, qn)
format = '%s' format = '%s'
Expand Down
23 changes: 23 additions & 0 deletions docs/db-api.txt
Expand Up @@ -376,6 +376,29 @@ You can evaluate a ``QuerySet`` in the following ways:
iterating over a ``QuerySet`` will take advantage of your database to iterating over a ``QuerySet`` will take advantage of your database to
load data and instantiate objects only as you need them. load data and instantiate objects only as you need them.



Pickling QuerySets
~~~~~~~~~~~~~~~~~~

If you pickle_ a ``QuerySet``, this will also force all the results to be
loaded into memory prior to pickling. This is because pickling is usually used
as a precursor to caching and when the cached queryset is reloaded, you want
the results to already be present. This means that when you unpickle a
``QuerySet``, it contains the results at the moment it was pickled, rather
than the results that are currently in the database.

If you only want to pickle the necessary information to recreate the
``Queryset`` from the database at a later time, pickle the ``query`` attribute
of the ``QuerySet``. You can then recreate the original ``QuerySet`` (without
any results loaded) using some code like this::

>>> import pickle
>>> query = pickle.loads(s) # Assuming 's' is the pickled string.
>>> qs = MyModel.objects.all()
>>> qs.query = query # Restore the original 'query'.

.. _pickle: http://docs.python.org/lib/module-pickle.html

Limiting QuerySets Limiting QuerySets
------------------ ------------------


Expand Down
23 changes: 22 additions & 1 deletion docs/request_response.txt
Expand Up @@ -402,6 +402,27 @@ hard-coded strings. If you use this technique, follow these guidelines:
content, you can't use the ``HttpResponse`` instance as a file-like content, you can't use the ``HttpResponse`` instance as a file-like
object. Doing so will raise ``Exception``. object. Doing so will raise ``Exception``.


Setting headers
~~~~~~~~~~~~~~~

To set a header in your response, just treat it like a dictionary::

>>> response = HttpResponse()
>>> response['Pragma'] = 'no-cache'

Telling the browser to treat the response as a file attachment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

To tell the browser to treat the response as a file attachment, use the
``mimetype`` argument and set the ``Content-Disposition`` header. For example,
this is how you might return a Microsoft Excel spreadsheet::

>>> response = HttpResponse(my_data, mimetype='application/vnd.ms-excel')
>>> response['Content-Disposition'] = 'attachment; filename=foo.xls'

There's nothing Django-specific about the ``Content-Disposition`` header, but
it's easy to forget the syntax, so we've included it here.

Methods Methods
------- -------


Expand All @@ -420,7 +441,7 @@ Methods
but since this is actually the value included in the HTTP ``Content-Type`` but since this is actually the value included in the HTTP ``Content-Type``
header, it can also include the character set encoding, which makes it header, it can also include the character set encoding, which makes it
more than just a MIME type specification. If ``mimetype`` is specified more than just a MIME type specification. If ``mimetype`` is specified
(not None), that value is used. Otherwise, ``content_type`` is used. If (not ``None``), that value is used. Otherwise, ``content_type`` is used. If
neither is given, the ``DEFAULT_CONTENT_TYPE`` setting is used. neither is given, the ``DEFAULT_CONTENT_TYPE`` setting is used.


``__setitem__(header, value)`` ``__setitem__(header, value)``
Expand Down
12 changes: 12 additions & 0 deletions tests/modeltests/basic/models.py
Expand Up @@ -398,4 +398,16 @@ def __unicode__(self):
>>> s = set([a10, a11, a12]) >>> s = set([a10, a11, a12])
>>> Article.objects.get(headline='Article 11') in s >>> Article.objects.get(headline='Article 11') in s
True True
# The 'select' argument to extra() supports names with dashes in them, as long
# as you use values().
>>> Article.objects.filter(pub_date__year=2008).extra(select={'dashed-value': '1'}).values('headline', 'dashed-value')
[{'headline': u'Article 11', 'dashed-value': 1}, {'headline': u'Article 12', 'dashed-value': 1}]
# If you use 'select' with extra() and names containing dashes on a query
# that's *not* a values() query, those extra 'select' values will silently be
# ignored.
>>> articles = Article.objects.filter(pub_date__year=2008).extra(select={'dashed-value': '1', 'undashedvalue': '2'})
>>> articles[0].undashedvalue
2
""" """
8 changes: 4 additions & 4 deletions tests/regressiontests/queries/models.py
Expand Up @@ -120,13 +120,13 @@ class Meta:
# A model and custom default manager combination. # A model and custom default manager combination.
class CustomManager(models.Manager): class CustomManager(models.Manager):
def get_query_set(self): def get_query_set(self):
return super(CustomManager, self).get_query_set().filter(public=True, qs = super(CustomManager, self).get_query_set()
tag__name='t1') return qs.filter(is_public=True, tag__name='t1')


class ManagedModel(models.Model): class ManagedModel(models.Model):
data = models.CharField(max_length=10) data = models.CharField(max_length=10)
tag = models.ForeignKey(Tag) tag = models.ForeignKey(Tag)
public = models.BooleanField(default=True) is_public = models.BooleanField(default=True)


objects = CustomManager() objects = CustomManager()
normal_manager = models.Manager() normal_manager = models.Manager()
Expand Down Expand Up @@ -698,7 +698,7 @@ def __unicode__(self):
Bug #7095 Bug #7095
Updates that are filtered on the model being updated are somewhat tricky to get Updates that are filtered on the model being updated are somewhat tricky to get
in MySQL. This exercises that case. in MySQL. This exercises that case.
>>> mm = ManagedModel.objects.create(data='mm1', tag=t1, public=True) >>> mm = ManagedModel.objects.create(data='mm1', tag=t1, is_public=True)
>>> ManagedModel.objects.update(data='mm') >>> ManagedModel.objects.update(data='mm')
"""} """}
Expand Down

0 comments on commit 5922dab

Please sign in to comment.