1
0
mirror of https://github.com/django/django.git synced 2025-07-04 01:39:20 +00:00

gis: Merged revisions 7485-7491,7493-7497 via svnmerge from trunk.

git-svn-id: http://code.djangoproject.com/svn/django/branches/gis@7498 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Justin Bronn 2008-04-28 14:08:46 +00:00
parent a125a1c9b6
commit e5b52f90f0
12 changed files with 119 additions and 46 deletions

View File

@ -716,24 +716,9 @@ class ChangeList(object):
qs = qs.select_related()
break
# Calculate lookup_order_field.
# If the order-by field is a field with a relationship, order by the
# value in the related table.
lookup_order_field = self.order_field
try:
f = self.lookup_opts.get_field(self.order_field, many_to_many=False)
except models.FieldDoesNotExist:
pass
else:
if isinstance(f.rel, models.OneToOneRel):
# For OneToOneFields, don't try to order by the related object's ordering criteria.
pass
elif isinstance(f.rel, models.ManyToOneRel):
rel_ordering = f.rel.to._meta.ordering and f.rel.to._meta.ordering[0] or f.rel.to._meta.pk.column
lookup_order_field = '%s.%s' % (f.rel.to._meta.db_table, rel_ordering)
# Set ordering.
qs = qs.order_by((self.order_type == 'desc' and '-' or '') + lookup_order_field)
if self.order_field:
qs = qs.order_by('%s%s' % ((self.order_type == 'desc' and '-' or ''), self.order_field))
# Apply keyword searches.
def construct_search(field_name):

View File

