Author: mtredinnick
Date: 2007-11-02 21:16:27 -0500 (Fri, 02 Nov 2007)
New Revision: 6638
Modified:
django/branches/queryset-refactor/django/contrib/sessions/middleware.py
django/branches/queryset-refactor/django/core/servers/basehttp.py
django/branches/queryset-refactor/django/middleware/http.py
django/branches/queryset-refactor/django/newforms/models.py
django/branches/queryset-refactor/django/utils/cache.py
django/branches/queryset-refactor/django/utils/http.py
django/branches/queryset-refactor/django/views/static.py
django/branches/queryset-refactor/docs/cache.txt
django/branches/queryset-refactor/docs/email.txt
django/branches/queryset-refactor/docs/form_preview.txt
django/branches/queryset-refactor/docs/modpython.txt
django/branches/queryset-refactor/docs/testing.txt
django/branches/queryset-refactor/tests/regressiontests/text/tests.py
Log:
queryset-refactor: Merged from trunk up to [6635].
Modified:
django/branches/queryset-refactor/django/contrib/sessions/middleware.py
===================================================================
--- django/branches/queryset-refactor/django/contrib/sessions/middleware.py
2007-11-03 02:15:27 UTC (rev 6637)
+++ django/branches/queryset-refactor/django/contrib/sessions/middleware.py
2007-11-03 02:16:27 UTC (rev 6638)
@@ -1,8 +1,8 @@
+import time
+
from django.conf import settings
from django.utils.cache import patch_vary_headers
-from email.Utils import formatdate
-import datetime
-import time
+from django.utils.http import cookie_date
TEST_COOKIE_NAME = 'testcookie'
TEST_COOKIE_VALUE = 'worked'
@@ -10,8 +10,9 @@
class SessionMiddleware(object):
def process_request(self, request):
- engine = __import__(settings.SESSION_ENGINE, {}, {}, [''])
- request.session =
engine.SessionStore(request.COOKIES.get(settings.SESSION_COOKIE_NAME, None))
+ engine = __import__(settings.SESSION_ENGINE, {}, {}, [''])
+ session_key = request.COOKIES.get(settings.SESSION_COOKIE_NAME, None)
+ request.session = engine.SessionStore(session_key)
def process_response(self, request, response):
# If request.session was modified, or if response.session was set, save
@@ -30,13 +31,8 @@
expires = None
else:
max_age = settings.SESSION_COOKIE_AGE
- rfcdate = formatdate(time.time() +
settings.SESSION_COOKIE_AGE)
-
- # Fixed length date must have '-' separation in the format
- # DD-MMM-YYYY for compliance with Netscape cookie standard
- expires =
datetime.datetime.strftime(datetime.datetime.utcnow() + \
-
datetime.timedelta(seconds=settings.SESSION_COOKIE_AGE), "%a, %d-%b-%Y %H:%M:%S
GMT")
-
+ expires_time = time.time() + settings.SESSION_COOKIE_AGE
+ expires = cookie_date(expires_time)
# Save the seesion data and refresh the client cookie.
request.session.save()
response.set_cookie(settings.SESSION_COOKIE_NAME,
Modified: django/branches/queryset-refactor/django/core/servers/basehttp.py
===================================================================
--- django/branches/queryset-refactor/django/core/servers/basehttp.py
2007-11-03 02:15:27 UTC (rev 6637)
+++ django/branches/queryset-refactor/django/core/servers/basehttp.py
2007-11-03 02:16:27 UTC (rev 6638)
@@ -9,14 +9,14 @@
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from types import ListType, StringType
-from email.Utils import formatdate
import mimetypes
import os
import re
import sys
-import time
import urllib
+from django.utils.http import http_date
+
__version__ = "0.1"
__all__ = ['WSGIServer','WSGIRequestHandler','demo_app']
@@ -376,7 +376,7 @@
self._write('HTTP/%s %s\r\n' % (self.http_version,self.status))
if 'Date' not in self.headers:
self._write(
- 'Date: %s\r\n' % (formatdate()[:26] + "GMT")
+ 'Date: %s\r\n' % http_date()
)
if self.server_software and 'Server' not in self.headers:
self._write('Server: %s\r\n' % self.server_software)
Modified: django/branches/queryset-refactor/django/middleware/http.py
===================================================================
--- django/branches/queryset-refactor/django/middleware/http.py 2007-11-03
02:15:27 UTC (rev 6637)
+++ django/branches/queryset-refactor/django/middleware/http.py 2007-11-03
02:16:27 UTC (rev 6638)
@@ -1,4 +1,4 @@
-from email.Utils import formatdate
+from django.utils.http import http_date
class ConditionalGetMiddleware(object):
"""
@@ -11,7 +11,7 @@
Also sets the Date and Content-Length response-headers.
"""
def process_response(self, request, response):
- response['Date'] = formatdate()[:26] + "GMT"
+ response['Date'] = http_date()
if not response.has_header('Content-Length'):
response['Content-Length'] = str(len(response.content))
@@ -23,7 +23,6 @@
response['Content-Length'] = '0'
if response.has_header('Last-Modified'):
- last_mod = response['Last-Modified']
if_modified_since = request.META.get('HTTP_IF_MODIFIED_SINCE',
None)
if if_modified_since == response['Last-Modified']:
response.status_code = 304
Modified: django/branches/queryset-refactor/django/newforms/models.py
===================================================================
--- django/branches/queryset-refactor/django/newforms/models.py 2007-11-03
02:15:27 UTC (rev 6637)
+++ django/branches/queryset-refactor/django/newforms/models.py 2007-11-03
02:16:27 UTC (rev 6638)
@@ -6,7 +6,6 @@
from django.utils.translation import ugettext
from django.utils.encoding import smart_unicode
-
from util import ValidationError
from forms import BaseForm, SortedDictFromList
from fields import Field, ChoiceField
@@ -17,7 +16,8 @@
'ModelChoiceField', 'ModelMultipleChoiceField'
)
-def save_instance(form, instance, fields=None, fail_message='saved',
commit=True):
+def save_instance(form, instance, fields=None, fail_message='saved',
+ commit=True):
"""
Saves bound Form ``form``'s cleaned_data into model instance ``instance``.
@@ -27,15 +27,17 @@
from django.db import models
opts = instance.__class__._meta
if form.errors:
- raise ValueError("The %s could not be %s because the data didn't
validate." % (opts.object_name, fail_message))
+ raise ValueError("The %s could not be %s because the data didn't"
+ " validate." % (opts.object_name, fail_message))
cleaned_data = form.cleaned_data
for f in opts.fields:
- if not f.editable or isinstance(f, models.AutoField) or not f.name in
cleaned_data:
+ if not f.editable or isinstance(f, models.AutoField) \
+ or not f.name in cleaned_data:
continue
if fields and f.name not in fields:
continue
- f.save_form_data(instance, cleaned_data[f.name])
- # Wrap up the saving of m2m data as a function
+ f.save_form_data(instance, cleaned_data[f.name])
+ # Wrap up the saving of m2m data as a function.
def save_m2m():
opts = instance.__class__._meta
cleaned_data = form.cleaned_data
@@ -45,28 +47,29 @@
if f.name in cleaned_data:
f.save_form_data(instance, cleaned_data[f.name])
if commit:
- # If we are committing, save the instance and the m2m data immediately
+ # If we are committing, save the instance and the m2m data immediately.
instance.save()
save_m2m()
else:
- # We're not committing. Add a method to the form to allow deferred
- # saving of m2m data
+ # We're not committing. Add a method to the form to allow deferred
+ # saving of m2m data.
form.save_m2m = save_m2m
return instance
def make_model_save(model, fields, fail_message):
- "Returns the save() method for a Form."
+ """Returns the save() method for a Form."""
def save(self, commit=True):
return save_instance(self, model(), fields, fail_message, commit)
return save
-
+
def make_instance_save(instance, fields, fail_message):
- "Returns the save() method for a Form."
+ """Returns the save() method for a Form."""
def save(self, commit=True):
return save_instance(self, instance, fields, fail_message, commit)
return save
-def form_for_model(model, form=BaseForm, fields=None,
formfield_callback=lambda f: f.formfield()):
+def form_for_model(model, form=BaseForm, fields=None,
+ formfield_callback=lambda f: f.formfield()):
"""
Returns a Form class for the given Django model class.
@@ -87,10 +90,12 @@
if formfield:
field_list.append((f.name, formfield))
base_fields = SortedDictFromList(field_list)
- return type(opts.object_name + 'Form', (form,),
- {'base_fields': base_fields, '_model': model, 'save':
make_model_save(model, fields, 'created')})
+ return type(opts.object_name + 'Form', (form,),
+ {'base_fields': base_fields, '_model': model,
+ 'save': make_model_save(model, fields, 'created')})
-def form_for_instance(instance, form=BaseForm, fields=None,
formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
+def form_for_instance(instance, form=BaseForm, fields=None,
+ formfield_callback=lambda f, **kwargs:
f.formfield(**kwargs)):
"""
Returns a Form class for the given Django model instance.
@@ -115,16 +120,22 @@
field_list.append((f.name, formfield))
base_fields = SortedDictFromList(field_list)
return type(opts.object_name + 'InstanceForm', (form,),
- {'base_fields': base_fields, '_model': model, 'save':
make_instance_save(instance, fields, 'changed')})
+ {'base_fields': base_fields, '_model': model,
+ 'save': make_instance_save(instance, fields, 'changed')})
def form_for_fields(field_list):
- "Returns a Form class for the given list of Django database field
instances."
- fields = SortedDictFromList([(f.name, f.formfield()) for f in field_list
if f.editable])
+ """
+ Returns a Form class for the given list of Django database field instances.
+ """
+ fields = SortedDictFromList([(f.name, f.formfield())
+ for f in field_list if f.editable])
return type('FormForFields', (BaseForm,), {'base_fields': fields})
class QuerySetIterator(object):
def __init__(self, queryset, empty_label, cache_choices):
- self.queryset, self.empty_label, self.cache_choices = queryset,
empty_label, cache_choices
+ self.queryset = queryset
+ self.empty_label = empty_label
+ self.cache_choices = cache_choices
def __iter__(self):
if self.empty_label is not None:
@@ -136,11 +147,13 @@
self.queryset._result_cache = None
class ModelChoiceField(ChoiceField):
- "A ChoiceField whose choices are a model QuerySet."
+ """A ChoiceField whose choices are a model QuerySet."""
# This class is a subclass of ChoiceField for purity, but it doesn't
# actually use any of ChoiceField's implementation.
+
def __init__(self, queryset, empty_label=u"---------", cache_choices=False,
- required=True, widget=Select, label=None, initial=None,
help_text=None):
+ required=True, widget=Select, label=None, initial=None,
+ help_text=None):
self.queryset = queryset
self.empty_label = empty_label
self.cache_choices = cache_choices
@@ -160,7 +173,8 @@
# *each* time _get_choices() is called (and, thus, each time
# self.choices is accessed) so that we can ensure the QuerySet has not
# been consumed.
- return QuerySetIterator(self.queryset, self.empty_label,
self.cache_choices)
+ return QuerySetIterator(self.queryset, self.empty_label,
+ self.cache_choices)
def _set_choices(self, value):
# This method is copied from ChoiceField._set_choices(). It's necessary
@@ -177,16 +191,20 @@
try:
value = self.queryset.model._default_manager.get(pk=value)
except self.queryset.model.DoesNotExist:
- raise ValidationError(ugettext(u'Select a valid choice. That
choice is not one of the available choices.'))
+ raise ValidationError(ugettext(u'Select a valid choice. That'
+ u' choice is not one of the'
+ u' available choices.'))
return value
class ModelMultipleChoiceField(ModelChoiceField):
- "A MultipleChoiceField whose choices are a model QuerySet."
+ """A MultipleChoiceField whose choices are a model QuerySet."""
hidden_widget = MultipleHiddenInput
+
def __init__(self, queryset, cache_choices=False, required=True,
- widget=SelectMultiple, label=None, initial=None, help_text=None):
- super(ModelMultipleChoiceField, self).__init__(queryset, None,
cache_choices,
- required, widget, label, initial, help_text)
+ widget=SelectMultiple, label=None, initial=None,
+ help_text=None):
+ super(ModelMultipleChoiceField, self).__init__(queryset, None,
+ cache_choices, required, widget, label, initial, help_text)
def clean(self, value):
if self.required and not value:
@@ -200,7 +218,9 @@
try:
obj = self.queryset.model._default_manager.get(pk=val)
except self.queryset.model.DoesNotExist:
- raise ValidationError(ugettext(u'Select a valid choice. %s is
not one of the available choices.') % val)
+ raise ValidationError(ugettext(u'Select a valid choice. %s is'
+ u' not one of the available'
+ u' choices.') % val)
else:
final_values.append(obj)
return final_values
Modified: django/branches/queryset-refactor/django/utils/cache.py
===================================================================
--- django/branches/queryset-refactor/django/utils/cache.py 2007-11-03
02:15:27 UTC (rev 6637)
+++ django/branches/queryset-refactor/django/utils/cache.py 2007-11-03
02:16:27 UTC (rev 6638)
@@ -13,17 +13,18 @@
different header content for headers named in "Vary" need to get different
cache keys to prevent delivery of wrong content.
-A example: i18n middleware would need to distinguish caches by the
+An example: i18n middleware would need to distinguish caches by the
"Accept-language" header.
"""
import md5
import re
import time
-from email.Utils import formatdate
+
from django.conf import settings
from django.core.cache import cache
from django.utils.encoding import smart_str, iri_to_uri
+from django.utils.http import http_date
cc_delim_re = re.compile(r'\s*,\s*')
@@ -40,7 +41,7 @@
str() to it.
"""
def dictitem(s):
- t = s.split('=',1)
+ t = s.split('=', 1)
if len(t) > 1:
return (t[0].lower(), t[1])
else:
@@ -64,7 +65,7 @@
if 'max-age' in cc and 'max_age' in kwargs:
kwargs['max_age'] = min(cc['max-age'], kwargs['max_age'])
- for (k,v) in kwargs.items():
+ for (k, v) in kwargs.items():
cc[k.replace('_', '-')] = v
cc = ', '.join([dictvalue(el) for el in cc.items()])
response['Cache-Control'] = cc
@@ -88,15 +89,14 @@
if not response.has_header('ETag'):
response['ETag'] = md5.new(response.content).hexdigest()
if not response.has_header('Last-Modified'):
- response['Last-Modified'] = formatdate()[:26] + "GMT"
+ response['Last-Modified'] = http_date()
if not response.has_header('Expires'):
- response['Expires'] = formatdate(time.time() + cache_timeout)[:26] +
"GMT"
+ response['Expires'] = http_date(time.time() + cache_timeout)
patch_cache_control(response, max_age=cache_timeout)
def add_never_cache_headers(response):
"""
- Add headers to a response to indicate that
- a page should never be cached.
+ Adds headers to a response to indicate that a page should never be cached.
"""
patch_response_headers(response, cache_timeout=-1)
@@ -119,13 +119,14 @@
response['Vary'] = ', '.join(vary)
def _generate_cache_key(request, headerlist, key_prefix):
- "Returns a cache key from the headers given in the header list."
+ """Returns a cache key from the headers given in the header list."""
ctx = md5.new()
for header in headerlist:
value = request.META.get(header, None)
if value is not None:
ctx.update(value)
- return 'views.decorators.cache.cache_page.%s.%s.%s' % (key_prefix,
iri_to_uri(request.path), ctx.hexdigest())
+ return 'views.decorators.cache.cache_page.%s.%s.%s' % (
+ key_prefix, iri_to_uri(request.path), ctx.hexdigest())
def get_cache_key(request, key_prefix=None):
"""
@@ -139,7 +140,8 @@
"""
if key_prefix is None:
key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
- cache_key = 'views.decorators.cache.cache_header.%s.%s' % (key_prefix,
iri_to_uri(request.path))
+ cache_key = 'views.decorators.cache.cache_header.%s.%s' % (
+ key_prefix, iri_to_uri(request.path))
headerlist = cache.get(cache_key, None)
if headerlist is not None:
return _generate_cache_key(request, headerlist, key_prefix)
@@ -163,9 +165,11 @@
key_prefix = settings.CACHE_MIDDLEWARE_KEY_PREFIX
if cache_timeout is None:
cache_timeout = settings.CACHE_MIDDLEWARE_SECONDS
- cache_key = 'views.decorators.cache.cache_header.%s.%s' % (key_prefix,
iri_to_uri(request.path))
+ cache_key = 'views.decorators.cache.cache_header.%s.%s' % (
+ key_prefix, iri_to_uri(request.path))
if response.has_header('Vary'):
- headerlist = ['HTTP_'+header.upper().replace('-', '_') for header in
vary_delim_re.split(response['Vary'])]
+ headerlist = ['HTTP_'+header.upper().replace('-', '_')
+ for header in vary_delim_re.split(response['Vary'])]
cache.set(cache_key, headerlist, cache_timeout)
return _generate_cache_key(request, headerlist, key_prefix)
else:
Modified: django/branches/queryset-refactor/django/utils/http.py
===================================================================
--- django/branches/queryset-refactor/django/utils/http.py 2007-11-03
02:15:27 UTC (rev 6637)
+++ django/branches/queryset-refactor/django/utils/http.py 2007-11-03
02:16:27 UTC (rev 6638)
@@ -1,4 +1,6 @@
import urllib
+from email.Utils import formatdate
+
from django.utils.encoding import smart_str, force_unicode
from django.utils.functional import allow_lazy
@@ -37,3 +39,29 @@
for k, v in query],
doseq)
+def cookie_date(epoch_seconds=None):
+ """
+ Formats the time to ensure compatibility with Netscape's cookie standard.
+
+ Accepts a floating point number expressed in seconds since the epoch, in
+ UTC - such as that outputted by time.time(). If set to None, defaults to
+ the current time.
+
+ Outputs a string in the format 'Wdy, DD-Mon-YYYY HH:MM:SS GMT'.
+ """
+ rfcdate = formatdate(epoch_seconds)
+ return '%s-%s-%s GMT' % (rfcdate[:7], rfcdate[8:11], rfcdate[12:25])
+
+def http_date(epoch_seconds=None):
+ """
+ Formats the time to match the RFC1123 date format as specified by HTTP
+ RFC2616 section 3.3.1.
+
+ Accepts a floating point number expressed in seconds since the epoch, in
+ UTC - such as that outputted by time.time(). If set to None, defaults to
+ the current time.
+
+ Outputs a string in the format 'Wdy, DD Mon YYYY HH:MM:SS GMT'.
+ """
+ rfcdate = formatdate(epoch_seconds)
+ return '%s GMT' % rfcdate[:25]
Modified: django/branches/queryset-refactor/django/views/static.py
===================================================================
--- django/branches/queryset-refactor/django/views/static.py 2007-11-03
02:15:27 UTC (rev 6637)
+++ django/branches/queryset-refactor/django/views/static.py 2007-11-03
02:16:27 UTC (rev 6638)
@@ -7,13 +7,14 @@
import os
import posixpath
import re
-import rfc822
import stat
import urllib
+from email.Utils import parsedate_tz, mktime_tz
from django.template import loader
from django.http import Http404, HttpResponse, HttpResponseRedirect,
HttpResponseNotModified
from django.template import Template, Context, TemplateDoesNotExist
+from django.utils.http import http_date
def serve(request, path, document_root=None, show_indexes=False):
"""
@@ -60,7 +61,7 @@
mimetype = mimetypes.guess_type(fullpath)[0]
contents = open(fullpath, 'rb').read()
response = HttpResponse(contents, mimetype=mimetype)
- response["Last-Modified"] = rfc822.formatdate(statobj[stat.ST_MTIME])
+ response["Last-Modified"] = http_date(statobj[stat.ST_MTIME])
return response
DEFAULT_DIRECTORY_INDEX_TEMPLATE = """
@@ -119,8 +120,7 @@
raise ValueError
matches = re.match(r"^([^;]+)(; length=([0-9]+))?$", header,
re.IGNORECASE)
- header_mtime = rfc822.mktime_tz(rfc822.parsedate_tz(
- matches.group(1)))
+ header_mtime = mktime_tz(parsedate_tz(matches.group(1)))
header_len = matches.group(3)
if header_len and int(header_len) != size:
raise ValueError
Modified: django/branches/queryset-refactor/docs/cache.txt
===================================================================
--- django/branches/queryset-refactor/docs/cache.txt 2007-11-03 02:15:27 UTC
(rev 6637)
+++ django/branches/queryset-refactor/docs/cache.txt 2007-11-03 02:16:27 UTC
(rev 6638)
@@ -291,13 +291,15 @@
Template fragment caching
=========================
+**New in development version**.
+
If you're after even more control, you can also cache template fragments using
-the ``cache`` template tag. To give your template access to this tag, put ``{%
-load cache %}`` near the top of your template.
+the ``cache`` template tag. To give your template access to this tag, put
+``{% load cache %}`` near the top of your template.
The ``{% cache %}`` template tag caches the contents of the block for a given
-amount of time. It takes at least two arguments: the cache timeout, in
-seconds, and the name to give the cache fragment. For example::
+amount of time. It takes at least two arguments: the cache timeout, in seconds,
+and the name to give the cache fragment. For example::
{% load cache %}
{% cache 500 sidebar %}
Modified: django/branches/queryset-refactor/docs/email.txt
===================================================================
--- django/branches/queryset-refactor/docs/email.txt 2007-11-03 02:15:27 UTC
(rev 6637)
+++ django/branches/queryset-refactor/docs/email.txt 2007-11-03 02:16:27 UTC
(rev 6638)
@@ -275,7 +275,7 @@
There are two ways to call ``attach()``:
* You can pass it a single argument that is an
- ``email.MIMBase.MIMEBase`` instance. This will be inserted directly
+ ``email.MIMEBase.MIMEBase`` instance. This will be inserted directly
into the resulting message.
* Alternatively, you can pass ``attach()`` three arguments:
Modified: django/branches/queryset-refactor/docs/form_preview.txt
===================================================================
--- django/branches/queryset-refactor/docs/form_preview.txt 2007-11-03
02:15:27 UTC (rev 6637)
+++ django/branches/queryset-refactor/docs/form_preview.txt 2007-11-03
02:16:27 UTC (rev 6638)
@@ -45,7 +45,7 @@
2. Create a ``FormPreview`` subclass that overrides the ``done()`` method::
- from django.contrib.formtools import FormPreview
+ from django.contrib.formtools.preview import FormPreview
from myapp.models import SomeModel
class SomeModelFormPreview(FormPreview):
Modified: django/branches/queryset-refactor/docs/modpython.txt
===================================================================
--- django/branches/queryset-refactor/docs/modpython.txt 2007-11-03
02:15:27 UTC (rev 6637)
+++ django/branches/queryset-refactor/docs/modpython.txt 2007-11-03
02:16:27 UTC (rev 6638)
@@ -150,7 +150,7 @@
<Location "/otherthing">
SetEnv DJANGO_SETTINGS_MODULE mysite.other_settings
- PythonInterpreter mysite_other
+ PythonInterpreter othersite
</Location>
</VirtualHost>
Modified: django/branches/queryset-refactor/docs/testing.txt
===================================================================
--- django/branches/queryset-refactor/docs/testing.txt 2007-11-03 02:15:27 UTC
(rev 6637)
+++ django/branches/queryset-refactor/docs/testing.txt 2007-11-03 02:16:27 UTC
(rev 6638)
@@ -721,7 +721,6 @@
...you can just refer to ``self.client``, like so::
from django.test import TestCase
- from django.test.client import Client
class SimpleTest(TestCase):
def test_details(self):
Modified: django/branches/queryset-refactor/tests/regressiontests/text/tests.py
===================================================================
--- django/branches/queryset-refactor/tests/regressiontests/text/tests.py
2007-11-03 02:15:27 UTC (rev 6637)
+++ django/branches/queryset-refactor/tests/regressiontests/text/tests.py
2007-11-03 02:16:27 UTC (rev 6638)
@@ -27,6 +27,14 @@
>>> urlquote_plus(u'Paris & Orl\xe9ans', safe="&")
u'Paris+&+Orl%C3%A9ans'
+### cookie_date, http_date ###############################################
+>>> from django.utils.http import cookie_date, http_date
+>>> t = 1167616461.0
+>>> cookie_date(t)
+'Mon, 01-Jan-2007 01:54:21 GMT'
+>>> http_date(t)
+'Mon, 01 Jan 2007 01:54:21 GMT'
+
### iri_to_uri ###########################################################
>>> from django.utils.encoding import iri_to_uri
>>> iri_to_uri(u'red%09ros\xe9#red')
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/django-updates?hl=en
-~----------~----~----~----~------~----~------~--~---