Author: kkubasik Date: 2009-07-22 07:43:04 -0500 (Wed, 22 Jul 2009) New Revision: 11292
Added: django/branches/soc2009/test-improvements/tests/regressiontests/urlpatterns_reverse/included_namespace_urls.py django/branches/soc2009/test-improvements/tests/regressiontests/urlpatterns_reverse/namespace_urls.py Modified: django/branches/soc2009/test-improvements/ django/branches/soc2009/test-improvements/django/conf/urls/defaults.py django/branches/soc2009/test-improvements/django/contrib/admin/options.py django/branches/soc2009/test-improvements/django/contrib/admin/sites.py django/branches/soc2009/test-improvements/django/contrib/admin/templates/admin/base.html django/branches/soc2009/test-improvements/django/contrib/admin/widgets.py django/branches/soc2009/test-improvements/django/contrib/admindocs/templates/admin_doc/index.html django/branches/soc2009/test-improvements/django/contrib/admindocs/views.py django/branches/soc2009/test-improvements/django/contrib/gis/db/models/manager.py django/branches/soc2009/test-improvements/django/contrib/gis/db/models/sql/query.py django/branches/soc2009/test-improvements/django/contrib/gis/tests/relatedapp/tests.py django/branches/soc2009/test-improvements/django/core/urlresolvers.py django/branches/soc2009/test-improvements/django/template/context.py django/branches/soc2009/test-improvements/django/template/defaulttags.py django/branches/soc2009/test-improvements/docs/howto/deployment/modwsgi.txt django/branches/soc2009/test-improvements/docs/intro/tutorial03.txt django/branches/soc2009/test-improvements/docs/ref/contrib/admin/_images/article_actions.png django/branches/soc2009/test-improvements/docs/ref/contrib/admin/index.txt django/branches/soc2009/test-improvements/docs/ref/forms/fields.txt django/branches/soc2009/test-improvements/docs/ref/models/querysets.txt django/branches/soc2009/test-improvements/docs/ref/templates/api.txt django/branches/soc2009/test-improvements/docs/ref/templates/builtins.txt django/branches/soc2009/test-improvements/docs/topics/forms/formsets.txt django/branches/soc2009/test-improvements/docs/topics/http/urls.txt django/branches/soc2009/test-improvements/docs/topics/i18n.txt django/branches/soc2009/test-improvements/docs/topics/testing.txt django/branches/soc2009/test-improvements/tests/regressiontests/admin_views/customadmin.py django/branches/soc2009/test-improvements/tests/regressiontests/admin_views/tests.py django/branches/soc2009/test-improvements/tests/regressiontests/admin_widgets/widgetadmin.py django/branches/soc2009/test-improvements/tests/regressiontests/urlpatterns_reverse/tests.py Log: [gsoc2009-testing] Upstream merge Property changes on: django/branches/soc2009/test-improvements ___________________________________________________________________ Name: svk:merge - 23ef3597-c209-482b-90c0-ea6045f15f7f:/local/django-gsoc:11062 23ef3597-c209-482b-90c0-ea6045f15f7f:/local/django/trunk:10927 bcc190cf-cafb-0310-a4f2-bffc1f526a37:/django/trunk:11227 + 23ef3597-c209-482b-90c0-ea6045f15f7f:/local/django-gsoc:11094 23ef3597-c209-482b-90c0-ea6045f15f7f:/local/django/trunk:10927 bcc190cf-cafb-0310-a4f2-bffc1f526a37:/django/trunk:11258 Modified: django/branches/soc2009/test-improvements/django/conf/urls/defaults.py =================================================================== --- django/branches/soc2009/test-improvements/django/conf/urls/defaults.py 2009-07-22 08:59:57 UTC (rev 11291) +++ django/branches/soc2009/test-improvements/django/conf/urls/defaults.py 2009-07-22 12:43:04 UTC (rev 11292) @@ -6,7 +6,16 @@ handler404 = 'django.views.defaults.page_not_found' handler500 = 'django.views.defaults.server_error' -include = lambda urlconf_module: [urlconf_module] +def include(arg, namespace=None, app_name=None): + if isinstance(arg, tuple): + # callable returning a namespace hint + if namespace: + raise ImproperlyConfigured('Cannot override the namespace for a dynamic module that provides a namespace') + urlconf_module, app_name, namespace = arg + else: + # No namespace hint - use manually provided namespace + urlconf_module = arg + return (urlconf_module, app_name, namespace) def patterns(prefix, *args): pattern_list = [] @@ -19,9 +28,10 @@ return pattern_list def url(regex, view, kwargs=None, name=None, prefix=''): - if type(view) == list: + if isinstance(view, (list,tuple)): # For include(...) processing. - return RegexURLResolver(regex, view[0], kwargs) + urlconf_module, app_name, namespace = view + return RegexURLResolver(regex, urlconf_module, kwargs, app_name=app_name, namespace=namespace) else: if isinstance(view, basestring): if not view: Modified: django/branches/soc2009/test-improvements/django/contrib/admin/options.py =================================================================== --- django/branches/soc2009/test-improvements/django/contrib/admin/options.py 2009-07-22 08:59:57 UTC (rev 11291) +++ django/branches/soc2009/test-improvements/django/contrib/admin/options.py 2009-07-22 12:43:04 UTC (rev 11292) @@ -226,24 +226,24 @@ return self.admin_site.admin_view(view)(*args, **kwargs) return update_wrapper(wrapper, view) - info = self.admin_site.name, self.model._meta.app_label, self.model._meta.module_name + info = self.model._meta.app_label, self.model._meta.module_name urlpatterns = patterns('', url(r'^$', wrap(self.changelist_view), - name='%sadmin_%s_%s_changelist' % info), + name='%s_%s_changelist' % info), url(r'^add/$', wrap(self.add_view), - name='%sadmin_%s_%s_add' % info), + name='%s_%s_add' % info), url(r'^(.+)/history/$', wrap(self.history_view), - name='%sadmin_%s_%s_history' % info), + name='%s_%s_history' % info), url(r'^(.+)/delete/$', wrap(self.delete_view), - name='%sadmin_%s_%s_delete' % info), + name='%s_%s_delete' % info), url(r'^(.+)/$', wrap(self.change_view), - name='%sadmin_%s_%s_change' % info), + name='%s_%s_change' % info), ) return urlpatterns @@ -582,11 +582,12 @@ 'save_on_top': self.save_on_top, 'root_path': self.admin_site.root_path, }) + context_instance = template.RequestContext(request, current_app=self.admin_site.name) return render_to_response(self.change_form_template or [ "admin/%s/%s/change_form.html" % (app_label, opts.object_name.lower()), "admin/%s/change_form.html" % app_label, "admin/change_form.html" - ], context, context_instance=template.RequestContext(request)) + ], context, context_instance=context_instance) def response_add(self, request, obj, post_url_continue='../%s/'): """ @@ -977,11 +978,12 @@ 'actions_on_bottom': self.actions_on_bottom, } context.update(extra_context or {}) + context_instance = template.RequestContext(request, current_app=self.admin_site.name) return render_to_response(self.change_list_template or [ 'admin/%s/%s/change_list.html' % (app_label, opts.object_name.lower()), 'admin/%s/change_list.html' % app_label, 'admin/change_list.html' - ], context, context_instance=template.RequestContext(request)) + ], context, context_instance=context_instance) def delete_view(self, request, object_id, extra_context=None): "The 'delete' admin view for this model." @@ -1032,11 +1034,12 @@ "app_label": app_label, } context.update(extra_context or {}) + context_instance = template.RequestContext(request, current_app=self.admin_site.name) return render_to_response(self.delete_confirmation_template or [ "admin/%s/%s/delete_confirmation.html" % (app_label, opts.object_name.lower()), "admin/%s/delete_confirmation.html" % app_label, "admin/delete_confirmation.html" - ], context, context_instance=template.RequestContext(request)) + ], context, context_instance=context_instance) def history_view(self, request, object_id, extra_context=None): "The 'history' admin view for this model." @@ -1059,11 +1062,12 @@ 'app_label': app_label, } context.update(extra_context or {}) + context_instance = template.RequestContext(request, current_app=self.admin_site.name) return render_to_response(self.object_history_template or [ "admin/%s/%s/object_history.html" % (app_label, opts.object_name.lower()), "admin/%s/object_history.html" % app_label, "admin/object_history.html" - ], context, context_instance=template.RequestContext(request)) + ], context, context_instance=context_instance) # # DEPRECATED methods. Modified: django/branches/soc2009/test-improvements/django/contrib/admin/sites.py =================================================================== --- django/branches/soc2009/test-improvements/django/contrib/admin/sites.py 2009-07-22 08:59:57 UTC (rev 11291) +++ django/branches/soc2009/test-improvements/django/contrib/admin/sites.py 2009-07-22 12:43:04 UTC (rev 11292) @@ -5,6 +5,7 @@ from django.contrib.auth import authenticate, login from django.db.models.base import ModelBase from django.core.exceptions import ImproperlyConfigured +from django.core.urlresolvers import reverse from django.shortcuts import render_to_response from django.utils.functional import update_wrapper from django.utils.safestring import mark_safe @@ -38,17 +39,14 @@ login_template = None app_index_template = None - def __init__(self, name=None): + def __init__(self, name=None, app_name='admin'): self._registry = {} # model_class class -> admin_class instance - # TODO Root path is used to calculate urls under the old root() method - # in order to maintain backwards compatibility we are leaving that in - # so root_path isn't needed, not sure what to do about this. - self.root_path = 'admin/' + self.root_path = None if name is None: - name = '' + self.name = 'admin' else: - name += '_' - self.name = name + self.name = name + self.app_name = app_name self._actions = {'delete_selected': actions.delete_selected} self._global_actions = self._actions.copy() @@ -114,20 +112,20 @@ 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 +157,9 @@ 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,43 +175,49 @@ 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. urlpatterns = patterns('', url(r'^$', wrap(self.index), - name='%sadmin_index' % self.name), + name='index'), url(r'^logout/$', wrap(self.logout), - name='%sadmin_logout'), + name='logout'), url(r'^password_change/$', - wrap(self.password_change), - name='%sadmin_password_change' % self.name), + wrap(self.password_change, cacheable=True), + name='password_change'), url(r'^password_change/done/$', - wrap(self.password_change_done), - name='%sadmin_password_change_done' % self.name), + wrap(self.password_change_done, cacheable=True), + name='password_change_done'), url(r'^jsi18n/$', - wrap(self.i18n_javascript), - name='%sadmin_jsi18n' % self.name), + wrap(self.i18n_javascript, cacheable=True), + name='jsi18n'), url(r'^r/(?P<content_type_id>\d+)/(?P<object_id>.+)/$', 'django.views.defaults.shortcut'), url(r'^(?P<app_label>\w+)/$', wrap(self.app_index), - name='%sadmin_app_list' % self.name), + name='app_list') ) # Add in each model's views. @@ -225,7 +229,7 @@ return urlpatterns def urls(self): - return self.get_urls() + return self.get_urls(), self.app_name, self.name urls = property(urls) def password_change(self, request): @@ -233,8 +237,11 @@ Handles the "change password" task -- both form display and validation. """ from django.contrib.auth.views import password_change - return password_change(request, - post_change_redirect='%spassword_change/done/' % self.root_path) + if self.root_path is not None: + url = '%spassword_change/done/' % self.root_path + else: + url = reverse('admin:password_change_done', current_app=self.name) + return password_change(request, post_change_redirect=url) def password_change_done(self, request): """ @@ -362,8 +369,9 @@ 'root_path': self.root_path, } context.update(extra_context or {}) + context_instance = template.RequestContext(request, current_app=self.name) return render_to_response(self.index_template or 'admin/index.html', context, - context_instance=template.RequestContext(request) + context_instance=context_instance ) index = never_cache(index) @@ -376,8 +384,9 @@ 'root_path': self.root_path, } context.update(extra_context or {}) + context_instance = template.RequestContext(request, current_app=self.name) return render_to_response(self.login_template or 'admin/login.html', context, - context_instance=template.RequestContext(request) + context_instance=context_instance ) def app_index(self, request, app_label, extra_context=None): @@ -419,9 +428,10 @@ 'root_path': self.root_path, } context.update(extra_context or {}) + context_instance = template.RequestContext(request, current_app=self.name) return render_to_response(self.app_index_template or ('admin/%s/app_index.html' % app_label, 'admin/app_index.html'), context, - context_instance=template.RequestContext(request) + context_instance=context_instance ) def root(self, request, url): Modified: django/branches/soc2009/test-improvements/django/contrib/admin/templates/admin/base.html =================================================================== --- django/branches/soc2009/test-improvements/django/contrib/admin/templates/admin/base.html 2009-07-22 08:59:57 UTC (rev 11291) +++ django/branches/soc2009/test-improvements/django/contrib/admin/templates/admin/base.html 2009-07-22 12:43:04 UTC (rev 11292) @@ -23,7 +23,30 @@ {% block branding %}{% endblock %} </div> {% if user.is_authenticated and user.is_staff %} - <div id="user-tools">{% trans 'Welcome,' %} <strong>{% firstof user.first_name user.username %}</strong>. {% block userlinks %}{% url django-admindocs-docroot as docsroot %}{% if docsroot %}<a href="{{ docsroot }}">{% trans 'Documentation' %}</a> / {% endif %}<a href="{{ root_path }}password_change/">{% trans 'Change password' %}</a> / <a href="{{ root_path }}logout/">{% trans 'Log out' %}</a>{% endblock %}</div> + <div id="user-tools"> + {% trans 'Welcome,' %} + <strong>{% firstof user.first_name user.username %}</strong>. + {% block userlinks %} + {% url django-admindocs-docroot as docsroot %} + {% if docsroot %} + <a href="{{ docsroot }}">{% trans 'Documentation' %}</a> / + {% endif %} + {% url admin:password_change as password_change_url %} + {% if password_change_url %} + <a href="{{ password_change_url }}"> + {% else %} + <a href="{{ root_path }}password_change/"> + {% endif %} + {% trans 'Change password' %}</a> / + {% url admin:logout as logout_url %} + {% if logout_url %} + <a href="{{ logout_url }}"> + {% else %} + <a href="{{ root_path }}logout/"> + {% endif %} + {% trans 'Log out' %}</a> + {% endblock %} + </div> {% endif %} {% block nav-global %}{% endblock %} </div> Modified: django/branches/soc2009/test-improvements/django/contrib/admin/widgets.py =================================================================== --- django/branches/soc2009/test-improvements/django/contrib/admin/widgets.py 2009-07-22 08:59:57 UTC (rev 11291) +++ django/branches/soc2009/test-improvements/django/contrib/admin/widgets.py 2009-07-22 12:43:04 UTC (rev 11292) @@ -125,7 +125,7 @@ if value: output.append(self.label_for_value(value)) return mark_safe(u''.join(output)) - + def base_url_parameters(self): params = {} if self.rel.limit_choices_to: @@ -137,14 +137,14 @@ v = str(v) items.append((k, v)) params.update(dict(items)) - return params - + return params + def url_parameters(self): from django.contrib.admin.views.main import TO_FIELD_VAR params = self.base_url_parameters() params.update({TO_FIELD_VAR: self.rel.get_related_field().name}) return params - + def label_for_value(self, value): key = self.rel.get_related_field().name obj = self.rel.to._default_manager.get(**{key: value}) @@ -165,10 +165,10 @@ else: value = '' return super(ManyToManyRawIdWidget, self).render(name, value, attrs) - + def url_parameters(self): return self.base_url_parameters() - + def label_for_value(self, value): return '' @@ -222,8 +222,7 @@ rel_to = self.rel.to info = (rel_to._meta.app_label, rel_to._meta.object_name.lower()) try: - related_info = (self.admin_site.name,) + info - related_url = reverse('%sadmin_%s_%s_add' % related_info) + related_url = reverse('admin:%s_%s_add' % info, current_app=self.admin_site.name) except NoReverseMatch: related_url = '../../../%s/%s/add/' % info self.widget.choices = self.choices Modified: django/branches/soc2009/test-improvements/django/contrib/admindocs/templates/admin_doc/index.html =================================================================== --- django/branches/soc2009/test-improvements/django/contrib/admindocs/templates/admin_doc/index.html 2009-07-22 08:59:57 UTC (rev 11291) +++ django/branches/soc2009/test-improvements/django/contrib/admindocs/templates/admin_doc/index.html 2009-07-22 12:43:04 UTC (rev 11292) @@ -1,6 +1,6 @@ {% extends "admin/base_site.html" %} {% load i18n %} -{% block breadcrumbs %}<div class="breadcrumbs"><a href="../">Home</a> › Documentation</div>{% endblock %} +{% block breadcrumbs %}<div class="breadcrumbs"><a href="{{ root_path }}">Home</a> › Documentation</div>{% endblock %} {% block title %}Documentation{% endblock %} {% block content %} Modified: django/branches/soc2009/test-improvements/django/contrib/admindocs/views.py =================================================================== --- django/branches/soc2009/test-improvements/django/contrib/admindocs/views.py 2009-07-22 08:59:57 UTC (rev 11291) +++ django/branches/soc2009/test-improvements/django/contrib/admindocs/views.py 2009-07-22 12:43:04 UTC (rev 11292) @@ -22,11 +22,14 @@ name = 'my site' def get_root_path(): - from django.contrib import admin try: - return urlresolvers.reverse(admin.site.root, args=['']) + return urlresolvers.reverse('admin:index') except urlresolvers.NoReverseMatch: - return getattr(settings, "ADMIN_SITE_ROOT_URL", "/admin/") + from django.contrib import admin + try: + return urlresolvers.reverse(admin.site.root, args=['']) + except urlresolvers.NoReverseMatch: + return getattr(settings, "ADMIN_SITE_ROOT_URL", "/admin/") def doc_index(request): if not utils.docutils_is_available: @@ -179,7 +182,7 @@ def model_detail(request, app_label, model_name): if not utils.docutils_is_available: return missing_docutils_page(request) - + # Get the model class. try: app_mod = models.get_app(app_label) Modified: django/branches/soc2009/test-improvements/django/contrib/gis/db/models/manager.py =================================================================== --- django/branches/soc2009/test-improvements/django/contrib/gis/db/models/manager.py 2009-07-22 08:59:57 UTC (rev 11291) +++ django/branches/soc2009/test-improvements/django/contrib/gis/db/models/manager.py 2009-07-22 12:43:04 UTC (rev 11292) @@ -19,6 +19,9 @@ 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) Modified: django/branches/soc2009/test-improvements/django/contrib/gis/db/models/sql/query.py =================================================================== --- django/branches/soc2009/test-improvements/django/contrib/gis/db/models/sql/query.py 2009-07-22 08:59:57 UTC (rev 11291) +++ django/branches/soc2009/test-improvements/django/contrib/gis/db/models/sql/query.py 2009-07-22 12:43:04 UTC (rev 11292) @@ -13,7 +13,9 @@ ALL_TERMS = sql.constants.QUERY_TERMS.copy() ALL_TERMS.update(SpatialBackend.gis_terms) +# Pulling out other needed constants/routines to avoid attribute lookups. TABLE_NAME = sql.constants.TABLE_NAME +get_proxied_model = sql.query.get_proxied_model class GeoQuery(sql.Query): """ @@ -153,7 +155,9 @@ opts = self.model._meta aliases = set() only_load = self.deferred_to_columns() - proxied_model = opts.proxy and opts.proxy_for_model or 0 + # Skip all proxy to the root proxied model + proxied_model = get_proxied_model(opts) + if start_alias: seen = {None: start_alias} for field, model in opts.get_fields_with_model(): @@ -205,6 +209,10 @@ """ values = [] aliases = self.extra_select.keys() + if self.aggregates: + # If we have an aggregate annotation, must extend the aliases + # so their corresponding row values are included. + aliases.extend([None for i in xrange(len(self.aggregates))]) # Have to set a starting row number offset that is used for # determining the correct starting row index -- needed for Modified: django/branches/soc2009/test-improvements/django/contrib/gis/tests/relatedapp/tests.py =================================================================== --- django/branches/soc2009/test-improvements/django/contrib/gis/tests/relatedapp/tests.py 2009-07-22 08:59:57 UTC (rev 11291) +++ django/branches/soc2009/test-improvements/django/contrib/gis/tests/relatedapp/tests.py 2009-07-22 12:43:04 UTC (rev 11292) @@ -231,8 +231,12 @@ q = pickle.loads(q_str) self.assertEqual(GeoQuery, q.__class__) - def test12_count(self): - "Testing `Count` aggregate use with the `GeoManager`. See #11087." + # TODO: fix on Oracle -- get the following error because the SQL is ordered + # by a geometry object, which Oracle apparently doesn't like: + # ORA-22901: cannot compare nested table or VARRAY or LOB attributes of an object type + @no_oracle + def test12a_count(self): + "Testing `Count` aggregate use with the `GeoManager` on geo-fields." # Creating a new City, 'Fort Worth', that uses the same location # as Dallas. dallas = City.objects.get(name='Dallas') @@ -242,6 +246,8 @@ loc = Location.objects.annotate(num_cities=Count('city')).get(id=dallas.location.id) self.assertEqual(2, loc.num_cities) + def test12b_count(self): + "Testing `Count` aggregate use with the `GeoManager` on non geo-fields. See #11087." # Creating some data for the Book/Author non-geo models that # use GeoManager. See #11087. tp = Author.objects.create(name='Trevor Paglen') @@ -250,13 +256,19 @@ 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. + # the annotation should have 3 for the number of books. Also testing + # with a `GeoValuesQuerySet` (see #11489). qs = Author.objects.annotate(num_books=Count('books')).filter(num_books__gt=1) + vqs = Author.objects.values('name').annotate(num_books=Count('books')).filter(num_books__gt=1) self.assertEqual(1, len(qs)) self.assertEqual(3, qs[0].num_books) + self.assertEqual(1, len(vqs)) + self.assertEqual(3, vqs[0]['num_books']) + # TODO: The phantom model does appear on Oracle. + @no_oracle def test13_select_related_null_fk(self): "Testing `select_related` on a nullable ForeignKey via `GeoManager`. See #11381." no_author = Book.objects.create(title='Without Author') Modified: django/branches/soc2009/test-improvements/django/core/urlresolvers.py =================================================================== --- django/branches/soc2009/test-improvements/django/core/urlresolvers.py 2009-07-22 08:59:57 UTC (rev 11291) +++ django/branches/soc2009/test-improvements/django/core/urlresolvers.py 2009-07-22 12:43:04 UTC (rev 11292) @@ -139,7 +139,7 @@ callback = property(_get_callback) class RegexURLResolver(object): - def __init__(self, regex, urlconf_name, default_kwargs=None): + def __init__(self, regex, urlconf_name, default_kwargs=None, app_name=None, namespace=None): # regex is a string representing a regular expression. # urlconf_name is a string representing the module containing URLconfs. self.regex = re.compile(regex, re.UNICODE) @@ -148,19 +148,29 @@ self._urlconf_module = self.urlconf_name self.callback = None self.default_kwargs = default_kwargs or {} - self._reverse_dict = MultiValueDict() + self.namespace = namespace + self.app_name = app_name + self._reverse_dict = None + self._namespace_dict = None + self._app_dict = None def __repr__(self): - return '<%s %s %s>' % (self.__class__.__name__, self.urlconf_name, self.regex.pattern) + return '<%s %s (%s:%s) %s>' % (self.__class__.__name__, self.urlconf_name, self.app_name, self.namespace, self.regex.pattern) - def _get_reverse_dict(self): - if not self._reverse_dict: - lookups = MultiValueDict() - for pattern in reversed(self.url_patterns): - p_pattern = pattern.regex.pattern - if p_pattern.startswith('^'): - p_pattern = p_pattern[1:] - if isinstance(pattern, RegexURLResolver): + def _populate(self): + lookups = MultiValueDict() + namespaces = {} + apps = {} + for pattern in reversed(self.url_patterns): + p_pattern = pattern.regex.pattern + if p_pattern.startswith('^'): + p_pattern = p_pattern[1:] + if isinstance(pattern, RegexURLResolver): + if pattern.namespace: + namespaces[pattern.namespace] = (p_pattern, pattern) + if pattern.app_name: + apps.setdefault(pattern.app_name, []).append(pattern.namespace) + else: parent = normalize(pattern.regex.pattern) for name in pattern.reverse_dict: for matches, pat in pattern.reverse_dict.getlist(name): @@ -168,14 +178,36 @@ for piece, p_args in parent: new_matches.extend([(piece + suffix, p_args + args) for (suffix, args) in matches]) lookups.appendlist(name, (new_matches, p_pattern + pat)) - else: - bits = normalize(p_pattern) - lookups.appendlist(pattern.callback, (bits, p_pattern)) - lookups.appendlist(pattern.name, (bits, p_pattern)) - self._reverse_dict = lookups + for namespace, (prefix, sub_pattern) in pattern.namespace_dict.items(): + namespaces[namespace] = (p_pattern + prefix, sub_pattern) + for app_name, namespace_list in pattern.app_dict.items(): + apps.setdefault(app_name, []).extend(namespace_list) + else: + bits = normalize(p_pattern) + lookups.appendlist(pattern.callback, (bits, p_pattern)) + lookups.appendlist(pattern.name, (bits, p_pattern)) + self._reverse_dict = lookups + self._namespace_dict = namespaces + self._app_dict = apps + + def _get_reverse_dict(self): + if self._reverse_dict is None: + self._populate() return self._reverse_dict reverse_dict = property(_get_reverse_dict) + def _get_namespace_dict(self): + if self._namespace_dict is None: + self._populate() + return self._namespace_dict + namespace_dict = property(_get_namespace_dict) + + def _get_app_dict(self): + if self._app_dict is None: + self._populate() + return self._app_dict + app_dict = property(_get_app_dict) + def resolve(self, path): tried = [] match = self.regex.search(path) @@ -261,12 +293,51 @@ def resolve(path, urlconf=None): return get_resolver(urlconf).resolve(path) -def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None): +def reverse(viewname, urlconf=None, args=None, kwargs=None, prefix=None, current_app=None): + resolver = get_resolver(urlconf) args = args or [] kwargs = kwargs or {} + if prefix is None: prefix = get_script_prefix() - return iri_to_uri(u'%s%s' % (prefix, get_resolver(urlconf).reverse(viewname, + + if not isinstance(viewname, basestring): + view = viewname + else: + parts = viewname.split(':') + parts.reverse() + view = parts[0] + path = parts[1:] + + resolved_path = [] + while path: + ns = path.pop() + + # Lookup the name to see if it could be an app identifier + try: + app_list = resolver.app_dict[ns] + # Yes! Path part matches an app in the current Resolver + if current_app and current_app in app_list: + # If we are reversing for a particular app, use that namespace + ns = current_app + elif ns not in app_list: + # The name isn't shared by one of the instances (i.e., the default) + # so just pick the first instance as the default. + ns = app_list[0] + except KeyError: + pass + + try: + extra, resolver = resolver.namespace_dict[ns] + resolved_path.append(ns) + prefix = prefix + extra + except KeyError, key: + if resolved_path: + raise NoReverseMatch("%s is not a registered namespace inside '%s'" % (key, ':'.join(resolved_path))) + else: + raise NoReverseMatch("%s is not a registered namespace" % key) + + return iri_to_uri(u'%s%s' % (prefix, resolver.reverse(view, *args, **kwargs))) def clear_url_caches(): Modified: django/branches/soc2009/test-improvements/django/template/context.py =================================================================== --- django/branches/soc2009/test-improvements/django/template/context.py 2009-07-22 08:59:57 UTC (rev 11291) +++ django/branches/soc2009/test-improvements/django/template/context.py 2009-07-22 12:43:04 UTC (rev 11292) @@ -9,10 +9,11 @@ class Context(object): "A stack container for variable context" - def __init__(self, dict_=None, autoescape=True): + def __init__(self, dict_=None, autoescape=True, current_app=None): dict_ = dict_ or {} self.dicts = [dict_] self.autoescape = autoescape + self.current_app = current_app def __repr__(self): return repr(self.dicts) @@ -96,8 +97,8 @@ Additional processors can be specified as a list of callables using the "processors" keyword argument. """ - def __init__(self, request, dict=None, processors=None): - Context.__init__(self, dict) + def __init__(self, request, dict=None, processors=None, current_app=None): + Context.__init__(self, dict, current_app=current_app) if processors is None: processors = () else: Modified: django/branches/soc2009/test-improvements/django/template/defaulttags.py =================================================================== --- django/branches/soc2009/test-improvements/django/template/defaulttags.py 2009-07-22 08:59:57 UTC (rev 11291) +++ django/branches/soc2009/test-improvements/django/template/defaulttags.py 2009-07-22 12:43:04 UTC (rev 11292) @@ -367,17 +367,17 @@ # {% url ... as var %} construct in which cause return nothing. url = '' try: - url = reverse(self.view_name, args=args, kwargs=kwargs) + url = reverse(self.view_name, args=args, kwargs=kwargs, current_app=context.current_app) except NoReverseMatch, e: if settings.SETTINGS_MODULE: project_name = settings.SETTINGS_MODULE.split('.')[0] try: url = reverse(project_name + '.' + self.view_name, - args=args, kwargs=kwargs) + args=args, kwargs=kwargs, current_app=context.current_app) except NoReverseMatch: if self.asvar is None: # Re-raise the original exception, not the one with - # the path relative to the project. This makes a + # the path relative to the project. This makes a # better error message. raise e else: Modified: django/branches/soc2009/test-improvements/docs/howto/deployment/modwsgi.txt =================================================================== --- django/branches/soc2009/test-improvements/docs/howto/deployment/modwsgi.txt 2009-07-22 08:59:57 UTC (rev 11291) +++ django/branches/soc2009/test-improvements/docs/howto/deployment/modwsgi.txt 2009-07-22 12:43:04 UTC (rev 11292) @@ -1,69 +1,118 @@ -.. _howto-deployment-modwsgi: - -========================================== -How to use Django with Apache and mod_wsgi -========================================== - -Deploying Django with Apache_ and `mod_wsgi`_ is the recommended way to get -Django into production. - -.. _Apache: http://httpd.apache.org/ -.. _mod_wsgi: http://code.google.com/p/modwsgi/ - -mod_wsgi is an Apache module which can be used to host any Python application -which supports the `Python WSGI interface`_, including Django. Django will work -with any version of Apache which supports mod_wsgi. - -.. _python wsgi interface: http://www.python.org/dev/peps/pep-0333/ - -The `official mod_wsgi documentation`_ is fantastic; it's your source for all -the details about how to use mod_wsgi. You'll probably want to start with the -`installation and configuration documentation`_. - -.. _official mod_wsgi documentation: http://code.google.com/p/modwsgi/ -.. _installation and configuration documentation: http://code.google.com/p/modwsgi/wiki/InstallationInstructions - -Basic Configuration -=================== - -Once you've got mod_wsgi installed and activated, edit your ``httpd.conf`` file -and add:: - - WSGIScriptAlias / /path/to/mysite/apache/django.wsgi - -The first bit above is the url you want to be serving your application at (``/`` -indicates the root url), and the second is the location of a "WSGI file" -- see -below -- on your system, usually inside of your project. This tells Apache -to serve any request below the given URL using the WSGI application defined by that file. - -Next we'll need to actually create this WSGI application, so create the file -mentioned in the second part of ``WSGIScriptAlias`` and add:: - - import os - import sys - - os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings' - - import django.core.handlers.wsgi - application = django.core.handlers.wsgi.WSGIHandler() - -If your project is not on your ``PYTHONPATH`` by default you can add:: - - sys.path.append('/usr/local/django') - -just above the final ``import`` line to place your project on the path. Remember to -replace 'mysite.settings' with your correct settings file, and '/usr/local/django' -with your own project's location. - -See the :ref:`Apache/mod_python documentation<howto-deployment-modpython>` for -directions on serving static media, and the `mod_wsgi documentation`_ for an -explanation of other directives and configuration options you can use. - -Details -======= - -For more details, see the `mod_wsgi documentation`_, which explains the above in -more detail, and walks through all the various options you've got when deploying -under mod_wsgi. - -.. _mod_wsgi documentation: http://code.google.com/p/modwsgi/wiki/IntegrationWithDjango +.. _howto-deployment-modwsgi: + +========================================== +How to use Django with Apache and mod_wsgi +========================================== + +Deploying Django with Apache_ and `mod_wsgi`_ is the recommended way to get +Django into production. + +.. _Apache: http://httpd.apache.org/ +.. _mod_wsgi: http://code.google.com/p/modwsgi/ + +mod_wsgi is an Apache module which can be used to host any Python application +which supports the `Python WSGI interface`_, including Django. Django will work +with any version of Apache which supports mod_wsgi. + +.. _python wsgi interface: http://www.python.org/dev/peps/pep-0333/ + +The `official mod_wsgi documentation`_ is fantastic; it's your source for all +the details about how to use mod_wsgi. You'll probably want to start with the +`installation and configuration documentation`_. + +.. _official mod_wsgi documentation: http://code.google.com/p/modwsgi/ +.. _installation and configuration documentation: http://code.google.com/p/modwsgi/wiki/InstallationInstructions + +Basic Configuration +=================== + +Once you've got mod_wsgi installed and activated, edit your ``httpd.conf`` file +and add:: + + WSGIScriptAlias / /path/to/mysite/apache/django.wsgi + +The first bit above is the url you want to be serving your application at (``/`` +indicates the root url), and the second is the location of a "WSGI file" -- see +below -- on your system, usually inside of your project. This tells Apache +to serve any request below the given URL using the WSGI application defined by that file. + +Next we'll need to actually create this WSGI application, so create the file +mentioned in the second part of ``WSGIScriptAlias`` and add:: + + import os + import sys + + os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings' + + import django.core.handlers.wsgi + application = django.core.handlers.wsgi.WSGIHandler() + +If your project is not on your ``PYTHONPATH`` by default you can add:: + + sys.path.append('/usr/local/django') + +just above the final ``import`` line to place your project on the path. Remember to +replace 'mysite.settings' with your correct settings file, and '/usr/local/django' +with your own project's location. + +Serving media files +=================== + +Django doesn't serve media files itself; it leaves that job to whichever Web +server you choose. + +We recommend using a separate Web server -- i.e., one that's not also running +Django -- for serving media. Here are some good choices: + + * lighttpd_ + * Nginx_ + * TUX_ + * A stripped-down version of Apache_ + * Cherokee_ + +If, however, you have no option but to serve media files on the same Apache +``VirtualHost`` as Django, you can set up Apache to serve some URLs as +static media, and others using the mod_wsgi interface to Django. + +This example sets up Django at the site root, but explicitly serves ``robots.txt``, +``favicon.ico``, any CSS file, and anything in the ``/media/`` URL space as a static +file. All other URLs will be served using mod_wsgi:: + + Alias /robots.txt /usr/local/wsgi/static/robots.txt + Alias /favicon.ico /usr/local/wsgi/static/favicon.ico + + AliasMatch /([^/]*\.css) /usr/local/wsgi/static/styles/$1 + + Alias /media/ /usr/local/wsgi/static/media/ + + <Directory /usr/local/wsgi/static> + Order deny,allow + Allow from all + </Directory> + + WSGIScriptAlias / /usr/local/wsgi/scripts/django.wsgi + + <Directory /usr/local/wsgi/scripts> + Order allow,deny + Allow from all + </Directory> + +.. _lighttpd: http://www.lighttpd.net/ +.. _Nginx: http://wiki.codemongers.com/Main +.. _TUX: http://en.wikipedia.org/wiki/TUX_web_server +.. _Apache: http://httpd.apache.org/ +.. _Cherokee: http://www.cherokee-project.com/ + +More details on configuring a mod_wsgi site to serve static files can be found +in the mod_wsgi documentation on `hosting static files`_. + +.. _hosting static files: http://code.google.com/p/modwsgi/wiki/ConfigurationGuidelines#Hosting_Of_Static_Files + +Details +======= + +For more details, see the `mod_wsgi documentation on Django integration`_, +which explains the above in more detail, and walks through all the various +options you've got when deploying under mod_wsgi. + +.. _mod_wsgi documentation on Django integration: http://code.google.com/p/modwsgi/wiki/IntegrationWithDjango Modified: django/branches/soc2009/test-improvements/docs/intro/tutorial03.txt =================================================================== --- django/branches/soc2009/test-improvements/docs/intro/tutorial03.txt 2009-07-22 08:59:57 UTC (rev 11291) +++ django/branches/soc2009/test-improvements/docs/intro/tutorial03.txt 2009-07-22 12:43:04 UTC (rev 11292) @@ -365,7 +365,7 @@ 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 Modified: django/branches/soc2009/test-improvements/docs/ref/contrib/admin/_images/article_actions.png =================================================================== --- django/branches/soc2009/test-improvements/docs/ref/contrib/admin/_images/article_actions.png 2009-07-22 08:59:57 UTC (rev 11291) +++ django/branches/soc2009/test-improvements/docs/ref/contrib/admin/_images/article_actions.png 2009-07-22 12:43:04 UTC (rev 11292) @@ -1,218 +1,124 @@ ‰PNG --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Django updates" group. To post to this group, send email to django-updates@googlegroups.com To unsubscribe from this group, send email to django-updates+unsubscr...@googlegroups.com For more options, visit this group at http://groups.google.com/group/django-updates?hl=en -~----------~----~----~----~------~----~------~--~---