1
0
mirror of https://github.com/django/django.git synced 2025-07-04 17:59:13 +00:00

[multi-db] Merge trunk to [3522]

git-svn-id: http://code.djangoproject.com/svn/django/branches/multiple-db-support@3523 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Jason Pellerin 2006-08-04 23:27:18 +00:00
parent 08c4207182
commit 238c6ecd5b
20 changed files with 227 additions and 51 deletions

View File

@ -70,6 +70,8 @@ answer newbie questions, and generally made Django that much better:
Clint Ecker Clint Ecker
gandalf@owca.info gandalf@owca.info
Baishampayan Ghose Baishampayan Ghose
martin.glueck@gmail.com
Simon Greenhill <dev@simon.net.nz>
Espen Grindhaug <http://grindhaug.org/> Espen Grindhaug <http://grindhaug.org/>
Brant Harris Brant Harris
hipertracker@gmail.com hipertracker@gmail.com

View File

@ -141,6 +141,7 @@ msgid "score date"
msgstr "счёт времени" msgstr "счёт времени"
#: contrib/comments/models.py:237 #: contrib/comments/models.py:237
#, fuzzy
msgid "karma score" msgid "karma score"
msgstr "Карма счёт" msgstr "Карма счёт"
@ -735,7 +736,7 @@ msgstr "К сожалению, запрашиваемая вами страни
#: contrib/admin/templates/admin/index.html:17 #: contrib/admin/templates/admin/index.html:17
#, python-format #, python-format
msgid "Models available in the %(name)s application." msgid "Models available in the %(name)s application."
msgstr "Модели доступны в %(name)s приложении." msgstr "Модели доступны в %(name) приложении."
#: contrib/admin/templates/admin/index.html:28 #: contrib/admin/templates/admin/index.html:28
#: contrib/admin/templates/admin/change_form.html:15 #: contrib/admin/templates/admin/change_form.html:15

View File

@ -10,8 +10,10 @@ include = lambda urlconf_module: [urlconf_module]
def patterns(prefix, *tuples): def patterns(prefix, *tuples):
pattern_list = [] pattern_list = []
for t in tuples: for t in tuples:
if type(t[1]) == list: regex, view_or_include = t[:2]
pattern_list.append(RegexURLResolver(t[0], t[1][0])) default_kwargs = t[2:]
if type(view_or_include) == list:
pattern_list.append(RegexURLResolver(regex, view_or_include[0], *default_kwargs))
else: else:
pattern_list.append(RegexURLPattern(t[0], prefix and (prefix + '.' + t[1]) or t[1], *t[2:])) pattern_list.append(RegexURLPattern(regex, prefix and (prefix + '.' + view_or_include) or view_or_include, *default_kwargs))
return pattern_list return pattern_list

View File