@ -51,6 +51,7 @@ class BaseDatabaseFeatures(object):
uses_case_insensitive_names = False
uses_custom_query_class = False
empty_fetchmany_value = []
update_can_self_select = True
class BaseDatabaseOperations(object):
"""

View File

@ -63,6 +63,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
autoindexes_primary_keys = False
inline_fk_references = False
empty_fetchmany_value = ()
update_can_self_select = False
class DatabaseOperations(BaseDatabaseOperations):
def date_extract_sql(self, lookup_type, field_name):

View File

@ -67,6 +67,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
autoindexes_primary_keys = False
inline_fk_references = False
empty_fetchmany_value = ()
update_can_self_select = False
class DatabaseOperations(BaseDatabaseOperations):
def date_extract_sql(self, lookup_type, field_name):

View File

@ -9,6 +9,7 @@ except ImportError:
from django.db import get_creation_module
from django.db.models import signals
from django.db.models.query_utils import QueryWrapper
from django.dispatch import dispatcher
from django.conf import settings
from django.core import validators
@ -226,6 +227,9 @@ class Field(object):
def get_db_prep_lookup(self, lookup_type, value):
"Returns field's value prepared for database lookup."
if hasattr(value, 'as_sql'):
sql, params = value.as_sql()
return QueryWrapper(('(%s)' % sql), params)
if lookup_type in ('exact', 'regex', 'iregex', 'gt', 'gte', 'lt', 'lte', 'month', 'day', 'search'):
return [value]
elif lookup_type in ('range', 'in'):

View File

@ -284,6 +284,7 @@ class QuerySet(object):
query = self.query.clone(sql.UpdateQuery)
query.add_update_values(kwargs)
query.execute_sql(None)
transaction.commit_unless_managed()
self._result_cache = None
update.alters_data = True
@ -496,9 +497,6 @@ class ValuesQuerySet(QuerySet):
# QuerySet.clone() will also set up the _fields attribute with the
# names of the model fields to select.
def __iter__(self):
return self.iterator()
def iterator(self):
self.query.trim_extra_select(self.extra_names)
names = self.query.extra_select.keys() + self.field_names

View File

@ -28,7 +28,7 @@ NULLABLE = 6
MULTI = 'multi'
SINGLE = 'single'
ORDER_PATTERN = re.compile(r'\?|[-+]?\w+$')
ORDER_PATTERN = re.compile(r'\?|[-+]?[.\w]+$')
ORDER_DIR = {
'ASC': ('ASC', 'DESC'),
'DESC': ('DESC', 'ASC')}

View File

@ -895,9 +895,15 @@ class Query(object):
Add a single filter to the query. The 'filter_expr' is a pair:
(filter_string, value). E.g. ('name__contains', 'fred')
If 'negate' is True, this is an exclude() filter. If 'trim' is True, we
automatically trim the final join group (used internally when
constructing nested queries).
If 'negate' is True, this is an exclude() filter. It's important to
note that this method does not negate anything in the where-clause
object when inserting the filter constraints. This is because negated
filters often require multiple calls to add_filter() and the negation
should only happen once. So the caller is responsible for this (the
caller will normally be add_q(), so that as an example).
If 'trim' is True, we automatically trim the final join group (used
internally when constructing nested queries).
If 'can_reuse' is a set, we are processing a component of a
multi-component filter (e.g. filter(Q1, Q2)). In this case, 'can_reuse'
@ -1001,7 +1007,6 @@ class Query(object):
self.where.add((alias, col, field, lookup_type, value), connector)
if negate:
self.where.negate()
for alias in join_list:
self.promote_alias(alias)
if final > 1 and lookup_type != 'isnull':
@ -1039,12 +1044,12 @@ class Query(object):
self.where.start_subtree(connector)
self.add_q(child, used_aliases)
self.where.end_subtree()
if q_object.negated:
self.where.children[-1].negate()
else:
self.add_filter(child, connector, q_object.negated,
can_reuse=used_aliases)
connector = q_object.connector
if q_object.negated:
self.where.negate()
if subtree:
self.where.end_subtree()

View File

@ -95,7 +95,7 @@ class UpdateQuery(Query):
def _setup_query(self):
"""
Runs on initialisation and after cloning. Any attributes that would
Runs on initialization and after cloning. Any attributes that would
normally be set in __init__ should go in here, instead, so that they
are also set up after a clone() call.
"""
@ -159,20 +159,37 @@ class UpdateQuery(Query):
# from other tables.
query = self.clone(klass=Query)
query.bump_prefix()
query.select = []
query.extra_select = {}
query.add_fields([query.model._meta.pk.name])
first_table = query.tables[0]
if query.alias_refcount[first_table] == 1:
# We can remove one table from the inner query.
query.unref_alias(first_table)
for i in xrange(1, len(query.tables)):
table = query.tables[i]
if query.alias_refcount[table]:
break
join_info = query.alias_map[table]
query.select = [(join_info[RHS_ALIAS], join_info[RHS_JOIN_COL])]
must_pre_select = False
else:
query.select = []
query.add_fields([query.model._meta.pk.name])
must_pre_select = not self.connection.features.update_can_self_select
# Now we adjust the current query: reset the where clause and get rid
# of all the tables we don't need (since they're in the sub-select).
self.where = self.where_class()
if self.related_updates:
if self.related_updates or must_pre_select:
# Either we're using the idents in multiple update queries (so
# don't want them to change), or the db backend doesn't support
# selecting from the updating table (e.g. MySQL).
idents = []
for rows in query.execute_sql(MULTI):
idents.extend([r[0] for r in rows])
self.add_filter(('pk__in', idents))
self.related_ids = idents
else:
# The fast path. Filters and updates in one query.
self.add_filter(('pk__in', query))
for alias in self.tables[1:]:
self.alias_refcount[alias] = 0
@ -349,6 +366,7 @@ class DateQuery(Query):
self.connection.ops.date_trunc_sql)
self.select = [select]
self.select_fields = [None]
self.select_related = False # See #7097.
self.distinct = True
self.order_by = order == 'ASC' and [1] or [-1]

View File

@ -527,7 +527,7 @@ applied to a query, not even the default ordering, call ``order_by()`` with no
parameters.
**New in Django development version:** The syntax for ordering across related
models has changed. See the `Django 0.96 documentation`_ for the old behaviour.
models has changed. See the `Django 0.96 documentation`_ for the old behavior.
.. _Django 0.96 documentation: http://www.djangoproject.com/documentation/0.96/model-api/#floatfield
@ -540,9 +540,9 @@ backend normally orders them.
**New in Django development version**
If you want to reverse the order in which a queryset's elements are returned,
you can use the ``reverse()`` method. Calling ``reverse()`` a second time
restores the ordering back to the normal direction.
Use the ``reverse()`` method to reverse the order in which a queryset's
elements are returned. Calling ``reverse()`` a second time restores the
ordering back to the normal direction.
To retrieve the ''last'' five items in a queryset, you could do this::
@ -552,7 +552,7 @@ Note that this is not quite the same as slicing from the end of a sequence in
Python. The above example will return the last item first, then the
penultimate item and so on. If we had a Python sequence and looked at
``seq[:-5]``, we would see the fifth-last item first. Django doesn't support
that mode of access (slicing from the end), since it is not possible to do it
that mode of access (slicing from the end), because it's not possible to do it
efficiently in SQL.
``distinct()``
@ -1660,7 +1660,7 @@ entry. The entries select by the second filter may or may not be the same as
the entries in the first filter. We are filtering the ``Blog`` items with each
filter statement, not the ``Entry`` items.
All of this behaviour also applies to ``exclude()``: all the conditions in a
All of this behavior also applies to ``exclude()``: all the conditions in a
single ``exclude()`` statement apply to a single instance (if those conditions
are talking about the same multi-valued relation). Conditions in subsequent
``filter()`` or ``exclude()`` calls that refer to the same relation may end up
@ -2101,24 +2101,24 @@ Updating multiple objects at once
**New in Django development version**
Sometimes you want to set a field to a particular value for all the objects in
a queryset. You can do this with the ``update()`` method. For example::
a ``QuerySet``. You can do this with the ``update()`` method. For example::
# Update all the headlines to the same value.
Entry.objects.all().update(headline='Everything is the same')
# Update all the headlines with pub_date in 2007.
Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')
You can only set non-relation fields and ``ForeignKey`` fields using this
method and the value you set the field to must be a normal Python value (you
can't set a field to be equal to some other field at the moment).
method, and the value you set the field to must be a hard-coded Python value
(i.e., you can't set a field to be equal to some other field at the moment).
To update ``ForeignKey`` fields, set the new value to be the new model
instance you want to point to. Example::
b = Blog.objects.get(pk=1)
# Make all entries belong to this blog.
# Change every Entry so that it belongs to this Blog.
Entry.objects.all().update(blog=b)
The ``update()`` method is applied instantly and doesn't return anything
(similar to ``delete()``). The only restriction on the queryset that is
(similar to ``delete()``). The only restriction on the ``QuerySet`` that is
updated is that it can only access one database table, the model's main
table. So don't try to filter based on related fields or anything like that;
it won't work.

View File

@ -246,7 +246,7 @@ FieldError: Cannot resolve keyword 'reporter_id' into field. Choices are: headli
>>> Reporter.objects.filter(article__reporter__exact=r).distinct()
[<Reporter: John Smith>]
# Check that implied __exact also works
# Check that implied __exact also works.
>>> Reporter.objects.filter(article__reporter=r).distinct()
[<Reporter: John Smith>]
@ -266,11 +266,24 @@ True
>>> Reporter.objects.order_by('first_name')
[<Reporter: John Smith>]
# Deletes using a join in the query
# You can delete using a JOIN in the query.
>>> Reporter.objects.filter(article__headline__startswith='This').delete()
>>> Reporter.objects.all()
[]
>>> Article.objects.all()
[]
# Check that Article.objects.select_related().dates() works properly when
# there are multiple Articles with the same date but different foreign-key
# objects (Reporters).
>>> r1 = Reporter.objects.create(first_name='Mike', last_name='Royko', email='royko@suntimes.com')
>>> r2 = Reporter.objects.create(first_name='John', last_name='Kass', email='jkass@tribune.com')
>>> a1 = Article.objects.create(headline='First', pub_date=datetime(1980, 4, 23), reporter=r1)
>>> a2 = Article.objects.create(headline='Second', pub_date=datetime(1980, 4, 23), reporter=r2)
>>> Article.objects.select_related().dates('pub_date', 'day')
[datetime.datetime(1980, 4, 23, 0, 0)]
>>> Article.objects.select_related().dates('pub_date', 'month')
[datetime.datetime(1980, 4, 1, 0, 0)]
>>> Article.objects.select_related().dates('pub_date', 'year')
[datetime.datetime(1980, 1, 1, 0, 0)]
"""}

