Permalink
Browse files

[soc2009/multidb] Merged up to trunk r11240.

git-svn-id: http://code.djangoproject.com/svn/django/branches/soc2009/multidb@11247 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
1 parent 94e002c commit 08ab082480f1beaf8dac5cddd0948a1c26f574d8 @alex alex committed Jul 16, 2009
View
@@ -131,6 +131,7 @@ answer newbie questions, and generally made Django that much better:
Andrew Durdin <adurdin@gmail.com>
dusk@woofle.net
Andy Dustman <farcepest@gmail.com>
+ J. Clifford Dyer <jcd@unc.edu>
Clint Ecker
Nick Efford <nick@efford.org>
eibaan@gmail.com
@@ -114,20 +114,20 @@ def add_action(self, action, name=None):
name = name or action.__name__
self._actions[name] = action
self._global_actions[name] = action
-
+
def disable_action(self, name):
"""
Disable a globally-registered action. Raises KeyError for invalid names.
"""
del self._actions[name]
-
+
def get_action(self, name):
"""
Explicitally get a registered global action wheather it's enabled or
not. Raises KeyError for invalid names.
"""
return self._global_actions[name]
-
+
def actions(self):
"""
Get all the enabled actions as an iterable of (name, func).
@@ -159,9 +159,9 @@ def check_dependencies(self):
if 'django.core.context_processors.auth' not in settings.TEMPLATE_CONTEXT_PROCESSORS:
raise ImproperlyConfigured("Put 'django.core.context_processors.auth' in your TEMPLATE_CONTEXT_PROCESSORS setting in order to use the admin application.")
- def admin_view(self, view):
+ def admin_view(self, view, cacheable=False):
"""
- Decorator to create an "admin view attached to this ``AdminSite``. This
+ Decorator to create an admin view attached to this ``AdminSite``. This
wraps the view and provides permission checking by calling
``self.has_permission``.
@@ -177,19 +177,25 @@ def get_urls(self):
url(r'^my_view/$', self.admin_view(some_view))
)
return urls
+
+ By default, admin_views are marked non-cacheable using the
+ ``never_cache`` decorator. If the view can be safely cached, set
+ cacheable=True.
"""
def inner(request, *args, **kwargs):
if not self.has_permission(request):
return self.login(request)
return view(request, *args, **kwargs)
+ if not cacheable:
+ inner = never_cache(inner)
return update_wrapper(inner, view)
def get_urls(self):
from django.conf.urls.defaults import patterns, url, include
- def wrap(view):
+ def wrap(view, cacheable=False):
def wrapper(*args, **kwargs):
- return self.admin_view(view)(*args, **kwargs)
+ return self.admin_view(view, cacheable)(*args, **kwargs)
return update_wrapper(wrapper, view)
# Admin-site-wide views.
@@ -201,13 +207,13 @@ def wrapper(*args, **kwargs):
wrap(self.logout),
name='%sadmin_logout'),
url(r'^password_change/$',
- wrap(self.password_change),
+ wrap(self.password_change, cacheable=True),
name='%sadmin_password_change' % self.name),
url(r'^password_change/done/$',
- wrap(self.password_change_done),
+ wrap(self.password_change_done, cacheable=True),
name='%sadmin_password_change_done' % self.name),
url(r'^jsi18n/$',
- wrap(self.i18n_javascript),
+ wrap(self.i18n_javascript, cacheable=True),
name='%sadmin_jsi18n' % self.name),
url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$',
'django.views.defaults.shortcut'),
@@ -19,6 +19,9 @@ def area(self, *args, **kwargs):
def centroid(self, *args, **kwargs):
return self.get_query_set().centroid(*args, **kwargs)
+ def collect(self, *args, **kwargs):
+ return self.get_query_set().collect(*args, **kwargs)
+
def difference(self, *args, **kwargs):
return self.get_query_set().difference(*args, **kwargs)
@@ -62,6 +62,14 @@ def centroid(self, **kwargs):
"""
return self._geom_attribute('centroid', **kwargs)
+ def collect(self, **kwargs):
+ """
+ Performs an aggregate collect operation on the given geometry field.
+ This is analagous to a union operation, but much faster because
+ boundaries are not dissolved.
+ """
+ return self._spatial_aggregate(aggregates.Collect, **kwargs)
+
def difference(self, geom, **kwargs):
"""
Returns the spatial difference of the geographic field in a `difference`
@@ -1,7 +1,7 @@
import os, unittest
from django.contrib.gis.geos import *
from django.contrib.gis.db.backend import SpatialBackend
-from django.contrib.gis.db.models import Count, Extent, F, Union
+from django.contrib.gis.db.models import Collect, Count, Extent, F, Union
from django.contrib.gis.tests.utils import no_mysql, no_oracle, no_spatialite
from django.conf import settings
from models import City, Location, DirectoryEntry, Parcel, Book, Author
@@ -237,7 +237,7 @@ def test12_count(self):
# as Dallas.
dallas = City.objects.get(name='Dallas')
ftworth = City.objects.create(name='Fort Worth', state='TX', location=dallas.location)
-
+
# Count annotation should be 2 for the Dallas location now.
loc = Location.objects.annotate(num_cities=Count('city')).get(id=dallas.location.id)
self.assertEqual(2, loc.num_cities)
@@ -250,7 +250,7 @@ def test12_count(self):
Book.objects.create(title='Blank Spots on the Map', author=tp)
wp = Author.objects.create(name='William Patry')
Book.objects.create(title='Patry on Copyright', author=wp)
-
+
# Should only be one author (Trevor Paglen) returned by this query, and
# the annotation should have 3 for the number of books.
qs = Author.objects.annotate(num_books=Count('books')).filter(num_books__gt=1)
@@ -264,6 +264,27 @@ def test13_select_related_null_fk(self):
# Should be `None`, and not a 'dummy' model.
self.assertEqual(None, b.author)
+ @no_mysql
+ @no_oracle
+ @no_spatialite
+ def test14_collect(self):
+ "Testing the `collect` GeoQuerySet method and `Collect` aggregate."
+ # Reference query:
+ # SELECT AsText(ST_Collect("relatedapp_location"."point")) FROM "relatedapp_city" LEFT OUTER JOIN
+ # "relatedapp_location" ON ("relatedapp_city"."location_id" = "relatedapp_location"."id")
+ # WHERE "relatedapp_city"."state" = 'TX';
+ ref_geom = fromstr('MULTIPOINT(-97.516111 33.058333,-96.801611 32.782057,-95.363151 29.763374,-96.801611 32.782057)')
+
+ c1 = City.objects.filter(state='TX').collect(field_name='location__point')
+ c2 = City.objects.filter(state='TX').aggregate(Collect('location__point'))['location__point__collect']
+
+ for coll in (c1, c2):
+ # Even though Dallas and Ft. Worth share same point, Collect doesn't
+ # consolidate -- that's why 4 points in MultiPoint.
+ self.assertEqual(4, len(coll))
+ self.assertEqual(ref_geom, coll)
+
+
# TODO: Related tests for KML, GML, and distance lookups.
def suite():
@@ -217,12 +217,13 @@ def sequence_reset_sql(self, style, model_list):
# continue to loop
break
for f in model._meta.many_to_many:
- table_name = self.quote_name(f.m2m_db_table())
- sequence_name = get_sequence_name(f.m2m_db_table())
- column_name = self.quote_name('id')
- output.append(query % {'sequence': sequence_name,
- 'table': table_name,
- 'column': column_name})
+ if not f.rel.through:
+ table_name = self.quote_name(f.m2m_db_table())
+ sequence_name = get_sequence_name(f.m2m_db_table())
+ column_name = self.quote_name('id')
+ output.append(query % {'sequence': sequence_name,
+ 'table': table_name,
+ 'column': column_name})
return output
def start_transaction_sql(self):
@@ -121,14 +121,15 @@ def sequence_reset_sql(self, style, model_list):
style.SQL_TABLE(qn(model._meta.db_table))))
break # Only one AutoField is allowed per model, so don't bother continuing.
for f in model._meta.many_to_many:
- output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
- (style.SQL_KEYWORD('SELECT'),
- style.SQL_FIELD(qn('%s_id_seq' % f.m2m_db_table())),
- style.SQL_FIELD(qn('id')),
- style.SQL_FIELD(qn('id')),
- style.SQL_KEYWORD('IS NOT'),
- style.SQL_KEYWORD('FROM'),
- style.SQL_TABLE(qn(f.m2m_db_table()))))
+ if not f.rel.through:
+ output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \
+ (style.SQL_KEYWORD('SELECT'),
+ style.SQL_FIELD(qn('%s_id_seq' % f.m2m_db_table())),
+ style.SQL_FIELD(qn('id')),
+ style.SQL_FIELD(qn('id')),
+ style.SQL_KEYWORD('IS NOT'),
+ style.SQL_KEYWORD('FROM'),
+ style.SQL_TABLE(qn(f.m2m_db_table()))))
return output
def savepoint_create_sql(self, sid):
@@ -464,7 +464,7 @@ should raise either a ``ValueError`` if the ``value`` is of the wrong sort (a
list when you were expecting an object, for example) or a ``TypeError`` if
your field does not support that type of lookup. For many fields, you can get
by with handling the lookup types that need special handling for your field
-and pass the rest of the :meth:`get_db_prep_lookup` method of the parent class.
+and pass the rest to the :meth:`get_db_prep_lookup` method of the parent class.
If you needed to implement ``get_db_prep_save()``, you will usually need to
implement ``get_db_prep_lookup()``. If you don't, ``get_db_prep_value`` will be
@@ -23,6 +23,10 @@ administrators immediate notification of any errors. The :setting:`ADMINS` will
get a description of the error, a complete Python traceback, and details about
the HTTP request that caused the error.
+By default, Django will send email from root@localhost. However, some mail
+providers reject all email from this address. To use a different sender
+address, modify the :setting:`SERVER_EMAIL` setting.
+
To disable this behavior, just remove all entries from the :setting:`ADMINS`
setting.
@@ -33,12 +37,12 @@ Django can also be configured to email errors about broken links (404 "page
not found" errors). Django sends emails about 404 errors when:
* :setting:`DEBUG` is ``False``
-
+
* :setting:`SEND_BROKEN_LINK_EMAILS` is ``True``
-
+
* Your :setting:`MIDDLEWARE_CLASSES` setting includes ``CommonMiddleware``
(which it does by default).
-
+
If those conditions are met, Django will e-mail the users listed in the
:setting:`MANAGERS` setting whenever your code raises a 404 and the request has
a referer. (It doesn't bother to e-mail for 404s that don't have a referer --
@@ -365,7 +365,7 @@ That takes care of setting ``handler404`` in the current module. As you can see
in ``django/conf/urls/defaults.py``, ``handler404`` is set to
:func:`django.views.defaults.page_not_found` by default.
-Three more things to note about 404 views:
+Four more things to note about 404 views:
* If :setting:`DEBUG` is set to ``True`` (in your settings module) then your
404 view will never be used (and thus the ``404.html`` template will never
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -762,12 +762,19 @@ documented in :ref:`topics-http-urls`::
anything, so you'll usually want to prepend your custom URLs to the built-in
ones.
-Note, however, that the ``self.my_view`` function registered above will *not*
-have any permission check done; it'll be accessible to the general public. Since
-this is usually not what you want, Django provides a convience wrapper to check
-permissions. This wrapper is :meth:`AdminSite.admin_view` (i.e.
-``self.admin_site.admin_view`` inside a ``ModelAdmin`` instance); use it like
-so::
+However, the ``self.my_view`` function registered above suffers from two
+problems:
+
+ * It will *not* perform and permission checks, so it will be accessible to
+ the general public.
+ * It will *not* provide any header details to prevent caching. This means if
+ the page retrieves data from the database, and caching middleware is
+ active, the page could show outdated information.
+
+Since this is usually not what you want, Django provides a convenience wrapper
+to check permissions and mark the view as non-cacheable. This wrapper is
+:meth:`AdminSite.admin_view` (i.e. ``self.admin_site.admin_view`` inside a
+``ModelAdmin`` instance); use it like so::
class MyModelAdmin(admin.ModelAdmin):
def get_urls(self):
@@ -781,7 +788,14 @@ Notice the wrapped view in the fifth line above::
(r'^my_view/$', self.admin_site.admin_view(self.my_view))
-This wrapping will protect ``self.my_view`` from unauthorized access.
+This wrapping will protect ``self.my_view`` from unauthorized access and will
+apply the ``django.views.decorators.cache.never_cache`` decorator to make sure
+it is not cached if the cache middleware is active.
+
+If the page is cacheable, but you still want the permission check to be performed,
+you can pass a ``cacheable=True`` argument to :meth:`AdminSite.admin_view`::
+
+ (r'^my_view/$', self.admin_site.admin_view(self.my_view, cacheable=True))
.. method:: ModelAdmin.formfield_for_foreignkey(self, db_field, request, **kwargs)
@@ -849,7 +863,7 @@ provided some extra mapping data that would not otherwise be available::
'osm_data': self.get_osm_info(),
}
return super(MyModelAdmin, self).change_view(request, object_id,
- extra_context=my_context))
+ extra_context=my_context)
``ModelAdmin`` media definitions
--------------------------------
@@ -177,9 +177,9 @@ The ``ContentTypeManager``
.. method:: models.ContentTypeManager.clear_cache()
Clears an internal cache used by
- :class:`~django.contrib.contenttypes.models.ContentType>` to keep track
+ :class:`~django.contrib.contenttypes.models.ContentType` to keep track
of which models for which it has created
- :class:`django.contrib.contenttypes.models.ContentType>` instances. You
+ :class:`django.contrib.contenttypes.models.ContentType` instances. You
probably won't ever need to call this method yourself; Django will call
it automatically when it's needed.
Oops, something went wrong.

0 comments on commit 08ab082

Please sign in to comment.