@ -8,7 +8,9 @@ var DateTimeShortcuts = {
clockInputs: [], clockInputs: [],
calendarDivName1: 'calendarbox', // name of calendar <div> that gets toggled calendarDivName1: 'calendarbox', // name of calendar <div> that gets toggled
calendarDivName2: 'calendarin', // name of <div> that contains calendar calendarDivName2: 'calendarin', // name of <div> that contains calendar
calendarLinkName: 'calendarlink',// name of the link that is used to toggle
clockDivName: 'clockbox', // name of clock <div> that gets toggled clockDivName: 'clockbox', // name of clock <div> that gets toggled
clockLinkName: 'clocklink', // name of the link that is used to toggle
admin_media_prefix: '', admin_media_prefix: '',
init: function() { init: function() {
// Deduce admin_media_prefix by looking at the <script>s in the // Deduce admin_media_prefix by looking at the <script>s in the
@ -46,6 +48,7 @@ var DateTimeShortcuts = {
now_link.appendChild(document.createTextNode(gettext('Now'))); now_link.appendChild(document.createTextNode(gettext('Now')));
var clock_link = document.createElement('a'); var clock_link = document.createElement('a');
clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');'); clock_link.setAttribute('href', 'javascript:DateTimeShortcuts.openClock(' + num + ');');
clock_link.id = DateTimeShortcuts.clockLinkName + num;
quickElement('img', clock_link, '', 'src', DateTimeShortcuts.admin_media_prefix + 'img/admin/icon_clock.gif', 'alt', gettext('Clock')); quickElement('img', clock_link, '', 'src', DateTimeShortcuts.admin_media_prefix + 'img/admin/icon_clock.gif', 'alt', gettext('Clock'));
shortcuts_span.appendChild(document.createTextNode('\240')); shortcuts_span.appendChild(document.createTextNode('\240'));
shortcuts_span.appendChild(now_link); shortcuts_span.appendChild(now_link);
@ -69,17 +72,6 @@ var DateTimeShortcuts = {
var clock_box = document.createElement('div'); var clock_box = document.createElement('div');
clock_box.style.display = 'none'; clock_box.style.display = 'none';
clock_box.style.position = 'absolute'; clock_box.style.position = 'absolute';
if (getStyle(document.body,'direction')!='rtl') {
clock_box.style.left = findPosX(clock_link) + 17 + 'px';
}
else {
// since style's width is in em, it'd be tough to calculate
// px value of it. let's use an estimated px for now
// TODO: IE returns wrong value for findPosX when in rtl mode
// (it returns as it was left aligned), needs to be fixed.
clock_box.style.left = findPosX(clock_link) - 110 + 'px';
}
clock_box.style.top = findPosY(clock_link) - 30 + 'px';
clock_box.className = 'clockbox module'; clock_box.className = 'clockbox module';
clock_box.setAttribute('id', DateTimeShortcuts.clockDivName + num); clock_box.setAttribute('id', DateTimeShortcuts.clockDivName + num);
document.body.appendChild(clock_box); document.body.appendChild(clock_box);
@ -98,7 +90,25 @@ var DateTimeShortcuts = {
quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissClock(' + num + ');'); quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissClock(' + num + ');');
}, },
openClock: function(num) { openClock: function(num) {
document.getElementById(DateTimeShortcuts.clockDivName + num).style.display = 'block'; var clock_box = document.getElementById(DateTimeShortcuts.clockDivName+num)
var clock_link = document.getElementById(DateTimeShortcuts.clockLinkName+num)
// Recalculate the clockbox position
// is it left-to-right or right-to-left layout ?
if (getStyle(document.body,'direction')!='rtl') {
clock_box.style.left = findPosX(clock_link) + 17 + 'px';
}
else {
// since style's width is in em, it'd be tough to calculate
// px value of it. let's use an estimated px for now
// TODO: IE returns wrong value for findPosX when in rtl mode
// (it returns as it was left aligned), needs to be fixed.
clock_box.style.left = findPosX(clock_link) - 110 + 'px';
}
clock_box.style.top = findPosY(clock_link) - 30 + 'px';
// Show the clock box
clock_box.style.display = 'block';
addEvent(window, 'click', function() { DateTimeShortcuts.dismissClock(num); return true; }); addEvent(window, 'click', function() { DateTimeShortcuts.dismissClock(num); return true; });
}, },
dismissClock: function(num) { dismissClock: function(num) {
@ -123,6 +133,7 @@ var DateTimeShortcuts = {
today_link.appendChild(document.createTextNode(gettext('Today'))); today_link.appendChild(document.createTextNode(gettext('Today')));
var cal_link = document.createElement('a'); var cal_link = document.createElement('a');
cal_link.setAttribute('href', 'javascript:DateTimeShortcuts.openCalendar(' + num + ');'); cal_link.setAttribute('href', 'javascript:DateTimeShortcuts.openCalendar(' + num + ');');
cal_link.id = DateTimeShortcuts.calendarLinkName + num;
quickElement('img', cal_link, '', 'src', DateTimeShortcuts.admin_media_prefix + 'img/admin/icon_calendar.gif', 'alt', gettext('Calendar')); quickElement('img', cal_link, '', 'src', DateTimeShortcuts.admin_media_prefix + 'img/admin/icon_calendar.gif', 'alt', gettext('Calendar'));
shortcuts_span.appendChild(document.createTextNode('\240')); shortcuts_span.appendChild(document.createTextNode('\240'));
shortcuts_span.appendChild(today_link); shortcuts_span.appendChild(today_link);
@ -149,18 +160,6 @@ var DateTimeShortcuts = {
var cal_box = document.createElement('div'); var cal_box = document.createElement('div');
cal_box.style.display = 'none'; cal_box.style.display = 'none';
cal_box.style.position = 'absolute'; cal_box.style.position = 'absolute';
// is it left-to-right or right-to-left layout ?
if (getStyle(document.body,'direction')!='rtl') {
cal_box.style.left = findPosX(cal_link) + 17 + 'px';
}
else {
// since style's width is in em, it'd be tough to calculate
// px value of it. let's use an estimated px for now
// TODO: IE returns wrong value for findPosX when in rtl mode
// (it returns as it was left aligned), needs to be fixed.
cal_box.style.left = findPosX(cal_link) - 180 + 'px';
}
cal_box.style.top = findPosY(cal_link) - 75 + 'px';
cal_box.className = 'calendarbox module'; cal_box.className = 'calendarbox module';
cal_box.setAttribute('id', DateTimeShortcuts.calendarDivName1 + num); cal_box.setAttribute('id', DateTimeShortcuts.calendarDivName1 + num);
document.body.appendChild(cal_box); document.body.appendChild(cal_box);
@ -195,7 +194,24 @@ var DateTimeShortcuts = {
quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissCalendar(' + num + ');'); quickElement('a', cancel_p, gettext('Cancel'), 'href', 'javascript:DateTimeShortcuts.dismissCalendar(' + num + ');');
}, },
openCalendar: function(num) { openCalendar: function(num) {
document.getElementById(DateTimeShortcuts.calendarDivName1+num).style.display = 'block'; var cal_box = document.getElementById(DateTimeShortcuts.calendarDivName1+num)
var cal_link = document.getElementById(DateTimeShortcuts.calendarLinkName+num)
// Recalculate the clockbox position
// is it left-to-right or right-to-left layout ?
if (getStyle(document.body,'direction')!='rtl') {
cal_box.style.left = findPosX(cal_link) + 17 + 'px';
}
else {
// since style's width is in em, it'd be tough to calculate
// px value of it. let's use an estimated px for now
// TODO: IE returns wrong value for findPosX when in rtl mode
// (it returns as it was left aligned), needs to be fixed.
cal_box.style.left = findPosX(cal_link) - 180 + 'px';
}
cal_box.style.top = findPosY(cal_link) - 75 + 'px';
cal_box.style.display = 'block';
addEvent(window, 'click', function() { DateTimeShortcuts.dismissCalendar(num); return true; }); addEvent(window, 'click', function() { DateTimeShortcuts.dismissCalendar(num); return true; });
}, },
dismissCalendar: function(num) { dismissCalendar: function(num) {

View File

@ -11,7 +11,7 @@ function showRelatedObjectLookupPopup(triggeringLink) {
} else { } else {
href = triggeringLink.href + '?pop=1'; href = triggeringLink.href + '?pop=1';
} }
var win = window.open(href, name, 'height=500,width=740,resizable=yes,scrollbars=yes'); var win = window.open(href, name, 'height=500,width=800,resizable=yes,scrollbars=yes');
win.focus(); win.focus();
return false; return false;
} }

View File

@ -0,0 +1,28 @@
{% extends "admin/change_form.html" %}
{% load i18n %}
{% block after_field_sets %}
<p>{% trans "First, enter a username and password. Then, you'll be able to edit more user options." %}</p>
<fieldset class="module aligned">
<div class="form-row">
{{ form.username.html_error_list }}
<label for="id_username" class="required">{% trans 'Username' %}:</label> {{ form.username }}
<p class="help">{{ username_help_text }}</p>
</div>
<div class="form-row">
{{ form.password1.html_error_list }}
<label for="id_password1" class="required">{% trans 'Password' %}:</label> {{ form.password1 }}
</div>
<div class="form-row">
{{ form.password2.html_error_list }}
<label for="id_password2" class="required">{% trans 'Password (again)' %}:</label> {{ form.password2 }}
<p class="help">{% trans 'Enter the same password as above, for verification.' %}</p>
</div>
</fieldset>
{% endblock %}

View File

@ -28,6 +28,9 @@ urlpatterns = patterns('',
# ('^doc/templates/$', 'django.views.admin.doc.template_index'), # ('^doc/templates/$', 'django.views.admin.doc.template_index'),
('^doc/templates/(?P<template>.*)/$', 'django.contrib.admin.views.doc.template_detail'), ('^doc/templates/(?P<template>.*)/$', 'django.contrib.admin.views.doc.template_detail'),
# "Add user" -- a special-case view
('^auth/user/add/$', 'django.contrib.admin.views.auth.user_add_stage'),
# Add/change/delete/history # Add/change/delete/history
('^([^/]+)/([^/]+)/$', 'django.contrib.admin.views.main.change_list'), ('^([^/]+)/([^/]+)/$', 'django.contrib.admin.views.main.change_list'),
('^([^/]+)/([^/]+)/add/$', 'django.contrib.admin.views.main.add_stage'), ('^([^/]+)/([^/]+)/add/$', 'django.contrib.admin.views.main.add_stage'),

View File

@ -0,0 +1,39 @@
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django import forms, template
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
def user_add_stage(request):
manipulator = UserCreationForm()
if request.method == 'POST':
new_data = request.POST.copy()
errors = manipulator.get_validation_errors(new_data)
if not errors:
new_user = manipulator.save(new_data)
msg = _('The %(name)s "%(obj)s" was added successfully.') % {'name': 'user', 'obj': new_user}
if request.POST.has_key("_addanother"):
request.user.message_set.create(message=msg)
return HttpResponseRedirect(request.path)
else:
request.user.message_set.create(message=msg + ' ' + _("You may edit it again below."))
return HttpResponseRedirect('../%s/' % new_user.id)
else:
errors = new_data = {}
form = forms.FormWrapper(manipulator, new_data, errors)
return render_to_response('admin/auth/user/add_form.html', {
'title': _('Add user'),
'form': form,
'is_popup': request.REQUEST.has_key('_popup'),
'add': True,
'change': False,
'has_delete_permission': False,
'has_change_permission': True,
'has_file_field': False,
'has_absolute_url': False,
'auto_populated_fields': (),
'bound_field_sets': (),
'first_form_field_id': 'id_username',
'opts': User._meta,
'username_help_text': User._meta.get_field('username').help_text,
}, context_instance=template.RequestContext(request))

View File

@ -5,6 +5,28 @@ from django.template import Context, loader
from django.core import validators from django.core import validators
from django import forms from django import forms
class UserCreationForm(forms.Manipulator):
"A form that creates a user, with no privileges, from the given username and password."
def __init__(self):
self.fields = (
forms.TextField(field_name='username', length=30, maxlength=30, is_required=True,
validator_list=[validators.isAlphaNumeric, self.isValidUsername]),
forms.PasswordField(field_name='password1', length=30, maxlength=60, is_required=True),
forms.PasswordField(field_name='password2', length=30, maxlength=60, is_required=True,
validator_list=[validators.AlwaysMatchesOtherField('password1', "The two password fields didn't match.")]),
)
def isValidUsername(self, field_data, all_data):
try:
User.objects.get(username=field_data)
except User.DoesNotExist:
return
raise validators.ValidationError, 'A user with that username already exists.'
def save(self, new_data):
"Creates the user."
return User.objects.create_user(new_data['username'], '', new_data['password1'])
class AuthenticationForm(forms.Manipulator): class AuthenticationForm(forms.Manipulator):
""" """
Base class for authenticating users. Extend this to get a form that accepts Base class for authenticating users. Extend this to get a form that accepts

View File

@ -829,6 +829,12 @@ def get_validation_errors(outfile, app=None):
f = opts.get_field(fn) f = opts.get_field(fn)
except models.FieldDoesNotExist: except models.FieldDoesNotExist:
e.add(opts, '"admin.list_filter" refers to %r, which isn\'t a field.' % fn) e.add(opts, '"admin.list_filter" refers to %r, which isn\'t a field.' % fn)
# date_hierarchy
if opts.admin.date_hierarchy:
try:
f = opts.get_field(opts.admin.date_hierarchy)
except models.FieldDoesNotExist:
e.add(opts, '"admin.date_hierarchy" refers to %r, which isn\'t a field.' % opts.admin.date_hierarchy)
# Check ordering attribute. # Check ordering attribute.
if opts.ordering: if opts.ordering:

View File

@ -130,12 +130,13 @@ class RegexURLPattern(object):
return reverse_helper(self.regex, *args, **kwargs) return reverse_helper(self.regex, *args, **kwargs)
class RegexURLResolver(object): class RegexURLResolver(object):
def __init__(self, regex, urlconf_name): def __init__(self, regex, urlconf_name, default_kwargs=None):
# regex is a string representing a regular expression. # regex is a string representing a regular expression.
# urlconf_name is a string representing the module containing urlconfs. # urlconf_name is a string representing the module containing urlconfs.
self.regex = re.compile(regex) self.regex = re.compile(regex)
self.urlconf_name = urlconf_name self.urlconf_name = urlconf_name
self.callback = None self.callback = None
self.default_kwargs = default_kwargs or {}
def resolve(self, path): def resolve(self, path):
tried = [] tried = []
@ -149,7 +150,8 @@ class RegexURLResolver(object):
tried.extend([(pattern.regex.pattern + ' ' + t) for t in e.args[0]['tried']]) tried.extend([(pattern.regex.pattern + ' ' + t) for t in e.args[0]['tried']])
else: else:
if sub_match: if sub_match:
return sub_match[0], sub_match[1], dict(match.groupdict(), **sub_match[2]) sub_match_dict = dict(self.default_kwargs, **sub_match[2])
return sub_match[0], sub_match[1], dict(match.groupdict(), **sub_match_dict)
tried.append(pattern.regex.pattern) tried.append(pattern.regex.pattern)
raise Resolver404, {'tried': tried, 'path': new_path} raise Resolver404, {'tried': tried, 'path': new_path}

View File

@ -137,9 +137,12 @@ class SchemaBuilder(object):
r_col = f.column r_col = f.column
table = opts.db_table table = opts.db_table
col = opts.get_field(f.rel.field_name).column col = opts.get_field(f.rel.field_name).column
# For MySQL, r_name must be unique in the first 64
# characters. So we are careful with character usage here.
r_name = '%s_refs_%s_%x' % (r_col, col,
abs(hash((r_table, table))))
sql = style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \ sql = style.SQL_KEYWORD('ALTER TABLE') + ' %s ADD CONSTRAINT %s FOREIGN KEY (%s) REFERENCES %s (%s);' % \
(quote_name(table), (quote_name(table), quote_name(r_name),
quote_name('%s_referencing_%s_%s' % (r_col, r_table, col)),
quote_name(r_col), quote_name(r_table), quote_name(col)) quote_name(r_col), quote_name(r_table), quote_name(col))
pending.setdefault(rel_class, []).append( pending.setdefault(rel_class, []).append(
BoundStatement(sql, db.connection)) BoundStatement(sql, db.connection))

View File

@ -289,8 +289,11 @@ class Field(object):
if self.choices: if self.choices:
return first_choice + list(self.choices) return first_choice + list(self.choices)
rel_model = self.rel.to rel_model = self.rel.to
return first_choice + [(x._get_pk_val(), str(x)) if hasattr(self.rel, 'get_related_field'):
for x in rel_model._default_manager.complex_filter(self.rel.limit_choices_to)] lst = [(getattr(x, self.rel.get_related_field().attname), str(x)) for x in rel_model._default_manager.complex_filter(self.rel.limit_choices_to)]
else:
lst = [(x._get_pk_val(), str(x)) for x in rel_model._default_manager.complex_filter(self.rel.limit_choices_to)]
return first_choice + lst
def get_choices_default(self): def get_choices_default(self):
if self.radio_admin: if self.radio_admin:

View File

@ -25,7 +25,7 @@ def add_lookup(rel_cls, field):
key = (module, name) key = (module, name)
# Has the model already been loaded? # Has the model already been loaded?
# If so, resolve the string reference right away # If so, resolve the string reference right away
model = get_model(rel_cls._meta.app_label,field.rel.to) model = get_model(rel_cls._meta.app_label, field.rel.to, False)
if model: if model:
field.rel.to = model field.rel.to = model
field.do_related_class(model, rel_cls) field.do_related_class(model, rel_cls)

View File

@ -12,7 +12,11 @@ class GZipMiddleware(object):
""" """
def process_response(self, request, response): def process_response(self, request, response):
patch_vary_headers(response, ('Accept-Encoding',)) patch_vary_headers(response, ('Accept-Encoding',))
if response.has_header('Content-Encoding'):
# Avoid gzipping if we've already got a content-encoding or if the
# content-type is Javascript (silly IE...)
is_js = "javascript" in response.headers.get('Content-Type', '').lower()
if response.has_header('Content-Encoding') or is_js:
return response return response
ae = request.META.get('HTTP_ACCEPT_ENCODING', '') ae = request.META.get('HTTP_ACCEPT_ENCODING', '')

View File

@ -2,12 +2,15 @@
The "contrib" add-ons The "contrib" add-ons
===================== =====================
Django aims to follow Python's "batteries included" philosophy. It ships with a Django aims to follow Python's `"batteries included" philosophy`_. It ships
variety of extra, optional tools that solve common Web-development problems. with a variety of extra, optional tools that solve common Web-development
problems.
This code lives in ``django/contrib`` in the Django distribution. Here's a This code lives in ``django/contrib`` in the Django distribution. Here's a
rundown of the packages in ``contrib``: rundown of the packages in ``contrib``:
.. _"batteries included" philosophy: http://docs.python.org/tut/node12.html#batteries-included
admin admin
===== =====

View File

@ -578,6 +578,9 @@ related ``Person`` *and* the related ``City``::
p = b.author # Hits the database. p = b.author # Hits the database.
c = p.hometown # Hits the database. c = p.hometown # Hits the database.
Note that ``select_related()`` does not follow foreign keys that have
``null=True``.
``extra(select=None, where=None, params=None, tables=None)`` ``extra(select=None, where=None, params=None, tables=None)``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -620,13 +620,10 @@ like to make should be possible by editing the stylesheet. We've got a
How do I create users without having to edit password hashes? How do I create users without having to edit password hashes?
------------------------------------------------------------- -------------------------------------------------------------
We don't recommend you create users via the admin interface, because at the If you'd like to use the admin site to create users, upgrade to the Django
moment it requires you to edit password hashes manually. (Passwords are hashed development version, where this problem was fixed on Aug. 4, 2006.
using one-way hash algorithms for security; there's currently no Web interface
for changing passwords by entering the actual password rather than the hash.)
To create a user, you'll have to use the Python API. See `creating users`_ for You can also use the Python API. See `creating users`_ for full info.
full info.
.. _creating users: http://www.djangoproject.com/documentation/authentication/#creating-users .. _creating users: http://www.djangoproject.com/documentation/authentication/#creating-users

View File

@ -263,12 +263,12 @@ Here's the example URLconf from the `Django overview`_::
from django.conf.urls.defaults import * from django.conf.urls.defaults import *
urlpatterns = patterns('', urlpatterns = patterns('',
(r'^articles/(\d{4})/$', 'myproject.news.views.year_archive'), (r'^articles/(\d{4})/$', 'mysite.news.views.year_archive'),
(r'^articles/(\d{4})/(\d{2})/$', 'myproject.news.views.month_archive'), (r'^articles/(\d{4})/(\d{2})/$', 'mysite.news.views.month_archive'),
(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'myproject.news.views.article_detail'), (r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'mysite.news.views.article_detail'),
) )
In this example, each view has a common prefix -- ``'myproject.news.views'``. In this example, each view has a common prefix -- ``'mysite.news.views'``.
Instead of typing that out for each entry in ``urlpatterns``, you can use the Instead of typing that out for each entry in ``urlpatterns``, you can use the
first argument to the ``patterns()`` function to specify a prefix to apply to first argument to the ``patterns()`` function to specify a prefix to apply to
each view function. each view function.
@ -277,7 +277,7 @@ With this in mind, the above example can be written more concisely as::
from django.conf.urls.defaults import * from django.conf.urls.defaults import *
urlpatterns = patterns('myproject.news.views', urlpatterns = patterns('mysite.news.views',
(r'^articles/(\d{4})/$', 'year_archive'), (r'^articles/(\d{4})/$', 'year_archive'),
(r'^articles/(\d{4})/(\d{2})/$', 'month_archive'), (r'^articles/(\d{4})/(\d{2})/$', 'month_archive'),
(r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'article_detail'), (r'^articles/(\d{4})/(\d{2})/(\d+)/$', 'article_detail'),
@ -389,3 +389,45 @@ to pass metadata and options to views.
.. _generic views: http://www.djangoproject.com/documentation/generic_views/ .. _generic views: http://www.djangoproject.com/documentation/generic_views/
.. _syndication framework: http://www.djangoproject.com/documentation/syndication/ .. _syndication framework: http://www.djangoproject.com/documentation/syndication/
Passing extra options to ``include()``
--------------------------------------
**New in the 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
the extra options.
For example, these two URLconf sets are functionally identical:
Set one::
# main.py
urlpatterns = patterns('',
(r'^blog/', include('inner'), {'blogid': 3}),
)
# inner.py
urlpatterns = patterns('',
(r'^archive/$', 'mysite.views.archive'),
(r'^about/$', 'mysite.views.about'),
)
Set two::
# main.py
urlpatterns = patterns('',
(r'^blog/', include('inner')),
)
# inner.py
urlpatterns = patterns('',
(r'^archive/$', 'mysite.views.archive', {'blogid': 3}),
(r'^about/$', 'mysite.views.about', {'blogid': 3}),
)
Note that extra options will *always* be passed to *every* line in the included
URLconf, regardless of whether the line's view actually accepts those options
as valid. For this reason, this technique is only useful if you're certain that
every view in the the included URLconf accepts the extra options you're passing.