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

newforms-admin: Merged from trunk up to [7668].

git-svn-id: http://code.djangoproject.com/svn/django/branches/newforms-admin@7669 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
Brian Rosner 2008-06-16 20:36:34 +00:00
parent d0af06bad8
commit a253ec3743
49 changed files with 5027 additions and 416 deletions

View File

@ -1,3 +1,5 @@
#!/usr/bin/env python
"""
gather_profile_stats.py /path/to/dir/of/profiles

View File

@ -11,9 +11,10 @@ gettext_noop = lambda s: s
DEBUG = False
TEMPLATE_DEBUG = False
# True if BaseHandler.get_response() should propagate raw exceptions
# rather than catching them. This is useful under some testing siutations,
# and should never be used on a live site.
# Whether the framework should propagate raw exceptions rather than catching
# them. This is useful under some testing siutations and should never be used
# on a live site.
DEBUG_PROPAGATE_EXCEPTIONS = False
# Whether to use the "Etag" header. This saves bandwidth but slows down performance.
@ -289,7 +290,7 @@ SESSION_COOKIE_DOMAIN = None # A string like ".lawren
SESSION_COOKIE_SECURE = False # Whether the session cookie should be secure (https:// only).
SESSION_COOKIE_PATH = '/' # The path of the session cookie.
SESSION_SAVE_EVERY_REQUEST = False # Whether to save the session data on every request.
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # Whether a user's session cookie expires when they close their browser.
SESSION_EXPIRE_AT_BROWSER_CLOSE = False # Whether a user's session cookie expires when the Web browser is closed.
SESSION_ENGINE = 'django.contrib.sessions.backends.db' # The module to store session data
SESSION_FILE_PATH = None # Directory to store session files if using the file session module. If None, the backend will use a sensible default.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,115 @@
# Estonian translation for the Django Project.
# Copyright (C) 2008, Django Project
# This file is distributed under the same license as the django package.
msgid ""
msgstr ""
"Project-Id-Version: Django"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2008-06-02 19:42+0300\n"
"PO-Revision-Date: 2008-06-16 19:55+0200\n"
"Last-Translator: Erik Allik <removethis-eallik_at_gmail-com>\n"
"Language-Team: Django-i18n <django-i18n@googlegroups.com>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: contrib/admin/media/js/SelectFilter2.js:33
#, perl-format
msgid "Available %s"
msgstr "Saadaval %s"
#: contrib/admin/media/js/SelectFilter2.js:41
msgid "Choose all"
msgstr "Vali kõik"
#: contrib/admin/media/js/SelectFilter2.js:46
msgid "Add"
msgstr "Lisa"
#: contrib/admin/media/js/SelectFilter2.js:48
msgid "Remove"
msgstr "Eemalda"
#: contrib/admin/media/js/SelectFilter2.js:53
#, perl-format
msgid "Chosen %s"
msgstr "Valitud %s"
#: contrib/admin/media/js/SelectFilter2.js:54
msgid "Select your choice(s) and click "
msgstr "Tee valik(ud) ja klõpsa"
#: contrib/admin/media/js/SelectFilter2.js:59
msgid "Clear all"
msgstr "Puhasta kõik"
#: contrib/admin/media/js/calendar.js:24
#: contrib/admin/media/js/dateparse.js:32
msgid ""
"January February March April May June July August September October November "
"December"
msgstr "Jaanuar Veebruar Märts Aprill Mai Juuni Juuli August September Oktoober November Detsember"
#: contrib/admin/media/js/calendar.js:25
msgid "S M T W T F S"
msgstr "P E T K N R L"
#: contrib/admin/media/js/dateparse.js:33
msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday"
msgstr "Pühapäev Esmaspäev Teisipäev Kolmapäev Neljapäev Reede Laupäev"
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:34
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:72
msgid "Show"
msgstr "Näita"
#: contrib/admin/media/js/admin/CollapsedFieldsets.js:63
msgid "Hide"
msgstr "Varja"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:47
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:81
msgid "Now"
msgstr "Praegu"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:51
msgid "Clock"
msgstr "Kell"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:78
msgid "Choose a time"
msgstr "Vali aeg"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:82
msgid "Midnight"
msgstr "Kesköö"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:83
msgid "6 a.m."
msgstr "6 hommikul"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:84
msgid "Noon"
msgstr "Keskpäev"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:88
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:183
msgid "Cancel"
msgstr "Tühista"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:128
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:177
msgid "Today"
msgstr "Täna"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:132
msgid "Calendar"
msgstr "Kalender"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:175
msgid "Yesterday"
msgstr "Eile"
#: contrib/admin/media/js/admin/DateTimeShortcuts.js:179
msgid "Tomorrow"
msgstr "Homme"

View File

@ -20,7 +20,7 @@ class FlatPage(models.Model):
verbose_name = _('flat page')
verbose_name_plural = _('flat pages')
ordering = ('url',)
def __unicode__(self):
return u"%s -- %s" % (self.url, self.title)
@ -38,7 +38,8 @@ class FlatPageAdmin(admin.ModelAdmin):
(None, {'fields': ('url', 'title', 'content', 'sites')}),
(_('Advanced options'), {'classes': ('collapse',), 'fields': ('enable_comments', 'registration_required', 'template_name')}),
)
list_filter = ('sites',)
list_display = ('url', 'title')
list_filter = ('sites', 'enable_comments', 'registration_required')
search_fields = ('url', 'title')
admin.site.register(FlatPage, FlatPageAdmin)

View File

@ -149,7 +149,7 @@ class FormWizard(object):
data = [(bf.name, bf.data or '') for bf in form] + [settings.SECRET_KEY]
# Use HIGHEST_PROTOCOL because it's the most efficient. It requires
# Python 2.3, but Django requires 2.3 anyway, so that's OK.
pickled = pickle.dumps(data, protocol=pickle.HIGHEST_PROTOCOL)
pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL)
return md5.new(pickled).hexdigest()
def determine_step(self, request, *args, **kwargs):

View File

@ -27,7 +27,7 @@ class ITRegionSelect(Select):
class ITProvinceSelect(Select):
"""
A Select widget that uses a list of IT regions as its choices.
A Select widget that uses a list of IT provinces as its choices.
"""
def __init__(self, attrs=None):
from it_province import PROVINCE_CHOICES

View File

@ -15,7 +15,7 @@ class Redirect(models.Model):
db_table = 'django_redirect'
unique_together=(('site', 'old_path'),)
ordering = ('old_path',)
def __unicode__(self):
return "%s ---> %s" % (self.old_path, self.new_path)
@ -26,6 +26,7 @@ class Redirect(models.Model):
from django.contrib import admin
class RedirectAdmin(admin.ModelAdmin):
list_display = ('old_path', 'new_path')
list_filter = ('site',)
search_fields = ('old_path', 'new_path')
radio_fields = {'site': admin.VERTICAL}

View File

@ -9,6 +9,8 @@ class Command(BaseCommand):
help='Specifies the output serialization format for fixtures.'),
make_option('--indent', default=None, dest='indent', type='int',
help='Specifies the indent level to use when pretty-printing output'),
make_option('-e', '--exclude', dest='exclude',action='append', default=[],
help='App to exclude (use multiple --exclude to exclude multiple apps).'),
)
help = 'Output the contents of the database as a fixture of the given format.'
args = '[appname ...]'
@ -16,12 +18,15 @@ class Command(BaseCommand):
def handle(self, *app_labels, **options):
from django.db.models import get_app, get_apps, get_models
format = options.get('format', 'json')
indent = options.get('indent', None)
format = options.get('format','json')
indent = options.get('indent',None)
exclude = options.get('exclude',[])
show_traceback = options.get('traceback', False)
excluded_apps = [get_app(app_label) for app_label in exclude]
if len(app_labels) == 0:
app_list = get_apps()
app_list = [app for app in get_apps() if app not in excluded_apps]
else:
app_list = [get_app(app_label) for app_label in app_labels]

View File

@ -111,7 +111,10 @@ class Command(BaseCommand):
models.add(obj.object.__class__)
obj.save()
label_found = True
except Exception, e:
except (SystemExit, KeyboardInterrupt):
raise
except Exception:
import traceback
fixture.close()
transaction.rollback()
transaction.leave_transaction_management()
@ -121,7 +124,7 @@ class Command(BaseCommand):
else:
sys.stderr.write(
self.style.ERROR("Problem installing fixture '%s': %s\n" %
(full_path, str(e))))
(full_path, traceback.format_exc())))
return
fixture.close()
except:

View File

@ -3,8 +3,7 @@ import os
from django.core.management.base import copy_helper, CommandError, LabelCommand
class Command(LabelCommand):
help = ("Creates a Django app directory structure for the given app name"
" in the current directory.")
help = "Creates a Django app directory structure for the given app name in the current directory."
args = "[appname]"
label = 'application name'
@ -16,6 +15,7 @@ class Command(LabelCommand):
def handle_label(self, app_name, directory=None, **options):
if directory is None:
directory = os.getcwd()
# Determine the project_name by using the basename of directory,
# which should be the full path of the project directory (or the
# current directory if no directory was passed).
@ -23,6 +23,15 @@ class Command(LabelCommand):
if app_name == project_name:
raise CommandError("You cannot create an app with the same name"
" (%r) as your project." % app_name)
# Check that the app_name cannot be imported.
try:
__import__(app_name)
except ImportError:
pass
else:
raise CommandError("%r conflicts with the name of an existing Python module and cannot be used as an app name. Please try another name." % app_name)
copy_helper(self.style, 'app', app_name, directory, project_name)
class ProjectCommand(Command):

View File

@ -3,8 +3,6 @@ import os
import re
from random import choice
INVALID_PROJECT_NAMES = ('django', 'site', 'test')
class Command(LabelCommand):
help = "Creates a Django project directory structure for the given project name in the current directory."
args = "[projectname]"
@ -20,13 +18,13 @@ class Command(LabelCommand):
# the parent directory.
directory = os.getcwd()
# Check that the project_name cannot be imported.
try:
proj_name = __import__(project_name)
if proj_name:
raise CommandError("%r conflicts with the name of an existing Python module and cannot be used as a project name. Please try another name." % project_name)
__import__(project_name)
except ImportError:
if project_name in INVALID_PROJECT_NAMES:
raise CommandError("%r contains an invalid project name. Please try another name." % project_name)
pass
else:
raise CommandError("%r conflicts with the name of an existing Python module and cannot be used as a project name. Please try another name." % project_name)
copy_helper(self.style, 'project', project_name, directory)

View File

@ -34,7 +34,16 @@ class Command(NoArgsCommand):
try:
__import__(app_name + '.management', {}, {}, [''])
except ImportError, exc:
if not exc.args[0].startswith('No module named management'):
# This is slightly hackish. We want to ignore ImportErrors
# if the "management" module itself is missing -- but we don't
# want to ignore the exception if the management module exists
# but raises an ImportError for some reason. The only way we
# can do this is to check the text of the exception. Note that
# we're a bit broad in how we check the text, because different
# Python implementations may not use the same text. CPython
# uses the text "No module named management".
msg = exc.args[0]
if not msg.startswith('No module named management') or 'management' not in msg:
raise
cursor = connection.cursor()

View File

@ -86,7 +86,7 @@ def reverse_helper(regex, *args, **kwargs):
"""
# TODO: Handle nested parenthesis in the following regex.
result = re.sub(r'\(([^)]+)\)', MatchChecker(args, kwargs), regex.pattern)
return result.replace('^', '').replace('$', '')
return result.replace('^', '').replace('$', '').replace('\\', '')
class MatchChecker(object):
"Class used in reverse RegexURLPattern lookup."