View File

@ -117,6 +117,24 @@ class LoopZ(models.Model):
class Meta:
ordering = ['z']
# A model and custom default manager combination.
class CustomManager(models.Manager):
def get_query_set(self):
return super(CustomManager, self).get_query_set().filter(public=True,
tag__name='t1')
class ManagedModel(models.Model):
data = models.CharField(max_length=10)
tag = models.ForeignKey(Tag)
public = models.BooleanField(default=True)
objects = CustomManager()
normal_manager = models.Manager()
def __unicode__(self):
return self.data
__test__ = {'API_TESTS':"""
>>> t1 = Tag(name='t1')
>>> t1.save()
@ -654,5 +672,34 @@ Bug #7045 -- extra tables used to crash SQL construction on the second use.
>>> s = qs.query.as_sql()
>>> s = qs.query.as_sql() # test passes if this doesn't raise an exception.
Bug #7098 -- Make sure semi-deprecated ordering by related models syntax still
works.
>>> Item.objects.values('note__note').order_by('queries_note.note', 'id')
[{'note__note': u'n2'}, {'note__note': u'n3'}, {'note__note': u'n3'}, {'note__note': u'n3'}]
Bug #7096 -- Make sure exclude() with multiple conditions continues to work.
>>> Tag.objects.filter(parent=t1, name='t3').order_by('name')
[<Tag: t3>]
>>> Tag.objects.exclude(parent=t1, name='t3').order_by('name')
[<Tag: t1>, <Tag: t2>, <Tag: t4>, <Tag: t5>]
>>> Item.objects.exclude(tags__name='t1', name='one').order_by('name').distinct()
[<Item: four>, <Item: three>, <Item: two>]
>>> Item.objects.filter(name__in=['three', 'four']).exclude(tags__name='t1').order_by('name')
[<Item: four>, <Item: three>]
More twisted cases, involving nested negations.
>>> Item.objects.exclude(~Q(tags__name='t1', name='one'))
[<Item: one>]
>>> Item.objects.filter(~Q(tags__name='t1', name='one'), name='two')
[<Item: two>]
>>> Item.objects.exclude(~Q(tags__name='t1', name='one'), name='two')
[<Item: four>, <Item: one>, <Item: three>]
Bug #7095
Updates that are filtered on the model being updated are somewhat tricky to get
in MySQL. This exercises that case.
>>> mm = ManagedModel.objects.create(data='mm1', tag=t1, public=True)
>>> ManagedModel.objects.update(data='mm')
"""}