From e07dae7b77abc39d3e5d99dece5d43a5b99890f4 Mon Sep 17 00:00:00 2001 From: Jason Pellerin Date: Mon, 4 Dec 2006 18:16:40 +0000 Subject: [PATCH] [multi-db] Merged trunk to [3890] git-svn-id: http://code.djangoproject.com/svn/django/branches/multiple-db-support@4152 bcc190cf-cafb-0310-a4f2-bffc1f526a37 --- AUTHORS | 1 + django/conf/global_settings.py | 4 -- django/contrib/admin/views/decorators.py | 2 +- django/contrib/auth/models.py | 2 + django/core/handlers/base.py | 29 +++--------- django/core/handlers/modpython.py | 33 ++++++-------- django/core/handlers/wsgi.py | 4 -- django/core/management.py | 57 ++++++++++++++++++------ docs/admin_css.txt | 4 +- docs/api_stability.txt | 4 +- docs/authentication.txt | 27 +++++++---- docs/faq.txt | 2 +- docs/forms.txt | 2 +- docs/release_notes_0.95.txt | 2 +- docs/settings.txt | 9 ---- docs/templates.txt | 7 ++- docs/templates_python.txt | 2 +- docs/testing.txt | 6 +-- docs/tutorial03.txt | 2 +- docs/tutorial04.txt | 2 +- 20 files changed, 105 insertions(+), 96 deletions(-) diff --git a/AUTHORS b/AUTHORS index 5ff4bc4da0..414dc81cbb 100644 --- a/AUTHORS +++ b/AUTHORS @@ -75,6 +75,7 @@ answer newbie questions, and generally made Django that much better: Jeremy Dunck Andy Dustman Clint Ecker + Enrico favo@exoweb.net gandalf@owca.info Baishampayan Ghose diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index cc168034ce..1cd75a399b 100644 --- a/django/conf/global_settings.py +++ b/django/conf/global_settings.py @@ -226,10 +226,6 @@ YEAR_MONTH_FORMAT = 'F Y' # http://www.djangoproject.com/documentation/templates/#now MONTH_DAY_FORMAT = 'F j' -# Whether to enable Psyco, which optimizes Python code. Requires Psyco. -# http://psyco.sourceforge.net/ -ENABLE_PSYCO = False - # Do you want to manage transactions manually? # Hint: you really don't! TRANSACTIONS_MANAGED = False diff --git a/django/contrib/admin/views/decorators.py b/django/contrib/admin/views/decorators.py index fce50909f0..9dfe651fe6 100644 --- a/django/contrib/admin/views/decorators.py +++ b/django/contrib/admin/views/decorators.py @@ -87,7 +87,7 @@ def staff_member_required(view_func): # The user data is correct; log in the user in and continue. else: - if user.is_staff: + if user.is_active and user.is_staff: login(request, user) # TODO: set last_login with an event. user.last_login = datetime.datetime.now() diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index eb5713ba57..73bcfe92aa 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -216,6 +216,8 @@ class User(models.Model): def has_module_perms(self, app_label): "Returns True if the user has any permissions in the given app label." + if not self.is_active: + return False if self.is_superuser: return True return bool(len([p for p in self.get_all_permissions() if p[:p.index('.')] == app_label])) diff --git a/django/core/handlers/base.py b/django/core/handlers/base.py index a24c26d0a0..213c528952 100644 --- a/django/core/handlers/base.py +++ b/django/core/handlers/base.py @@ -89,7 +89,8 @@ class BaseHandler(object): return response except http.Http404, e: if settings.DEBUG: - return self.get_technical_error_response(request, is404=True, exception=e) + from django.views import debug + return debug.technical_404_response(request, e) else: callback, param_dict = resolver.resolve404() return callback(request, **param_dict) @@ -99,7 +100,8 @@ class BaseHandler(object): pass # See http://code.djangoproject.com/ticket/1023 except: # Handle everything else, including SuspiciousOperation, etc. if settings.DEBUG: - return self.get_technical_error_response(request) + from django.views import debug + return debug.technical_500_response(request, *sys.exc_info()) else: # Get the exception info now, in case another exception is thrown later. exc_info = sys.exc_info() @@ -112,26 +114,9 @@ class BaseHandler(object): request_repr = "Request repr() unavailable" message = "%s\n\n%s" % (self._get_traceback(exc_info), request_repr) mail_admins(subject, message, fail_silently=True) - return self.get_friendly_error_response(request, resolver) - - def get_friendly_error_response(self, request, resolver): - """ - Returns an HttpResponse that displays a PUBLIC error message for a - fundamental error. - """ - callback, param_dict = resolver.resolve500() - return callback(request, **param_dict) - - def get_technical_error_response(self, request, is404=False, exception=None): - """ - Returns an HttpResponse that displays a TECHNICAL error message for a - fundamental error. - """ - from django.views import debug - if is404: - return debug.technical_404_response(request, exception) - else: - return debug.technical_500_response(request, *sys.exc_info()) + # Return an HttpResponse that displays a friendly error message. + callback, param_dict = resolver.resolve500() + return callback(request, **param_dict) def _get_traceback(self, exc_info=None): "Helper function to return the traceback as a string" diff --git a/django/core/handlers/modpython.py b/django/core/handlers/modpython.py index 78fc9f1759..bf759db068 100644 --- a/django/core/handlers/modpython.py +++ b/django/core/handlers/modpython.py @@ -139,10 +139,6 @@ class ModPythonHandler(BaseHandler): # that use settings now can work from django.conf import settings - if settings.ENABLE_PSYCO: - import psyco - psyco.profile() - # if we need to set up middleware, now that settings works we can do it now. if self._request_middleware is None: self.load_middleware() @@ -160,23 +156,20 @@ class ModPythonHandler(BaseHandler): dispatcher.send(signal=signals.request_finished) # Convert our custom HttpResponse object back into the mod_python req. - populate_apache_request(response, req) - return 0 # mod_python.apache.OK + req.content_type = response['Content-Type'] + for key, value in response.headers.items(): + if key != 'Content-Type': + req.headers_out[key] = value + for c in response.cookies.values(): + req.headers_out.add('Set-Cookie', c.output(header='')) + req.status = response.status_code + try: + for chunk in response: + req.write(chunk) + finally: + response.close() -def populate_apache_request(http_response, mod_python_req): - "Populates the mod_python request object with an HttpResponse" - mod_python_req.content_type = http_response['Content-Type'] - for key, value in http_response.headers.items(): - if key != 'Content-Type': - mod_python_req.headers_out[key] = value - for c in http_response.cookies.values(): - mod_python_req.headers_out.add('Set-Cookie', c.output(header='')) - mod_python_req.status = http_response.status_code - try: - for chunk in http_response: - mod_python_req.write(chunk) - finally: - http_response.close() + return 0 # mod_python.apache.OK def handler(req): # mod_python hooks into this function. diff --git a/django/core/handlers/wsgi.py b/django/core/handlers/wsgi.py index 4d5c65b070..2998bd31f6 100644 --- a/django/core/handlers/wsgi.py +++ b/django/core/handlers/wsgi.py @@ -174,10 +174,6 @@ class WSGIHandler(BaseHandler): def __call__(self, environ, start_response): from django.conf import settings - if settings.ENABLE_PSYCO: - import psyco - psyco.profile() - # Set up middleware if needed. We couldn't do this earlier, because # settings weren't available. if self._request_middleware is None: diff --git a/django/core/management.py b/django/core/management.py index f6264459c2..c0373fcaf1 100644 --- a/django/core/management.py +++ b/django/core/management.py @@ -230,7 +230,6 @@ def get_sql_indexes(app): from django.db import model_connection_name from django.db.models import get_models connection_output = {} - for model in get_models(app): opts = model._meta connection_name = model_connection_name(model) @@ -242,6 +241,23 @@ def get_sql_indexes(app): get_sql_indexes.help_doc = "Prints the CREATE INDEX SQL statements for the given model module name(s)." get_sql_indexes.args = APP_ARGS +def _get_sql_index(model): + "Returns the CREATE INDEX SQL statements for a specific model" + from django.db import backend + output = [] + + for f in model._meta.fields: + if f.db_index: + unique = f.unique and 'UNIQUE ' or '' + output.append( + style.SQL_KEYWORD('CREATE %sINDEX' % unique) + ' ' + \ + style.SQL_TABLE('%s_%s' % (model._meta.db_table, f.column)) + ' ' + \ + style.SQL_KEYWORD('ON') + ' ' + \ + style.SQL_TABLE(backend.quote_name(model._meta.db_table)) + ' ' + \ + "(%s);" % style.SQL_FIELD(backend.quote_name(f.column)) + ) + return output + def get_sql_all(app): "Returns a list of CREATE TABLE SQL, initial-data inserts, and CREATE INDEX SQL for the given module." return get_sql_create(app) + get_sql_initial_data(app) + get_sql_indexes(app) @@ -269,7 +285,7 @@ def _collate(connection_output, reverse=False): connection_name) return map(str, final_output) -def syncdb(verbosity=2, interactive=True): +def syncdb(verbosity=1, interactive=True): "Creates the database tables for all apps in INSTALLED_APPS whose tables haven't already been created." from django.conf import settings from django.db import models, transaction @@ -296,8 +312,9 @@ def syncdb(verbosity=2, interactive=True): for app in models.get_apps(): # Install each application (models already installed will be skipped) created, pending = _install(app, commit=False, initial_data=False, - pending_allowed=True, pending=pending) - if verbosity >= 2: + pending_allowed=True, pending=pending, + verbosity=verbosity) + if verbosity >= 1: for model in created: print "Created table %s" % model._meta.db_table created_models.extend(created) @@ -311,6 +328,8 @@ def syncdb(verbosity=2, interactive=True): # Send the post_syncdb signal, so individual apps can do whatever they need # to do at this point. for app in models.get_apps(): + if verbosity >= 2: + print "Sending post-syncdb signal for application", app.__name__.split('.')[-2] dispatcher.send(signal=signals.post_syncdb, sender=app, app=app, created_models=created_models, verbosity=verbosity, interactive=interactive) @@ -322,7 +341,7 @@ def syncdb(verbosity=2, interactive=True): if model in created_models: try: if (model._default_manager.load_initial_data() - and verbosity >= 2): + and verbosity >= 1): print "Installed initial data for %s model" % model._meta.object_name except Exception, e: sys.stderr.write("Failed to install initial SQL data for %s model: %s" % \ @@ -391,7 +410,7 @@ def install(app): _install(app) def _install(app, commit=True, initial_data=True, pending_allowed=False, - pending=None): + pending=None, verbosity=1): from django.db import connection, models, transaction import sys @@ -407,6 +426,9 @@ def _install(app, commit=True, initial_data=True, pending_allowed=False, if pending is None: pending = {} for model in models.get_models(app, creation_order=True): + if verbosity >= 2: + print "Processing %s.%s model" % (app_name, + model._meta.object_name) manager = model._default_manager tables = manager.get_table_list() models_installed = manager.get_installed_models(tables) @@ -445,7 +467,7 @@ The full error: """ % (app_name, app_name)) + style.ERROR_OUTPUT(str(e)) + '\n') install.help_doc = "Executes ``sqlall`` for the given app(s) in the current database." install.args = APP_ARGS -def reset(app): +def reset(app, interactive=True): "Executes the equivalent of 'get_sql_reset' in the current database." from django.db import connection, transaction app_name = app.__name__.split('.')[-2] @@ -456,21 +478,25 @@ def reset(app): _check_for_validation_errors(app) sql_list = get_sql_reset(app) - confirm = raw_input(""" + if interactive: + confirm = raw_input(""" You have requested a database reset. This will IRREVERSIBLY DESTROY any data in your database. Are you sure you want to do this? Type 'yes' to continue, or 'no' to cancel: """) + else: + confirm = 'yes' + if confirm == 'yes': try: cursor = connection.cursor() for sql in sql_list: cursor.execute(sql) except Exception, e: - sys.stderr.write(style.ERROR("""Error: %s couldn't be installed. Possible reasons: + sys.stderr.write(style.ERROR("""Error: %s couldn't be reset. Possible reasons: * The database isn't running or isn't configured correctly. - * At least one of the database tables already exists. + * At least one of the database tables doesn't exist. * The SQL was invalid. Hint: Look at the output of 'django-admin.py sqlreset %s'. That's the SQL this command wasn't able to run. The full error: """ % (app_name, app_name)) + style.ERROR_OUTPUT(str(e)) + '\n') @@ -1031,7 +1057,7 @@ def runfcgi(args): runfastcgi(args) runfcgi.args = '[various KEY=val options, use `runfcgi help` for help]' -def test(verbosity, app_labels): +def test(app_labels, verbosity=1): "Runs the test suite for the specified applications" from django.conf import settings from django.db.models import get_app, get_apps @@ -1133,7 +1159,7 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING, argv=None): help='Tells Django to NOT prompt the user for input of any kind.') parser.add_option('--noreload', action='store_false', dest='use_reloader', default=True, help='Tells Django to NOT use the auto-reloader when running the development server.') - parser.add_option('--verbosity', action='store', dest='verbosity', default='2', + parser.add_option('--verbosity', action='store', dest='verbosity', default='1', type='choice', choices=['0', '1', '2'], help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'), parser.add_option('--adminmedia', dest='admin_media_path', default='', help='Specifies the directory from which to serve admin media for runserver.'), @@ -1182,7 +1208,7 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING, argv=None): parser.print_usage_and_exit() elif action == 'test': try: - action_mapping[action](int(options.verbosity), args[1:]) + action_mapping[action](args[1:], int(options.verbosity)) except IndexError: parser.print_usage_and_exit() elif action in ('startapp', 'startproject'): @@ -1216,7 +1242,10 @@ def execute_from_command_line(action_mapping=DEFAULT_ACTION_MAPPING, argv=None): if action not in NO_SQL_TRANSACTION: print style.SQL_KEYWORD("BEGIN;") for mod in mod_list: - output = action_mapping[action](mod) + if action == 'reset': + output = action_mapping[action](mod, options.interactive) + else: + output = action_mapping[action](mod) if output: print '\n'.join(output) if action not in NO_SQL_TRANSACTION: diff --git a/docs/admin_css.txt b/docs/admin_css.txt index ec402f7142..5822e26e45 100644 --- a/docs/admin_css.txt +++ b/docs/admin_css.txt @@ -82,7 +82,7 @@ There are also a few styles for styling text. .help This is a custom class for blocks of inline help text explaining the function of form elements. It makes text smaller and gray, and when applied - to ``p`` elements withing ``.form-row`` elements (see Form Styles below), + to ``p`` elements within ``.form-row`` elements (see Form Styles below), it will offset the text to align with the form field. Use this for help text, instead of ``small quiet``. It works on other elements, but try to put the class on a ``p`` whenever you can. @@ -170,4 +170,4 @@ Labels Form labels should always precede the field, except in the case of checkboxes and radio buttons, where the ``input`` should come first. Any explanation or help text should follow the ``label`` in a ``p`` with class -``.help``. \ No newline at end of file +``.help``. diff --git a/docs/api_stability.txt b/docs/api_stability.txt index a9d6904735..18885fbe63 100644 --- a/docs/api_stability.txt +++ b/docs/api_stability.txt @@ -82,7 +82,7 @@ that 90% of Django can be considered forwards-compatible at this point. That said, these APIs should *not* be considered stable, and are likely to change: - - `Forms and validation`_ will most likely be compeltely rewritten to + - `Forms and validation`_ will most likely be completely rewritten to deemphasize Manipulators in favor of validation-aware models. - `Serialization`_ is under heavy development; changes are likely. @@ -91,7 +91,7 @@ change: API changes may be necessary. - Generic relations will most likely be moved out of core and into the - content-types contrib package to avoid core dependacies on optional + content-types contrib package to avoid core dependancies on optional components. - The comments framework, which is yet undocumented, will likely get a complete diff --git a/docs/authentication.txt b/docs/authentication.txt index 6d345adaec..2a61ec82b5 100644 --- a/docs/authentication.txt +++ b/docs/authentication.txt @@ -66,8 +66,8 @@ Fields long and can contain any character. See the "Passwords" section below. * ``is_staff`` -- Boolean. Designates whether this user can access the admin site. - * ``is_active`` -- Boolean. Designates whether this user can log into the - Django admin. Set this to ``False`` instead of deleting accounts. + * ``is_active`` -- Boolean. Designates whether this account can be used + to log in. Set this flag to ``False`` instead of deleting accounts. * ``is_superuser`` -- Boolean. Designates that this user has all permissions without explicitly assigning them. * ``last_login`` -- A datetime of the user's last login. Is set to the @@ -99,7 +99,9 @@ custom methods: should prefer using ``is_authenticated()`` to this method. * ``is_authenticated()`` -- Always returns ``True``. This is a way to - tell if the user has been authenticated. + tell if the user has been authenticated. This does not imply any + permissions, and doesn't check if the user is active - it only indicates + that the user has provided a valid username and password. * ``get_full_name()`` -- Returns the ``first_name`` plus the ``last_name``, with a space in between. @@ -120,13 +122,16 @@ custom methods: * ``has_perm(perm)`` -- Returns ``True`` if the user has the specified permission, where perm is in the format ``"package.codename"``. + If the user is inactive, this method will always return ``False``. * ``has_perms(perm_list)`` -- Returns ``True`` if the user has each of the specified permissions, where each perm is in the format - ``"package.codename"``. + ``"package.codename"``. If the user is inactive, this method will + always return ``False``. * ``has_module_perms(package_name)`` -- Returns ``True`` if the user has any permissions in the given package (the Django app label). + If the user is inactive, this method will always return ``False``. * ``get_and_delete_messages()`` -- Returns a list of ``Message`` objects in the user's queue and deletes the messages from the queue. @@ -283,7 +288,10 @@ password is invalid, ``authenticate()`` returns ``None``. Example:: from django.contrib.auth import authenticate user = authenticate(username='john', password='secret') if user is not None: - print "You provided a correct username and password!" + if user.is_active: + print "You provided a correct username and password!" + else: + print "Your account has been disabled!" else: print "Your username and password were incorrect." @@ -301,10 +309,13 @@ This example shows how you might use both ``authenticate()`` and ``login()``:: password = request.POST['password'] user = authenticate(username=username, password=password) if user is not None: - login(request, user) - # Redirect to a success page. + if user.is_active: + login(request, user) + # Redirect to a success page. + else: + # Return a 'disabled account' error message else: - # Return an error message. + # Return an 'invalid login' error message. How to log a user out --------------------- diff --git a/docs/faq.txt b/docs/faq.txt index e1f344c811..c7f92d3580 100644 --- a/docs/faq.txt +++ b/docs/faq.txt @@ -499,7 +499,7 @@ specify an object to edit or delete. How do I add database-specific options to my CREATE TABLE statements, such as specifying MyISAM as the table type? ------------------------------------------------------------------------------------------------------------------ -We try to avoid adding special cases in the Django code to accomodate all the +We try to avoid adding special cases in the Django code to accommodate all the database-specific options such as table type, etc. If you'd like to use any of these options, create an `SQL initial data file`_ that contains ``ALTER TABLE`` statements that do what you want to do. The initial data files are executed in diff --git a/docs/forms.txt b/docs/forms.txt index 2b00cb67d6..767225cfd4 100644 --- a/docs/forms.txt +++ b/docs/forms.txt @@ -489,7 +489,7 @@ required fields are present and non-empty. For each field that passes that test *and if the form submission contained data* for that field, all the validators for that field are called in turn. The emphasized portion in the last sentence is important: if a form field is not submitted (because it -contains no data -- which is normal HTML behaviour), the validators are not +contains no data -- which is normal HTML behavior), the validators are not run against the field. This feature is particularly important for models using diff --git a/docs/release_notes_0.95.txt b/docs/release_notes_0.95.txt index e5b89e5a7a..3709cacf5a 100644 --- a/docs/release_notes_0.95.txt +++ b/docs/release_notes_0.95.txt @@ -110,7 +110,7 @@ many common questions appear with some regularity, and any particular problem may already have been answered. Finally, for those who prefer the more immediate feedback offered by IRC, -there's a #django channel or irc.freenode.net that is regularly populated by +there's a #django channel on irc.freenode.net that is regularly populated by Django users and developers from around the world. Friendly people are usually available at any hour of the day -- to help, or just to chat. diff --git a/docs/settings.txt b/docs/settings.txt index 57483cbef3..3f6e9b0e79 100644 --- a/docs/settings.txt +++ b/docs/settings.txt @@ -401,15 +401,6 @@ Subject-line prefix for e-mail messages sent with ``django.core.mail.mail_admins or ``django.core.mail.mail_managers``. You'll probably want to include the trailing space. -ENABLE_PSYCO ------------- - -Default: ``False`` - -Whether to enable Psyco, which optimizes Python code. Requires Psyco_. - -.. _Psyco: http://psyco.sourceforge.net/ - IGNORABLE_404_ENDS ------------------ diff --git a/docs/templates.txt b/docs/templates.txt index c9e76d6c94..b263a64aec 100644 --- a/docs/templates.txt +++ b/docs/templates.txt @@ -540,6 +540,11 @@ The arguments can be hard-coded strings, so the following is valid:: ... {% endifequal %} +It is only possible to compare an argument to template variables or strings. +You cannot check for equality with Python objects such as ``True`` or +``False``. If you need to test if something is true or false, use the ``if`` +and ``ifnot`` tags instead. + ifnotequal ~~~~~~~~~~ @@ -1051,7 +1056,7 @@ Formats a date as the time since that date (i.e. "4 days, 6 hours"). Takes an optional argument that is a variable containing the date to use as the comparison point (without the argument, the comparison point is *now*). For example, if ``blog_date`` is a date instance representing midnight on 1 -June 2006, and ``comment_date`` is a date instanace for 08:00 on 1 June 2006, +June 2006, and ``comment_date`` is a date instance for 08:00 on 1 June 2006, then ``{{ comment_date|timesince:blog_date }}`` would return "8 hours". timeuntil diff --git a/docs/templates_python.txt b/docs/templates_python.txt index 39e5b9d91a..8129b209c7 100644 --- a/docs/templates_python.txt +++ b/docs/templates_python.txt @@ -817,7 +817,7 @@ Inclusion tags Another common type of template tag is the type that displays some data by rendering *another* template. For example, Django's admin interface uses custom -template tags to display the buttons along the botton of the "add/change" form +template tags to display the buttons along the bottom of the "add/change" form pages. Those buttons always look the same, but the link targets change depending on the object being edited -- so they're a perfect case for using a small template that is filled with details from the current object. (In the admin's diff --git a/docs/testing.txt b/docs/testing.txt index b1ede3e4cc..19eef9f071 100644 --- a/docs/testing.txt +++ b/docs/testing.txt @@ -389,7 +389,7 @@ an alternative framework as if they were normal Django tests. When you run ``./manage.py test``, Django looks at the ``TEST_RUNNER`` setting to determine what to do. By default, ``TEST_RUNNER`` points to ``django.test.simple.run_tests``. This method defines the default Django -testing behaviour. This behaviour involves: +testing behavior. This behavior involves: #. Performing global pre-test setup #. Creating the test database @@ -435,7 +435,7 @@ a number of utility methods in the ``django.test.utils`` module. ``create_test_db(verbosity=1, autoclobber=False)`` Creates a new test database, and run ``syncdb`` against it. - ``verbosity`` has the same behaviour as in the test runner. + ``verbosity`` has the same behavior as in the test runner. ``Autoclobber`` describes the behavior that will occur if a database with the same name as the test database is discovered. If ``autoclobber`` is False, @@ -450,4 +450,4 @@ a number of utility methods in the ``django.test.utils`` module. Destroys the database with the name ``settings.DATABASE_NAME`` matching, and restores the value of ``settings.DATABASE_NAME`` to the provided name. - ``verbosity`` has the same behaviour as in the test runner. + ``verbosity`` has the same behavior as in the test runner. diff --git a/docs/tutorial03.txt b/docs/tutorial03.txt index 248d234043..b4f1d303dc 100644 --- a/docs/tutorial03.txt +++ b/docs/tutorial03.txt @@ -91,7 +91,7 @@ Finally, it calls that ``detail()`` function like so:: The ``poll_id='23'`` part comes from ``(?P\d+)``. Using parenthesis around a pattern "captures" the text matched by that pattern and sends it as an argument to the view function; the ``?P`` defines the name that will be used to -identify the matched pattern; and ``\d+`` is a regular experession to match a sequence of +identify the matched pattern; and ``\d+`` is a regular expression to match a sequence of digits (i.e., a number). Because the URL patterns are regular expressions, there really is no limit on diff --git a/docs/tutorial04.txt b/docs/tutorial04.txt index f234ed0ce1..c5e2ea3cea 100644 --- a/docs/tutorial04.txt +++ b/docs/tutorial04.txt @@ -207,7 +207,7 @@ for the polls app, we manually specify a template name for the results view: template. Note that we use ``dict()`` to return an altered dictionary in place. In previous parts of the tutorial, the templates have been provided with a context -that contains the ``poll` and ``latest_poll_list`` context variables. However, +that contains the ``poll`` and ``latest_poll_list`` context variables. However, the generic views provide the variables ``object`` and ``object_list`` as context. Therefore, you need to change your templates to match the new context variables. Go through your templates, and modify any reference to ``latest_poll_list`` to