View File

@ -52,6 +52,10 @@ class BaseDatabaseFeatures(object):
uses_custom_query_class = False
empty_fetchmany_value = []
update_can_self_select = True
supports_usecs = True
time_field_needs_date = False
interprets_empty_strings_as_nulls = False
date_field_supports_time_value = True
class BaseDatabaseOperations(object):
"""
@ -202,8 +206,8 @@ class BaseDatabaseOperations(object):
def query_class(self, DefaultQueryClass):
"""
Given the default QuerySet class, returns a custom QuerySet class
to use for this backend. Returns None if a custom QuerySet isn't used.
Given the default Query class, returns a custom Query class
to use for this backend. Returns None if a custom Query isn't used.
See also BaseDatabaseFeatures.uses_custom_query_class, which regulates
whether this method is called at all.
"""
@ -266,3 +270,8 @@ class BaseDatabaseOperations(object):
tablespaces.
"""
return None
def prep_for_like_query(self, x):
"""Prepares a value for use in a LIKE query."""
from django.utils.encoding import smart_unicode
return smart_unicode(x).replace("\\", "\\\\").replace("%", "\%").replace("_", "\_")

View File

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

View File

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

View File

@ -31,6 +31,9 @@ class DatabaseFeatures(BaseDatabaseFeatures):
supports_tablespaces = True
uses_case_insensitive_names = True
uses_custom_query_class = True
time_field_needs_date = True
interprets_empty_strings_as_nulls = True
date_field_supports_time_value = False
class DatabaseOperations(BaseDatabaseOperations):
def autoinc_sql(self, table, column):

