diff --git a/AUTHORS b/AUTHORS index a2cf8c68cc..ec4a222a4e 100644 --- a/AUTHORS +++ b/AUTHORS @@ -44,6 +44,7 @@ answer newbie questions, and generally made Django that much better: adurdin@gmail.com Andreas andy@jadedplanet.net + Fabrice Aneche ant9000@netwise.it David Ascher Arthur diff --git a/django/__init__.py b/django/__init__.py index 5d5461c867..a91a7e13c3 100644 --- a/django/__init__.py +++ b/django/__init__.py @@ -1 +1 @@ -VERSION = (0, 96, 'pre') +VERSION = (0, 96, None) diff --git a/django/contrib/auth/models.py b/django/contrib/auth/models.py index 4f4f0b7538..c3aa863892 100644 --- a/django/contrib/auth/models.py +++ b/django/contrib/auth/models.py @@ -45,7 +45,7 @@ class Permission(models.Model): ordering = ('content_type', 'codename') def __str__(self): - return "%s | %s" % (self.content_type, self.name) + return "%s | %s | %s" % (self.content_type.app_label, self.content_type, self.name) class Group(models.Model): """Groups are a generic way of categorizing users to apply permissions, or some other label, to those users. A user can belong to any number of groups. diff --git a/django/contrib/localflavor/fr/__init__.py b/django/contrib/localflavor/fr/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/django/contrib/localflavor/fr/forms.py b/django/contrib/localflavor/fr/forms.py new file mode 100644 index 0000000000..ee87c5cda2 --- /dev/null +++ b/django/contrib/localflavor/fr/forms.py @@ -0,0 +1,44 @@ +""" +FR-specific Form helpers +""" + +from django.newforms import ValidationError +from django.newforms.fields import Field, RegexField, Select, EMPTY_VALUES +from django.newforms.util import smart_unicode +from django.utils.translation import gettext +import re + +phone_digits_re = re.compile(r'^0\d(\s|\.)?(\d{2}(\s|\.)?){3}\d{2}$') + +class FRZipCodeField(RegexField): + def __init__(self, *args, **kwargs): + super(FRZipCodeField, self).__init__(r'^\d{5}$', + max_length=None, min_length=None, + error_message=gettext(u'Enter a zip code in the format XXXXX.'), + *args, **kwargs) + +class FRPhoneNumberField(Field): + """ + Validate local French phone number (not international ones) + The correct format is '0X XX XX XX XX'. + '0X.XX.XX.XX.XX' and '0XXXXXXXXX' validate but are corrected to + '0X XX XX XX XX'. + """ + def clean(self, value): + super(FRPhoneNumberField, self).clean(value) + if value in EMPTY_VALUES: + return u'' + value = re.sub('(\.|\s)', '', smart_unicode(value)) + m = phone_digits_re.search(value) + if m: + return u'%s %s %s %s %s' % (value[0:2], value[2:4], value[4:6], value[6:8], value[8:10]) + raise ValidationError(u'Phone numbers must be in 0X XX XX XX XX format.') + +class FRDepartmentSelect(Select): + """ + A Select widget that uses a list of FR departments as its choices. + """ + def __init__(self, attrs=None): + from fr_department import DEPARTMENT_ASCII_CHOICES # relative import + super(FRDepartmentSelect, self).__init__(attrs, choices=DEPARTMENT_ASCII_CHOICES) + diff --git a/django/contrib/localflavor/fr/fr_department.py b/django/contrib/localflavor/fr/fr_department.py new file mode 100644 index 0000000000..02de295f88 --- /dev/null +++ b/django/contrib/localflavor/fr/fr_department.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- + +DEPARTMENT_ASCII_CHOICES = ( + ('01', '01 - Ain'), + ('02', '02 - Aisne'), + ('03', '03 - Allier'), + ('04', '04 - Alpes-de-Haute-Provence'), + ('05', '05 - Hautes-Alpes'), + ('06', '06 - Alpes-Maritimes'), + ('07', '07 - Ardeche'), + ('08', '08 - Ardennes'), + ('09', '09 - Ariege'), + ('10', '10 - Aube'), + ('11', '11 - Aude'), + ('12', '12 - Aveyron'), + ('13', '13 - Bouches-du-Rhone'), + ('14', '14 - Calvados'), + ('15', '15 - Cantal'), + ('16', '16 - Charente'), + ('17', '17 - Charente-Maritime'), + ('18', '18 - Cher'), + ('19', '19 - Correze'), + ('21', '21 - Cote-d\'Or'), + ('22', '22 - Cotes-d\'Armor'), + ('23', '23 - Creuse'), + ('24', '24 - Dordogne'), + ('25', '25 - Doubs'), + ('26', '26 - Drome'), + ('27', '27 - Eure'), + ('28', '28 - Eure-et-Loire'), + ('29', '29 - Finistere'), + ('2A', '2A - Corse-du-Sud'), + ('2B', '2B - Haute-Corse'), + ('30', '30 - Gard'), + ('31', '31 - Haute-Garonne'), + ('32', '32 - Gers'), + ('33', '33 - Gironde'), + ('34', '34 - Herault'), + ('35', '35 - Ille-et-Vilaine'), + ('36', '36 - Indre'), + ('37', '37 - Indre-et-Loire'), + ('38', '38 - Isere'), + ('39', '39 - Jura'), + ('40', '40 - Landes'), + ('41', '41 - Loir-et-Cher'), + ('42', '42 - Loire'), + ('43', '43 - Haute-Loire'), + ('44', '44 - Loire-Atlantique'), + ('45', '45 - Loiret'), + ('46', '46 - Lot'), + ('47', '47 - Lot-et-Garonne'), + ('48', '48 - Lozere'), + ('49', '49 - Maine-et-Loire'), + ('50', '50 - Manche'), + ('51', '51 - Marne'), + ('52', '52 - Haute-Marne'), + ('53', '53 - Mayenne'), + ('54', '54 - Meurthe-et-Moselle'), + ('55', '55 - Meuse'), + ('56', '56 - Morbihan'), + ('57', '57 - Moselle'), + ('58', '58 - Nievre'), + ('59', '59 - Nord'), + ('60', '60 - Oise'), + ('61', '61 - Orne'), + ('62', '62 - Pas-de-Calais'), + ('63', '63 - Puy-de-Dome'), + ('64', '64 - Pyrenees-Atlantiques'), + ('65', '65 - Hautes-Pyrenees'), + ('66', '66 - Pyrenees-Orientales'), + ('67', '67 - Bas-Rhin'), + ('68', '68 - Haut-Rhin'), + ('69', '69 - Rhone'), + ('70', '70 - Haute-Saone'), + ('71', '71 - Saone-et-Loire'), + ('72', '72 - Sarthe'), + ('73', '73 - Savoie'), + ('74', '74 - Haute-Savoie'), + ('75', '75 - Paris'), + ('76', '76 - Seine-Maritime'), + ('77', '77 - Seine-et-Marne'), + ('78', '78 - Yvelines'), + ('79', '79 - Deux-Sevres'), + ('80', '80 - Somme'), + ('81', '81 - Tarn'), + ('82', '82 - Tarn-et-Garonne'), + ('83', '83 - Var'), + ('84', '84 - Vaucluse'), + ('85', '85 - Vendee'), + ('86', '86 - Vienne'), + ('87', '87 - Haute-Vienne'), + ('88', '88 - Vosges'), + ('89', '89 - Yonne'), + ('90', '90 - Territoire de Belfort'), + ('91', '91 - Essonne'), + ('92', '92 - Hauts-de-Seine'), + ('93', '93 - Seine-Saint-Denis'), + ('94', '94 - Val-de-Marne'), + ('95', '95 - Val-d\'Oise'), + ('2A', '2A - Corse du sud'), + ('2B', '2B - Haute Corse'), + ('971', '971 - Guadeloupe'), + ('972', '972 - Martinique'), + ('973', '973 - Guyane'), + ('974', '974 - La Reunion'), + ('975', '975 - Saint-Pierre-et-Miquelon'), + ('976', '976 - Mayotte'), + ('984', '984 - Terres Australes et Antarctiques'), + ('986', '986 - Wallis et Futuna'), + ('987', '987 - Polynesie Francaise'), + ('988', '988 - Nouvelle-Caledonie'), +) diff --git a/django/contrib/localflavor/jp/__init__.py b/django/contrib/localflavor/jp/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/django/contrib/localflavor/jp/forms.py b/django/contrib/localflavor/jp/forms.py new file mode 100644 index 0000000000..4d93a21227 --- /dev/null +++ b/django/contrib/localflavor/jp/forms.py @@ -0,0 +1,38 @@ +""" +JP-specific Form helpers +""" + +from django.core import validators +from django.newforms import ValidationError +from django.utils.translation import gettext +from django.newforms.fields import RegexField, Select + +import re + +class JPPostalCodeField(RegexField): + """ + A form field that validates its input is a Japanese postcode. + + Accepts 7 digits, with or without a hyphen. + """ + def __init__(self, *args, **kwargs): + super(JPPostalCodeField, self).__init__(r'^\d{3}-\d{4}$|^\d{7}$', + max_length=None, min_length=None, + error_message=gettext(u'Enter a postal code in the format XXXXXXX or XXX-XXXX.'), + *args, **kwargs) + + def clean(self, value): + """ + Validates the input and returns a string that contains only numbers. + Returns an empty string for empty values. + """ + v = super(JPPostalCodeField, self).clean(value) + return v.replace('-', '') + +class JPPrefectureSelect(Select): + """ + A Select widget that uses a list of Japanese prefectures as its choices. + """ + def __init__(self, attrs=None): + from jp_prefectures import JP_PREFECTURES + super(JPPrefectureSelect, self).__init__(attrs, choices=JP_PREFECTURES) diff --git a/django/contrib/localflavor/jp/jp_prefectures.py b/django/contrib/localflavor/jp/jp_prefectures.py new file mode 100644 index 0000000000..72ac4f9474 --- /dev/null +++ b/django/contrib/localflavor/jp/jp_prefectures.py @@ -0,0 +1,51 @@ +from django.utils.translation import gettext_lazy as gettext_lazy + +JP_PREFECTURES = ( + ('hokkaido', gettext_lazy('Hokkaido'),), + ('aomori', gettext_lazy('Aomori'),), + ('iwate', gettext_lazy('Iwate'),), + ('miyagi', gettext_lazy('Miyagi'),), + ('akita', gettext_lazy('Akita'),), + ('yamagata', gettext_lazy('Yamagata'),), + ('fukushima', gettext_lazy('Fukushima'),), + ('ibaraki', gettext_lazy('Ibaraki'),), + ('tochigi', gettext_lazy('Tochigi'),), + ('gunma', gettext_lazy('Gunma'),), + ('saitama', gettext_lazy('Saitama'),), + ('chiba', gettext_lazy('Chiba'),), + ('tokyo', gettext_lazy('Tokyo'),), + ('kanagawa', gettext_lazy('Kanagawa'),), + ('yamanashi', gettext_lazy('Yamanashi'),), + ('nagano', gettext_lazy('Nagano'),), + ('niigata', gettext_lazy('Niigata'),), + ('toyama', gettext_lazy('Toyama'),), + ('ishikawa', gettext_lazy('Ishikawa'),), + ('fukui', gettext_lazy('Fukui'),), + ('gifu', gettext_lazy('Gifu'),), + ('shizuoka', gettext_lazy('Shizuoka'),), + ('aichi', gettext_lazy('Aichi'),), + ('mie', gettext_lazy('Mie'),), + ('shiga', gettext_lazy('Shiga'),), + ('kyoto', gettext_lazy('Kyoto'),), + ('osaka', gettext_lazy('Osaka'),), + ('hyogo', gettext_lazy('Hyogo'),), + ('nara', gettext_lazy('Nara'),), + ('wakayama', gettext_lazy('Wakayama'),), + ('tottori', gettext_lazy('Tottori'),), + ('shimane', gettext_lazy('Shimane'),), + ('okayama', gettext_lazy('Okayama'),), + ('hiroshima', gettext_lazy('Hiroshima'),), + ('yamaguchi', gettext_lazy('Yamaguchi'),), + ('tokushima', gettext_lazy('Tokushima'),), + ('kagawa', gettext_lazy('Kagawa'),), + ('ehime', gettext_lazy('Ehime'),), + ('kochi', gettext_lazy('Kochi'),), + ('fukuoka', gettext_lazy('Fukuoka'),), + ('saga', gettext_lazy('Saga'),), + ('nagasaki', gettext_lazy('Nagasaki'),), + ('kumamoto', gettext_lazy('Kumamoto'),), + ('oita', gettext_lazy('Oita'),), + ('miyazaki', gettext_lazy('Miyazaki'),), + ('kagoshima', gettext_lazy('Kagoshima'),), + ('okinawa', gettext_lazy('Okinawa'),), +) diff --git a/django/core/cache/backends/memcached.py b/django/core/cache/backends/memcached.py index 180f95da73..1ab019221a 100644 --- a/django/core/cache/backends/memcached.py +++ b/django/core/cache/backends/memcached.py @@ -3,9 +3,12 @@ from django.core.cache.backends.base import BaseCache, InvalidCacheBackendError try: - import memcache + import cmemcache as memcache except ImportError: - raise InvalidCacheBackendError, "Memcached cache backend requires the 'memcache' library" + try: + import memcache + except: + raise InvalidCacheBackendError("Memcached cache backend requires either the 'memcache' or 'cmemcache' library") class CacheClass(BaseCache): def __init__(self, server, params): diff --git a/django/core/management.py b/django/core/management.py index 1cd3753740..26551fdc88 100644 --- a/django/core/management.py +++ b/django/core/management.py @@ -299,7 +299,7 @@ def get_sql_delete(app): from django.db.backends.util import truncate_name introspection = get_introspection_module() - # This should work even if a connecton isn't available + # This should work even if a connection isn't available try: cursor = connection.cursor() except: @@ -486,7 +486,7 @@ def get_sql_indexes_for_model(model): unique = f.unique and 'UNIQUE ' or '' output.append( style.SQL_KEYWORD('CREATE %sINDEX' % unique) + ' ' + \ - truncate_name(style.SQL_TABLE('%s_%s' % (model._meta.db_table, f.column)), backend.get_max_name_length()) + ' ' + \ + style.SQL_TABLE(backend.quote_name('%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)) @@ -546,6 +546,7 @@ def syncdb(verbosity=1, interactive=True): created_models = set() pending_references = {} + # Create the tables for each model for app in models.get_apps(): app_name = app.__name__.split('.')[-2] model_list = models.get_models(app) @@ -567,6 +568,11 @@ def syncdb(verbosity=1, interactive=True): cursor.execute(statement) table_list.append(table_name_converter(model._meta.db_table)) + # Create the m2m tables. This must be done after all tables have been created + # to ensure that all referred tables will exist. + for app in models.get_apps(): + app_name = app.__name__.split('.')[-2] + model_list = models.get_models(app) for model in model_list: if model in created_models: sql = _get_many_to_many_sql_for_model(model) @@ -576,7 +582,7 @@ def syncdb(verbosity=1, interactive=True): for statement in sql: cursor.execute(statement) - transaction.commit_unless_managed() + transaction.commit_unless_managed() # Send the post_syncdb signal, so individual apps can do whatever they need # to do at this point. @@ -1383,18 +1389,26 @@ def load_data(fixture_labels, verbosity=1): app_fixtures = [os.path.join(os.path.dirname(app.__file__),'fixtures') for app in get_apps()] for fixture_label in fixture_labels: + parts = fixture_label.split('.') + if len(parts) == 1: + fixture_name = fixture_label + formats = serializers.get_serializer_formats() + else: + fixture_name, format = '.'.join(parts[:-1]), parts[-1] + if format in serializers.get_serializer_formats(): + formats = [format] + else: + formats = [] + if verbosity > 0: - print "Loading '%s' fixtures..." % fixture_label + if formats: + print "Loading '%s' fixtures..." % fixture_name + else: + print "Skipping fixture '%s': %s is not a known serialization format" % (fixture_name, format) + for fixture_dir in app_fixtures + list(settings.FIXTURE_DIRS) + ['']: if verbosity > 1: print "Checking %s for fixtures..." % humanize(fixture_dir) - parts = fixture_label.split('.') - if len(parts) == 1: - fixture_name = fixture_label - formats = serializers.get_serializer_formats() - else: - fixture_name, format = '.'.join(parts[:-1]), parts[-1] - formats = [format] label_found = False for format in formats: diff --git a/django/middleware/cache.py b/django/middleware/cache.py index 58800b24da..a88b4956b2 100644 --- a/django/middleware/cache.py +++ b/django/middleware/cache.py @@ -10,7 +10,7 @@ class CacheMiddleware(object): Only parameter-less GET or HEAD-requests with status code 200 are cached. If CACHE_MIDDLEWARE_ANONYMOUS_ONLY is set to True, only anonymous requests - (i.e., those node made by a logged-in user) will be cached. This is a + (i.e., those not made by a logged-in user) will be cached. This is a simple and effective way of avoiding the caching of the Django admin (and any other user-specific content). diff --git a/django/template/defaulttags.py b/django/template/defaulttags.py index b18fa1dce7..cf72d8f26e 100644 --- a/django/template/defaulttags.py +++ b/django/template/defaulttags.py @@ -354,6 +354,23 @@ class WidthRatioNode(Node): return '' return str(int(round(ratio))) +class WithNode(Node): + def __init__(self, var, name, nodelist): + self.var = var + self.name = name + self.nodelist = nodelist + + def __repr__(self): + return "" + + def render(self, context): + val = self.var.resolve(context) + context.push() + context[self.name] = val + output = self.nodelist.render(context) + context.pop() + return output + #@register.tag def comment(parser, token): """ @@ -595,8 +612,8 @@ def do_if(parser, token): :: - {% if althlete_list %} - Number of athletes: {{ althete_list|count }} + {% if athlete_list %} + Number of athletes: {{ athlete_list|count }} {% else %} No athletes. {% endif %} @@ -967,3 +984,23 @@ def widthratio(parser, token): return WidthRatioNode(parser.compile_filter(this_value_expr), parser.compile_filter(max_value_expr), max_width) widthratio = register.tag(widthratio) + +#@register.tag +def do_with(parser, token): + """ + Add a value to the context (inside of this block) for caching and easy + access. For example:: + + {% with person.some_sql_method as total %} + {{ total }} object{{ total|pluralize }} + {% endwith %} + """ + bits = list(token.split_contents()) + if len(bits) != 4 or bits[2] != "as": + raise TemplateSyntaxError, "%r expected format is 'value as name'" % tagname + var = parser.compile_filter(bits[1]) + name = bits[3] + nodelist = parser.parse(('endwith',)) + parser.delete_first_token() + return WithNode(var, name, nodelist) +do_with = register.tag('with', do_with) diff --git a/docs/add_ons.txt b/docs/add_ons.txt index 8f47847e3e..b92ae0bd17 100644 --- a/docs/add_ons.txt +++ b/docs/add_ons.txt @@ -9,6 +9,13 @@ problems. This code lives in ``django/contrib`` in the Django distribution. Here's a rundown of the packages in ``contrib``: +.. admonition:: Note + + For most of these add-ons -- specifically, the add-ons that include either + models or template tags -- you'll need to add the package name (e.g., + ``'django.contrib.admin'``) to your ``INSTALLED_APPS`` setting and re-run + ``manage.py syncdb``. + .. _"batteries included" philosophy: http://docs.python.org/tut/node12.html#batteries-included admin @@ -51,8 +58,6 @@ See the `csrf documentation`_. formtools ========= -**New in Django development version** - A set of high-level abstractions for Django forms (django.newforms). django.contrib.formtools.preview @@ -142,8 +147,6 @@ See the `flatpages documentation`_. localflavor =========== -**New in Django development version** - A collection of various Django snippets that are useful only for a particular country or culture. For example, ``django.contrib.localflavor.usa.forms`` contains a ``USZipCodeField`` that you can use to validate U.S. zip codes. @@ -151,13 +154,20 @@ contains a ``USZipCodeField`` that you can use to validate U.S. zip codes. markup ====== -A collection of template filters that implement these common markup languages: +A collection of template filters that implement common markup languages: - * `Textile`_ - * `Markdown`_ - * `ReST (ReStructured Text)`_ + * ``textile`` -- implements `Textile`_ + * ``markdown`` -- implements `Markdown`_ + * ``restructuredtext`` -- implements `ReST (ReStructured Text)`_ -For documentation, read the source code in django/contrib/markup/templatetags/markup.py. +In each case, the filter expects formatted markup as a string and returns a +string representing the marked-up text. For example, the ``textile`` filter +converts text that is marked-up in Textile format to HTML. + +To activate these filters, add ``'django.contrib.markup'`` to your +``INSTALLED_APPS`` setting. Once you've done that, use ``{% load markup %}`` in +a template, and you'll have access to these filters. For more documentation, +read the source code in django/contrib/markup/templatetags/markup.py. .. _Textile: http://en.wikipedia.org/wiki/Textile_%28markup_language%29 .. _Markdown: http://en.wikipedia.org/wiki/Markdown diff --git a/docs/cache.txt b/docs/cache.txt index 054d99819d..e245e100e7 100644 --- a/docs/cache.txt +++ b/docs/cache.txt @@ -66,10 +66,19 @@ deleting arbitrary data in the cache. All data is stored directly in memory, so there's no overhead of database or filesystem usage. After installing Memcached itself, you'll need to install the Memcached Python -bindings. They're in a single Python module, memcache.py, available at -ftp://ftp.tummy.com/pub/python-memcached/ . If that URL is no longer valid, -just go to the Memcached Web site (http://www.danga.com/memcached/) and get the -Python bindings from the "Client APIs" section. +bindings. Two versions of this are available. Choose and install *one* of the +following modules: + + * The fastest available option is a module called ``cmemcache``, available + at http://gijsbert.org/cmemcache/ . (This module is only compatible with + the Django development version. Django 0.96 is only compatible with the + second option, below.) + + * If you can't install ``cmemcache``, you can install ``python-memcached``, + available at ftp://ftp.tummy.com/pub/python-memcached/ . If that URL is + no longer valid, just go to the Memcached Web site + (http://www.danga.com/memcached/) and get the Python bindings from the + "Client APIs" section. To use Memcached with Django, set ``CACHE_BACKEND`` to ``memcached://ip:port/``, where ``ip`` is the IP address of the Memcached diff --git a/docs/django-admin.txt b/docs/django-admin.txt index ef1d73cdbd..b6028dc2a0 100644 --- a/docs/django-admin.txt +++ b/docs/django-admin.txt @@ -100,8 +100,6 @@ if you're ever curious to see the full list of defaults. dumpdata [appname appname ...] ------------------------------ -**New in Django development version** - Output to standard output all data in the database associated with the named application(s). @@ -117,8 +115,6 @@ The output of ``dumpdata`` can be used as input for ``loaddata``. flush ----- -**New in Django development version** - Return the database to the state it was in immediately after syncdb was executed. This means that all data will be removed from the database, any post-synchronization handlers will be re-executed, and the ``initial_data`` @@ -165,18 +161,9 @@ needed. ``inspectdb`` works with PostgreSQL, MySQL and SQLite. Foreign-key detection only works in PostgreSQL and with certain types of MySQL tables. -install [appname appname ...] ------------------------------ - -**Removed in Django development version** - -Executes the equivalent of ``sqlall`` for the given appnames. - loaddata [fixture fixture ...] ------------------------------ -**New in Django development version** - Searches for and loads the contents of the named fixture into the database. A *Fixture* is a collection of files that contain the serialized contents of @@ -350,14 +337,12 @@ Prints the DROP TABLE SQL statements for the given appnames. sqlcustom [appname appname ...] ------------------------------- -**New in Django development version** - Prints the custom SQL statements for the given appnames. For each model in each specified app, this command looks for the file ``/sql/.sql``, where ```` is the given appname and ```` is the model's name in lowercase. For example, if you have an -app ``news`` that includes a ``Story`` model, ``sqlinitialdata`` will attempt +app ``news`` that includes a ``Story`` model, ``sqlcustom`` will attempt to read a file ``news/sql/story.sql`` and append it to the output of this command. @@ -373,13 +358,6 @@ sqlindexes [appname appname ...] Prints the CREATE INDEX SQL statements for the given appnames. -sqlinitialdata [appname appname ...] --------------------------------------------- - -**Removed in Django development version** - -This method has been renamed ``sqlcustom`` in the development version of Django. - sqlreset [appname appname ...] -------------------------------------- @@ -426,8 +404,6 @@ fixture data files. test ---- -**New in Django development version** - Discover and run tests for all installed models. See `Testing Django applications`_ for more information. .. _testing django applications: ../testing/ @@ -475,8 +451,6 @@ setting the Python path for you. --format -------- -**New in Django development version** - Example usage:: django-admin.py dumpdata --format=xml @@ -493,8 +467,6 @@ options. --indent -------- -**New in Django development version** - Example usage:: django-admin.py dumpdata --indent=4 @@ -506,8 +478,6 @@ Pretty-printing will only be enabled if the indent option is provided. --noinput --------- -**New in Django development version** - Inform django-admin that the user should NOT be prompted for any input. Useful if the django-admin script will be executed as an unattended, automated script. @@ -530,8 +500,6 @@ Example output:: --verbosity ----------- -**New in Django development version** - Example usage:: django-admin.py syncdb --verbosity=2 @@ -543,8 +511,6 @@ and `2` is verbose output. --adminmedia ------------ -**New in Django development version** - Example usage:: django-admin.py manage.py --adminmedia=/tmp/new-admin-style/ diff --git a/docs/generic_views.txt b/docs/generic_views.txt index a136c72a07..7659a428c5 100644 --- a/docs/generic_views.txt +++ b/docs/generic_views.txt @@ -97,8 +97,7 @@ which is a dictionary of the parameters captured in the URL. * ``extra_context``: A dictionary of values to add to the template context. By default, this is an empty dictionary. If a value in the dictionary is callable, the generic view will call it - just before rendering the template. (**This is new in the - Django development version.**) + just before rendering the template. **Example:** @@ -752,10 +751,10 @@ If the results are paginated, the context will contain these extra variables: * ``previous``: The previous page number, as an integer. This is 1-based. - * `last_on_page`: **New in Django development version** The number of the + * `last_on_page`: The number of the last result on the current page. This is 1-based. - * `first_on_page`: **New in Django development version** The number of the + * `first_on_page`: The number of the first result on the current page. This is 1-based. * ``pages``: The total number of pages, as an integer. diff --git a/docs/install.txt b/docs/install.txt index 3eede02af0..dbbde93f83 100644 --- a/docs/install.txt +++ b/docs/install.txt @@ -86,25 +86,17 @@ Installing the official version Distribution-provided packages will typically allow for automatic installation of dependancies and easy upgrade paths. - 2. Download Django-0.95.tar.gz from our `download page`_. + 2. Download the latest release from our `download page`_. - 3. ``tar xzvf Django-0.95.tar.gz`` + 3. Untar the downloaded file (e.g. ``tar xzvf Django-NNN.tar.gz``). - 4. ``cd Django-0.95`` + 4. Change into the downloaded directory (e.g. ``cd Django-NNN``). - 5. ``sudo python setup.py install`` - -Note that the last command will automatically download and install setuptools_ -if you don't already have it installed. This requires a working Internet -connection and may cause problems on Python 2.5. If you run into problems, -try using our development version by following the instructions below. The -development version no longer uses setuptools nor requires an Internet -connection. + 5. Run ``sudo python setup.py install``. The command will install Django in your Python installation's ``site-packages`` directory. -.. _setuptools: http://peak.telecommunity.com/DevCenter/setuptools .. _distribution specific notes: ../distributions/ Installing the development version diff --git a/docs/middleware.txt b/docs/middleware.txt index 7635442a5c..0d533443d3 100644 --- a/docs/middleware.txt +++ b/docs/middleware.txt @@ -104,8 +104,6 @@ Also removes the content from any response to a HEAD request and sets the django.middleware.http.SetRemoteAddrFromForwardedFor ---------------------------------------------------- -**New in Django development version** - Sets ``request.META['REMOTE_ADDR']`` based on ``request.META['HTTP_X_FORWARDED_FOR']``, if the latter is set. This is useful if you're sitting behind a reverse proxy that causes each request's diff --git a/docs/model-api.txt b/docs/model-api.txt index 26686b02fe..155ef63271 100644 --- a/docs/model-api.txt +++ b/docs/model-api.txt @@ -362,9 +362,8 @@ Like a ``PositiveIntegerField``, but only allows values under a certain containing only letters, numbers, underscores or hyphens. They're generally used in URLs. -In the Django development version, you can specify ``maxlength``. If -``maxlength`` is not specified, Django will use a default length of 50. In -previous Django versions, there's no way to override the length of 50. +Like a CharField, you can specify ``maxlength``. If ``maxlength`` is +not specified, Django will use a default length of 50. Implies ``db_index=True``. @@ -1457,8 +1456,8 @@ user searches for ``john lennon``, Django will do the equivalent of this SQL WHERE (first_name ILIKE '%john%' OR last_name ILIKE '%john%') AND (first_name ILIKE '%lennon%' OR last_name ILIKE '%lennon%') -**New in Django development version:** For faster and/or more restrictive -searches, prefix the field name with an operator: +For faster and/or more restrictive searches, prefix the field name +with an operator: ``^`` Matches the beginning of the field. For example, if ``search_fields`` is @@ -1754,8 +1753,6 @@ But this template code is good:: The ``permalink`` decorator ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -**New in Django development version.** - The problem with the way we wrote ``get_absolute_url()`` above is that it slightly violates the DRY principle: the URL for this object is defined both in the URLConf file and in the model. diff --git a/docs/newforms.txt b/docs/newforms.txt index 33aaf14357..ddb850f54c 100644 --- a/docs/newforms.txt +++ b/docs/newforms.txt @@ -9,9 +9,10 @@ framework. This document explains how to use this new library. Migration plan ============== -``django.newforms`` currently is only available in the Django development version --- i.e., it's not available in the Django 0.95 release. For the next Django -release, our plan is to do the following: +``django.newforms`` currently is only available in Django beginning +with the 0.96 release. the Django development version -- i.e., it's +not available in the Django 0.95 release. For the next Django release, +our plan is to do the following: * As of revision [4208], we've copied the current ``django.forms`` to ``django.oldforms``. This allows you to upgrade your code *now* rather @@ -859,6 +860,16 @@ level and at the form instance level, and the latter gets precedence:: Url: Comment: +Creating custom fields +---------------------- + +If the built-in ``Field`` classes don't meet your needs, you can easily create +custom ``Field`` classes. To do this, just create a subclass of +``django.newforms.Field``. Its only requirements are that it implement a +``clean()`` method and that its ``__init__()`` method accept the core arguments +mentioned above (``required``, ``label``, ``initial``, ``widget``, +``help_text``). + More coming soon ================ diff --git a/docs/release_notes_0.96.txt b/docs/release_notes_0.96.txt new file mode 100644 index 0000000000..f62780c6b2 --- /dev/null +++ b/docs/release_notes_0.96.txt @@ -0,0 +1,264 @@ +================================= +Django version 0.96 release notes +================================= + +Welcome to Django 0.96! + +The primary goal for 0.96 is a cleanup and stabilization of the features +introduced in 0.95. There have been a few small `backwards-incompatible +changes`_ since 0.95, but the upgrade process should be fairly simple +and should not require major changes to existing applications. + +However, we're also releasing 0.96 now because we have a set of +backwards-incompatible changes scheduled for the near future. Once +completed, they will involve some code changes for application +developers, so we recommend that you stick with Django 0.96 until the +next official release; then you'll be able to upgrade in one step +instead of needing to make incremental changes to keep up with the +development version of Django. + +Backwards-incompatible changes +============================== + +The following changes may require you to update your code when you switch from +0.95 to 0.96: + +``MySQLdb`` version requirement +------------------------------- + +Due to a bug in older versions of the ``MySQLdb`` Python module (which +Django uses to connect to MySQL databases), Django's MySQL backend now +requires version 1.2.1p2 or higher of `MySQLdb`, and will raise +exceptions if you attempt to use an older version. + +If you're currently unable to upgrade your copy of ``MySQLdb`` to meet +this requirement, a separate, backwards-compatible backend, called +"mysql_old", has been added to Django. To use this backend, change +the ``DATABASE_ENGINE`` setting in your Django settings file from +this:: + + DATABASE_ENGINE = "mysql" + +to this:: + + DATABASE_ENGINE = "mysql_old" + +However, we strongly encourage MySQL users to upgrade to a more recent +version of `MySQLdb` as soon as possible, The "mysql_old" backend is +provided only to ease this transition, and is considered deprecated; +aside from any necessary security fixes, it will not be actively +maintained, and it will be removed in a future release of Django. + +Also, note that some features, like the new ``DATABASE_OPTIONS`` +setting (see the `databases documentation`_ for details), are only +available on the "mysql" backend, and will not be made available for +"mysql_old". + +.. _databases documentation: ../databases/ + +Database constraint names changed +--------------------------------- + +The format of the constraint names Django generates for foreign key +references have changed slightly. These names are generally only used +when it is not possible to put the reference directly on the affected +column, so they are not always visible. + +The effect of this change is that running ``manage.py reset`` and +similar commands against an existing database may generate SQL with +the new form of constraint name, while the database itself contains +constraints named in the old form; this will cause the database server +to raise an error message about modifying non-existent constraints. + +If you need to work around this, there are two methods available: + + 1. Redirect the output of ``manage.py`` to a file, and edit the + generated SQL to use the correct constraint names before + executing it. + + 2. Examine the output of ``manage.py sqlall`` to see the new-style + constraint names, and use that as a guide to rename existing + constraints in your database. + +Name changes in ``manage.py`` +----------------------------- + +A few of the options to ``manage.py`` have changed with the addition of fixture +support: + + * There are new ``dumpdata`` and ``loaddata`` commands which, as + you might expect, will dump and load data to/from the + database. These commands can operate against any of Django's + supported serialization formats. + + * The ``sqlinitialdata`` command has been renamed to ``sqlcustom`` to + emphasize that ``loaddata`` should be used for data (and ``sqlcustom`` for + other custom SQL -- views, stored procedures, etc.). + + * The vestigial ``install`` command has been removed. Use ``syncdb``. + +Backslash escaping changed +-------------------------- + +The Django database API now escapes backslashes given as query parameters. If +you have any database API code that matches backslashes, and it was working before +(despite the lack of escaping), you'll have to change your code to "unescape" the +slashes one level. + +For example, this used to work:: + + # Find text containing a single backslash + MyModel.objects.filter(text__contains='\\\\') + +The above is now incorrect, and should be rewritten as:: + + # Find text containing a single backslash + MyModel.objects.filter(text__contains='\\') + +Removed ENABLE_PSYCO setting +---------------------------- + +The ``ENABLE_PSYCO`` setting no longer exists. If your settings file includes +``ENABLE_PSYCO`` it will have no effect; to use Psyco_, we recommend +writing a middleware class to activate it. + +.. _psyco: http://psyco.sourceforge.net/ + +What's new in 0.96? +=================== + +This revision represents over a thousand source commits and over four hundred +bug fixes, so we can't possibly catalog all the changes. Here, we describe the +most notable changes in this release. + +New forms library +----------------- + +``django.newforms`` is Django's new form-handling library. It's a +replacement for ``django.forms``, the old form/manipulator/validation +framework. Both APIs are available in 0.96, but over the next two +releases we plan to switch completely to the new forms system, and +deprecate and remove the old system. + +There are three elements to this transition: + + * We've copied the current ``django.forms`` to + ``django.oldforms``. This allows you to upgrade your code *now* + rather than waiting for the backwards-incompatible change and + rushing to fix your code after the fact. Just change your + import statements like this:: + + from django import forms # 0.95-style + from django import oldforms as forms # 0.96-style + + * The next official release of Django will move the current + ``django.newforms`` to ``django.forms``. This will be a + backwards-incompatible change, and anyone still using the old + version of ``django.forms`` at that time will need to change + their import statements as described above. + + * The next release after that will completely remove + ``django.oldforms``. + +Although the ``newforms`` library will continue to evolve, it's ready for use +for most common cases. We recommend that anyone new to form handling skip the +old forms system and start with the new. + +For more information about ``django.newforms``, read the `newforms +documentation`_. + +.. _newforms documentation: ../newforms/ + +URLconf improvements +-------------------- + +You can now use any callable as the callback in URLconfs (previously, only +strings that referred to callables were allowed). This allows a much more +natural use of URLconfs. For example, this URLconf:: + + from django.conf.urls.defaults import * + + urlpatterns = patterns('', + ('^myview/$', 'mysite.myapp.views.myview') + ) + +can now be rewritten as:: + + from django.conf.urls.defaults import * + from mysite.myapp.views import myview + + urlpatterns = patterns('', + ('^myview/$', myview) + ) + +One useful application of this can be seen when using decorators; this +change allows you to apply decorators to views *in your +URLconf*. Thus, you can make a generic view require login very +easily:: + + from django.conf.urls.defaults import * + from django.contrib.auth.decorators import login_required + from django.views.generic.list_detail import object_list + from mysite.myapp.models import MyModel + + info = { + "queryset" : MyModel.objects.all(), + } + + urlpatterns = patterns('', + ('^myview/$', login_required(object_list), info) + ) + +Note that both syntaxes (strings and callables) are valid, and will continue to +be valid for the foreseeable future. + +The test framework +------------------ + +Django now includes a test framework so you can start transmuting fear into +boredom (with apologies to Kent Beck). You can write tests based on doctest_ +or unittest_ and test your views with a simple test client. + +There is also new support for "fixtures" -- initial data, stored in any of the +supported `serialization formats`_, that will be loaded into your database at the +start of your tests. This makes testing with real data much easier. + +See `the testing documentation`_ for the full details. + +.. _doctest: http://docs.python.org/lib/module-doctest.html +.. _unittest: http://docs.python.org/lib/module-unittest.html +.. _the testing documentation: ../testing/ +.. _serialization formats: ../serialization/ + +Improvements to the admin interface +----------------------------------- + +A small change, but a very nice one: dedicated views for adding and +updating users have been added to the admin interface, so you no +longer need to worry about working with hashed passwords in the admin. + +Thanks +====== + +Since 0.95, a number of people have stepped forward and taken a major +new role in Django's development. We'd like to thank these people for +all their hard work: + + * Russell Keith-Magee and Malcolm Tredinnick for their major code + contributions. This release wouldn't have been possible without them. + + * Our new release manager, James Bennett, for his work in getting out + 0.95.1, 0.96, and (hopefully) future release. + + * Our ticket managers Chris Beaven (aka SmileyChris), Simon Greenhill, + Michael Radziej, and Gary Wilson. They agreed to take on the monumental + task of wrangling our tickets into nicely cataloged submission. Figuring + out what to work on is now about a million times easier; thanks again, + guys. + + * Everyone who submitted a bug report, patch or ticket comment. We can't + possibly thank everyone by name -- over 200 developers submitted patches + that went into 0.96 -- but everyone who's contributed to Django is listed + in AUTHORS_. + +.. _AUTHORS: http://code.djangoproject.com/browser/django/trunk/AUTHORS diff --git a/docs/request_response.txt b/docs/request_response.txt index 6dfe78a686..2b79903d13 100644 --- a/docs/request_response.txt +++ b/docs/request_response.txt @@ -384,7 +384,6 @@ Methods Deletes the cookie with the given key. Fails silently if the key doesn't exist. - The ``path`` and ``domain`` arguments are new in the Django development version. Due to the way cookies work, ``path`` and ``domain`` should be the same values you used in ``set_cookie()`` -- otherwise the cookie may not be deleted. diff --git a/docs/sessions.txt b/docs/sessions.txt index 8c54c1634a..55fbc2c3da 100644 --- a/docs/sessions.txt +++ b/docs/sessions.txt @@ -158,7 +158,7 @@ is defined in ``django/contrib/sessions/models.py``. Because it's a normal model, you can access sessions using the normal Django database API:: >>> from django.contrib.sessions.models import Session - >>> s = Session.objects.get_object(pk='2b1189a188b44ad18c35e113ac6ceead') + >>> s = Session.objects.get(pk='2b1189a188b44ad18c35e113ac6ceead') >>> s.expire_date datetime.datetime(2005, 8, 20, 13, 35, 12) @@ -265,8 +265,6 @@ The name of the cookie to use for sessions. This can be whatever you want. SESSION_COOKIE_SECURE --------------------- -**New in Django development version** - Default: ``False`` Whether to use a secure cookie for the session cookie. If this is set to diff --git a/docs/settings.txt b/docs/settings.txt index 6f85e312c0..63b5cceef9 100644 --- a/docs/settings.txt +++ b/docs/settings.txt @@ -429,8 +429,6 @@ trailing space. FIXTURE_DIRS ------------- -**New in Django development version** - Default: ``()`` (Empty tuple) List of locations of the fixture data files, in search order. Note that @@ -716,8 +714,6 @@ See the `session docs`_. SESSION_COOKIE_SECURE --------------------- -**New in Django development version** - Default: ``False`` Whether to use a secure cookie for the session cookie. If this is set to @@ -812,8 +808,6 @@ misspelled) variables. See `How invalid variables are handled`_. TEST_RUNNER ----------- -**New in Django development version** - Default: ``'django.test.simple.run_tests'`` The name of the method to use for starting the test suite. See @@ -824,8 +818,6 @@ The name of the method to use for starting the test suite. See TEST_DATABASE_NAME ------------------ -**New in Django development version** - Default: ``None`` The name of database to use when running the test suite. If a value of diff --git a/docs/templates.txt b/docs/templates.txt index d53270ac16..8ab383c461 100644 --- a/docs/templates.txt +++ b/docs/templates.txt @@ -112,8 +112,6 @@ know how to write Python code. Comments ======== -**New in Django development version** - To comment-out part of a template, use the comment syntax: ``{# #}``. For example, this template would render as ``'hello'``:: @@ -253,8 +251,8 @@ Here are some tips for working with inheritance: if you want to add to the contents of a parent block instead of completely overriding it. - * **New in Django development version:** For extra readability, you can - optionally give a *name* to your ``{% endblock %}`` tag. For example:: + * For extra readability, you can optionally give a *name* to your + ``{% endblock %}`` tag. For example:: {% block content %} ... @@ -548,9 +546,9 @@ The 'ifchanged' block tag is used within a loop. It has two possible uses. {{ date|date:"j" }} {% endfor %} -2. **New in Django development version.** 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:: +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:: {% for date in days %} {% ifchanged date.date %} {{ date.date }} {% endifchanged %} @@ -739,6 +737,7 @@ The following snippet of template code would accomplish this dubious task::
  • {{ item }}
  • {% endfor %} + {% endfor %} @@ -828,13 +827,9 @@ The argument tells which template bit to output: ``closecomment`` ``#}`` ================== ======= -Note: ``opencomment`` and ``closecomment`` are new in the Django development version. - url ~~~ -**New in Django development version** - **Note that the syntax for this tag may change in the future, as we make it more robust.** Returns an absolute URL (i.e., a URL without the domain name) matching a given @@ -878,6 +873,23 @@ Above, if ``this_value`` is 175 and ``max_value`` is 200, the the image in the above example will be 88 pixels wide (because 175/200 = .875; .875 * 100 = 87.5 which is rounded up to 88). +with +~~~~ + +**New in Django development version** + +Caches a complex variable under a simpler name. This is useful when accessing +an "expensive" method (e.g., one that hits the database) multiple times. + +For example:: + + {% with business.employees.count as total %} + {{ total }} employee{{ total|pluralize }} + {% endwith %} + +The populated variable (in the example above, ``total``) is only available +between the ``{% with %}`` and ``{% endwith %}`` tags. + Built-in filter reference ------------------------- @@ -976,9 +988,7 @@ place -- but only if there's a decimal part to be displayed. For example: * ``36.15`` gets converted to ``36.2`` * ``36`` gets converted to ``36`` -**New in Django development version** - -If used with a numeric integer argument, ``floatformat`` rounds a number to that +If used with a numeric integer argument, ``floatformat`` rounds a number to that many decimal places. For example: * ``36.1234`` with floatformat:3 gets converted to ``36.123`` @@ -991,7 +1001,7 @@ For example: * ``36.1234`` with floatformat:-3 gets converted to ``36.123`` * ``36`` with floatformat:-4 gets converted to ``36`` -Using ``floatformat`` with no argument is equivalent to using ``floatformat`` with +Using ``floatformat`` with no argument is equivalent to using ``floatformat`` with an argument of ``-1``. get_digit diff --git a/docs/testing.txt b/docs/testing.txt index f7fd402502..31cea791d3 100644 --- a/docs/testing.txt +++ b/docs/testing.txt @@ -2,8 +2,6 @@ Testing Django applications =========================== -**New in Django development version**. - Automated testing is an extremely useful weapon in the bug-killing arsenal of the modern developer. When initially writing code, a test suite can be used to validate that code behaves as expected. When refactoring or @@ -227,6 +225,12 @@ can be invoked on the ``Client`` instance. The key-value pairs in the data dictionary will be encoded as a multipart message and used to create the POST data payload. + To submit multiple values for a given key (for example, to specify + the selections for a multiple selection list), provide the values as a + list or tuple for the required key. For example, a data dictionary of + ``{'choices': ('a','b','d')}`` would submit three selected rows for the + field named ``choices``. + Submitting files is a special case. To POST a file, you need only provide the file field name as a key, and a file handle to the file you wish to upload as a value. The Test Client will populate the two POST fields (i.e., diff --git a/docs/url_dispatch.txt b/docs/url_dispatch.txt index 3f51ce4b91..85c87de680 100644 --- a/docs/url_dispatch.txt +++ b/docs/url_dispatch.txt @@ -400,8 +400,6 @@ to pass metadata and options to views. Passing extra options to ``include()`` -------------------------------------- -**New in 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. @@ -442,8 +440,6 @@ every view in the the included URLconf accepts the extra options you're passing. Passing callable objects instead of strings =========================================== -**New in Django development version.** - Some developers find it more natural to pass the actual Python function object rather than a string containing the path to its module. This alternative is supported -- you can pass any callable object as the view.