Author: adrian
Date: 2007-01-22 22:31:02 -0600 (Mon, 22 Jan 2007)
New Revision: 4400
Modified:
django/branches/newforms-admin/django/db/models/manager.py
django/branches/newforms-admin/django/db/models/query.py
django/branches/newforms-admin/django/oldforms/__init__.py
django/branches/newforms-admin/docs/db-api.txt
django/branches/newforms-admin/docs/generic_views.txt
django/branches/newforms-admin/docs/model-api.txt
django/branches/newforms-admin/docs/settings.txt
django/branches/newforms-admin/docs/tutorial03.txt
django/branches/newforms-admin/docs/url_dispatch.txt
django/branches/newforms-admin/tests/modeltests/lookup/models.py
Log:
newforms-admin: Merged to [4399]
Modified: django/branches/newforms-admin/django/db/models/manager.py
===================================================================
--- django/branches/newforms-admin/django/db/models/manager.py 2007-01-23
02:30:55 UTC (rev 4399)
+++ django/branches/newforms-admin/django/db/models/manager.py 2007-01-23
04:31:02 UTC (rev 4400)
@@ -1,4 +1,4 @@
-from django.db.models.query import QuerySet
+from django.db.models.query import QuerySet, EmptyQuerySet
from django.dispatch import dispatcher
from django.db.models import signals
from django.db.models.fields import FieldDoesNotExist
@@ -41,12 +41,18 @@
#######################
# PROXIES TO QUERYSET #
#######################
+
+ def get_empty_query_set(self):
+ return EmptyQuerySet(self.model)
def get_query_set(self):
"""Returns a new QuerySet object. Subclasses can override this method
to easily customise the behaviour of the Manager.
"""
return QuerySet(self.model)
+
+ def none(self):
+ return self.get_empty_query_set()
def all(self):
return self.get_query_set()
Modified: django/branches/newforms-admin/django/db/models/query.py
===================================================================
--- django/branches/newforms-admin/django/db/models/query.py 2007-01-23
02:30:55 UTC (rev 4399)
+++ django/branches/newforms-admin/django/db/models/query.py 2007-01-23
04:31:02 UTC (rev 4400)
@@ -25,6 +25,9 @@
# Larger values are slightly faster at the expense of more storage space.
GET_ITERATOR_CHUNK_SIZE = 100
+class EmptyResultSet(Exception):
+ pass
+
####################
# HELPER FUNCTIONS #
####################
@@ -168,7 +171,12 @@
extra_select = self._select.items()
cursor = connection.cursor()
- select, sql, params = self._get_sql_clause()
+
+ try:
+ select, sql, params = self._get_sql_clause()
+ except EmptyResultSet:
+ raise StopIteration
+
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") +
",".join(select) + sql, params)
fill_cache = self._select_related
index_end = len(self.model._meta.fields)
@@ -192,7 +200,12 @@
counter._offset = None
counter._limit = None
counter._select_related = False
- select, sql, params = counter._get_sql_clause()
+
+ try:
+ select, sql, params = counter._get_sql_clause()
+ except EmptyResultSet:
+ return 0
+
cursor = connection.cursor()
if self._distinct:
id_col = "%s.%s" % (backend.quote_name(self.model._meta.db_table),
@@ -523,7 +536,12 @@
field_names = [f.attname for f in self.model._meta.fields]
cursor = connection.cursor()
- select, sql, params = self._get_sql_clause()
+
+ try:
+ select, sql, params = self._get_sql_clause()
+ except EmptyResultSet:
+ raise StopIteration
+
select = ['%s.%s' % (backend.quote_name(self.model._meta.db_table),
backend.quote_name(c)) for c in columns]
cursor.execute("SELECT " + (self._distinct and "DISTINCT " or "") +
",".join(select) + sql, params)
while 1:
@@ -545,7 +563,12 @@
if self._field.null:
self._where.append('%s.%s IS NOT NULL' % \
(backend.quote_name(self.model._meta.db_table),
backend.quote_name(self._field.column)))
- select, sql, params = self._get_sql_clause()
+
+ try:
+ select, sql, params = self._get_sql_clause()
+ except EmptyResultSet:
+ raise StopIteration
+
sql = 'SELECT %s %s GROUP BY 1 ORDER BY 1 %s' % \
(backend.get_date_trunc_sql(self._kind, '%s.%s' %
(backend.quote_name(self.model._meta.db_table),
backend.quote_name(self._field.column))), sql, self._order)
@@ -562,7 +585,26 @@
c._kind = self._kind
c._order = self._order
return c
+
+class EmptyQuerySet(QuerySet):
+ def __init__(self, model=None):
+ super(EmptyQuerySet, self).__init__(model)
+ self._result_cache = []
+
+ def iterator(self):
+ raise StopIteration
+
+ def count(self):
+ return 0
+
+ def delete(self):
+ pass
+ def _clone(self, klass=None, **kwargs):
+ c = super(EmptyQuerySet, self)._clone(klass, **kwargs)
+ c._result_cache = []
+ return c
+
class QOperator(object):
"Base class for QAnd and QOr"
def __init__(self, *args):
@@ -571,10 +613,14 @@
def get_sql(self, opts):
joins, where, params = SortedDict(), [], []
for val in self.args:
- joins2, where2, params2 = val.get_sql(opts)
- joins.update(joins2)
- where.extend(where2)
- params.extend(params2)
+ try:
+ joins2, where2, params2 = val.get_sql(opts)
+ joins.update(joins2)
+ where.extend(where2)
+ params.extend(params2)
+ except EmptyResultSet:
+ if not isinstance(self, QOr):
+ raise EmptyResultSet
if where:
return joins, ['(%s)' % self.operator.join(where)], params
return joins, [], params
@@ -628,8 +674,11 @@
self.q = q
def get_sql(self, opts):
- joins, where, params = self.q.get_sql(opts)
- where2 = ['(NOT (%s))' % " AND ".join(where)]
+ try:
+ joins, where, params = self.q.get_sql(opts)
+ where2 = ['(NOT (%s))' % " AND ".join(where)]
+ except EmptyResultSet:
+ return SortedDict(), [], []
return joins, where2, params
def get_where_clause(lookup_type, table_prefix, field_name, value):
@@ -645,11 +694,7 @@
if in_string:
return '%s%s IN (%s)' % (table_prefix, field_name, in_string)
else:
- # Most backends do not accept an empty string inside the IN
- # expression, i.e. cannot do "WHERE ... IN ()". Since there are
- # also some backends that do not accept "WHERE false", we instead
- # use an expression that always evaluates to False.
- return '0=1'
+ raise EmptyResultSet
elif lookup_type == 'range':
return '%s%s BETWEEN %%s AND %%s' % (table_prefix, field_name)
elif lookup_type in ('year', 'month', 'day'):
Modified: django/branches/newforms-admin/django/oldforms/__init__.py
===================================================================
--- django/branches/newforms-admin/django/oldforms/__init__.py 2007-01-23
02:30:55 UTC (rev 4399)
+++ django/branches/newforms-admin/django/oldforms/__init__.py 2007-01-23
04:31:02 UTC (rev 4400)
@@ -569,7 +569,7 @@
"This SelectField provides 'Yes', 'No' and 'Unknown', mapping results to
True, False or None"
def __init__(self, field_name, is_required=False, validator_list=None):
if validator_list is None: validator_list = []
- SelectField.__init__(self, field_name, choices=[('1', 'Unknown'),
('2', 'Yes'), ('3', 'No')],
+ SelectField.__init__(self, field_name, choices=[('1', _('Unknown')),
('2', _('Yes')), ('3', _('No'))],
is_required=is_required, validator_list=validator_list)
def render(self, data):
Modified: django/branches/newforms-admin/docs/db-api.txt
===================================================================
--- django/branches/newforms-admin/docs/db-api.txt 2007-01-23 02:30:55 UTC
(rev 4399)
+++ django/branches/newforms-admin/docs/db-api.txt 2007-01-23 04:31:02 UTC
(rev 4400)
@@ -525,7 +525,22 @@
[datetime.datetime(2005, 3, 20), datetime.datetime(2005, 2, 20)]
>>> Entry.objects.filter(headline__contains='Lennon').dates('pub_date',
'day')
[datetime.datetime(2005, 3, 20)]
+
+``none()``
+~~~~~~~~~~
+**New in Django development version**
+
+Returns an ``EmptyQuerySet`` -- a ``QuerySet`` that always evaluates to
+an empty list. This can be used in cases where you know that you should
+return an empty result set and your caller is expecting a ``QuerySet``
+object (instead of returning an empty list, for example.)
+
+Examples::
+
+ >>> Entry.objects.none()
+ []
+
``select_related()``
~~~~~~~~~~~~~~~~~~~~
Modified: django/branches/newforms-admin/docs/generic_views.txt
===================================================================
--- django/branches/newforms-admin/docs/generic_views.txt 2007-01-23
02:30:55 UTC (rev 4399)
+++ django/branches/newforms-admin/docs/generic_views.txt 2007-01-23
04:31:02 UTC (rev 4400)
@@ -99,7 +99,7 @@
dictionary is callable, the generic view will call it
just before rendering the template. (**This is new in the
Django development version.**)
-
+
**Example:**
Given the following URL patterns::
@@ -205,11 +205,11 @@
``<app_label>/<model_name>_archive.html`` by default, where:
* ``<model_name>`` is your model's name in all lowercase. For a model
- ``StaffMember``, that'd be ``staffmember``.
+ ``StaffMember``, that'd be ``staffmember``.
* ``<app_label>`` is the right-most part of the full Python path to
- your model's app. For example, if your model lives in
- ``apps/blog/models.py``, that'd be ``blog``.
+ your model's app. For example, if your model lives in
+ ``apps/blog/models.py``, that'd be ``blog``.
**Template context:**
@@ -266,9 +266,9 @@
the view's template. See the `RequestContext docs`_.
* ``template_object_name``: Designates the name of the template variable
- to use in the template context. By default, this is ``'object'``. The
- view will append ``'_list'`` to the value of this parameter in
- determining the variable's name.
+ to use in the template context. By default, this is ``'object'``. The
+ view will append ``'_list'`` to the value of this parameter in
+ determining the variable's name.
* ``make_object_list``: A boolean specifying whether to retrieve the full
list of objects for this year and pass those to the template. If
``True``,
@@ -360,9 +360,9 @@
the view's template. See the `RequestContext docs`_.
* ``template_object_name``: Designates the name of the template variable
- to use in the template context. By default, this is ``'object'``. The
- view will append ``'_list'`` to the value of this parameter in
- determining the variable's name.
+ to use in the template context. By default, this is ``'object'``. The
+ view will append ``'_list'`` to the value of this parameter in
+ determining the variable's name.
* ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
@@ -441,9 +441,9 @@
the view's template. See the `RequestContext docs`_.
* ``template_object_name``: Designates the name of the template variable
- to use in the template context. By default, this is ``'object'``. The
- view will append ``'_list'`` to the value of this parameter in
- determining the variable's name.
+ to use in the template context. By default, this is ``'object'``. The
+ view will append ``'_list'`` to the value of this parameter in
+ determining the variable's name.
* ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
@@ -526,9 +526,9 @@
the view's template. See the `RequestContext docs`_.
* ``template_object_name``: Designates the name of the template variable
- to use in the template context. By default, this is ``'object'``. The
- view will append ``'_list'`` to the value of this parameter in
- determining the variable's name.
+ to use in the template context. By default, this is ``'object'``. The
+ view will append ``'_list'`` to the value of this parameter in
+ determining the variable's name.
* ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
@@ -638,7 +638,7 @@
the view's template. See the `RequestContext docs`_.
* ``template_object_name``: Designates the name of the template variable
- to use in the template context. By default, this is ``'object'``.
+ to use in the template context. By default, this is ``'object'``.
* ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
@@ -710,9 +710,9 @@
the view's template. See the `RequestContext docs`_.
* ``template_object_name``: Designates the name of the template variable
- to use in the template context. By default, this is ``'object'``. The
- view will append ``'_list'`` to the value of this parameter in
- determining the variable's name.
+ to use in the template context. By default, this is ``'object'``. The
+ view will append ``'_list'`` to the value of this parameter in
+ determining the variable's name.
* ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
@@ -824,7 +824,7 @@
the view's template. See the `RequestContext docs`_.
* ``template_object_name``: Designates the name of the template variable
- to use in the template context. By default, this is ``'object'``.
+ to use in the template context. By default, this is ``'object'``.
* ``mimetype``: The MIME type to use for the resulting document. Defaults
to the value of the ``DEFAULT_CONTENT_TYPE`` setting.
@@ -973,7 +973,7 @@
the view's template. See the `RequestContext docs`_.
* ``template_object_name``: Designates the name of the template variable
- to use in the template context. By default, this is ``'object'``.
+ to use in the template context. By default, this is ``'object'``.
**Template name:**
@@ -1054,7 +1054,7 @@
the view's template. See the `RequestContext docs`_.
* ``template_object_name``: Designates the name of the template variable
- to use in the template context. By default, this is ``'object'``.
+ to use in the template context. By default, this is ``'object'``.
**Template name:**
Modified: django/branches/newforms-admin/docs/model-api.txt
===================================================================
--- django/branches/newforms-admin/docs/model-api.txt 2007-01-23 02:30:55 UTC
(rev 4399)
+++ django/branches/newforms-admin/docs/model-api.txt 2007-01-23 04:31:02 UTC
(rev 4400)
@@ -1408,8 +1408,11 @@
somebody submits a search query in that text box.
These fields should be some kind of text field, such as ``CharField`` or
-``TextField``.
+``TextField``. You can also perform a related lookup on a ``ForeignKey`` with
+the lookup API "follow" notation::
+ search_fields = ['foreign_key__related_fieldname']
+
When somebody does a search in the admin search box, Django splits the search
query into words and returns all objects that contain each of the words, case
insensitive, where each word must be in at least one of ``search_fields``. For
Modified: django/branches/newforms-admin/docs/settings.txt
===================================================================
--- django/branches/newforms-admin/docs/settings.txt 2007-01-23 02:30:55 UTC
(rev 4399)
+++ django/branches/newforms-admin/docs/settings.txt 2007-01-23 04:31:02 UTC
(rev 4400)
@@ -557,6 +557,11 @@
URL that handles the media served from ``MEDIA_ROOT``.
Example: ``"http://media.lawrence.com"``
+Note that this should have a trailing slash if it has a path component.
+
+Good: ``"http://www.example.com/static/"``
+Bad: ``"http://www.example.com/static"``
+
MIDDLEWARE_CLASSES
------------------
Modified: django/branches/newforms-admin/docs/tutorial03.txt
===================================================================
--- django/branches/newforms-admin/docs/tutorial03.txt 2007-01-23 02:30:55 UTC
(rev 4399)
+++ django/branches/newforms-admin/docs/tutorial03.txt 2007-01-23 04:31:02 UTC
(rev 4400)
@@ -260,8 +260,7 @@
latest_poll_list = Poll.objects.all().order_by('-pub_date')[:5]
return render_to_response('polls/index.html', {'latest_poll_list':
latest_poll_list})
-Note that we no longer need to import ``loader``, ``Context`` or
-``HttpResponse``.
+Note that once we've done this in all these views, we no longer need to import
``loader``, ``Context`` and ``HttpResponse``.
The ``render_to_response()`` function takes a template name as its first
argument and a dictionary as its optional second argument. It returns an
Modified: django/branches/newforms-admin/docs/url_dispatch.txt
===================================================================
--- django/branches/newforms-admin/docs/url_dispatch.txt 2007-01-23
02:30:55 UTC (rev 4399)
+++ django/branches/newforms-admin/docs/url_dispatch.txt 2007-01-23
04:31:02 UTC (rev 4400)
@@ -393,7 +393,7 @@
Passing extra options to ``include()``
--------------------------------------
-**New in the Django development version.**
+**New in Django development version.**
Similarly, you can pass extra options to ``include()``. When you pass extra
options to ``include()``, *each* line in the included URLconf will be passed
@@ -435,7 +435,7 @@
Passing callable objects instead of strings
===========================================
-**New in the Django development version.**
+**New in Django development version.**
Some developers find it more natural to pass the actual Python function object
rather than a string containing the path to its module. This alternative is
Modified: django/branches/newforms-admin/tests/modeltests/lookup/models.py
===================================================================
--- django/branches/newforms-admin/tests/modeltests/lookup/models.py
2007-01-23 02:30:55 UTC (rev 4399)
+++ django/branches/newforms-admin/tests/modeltests/lookup/models.py
2007-01-23 04:31:02 UTC (rev 4400)
@@ -191,4 +191,19 @@
>>> Article.objects.filter(headline__contains='\\')
[<Article: Article with \ backslash>]
+# none() returns an EmptyQuerySet that behaves like any other QuerySet object
+>>> Article.objects.none()
+[]
+>>> Article.objects.none().filter(headline__startswith='Article')
+[]
+>>> Article.objects.none().count()
+0
+
+# using __in with an empty list should return an empty query set
+>>> Article.objects.filter(id__in=[])
+[]
+
+>>> Article.objects.exclude(id__in=[])
+[<Article: Article with \ backslash>, <Article: Article% with percent sign>,
<Article: Article_ with underscore>, <Article: Article 5>, <Article: Article
6>, <Article: Article 4>, <Article: Article 2>, <Article: Article 3>, <Article:
Article 7>, <Article: Article 1>]
+
"""}
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/django-updates?hl=en
-~----------~----~----~----~------~----~------~--~---