View File

@ -7,7 +7,7 @@ try:
except ImportError:
from django.utils import _decimal as decimal # for Python 2.3
from django.db import get_creation_module
from django.db import connection, get_creation_module
from django.db.models import signals
from django.db.models.query_utils import QueryWrapper
from django.dispatch import dispatcher
@ -30,9 +30,6 @@ class NOT_PROVIDED:
BLANK_CHOICE_DASH = [("", "---------")]
BLANK_CHOICE_NONE = [("", "None")]
# prepares a value for use in a LIKE query
prep_for_like_query = lambda x: smart_unicode(x).replace("\\", "\\\\").replace("%", "\%").replace("_", "\_")
class FieldDoesNotExist(Exception):
pass
@ -91,7 +88,7 @@ class Field(object):
self.blank, self.null = blank, null
# Oracle treats the empty string ('') as null, so coerce the null
# option whenever '' is a possible value.
if self.empty_strings_allowed and settings.DATABASE_ENGINE == 'oracle':
if self.empty_strings_allowed and connection.features.interprets_empty_strings_as_nulls:
self.null = True
self.core, self.rel, self.default = core, rel, default
self.editable = editable
@ -227,13 +224,13 @@ class Field(object):
elif lookup_type in ('range', 'in'):
return value
elif lookup_type in ('contains', 'icontains'):
return ["%%%s%%" % prep_for_like_query(value)]
return ["%%%s%%" % connection.ops.prep_for_like_query(value)]
elif lookup_type == 'iexact':
return [prep_for_like_query(value)]
return [connection.ops.prep_for_like_query(value)]
elif lookup_type in ('startswith', 'istartswith'):
return ["%s%%" % prep_for_like_query(value)]
return ["%s%%" % connection.ops.prep_for_like_query(value)]
elif lookup_type in ('endswith', 'iendswith'):
return ["%%%s" % prep_for_like_query(value)]
return ["%%%s" % connection.ops.prep_for_like_query(value)]
elif lookup_type == 'isnull':
return []
elif lookup_type == 'year':
@ -244,9 +241,12 @@ class Field(object):
if settings.DATABASE_ENGINE == 'sqlite3':
first = '%s-01-01'
second = '%s-12-31 23:59:59.999999'
elif settings.DATABASE_ENGINE == 'oracle' and self.get_internal_type() == 'DateField':
elif not connection.features.date_field_supports_time_value and self.get_internal_type() == 'DateField':
first = '%s-01-01'
second = '%s-12-31'
elif not connection.features.supports_usecs:
first = '%s-01-01 00:00:00'
second = '%s-12-31 23:59:59.99'
else:
first = '%s-01-01 00:00:00'
second = '%s-12-31 23:59:59.999999'
@ -263,7 +263,7 @@ class Field(object):
if callable(self.default):
return self.default()
return force_unicode(self.default, strings_only=True)
if not self.empty_strings_allowed or (self.null and settings.DATABASE_ENGINE != 'oracle'):
if not self.empty_strings_allowed or (self.null and not connection.features.interprets_empty_strings_as_nulls):
return None
return ""
@ -614,7 +614,7 @@ class DateTimeField(DateField):
if value is not None:
# MySQL will throw a warning if microseconds are given, because it
# doesn't support microseconds.
if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'):
if not connection.features.supports_usecs and hasattr(value, 'microsecond'):
value = value.replace(microsecond=0)
value = smart_unicode(value)
return Field.get_db_prep_save(self, value)
@ -848,7 +848,7 @@ class FilePathField(Field):
self.path, self.match, self.recursive = path, match, recursive
kwargs['max_length'] = kwargs.get('max_length', 100)
Field.__init__(self, verbose_name, name, **kwargs)
def formfield(self, **kwargs):
defaults = {
'path': self.path,
@ -1060,7 +1060,7 @@ class TimeField(Field):
return "TimeField"
def get_db_prep_lookup(self, lookup_type, value):
if settings.DATABASE_ENGINE == 'oracle':
if connection.features.time_field_needs_date:
# Oracle requires a date in order to parse.
def prep(value):
if isinstance(value, datetime.time):
@ -1087,9 +1087,9 @@ class TimeField(Field):
if value is not None:
# MySQL will throw a warning if microseconds are given, because it
# doesn't support microseconds.
if settings.DATABASE_ENGINE == 'mysql' and hasattr(value, 'microsecond'):
if not connection.features.supports_usecs and hasattr(value, 'microsecond'):
value = value.replace(microsecond=0)
if settings.DATABASE_ENGINE == 'oracle':
if connection.features.time_field_needs_date:
# cx_Oracle expects a datetime.datetime to persist into TIMESTAMP field.
if isinstance(value, datetime.time):
value = datetime.datetime(1900, 1, 1, value.hour, value.minute,

View File

@ -351,7 +351,7 @@ def create_many_related_manager(superclass):
self.target_col_name = target_col_name
self._pk_val = self.instance._get_pk_val()
if self._pk_val is None:
raise ValueError("%r instance needs to have a primary key value before a many-to-many relationship can be used." % model)
raise ValueError("%r instance needs to have a primary key value before a many-to-many relationship can be used." % instance.__class__.__name__)
def get_query_set(self):
return superclass.get_query_set(self).filter(**(self.core_filters))

View File

@ -513,7 +513,9 @@ class ValuesQuerySet(QuerySet):
# names of the model fields to select.
def iterator(self):
self.query.trim_extra_select(self.extra_names)
if (not self.extra_names and
len(self.field_names) != len(self.model._meta.fields)):
self.query.trim_extra_select(self.extra_names)
names = self.query.extra_select.keys() + self.field_names
for row in self.query.results_iter():
yield dict(zip(names, row))

View File

@ -2,10 +2,9 @@
Query subclasses which provide extra functionality beyond simple data retrieval.
"""
from django.contrib.contenttypes import generic
from django.core.exceptions import FieldError
from django.db.models.sql.constants import *
from django.db.models.sql.datastructures import RawValue, Date
from django.db.models.sql.datastructures import Date
from django.db.models.sql.query import Query
from django.db.models.sql.where import AND
@ -43,6 +42,7 @@ class DeleteQuery(Query):
More than one physical query may be executed if there are a
lot of values in pk_list.
"""
from django.contrib.contenttypes import generic
cls = self.model
for related in cls._meta.get_all_related_many_to_many_objects():
if not isinstance(related.field, generic.GenericRelation):
@ -382,4 +382,3 @@ class CountQuery(Query):
def get_ordering(self):
return ()

View File

@ -18,7 +18,7 @@ class WhereNode(tree.Node):
Used to represent the SQL where-clause.
The class is tied to the Query class that created it (in order to create
the corret SQL).
the correct SQL).
The children in this tree are usually either Q-like objects or lists of
[table_alias, field_name, field_class, lookup_type, value]. However, a

View File

@ -108,7 +108,7 @@ class CommonMiddleware(object):
if response.has_header('ETag'):
etag = response['ETag']
else:
etag = md5.new(response.content).hexdigest()
etag = '"%s"' % md5.new(response.content).hexdigest()
if response.status_code >= 200 and response.status_code < 300 and request.META.get('HTTP_IF_NONE_MATCH') == etag:
cookies = response.cookies
response = http.HttpResponseNotModified()

View File

@ -514,7 +514,6 @@ class URLField(RegexField):
return value
if self.verify_exists:
import urllib2
from django.conf import settings
headers = {
"Accept": "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5",
"Accept-Language": "en-us,en;q=0.5",

View File

@ -544,13 +544,7 @@ class ModelChoiceField(ChoiceField):
# the queryset.
return ModelChoiceIterator(self)
def _set_choices(self, value):
# This method is copied from ChoiceField._set_choices(). It's necessary
# because property() doesn't allow a subclass to overwrite only
# _get_choices without implementing _set_choices.
self._choices = self.widget.choices = list(value)
choices = property(_get_choices, _set_choices)
choices = property(_get_choices, ChoiceField._set_choices)
def clean(self, value):
Field.clean(self, value)

View File

@ -104,7 +104,7 @@ def patch_response_headers(response, cache_timeout=None):
if cache_timeout < 0:
cache_timeout = 0 # Can't have max-age negative
if not response.has_header('ETag'):
response['ETag'] = md5.new(response.content).hexdigest()
response['ETag'] = '"%s"' % md5.new(response.content).hexdigest()
if not response.has_header('Last-Modified'):
response['Last-Modified'] = http_date()
if not response.has_header('Expires'):

View File

@ -68,8 +68,7 @@ def shortcut(request, content_type_id, object_id):
def page_not_found(request, template_name='404.html'):
"""
Default 404 handler, which looks for the requested URL in the redirects
table, redirects if found, and displays 404 page if not redirected.
Default 404 handler.
Templates: `404.html`
Context:

View File

@ -270,17 +270,17 @@ to create a superuser at a later date, you can use a command line utility.
manage.py createsuperuser --username=joe --email=joe@example.com
You will be prompted for a password. Once entered, the user is created. If you
leave off the ``--username`` or the ``--email`` option, It will prompt you for
those values as well.
You will be prompted for a password. After you enter one, the user will be
created immediately. If you leave off the ``--username`` or the ``--email``
options, it will prompt you for those values.
If you're using an older release of Django, the old way of creating a superuser
on the command line still works::
python /path/to/django/contrib/auth/create_superuser.py
Where ``/path/to`` is the path to the Django codebase on your filesystem. The
``manage.py`` command is prefered since it'll figure out the correct path and
...where ``/path/to`` is the path to the Django codebase on your filesystem. The
``manage.py`` command is preferred because it figures out the correct path and
environment for you.
Storing additional information about users

View File

@ -148,15 +148,17 @@ Once you've claimed a ticket, you have a responsibility to work on that ticket
in a reasonably timely fashion. If you don't have time to work on it, either
unclaim it or don't claim it in the first place!
Core Django developers go through the list of claimed tickets from time to
Ticket triagers go through the list of claimed tickets from time to
time, checking whether any progress has been made. If there's no sign of
progress on a particular claimed ticket for a week or two after it's been
claimed, we will unclaim it for you so that it's no longer monopolized and
progress on a particular claimed ticket for a week or two, a triager may ask
you to relinquish the ticket claim so that it's no longer monopolized and
somebody else can claim it.
If you've claimed a ticket and it's taking a long time (days or weeks) to code,
keep everybody updated by posting comments on the ticket. That way, we'll know
not to unclaim it. More communication is better than less communication!
keep everybody updated by posting comments on the ticket. If you don't provide
regular updates, and you don't respond to a request for a progress report,
your claim on the ticket may be revoked. As always, more communication is
better than less communication!
Which tickets should be claimed?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -177,10 +179,10 @@ Patch style
English than in code. Indentation is the most common example; it's hard to
read patches when the only difference in code is that it's indented.
* When creating patches, always run ``svn diff`` from the top-level
``trunk`` directory -- i.e., the one that contains ``django``, ``docs``,
``tests``, ``AUTHORS``, etc. This makes it easy for other people to apply
your patches.
* When creating patches, always run ``svn diff`` from the top-level
``trunk`` directory -- i.e., the one that contains ``django``, ``docs``,
``tests``, ``AUTHORS``, etc. This makes it easy for other people to apply
your patches.
* Attach patches to a ticket in the `ticket tracker`_, using the "attach file"
button. Please *don't* put the patch in the ticket description or comment
@ -234,22 +236,28 @@ Since a picture is worth a thousand words, let's start there:
:width: 590
:alt: Django's ticket workflow
We've got two roles here:
We've got two official roles here:
* Core developers: people with commit access who make the decisions and
write the bulk of the code.
* Ticket triagers: community members who keep track of tickets, making
sure the tickets are always categorized correctly.
* Core developers: people with commit access who make the big decisions
and write the bulk of the code.
* Ticket triagers: trusted community members with a proven history of
working with the Django community. As a result of this history, they
have been entrusted by the core developers to make some of the smaller
decisions about tickets.
Second, note the five triage stages:
1. A ticket starts as "Unreviewed", meaning that a triager has yet to
examine the ticket and move it along.
1. A ticket starts as "Unreviewed", meaning that nobody has examined
the ticket.
2. "Design decision needed" means "this concept requires a design
decision," which should be discussed either in the ticket comments or on
django-developers.
`django-developers`_. The "Design decision needed" step will generally
only be used for feature requests. It can also be used for issues
that *might* be bugs, depending on opinion or interpretation. Obvious
bugs (such as crashes, incorrect query results, or non-compliance with a
standard) skip this step and move straight to "Accepted".
3. Once a ticket is ruled to be approved for fixing, it's moved into the
"Accepted" stage. This stage is where all the real work gets done.
@ -269,7 +277,7 @@ ticket has or needs in order to be "ready for checkin":
"Has patch"
This means the ticket has an associated patch_. These will be
reviewed to see if the patch is "good".
reviewed by the triage team to see if the patch is "good".
"Needs documentation"
This flag is used for tickets with patches that need associated
@ -292,7 +300,11 @@ A ticket can be resolved in a number of ways:
Django and the issue is fixed.
"invalid"
Used if the ticket is found to be incorrect or a user error.
Used if the ticket is found to be incorrect. This means that the
issue in the ticket is actually the result of a user error, or
describes a problem with something other than Django, or isn't
a bug report or feature request at all (for example, some new users
submit support queries as tickets).
"wontfix"
Used when a core developer decides that this request is not
@ -305,7 +317,8 @@ A ticket can be resolved in a number of ways:
tickets, we keep all the discussion in one place, which helps everyone.
"worksforme"
Used when the triage team is unable to replicate the original bug.
Used when the the ticket doesn't contain enough detail to replicate
the original bug.
If you believe that the ticket was closed in error -- because you're
still having the issue, or it's popped up somewhere else, or the triagers have
@ -316,6 +329,55 @@ reopen tickets that have been marked as "wontfix" by core developers.
.. _good patch: `Patch style`_
.. _patch: `Submitting patches`_
Triage by the general community
-------------------------------
Although the Core Developers and Ticket Triagers make the big decisions in
the ticket triage process, there is also a lot that general community
members can do to help the triage process. In particular, you can help out by:
* Closing "Unreviewed" tickets as "invalid", "worksforme", or "duplicate".
* Promoting "Unreviewed" tickets to "Design Decision Required" if there
is a design decision that needs to be made, or "Accepted" if they are
an obvious bug.
* Correcting the "Needs Tests", "Needs documentation", or "Has Patch" flags
for tickets where they are incorrectly set.
* Checking that old tickets are still valid. If a ticket hasn't seen
any activity in a long time, it's possible that the problem has been
fixed, but the ticket hasn't been closed.
* Contact the owners of tickets that have been claimed, but have not seen
any recent activity. If the owner doesn't respond after a week or so,
remove the owner's claim on the ticket.
* Identifying trends and themes in the tickets. If there a lot of bug reports
about a particular part of Django, it possibly indicates that we need
to consider refactoring that part of the code. If a trend is emerging,
you should raise it for discussion (referencing the relevant tickets)
on `django-developers`_.
However, we do ask that as a general community member working in the
ticket database:
* Please **don't** close tickets as "wontfix". The core developers will
make the final determination of the fate of a ticket, usually after
consultation with the community.
* Please **don't** promote tickets to "Ready for checkin" unless they are
*trivial* changes - for example, spelling mistakes or
broken links in documentation.
* Please **don't** reverse a decision that has been made by a core
developer. If you disagree with a discussion that has been made,
please post a message to `django-developers`_.
* Please be conservative in your actions. If you're unsure if you should
be making a change, don't make the change - leave a comment with your
concerns on the ticket, or post a message to `django-developers`_.
Submitting and maintaining translations
=======================================

View File

@ -1373,6 +1373,17 @@ SQL equivalent::
SELECT ... WHERE id IN (1, 3, 4);
You can also use a queryset to dynamically evaluate the list of values
instead of providing a list of literal values. The queryset must be
reduced to a list of individual values using the ``values()`` method,
and then converted into a query using the ``query`` attribute::
Entry.objects.filter(blog__in=Blog.objects.filter(name__contains='Cheddar').values('pk').query)
This queryset will be evaluated as subselect statement::
SELET ... WHERE blog.id IN (SELECT id FROM ... WHERE NAME LIKE '%Cheddar%')
startswith
~~~~~~~~~~
@ -2026,6 +2037,37 @@ Each "reverse" operation described in this section has an immediate effect on
the database. Every addition, creation and deletion is immediately and
automatically saved to the database.
One-to-one relationships
------------------------
One-to-one relationships are very similar to Many-to-one relationships.
If you define a OneToOneField on your model, instances of that model will have
access to the related object via a simple attribute of the model.
For example::
class EntryDetail(models.Model):
entry = models.OneToOneField(Entry)
details = models.TextField()
ed = EntryDetail.objects.get(id=2)
ed.entry # Returns the related Entry object.
The difference comes in reverse queries. The related model in a One-to-one
relationship also has access to a ``Manager`` object; however, that ``Manager``
represents a single object, rather than a collection of objects::
e = Entry.objects.get(id=2)
e.entrydetail # returns the related EntryDetail object
If no object has been assigned to this relationship, Django will raise
a ``DoesNotExist`` exception.
Instances can be assigned to the reverse relationship in the same way as
you would assign the forward relationship::
e.entrydetail = ed
Many-to-many relationships
--------------------------
@ -2053,12 +2095,6 @@ above example, if the ``ManyToManyField`` in ``Entry`` had specified
``related_name='entries'``, then each ``Author`` instance would have an
``entries`` attribute instead of ``entry_set``.
One-to-one relationships
------------------------
The semantics of one-to-one relationships will be changing soon, so we don't
recommend you use them.
How are the backward relationships possible?
--------------------------------------------

View File

@ -93,6 +93,31 @@ backend. See the `cache documentation`_ for more information.
.. _cache documentation: ../cache/
createsuperuser
---------------
**New in Django development version**
Creates a superuser account (a user who has all permissions). This is
useful if you need to create an initial superuser account but did not
do so during ``syncdb``, or if you need to programmatically generate
superuser accounts for your site(s).
When run interactively, this command will prompt for a password for
the new superuser account. When run non-interactively, no password
will be set, and the superuser account will not be able to log in until
a password has been manually set for it.
The username and e-mail address for the new account can be supplied by
using the ``--username`` and ``--email`` arguments on the command
line. If either of those is not supplied, ``createsuperuser`` will prompt for
it when running interactively.
This command is only available if Django's `authentication system`_
(``django.contrib.auth``) is installed.
.. _authentication system: ../authentication/
dbshell
-------
@ -109,31 +134,6 @@ the program name (``psql``, ``mysql``, ``sqlite3``) will find the program in
the right place. There's no way to specify the location of the program
manually.
createsuperuser
---------------
**New in Django development version**
Creates a superuser account (a user who has all permissions). This is
useful if you need to create an initial superuser account but did not
do so during ``syncdb``, or if you need to programmatically generate
superuser accounts for your site(s).
When run interactively, this command will prompt for a password for
the new superuser account; when run non-interactively, no password
will be set and the superuser account will not be able to log in until
a password has been manually set for it.
The username and e-mail address for the new account can be supplied by
using the ``--username`` and ``--email`` arguments on the command
line; if not supplied, ``createsuperuser`` will prompt for them when
running interactively.
This command is only available if Django's `authentication system`_
(``django.contrib.auth``) is installed.
.. _authentication system: ../authentication/
diffsettings
------------
@ -164,6 +164,22 @@ dumped.
.. _custom manager: ../model-api/#custom-managers
--exclude
~~~~~~~~~
**New in Django development version**
Exclude a specific application from the applications whose contents is
output. For example, to specifically exclude the `auth` application from
the output, you would call::
django-admin.py dumpdata --exclude=auth
If you want to exclude multiple applications, use multiple ``--exclude``
directives::
django-admin.py dumpdata --exclude=auth --exclude=contenttype
--format
~~~~~~~~
@ -338,9 +354,9 @@ The ``dumpdata`` command can be used to generate input for ``loaddata``.
Use ``--verbosity`` to specify the amount of notification and debug information
that ``django-admin.py`` should print to the console.
* ``0`` means no input.
* ``1`` means normal input (default).
* ``2`` means verbose input.
* ``0`` means no output.
* ``1`` means normal output (default).
* ``2`` means verbose output.
Example usage::
@ -422,6 +438,10 @@ means any Python code changes you make while the server is running will *not*
take effect if the particular Python modules have already been loaded into
memory.
Example usage::
django-admin.py runserver --noreload
Examples of using different ports and addresses
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@ -451,14 +471,6 @@ documentation.
.. _serving static files: ../static_files/
Turning off auto-reload
~~~~~~~~~~~~~~~~~~~~~~~
To disable auto-reloading of code while the development server is running, use the
``--noreload`` option, like so::
django-admin.py runserver --noreload
shell
-----
@ -581,9 +593,9 @@ data files.
Use ``--verbosity`` to specify the amount of notification and debug information
that ``django-admin.py`` should print to the console.
* ``0`` means no input.
* ``1`` means normal input (default).
* ``2`` means verbose input.
* ``0`` means no output.
* ``1`` means normal output (default).
* ``2`` means verbose output.
Example usage::
@ -617,9 +629,9 @@ is being executed as an unattended, automated script.
Use ``--verbosity`` to specify the amount of notification and debug information
that ``django-admin.py`` should print to the console.
* ``0`` means no input.
* ``1`` means normal input (default).
* ``2`` means verbose input.
* ``0`` means no output.
* ``1`` means normal output (default).
* ``2`` means verbose output.
Example usage::
@ -693,9 +705,9 @@ To run on 1.2.3.4:7000 with a ``test`` fixture::
Use ``--verbosity`` to specify the amount of notification and debug information
that ``django-admin.py`` should print to the console.
* ``0`` means no input.
* ``1`` means normal input (default).
* ``2`` means verbose input.
* ``0`` means no output.
* ``1`` means normal output (default).
* ``2`` means verbose output.
Example usage::

View File

@ -226,7 +226,7 @@ When will you release Django 1.0?
Short answer: When we're comfortable with Django's APIs, have added all
features that we feel are necessary to earn a "1.0" status, and are ready to
begin maintaining backwards compatibility.
begin maintaining backwards compatibility.
The merging of Django's `Queryset Refactor branch`_ went a long way toward Django
1.0. Merging the `Newforms Admin branch` will be another important step.
@ -260,8 +260,8 @@ Where can I find Django developers for hire?
Consult our `developers for hire page`_ for a list of Django developers who
would be happy to help you.
You might also be interested in posting a job to http://djangogigs.com/ .
If you want to find Django-capable people in your local area, try
You might also be interested in posting a job to http://djangogigs.com/ .
If you want to find Django-capable people in your local area, try
http://djangopeople.net/ .
.. _developers for hire page: http://code.djangoproject.com/wiki/DevelopersForHire
@ -646,18 +646,18 @@ You can also use the Python API. See `creating users`_ for full info.
.. _creating users: ../authentication/#creating-users
Getting Help
Getting help
============
How do I do X? Why doesn't Y work? Where can I go to get help?
--------------------------------------------------------------
If this FAQ doesn't contain an answer to your question, you might want to
try the `django-users mailing list`_. Feel free to ask any question related
to installing, using, or debugging Django.
try the `django-users mailing list`_. Feel free to ask any question related
to installing, using, or debugging Django.
If you prefer IRC, the `#django IRC channel`_ on freenode is an active
community of helpful individuals who may be able to solve your problem.
If you prefer IRC, the `#django IRC channel`_ on the Freenode IRC network is an
active community of helpful individuals who may be able to solve your problem.
.. _`django-users mailing list`: http://groups.google.com/group/django-users
.. _`#django IRC channel`: irc://irc.freenode.net/django
@ -666,35 +666,36 @@ Why hasn't my message appeared on django-users?
-----------------------------------------------
django-users_ has a lot of subscribers. This is good for the community, as
there are lot of people that can contribute answers to questions.
it means many people are available to contribute answers to questions.
Unfortunately, it also means that django-users_ is an attractive target for
spammers.
spammers.
In order to combat the spam problem, when you join the django-users_ mailing
list, we manually moderate the first message you send to the list. This means
that spammers get caught, but it also means that your first question to the
list, we manually moderate the first message you send to the list. This means
that spammers get caught, but it also means that your first question to the
list might take a little longer to get answered. We apologize for any
inconvenience that this policy may cause.
.. _django-users: http://groups.google.com/group/django-users
Nobody on django-users answered my question? What should I do?
Nobody on django-users answered my question! What should I do?
--------------------------------------------------------------
Wait. Ask again later. Try making your question more specific, or provide
a better example of your problem.
Try making your question more specific, or provide a better example of your
problem.
Remember, the readers of django-users_ are all volunteers. If nobody has
answered your question, it may be because nobody knows the answer, it may
be because nobody can understand the question, or it may be that everybody
that can help is extremely busy.
As with most open-source mailing lists, the folks on django-users_ are
volunteers. If nobody has answered your question, it may be because nobody
knows the answer, it may be because nobody can understand the question, or it
may be that everybody that can help is busy. One thing you might try is to ask
the question on IRC -- visit the `#django IRC channel`_ on the Freenode IRC
network.
Resist any temptation to mail the `django-developers mailing list`_ in an
attempt to get an answer to your question. django-developers_ is for discussing
the development of Django itself. Attempts to use django-developers_ as
a second-tier support mechanism will not be met an enthusiastic response.
You might notice we have a second mailing list, called django-developers_ --
but please don't e-mail support questions to this mailing list. This list is
for discussion of the development of Django itself. Asking a tech support
question there is considered quite impolite.
.. _`django-developers mailing list`: http://groups.google.com/group/django-developers
.. _django-developers: http://groups.google.com/group/django-developers
I think I've found a bug! What should I do?
@ -708,15 +709,14 @@ Detailed instructions on how to handle a potential bug can be found in our
I think I've found a security problem! What should I do?
--------------------------------------------------------
If you think you have found a security problem with Django, please send
a message to security@djangoproject.com. This is a private list only
open to long-time, highly trusted Django developers, and its archives
are not publicly readable.
If you think you've found a security problem with Django, please send a message
to security@djangoproject.com. This is a private list only open to long-time,
highly trusted Django developers, and its archives are not publicly readable.
Due to the sensitive nature of security issues, we ask that if you think you
have found a security problem, *please* don't send a message to one of the
have found a security problem, *please* don't send a message to one of the
public mailing lists. Django has a `policy for handling security issues`_;
while a defect is outstanding, we would like to minimize any damage that
while a defect is outstanding, we would like to minimize any damage that
could be inflicted through public knowledge of that defect.
.. _`policy for handling security issues`: ../contributing/#reporting-security-issues
@ -742,10 +742,10 @@ ignored" and "a ticket has not been attended to yet." Django's ticket system
contains hundreds of open tickets, of various degrees of impact on end-user
functionality, and Django's developers have to review and prioritize.
On top of that - the team working on Django are all volunteers. As a result,
the amount of time that we have to work on Django is limited, and will vary
from week to week depending on how much spare time we have. If we are busy, we
may not be able to spend as much time on Django as we might want.
On top of that: the people who work on Django are all volunteers. As a result,
the amount of time that we have to work on the framework is limited and will
vary from week to week depending on our spare time. If we're busy, we may not
be able to spend as much time on Django as we might want.
Besides, if your feature request stands no chance of inclusion in Django, we
won't ignore it -- we'll just close the ticket. So if your ticket is still

View File

@ -669,7 +669,7 @@ unless you want to override the default primary-key behavior.
If ``True``, this field must be unique throughout the table.
This is enforced at the database level and at the Django admin-form level. If
you try to add save a model with a duplicate value in a ``unique`` field, a
you try to save a model with a duplicate value in a ``unique`` field, a
``django.db.IntegrityError`` will be raised by the model's ``save()`` method.
``unique_for_date``

View File

@ -112,7 +112,7 @@ It also has these methods:
Sets a test cookie to determine whether the user's browser supports
cookies. Due to the way cookies work, you won't be able to test this
until the user's next page request. See "Setting test cookies" below for
until the user's next page request. See `Setting test cookies`_ below for
more information.
* ``test_cookie_worked()``
@ -120,7 +120,7 @@ It also has these methods:
Returns either ``True`` or ``False``, depending on whether the user's
browser accepted the test cookie. Due to the way cookies work, you'll
have to call ``set_test_cookie()`` on a previous, separate page request.
See "Setting test cookies" below for more information.
See `Setting test cookies`_ below for more information.
* ``delete_test_cookie()``
@ -139,10 +139,10 @@ It also has these methods:
in 5 minutes.
* If ``value`` is a ``datetime`` or ``timedelta`` object, the
session will expire at that specific time.
* If ``value`` is ``0`` then the user's session cookie will expire
when their browser is closed.
session will expire at that specific date/time.
* If ``value`` is ``0``, the user's session cookie will expire
when the user's Web browser is closed.
* If ``value`` is ``None``, the session reverts to using the global
session expiry policy.
@ -168,7 +168,7 @@ It also has these methods:
**New in Django development version**
Returns either ``True`` or ``False``, depending on whether the user's
session cookie will expire when their browser is closed.
session cookie will expire when the user's Web browser is closed.
You can edit ``request.session`` at any point in your view. You can edit it
multiple times.

View File

@ -394,6 +394,8 @@ site with ``DEBUG`` turned on.
DEBUG_PROPAGATE_EXCEPTIONS
--------------------------
**New in Django development version**
Default: ``False``
If set to True, Django's normal exception handling of view functions

View File

@ -819,7 +819,7 @@ The 'ifchanged' block tag is used within a loop. It has two possible uses.
2. If given a variable, check whether that variable has changed. For
example, the following shows the date every time it changes, but
only shows the hour if both the hour and the date has changed::
only shows the hour if both the hour and the date have changed::
{% for date in days %}
{% ifchanged date.date %} {{ date.date }} {% endifchanged %}

View File

@ -1059,8 +1059,8 @@ Passing template variables to the tag
Although you can pass any number of arguments to a template tag using
``token.split_contents()``, the arguments are all unpacked as
string literals. A little more work is required in order to dynamic content (a
template variable) to a template tag as an argument.
string literals. A little more work is required in order to pass dynamic
content (a template variable) to a template tag as an argument.
While the previous examples have formatted the current time into a string and
returned the string, suppose you wanted to pass in a ``DateTimeField`` from an
@ -1169,10 +1169,11 @@ Our earlier ``current_time`` function could thus be written like this::
In Python 2.4, the decorator syntax also works::
@register.simple_tag
def current_time(token):
def current_time(format_string):
...
A couple of things to note about the ``simple_tag`` helper function:
* Checking for the required number of arguments, etc, has already been
done by the time our function is called, so we don't need to do that.
* The quotes around the argument (if any) have already been stripped away,

View File

@ -22,8 +22,8 @@ installed.
.. admonition:: Where to get help:
If you're having trouble going through this tutorial, please post a message
to `django-users`_ or drop by `#django`_ on ``irc.freenode.net`` and we'll
try to help.
to `django-users`_ or drop by `#django`_ on ``irc.freenode.net`` to chat
with other Django users who might be able to help.
.. _django-users: http://groups.google.com/group/django-users
.. _#django: irc://irc.freenode.net/django

View File

@ -39,6 +39,14 @@ __test__ = {'API_TESTS':"""
# Create an Article.
>>> a1 = Article(id=None, headline='Django lets you build Web apps easily')
# You can't associate it with a Publication until it's been saved.
>>> a1.publications.add(p1)
Traceback (most recent call last):
...
ValueError: 'Article' instance needs to have a primary key value before a many-to-many relationship can be used.
# Save it!
>>> a1.save()
# Associate the Article with a Publication.

View File

@ -175,6 +175,12 @@ False
>>> Article.objects.filter(reporter__in=[r,r2]).distinct()
[<Article: John's second story>, <Article: Paul's story>, <Article: This is a test>]
# You can also use a queryset instead of a literal list of instances.
# The queryset must be reduced to a list of values using values(),
# then converted into a query
>>> Article.objects.filter(reporter__in=Reporter.objects.filter(first_name='John').values('pk').query).distinct()
[<Article: John's second story>, <Article: This is a test>]
# You need two underscores between "reporter" and "id" -- not one.
>>> Article.objects.filter(reporter_id__exact=1)
Traceback (most recent call last):

View File

@ -772,7 +772,7 @@ True
False
>>> r[1].name, r[1].value, r[1].choice_value, r[1].choice_label
('beatle', u'J', u'P', u'Paul')
>>> r[10]
>>> r[10] # doctest: +IGNORE_EXCEPTION_DETAIL
Traceback (most recent call last):
...
IndexError: list index out of range

View File

@ -507,6 +507,12 @@ True
>>> [sorted(d.items()) for d in dicts]
[[('id', 1), ('rank', 2)], [('id', 2), ('rank', 1)], [('id', 3), ('rank', 3)]]
Bug #7256
# An empty values() call includes all aliases, including those from an extra()
>>> dicts = qs.values().order_by('id')
>>> [sorted(d.items()) for d in dicts]
[[('author_id', 2), ('good', 0), ('id', 1), ('rank', 2)], [('author_id', 3), ('good', 0), ('id', 2), ('rank', 1)], [('author_id', 1), ('good', 1), ('id', 3), ('rank', 3)]]
Bugs #2874, #3002
>>> qs = Item.objects.select_related().order_by('note__note', 'name')
>>> list(qs)

View File

@ -17,6 +17,7 @@ test_data = (
('^hardcoded/$', 'hardcoded/', [], {}),
('^hardcoded/$', 'hardcoded/', ['any arg'], {}),
('^hardcoded/$', 'hardcoded/', [], {'kwarg': 'foo'}),
('^hardcoded/doc\\.pdf$', 'hardcoded/doc.pdf', [], {}),
('^people/(?P<state>\w\w)/(?P<name>\w+)/$', 'people/il/adrian/', [], {'state': 'il', 'name': 'adrian'}),
('^people/(?P<state>\w\w)/(?P<name>\d)/$', NoReverseMatch, [], {'state': 'il', 'name': 'adrian'}),
('^people/(?P<state>\w\w)/(?P<name>\w+)/$', NoReverseMatch, [], {'state': 'il'}),