Author: mtredinnick
Date: 2007-11-02 21:15:27 -0500 (Fri, 02 Nov 2007)
New Revision: 6637

Added:
   django/branches/queryset-refactor/tests/regressiontests/i18n/misc.py
Modified:
   django/branches/queryset-refactor/django/conf/locale/es/LC_MESSAGES/django.mo
   django/branches/queryset-refactor/django/conf/locale/es/LC_MESSAGES/django.po
   django/branches/queryset-refactor/django/contrib/auth/forms.py
   django/branches/queryset-refactor/django/core/management/__init__.py
   django/branches/queryset-refactor/django/core/management/commands/startapp.py
   django/branches/queryset-refactor/django/db/backends/__init__.py
   
django/branches/queryset-refactor/django/db/backends/postgresql_psycopg2/base.py
   django/branches/queryset-refactor/django/db/backends/util.py
   django/branches/queryset-refactor/django/newforms/fields.py
   django/branches/queryset-refactor/django/utils/translation/trans_real.py
   django/branches/queryset-refactor/docs/django-admin.txt
   django/branches/queryset-refactor/tests/regressiontests/forms/regressions.py
   django/branches/queryset-refactor/tests/regressiontests/i18n/tests.py
   django/branches/queryset-refactor/tests/regressiontests/templates/urls.py
Log:
queryset-refactor: Merged from trunk up to [6623].


Modified: 
django/branches/queryset-refactor/django/conf/locale/es/LC_MESSAGES/django.mo
===================================================================
(Binary files differ)

Modified: 
django/branches/queryset-refactor/django/conf/locale/es/LC_MESSAGES/django.po
===================================================================
--- 
django/branches/queryset-refactor/django/conf/locale/es/LC_MESSAGES/django.po   
    2007-11-03 02:04:59 UTC (rev 6636)
+++ 
django/branches/queryset-refactor/django/conf/locale/es/LC_MESSAGES/django.po   
    2007-11-03 02:15:27 UTC (rev 6637)
@@ -1016,7 +1016,7 @@
 
 #: contrib/admin/views/main.py:780
 msgid "Database error"
-msgstr "Erorr en la base de datos"
+msgstr "Error en la base de datos"
 
 #: contrib/auth/forms.py:17
 #: contrib/auth/forms.py:138

Modified: django/branches/queryset-refactor/django/contrib/auth/forms.py
===================================================================
--- django/branches/queryset-refactor/django/contrib/auth/forms.py      
2007-11-03 02:04:59 UTC (rev 6636)
+++ django/branches/queryset-refactor/django/contrib/auth/forms.py      
2007-11-03 02:15:27 UTC (rev 6637)
@@ -104,7 +104,7 @@
                 'site_name': site_name,
                 'user': user,
                 }
-            send_mail('Password reset on %s' % site_name, 
t.render(Context(c)), None, [user.email])
+            send_mail(_('Password reset on %s') % site_name, 
t.render(Context(c)), None, [user.email])
 
 class PasswordChangeForm(oldforms.Manipulator):
     "A form that lets a user change his password."

Modified: django/branches/queryset-refactor/django/core/management/__init__.py
===================================================================
--- django/branches/queryset-refactor/django/core/management/__init__.py        
2007-11-03 02:04:59 UTC (rev 6636)
+++ django/branches/queryset-refactor/django/core/management/__init__.py        
2007-11-03 02:15:27 UTC (rev 6637)
@@ -242,6 +242,8 @@
     """
     Configures the runtime environment. This can also be used by external
     scripts wanting to set up a similar environment to manage.py.
+    Returns the project directory (assuming the passed settings module is
+    directly in the project directory).
     """
     # Add this project to sys.path so that it's importable in the conventional
     # way. For example, if this file (manage.py) lives in a directory

Modified: 
django/branches/queryset-refactor/django/core/management/commands/startapp.py
===================================================================
--- 
django/branches/queryset-refactor/django/core/management/commands/startapp.py   
    2007-11-03 02:04:59 UTC (rev 6636)
+++ 
django/branches/queryset-refactor/django/core/management/commands/startapp.py   
    2007-11-03 02:15:27 UTC (rev 6637)
@@ -1,8 +1,10 @@
-from django.core.management.base import copy_helper, CommandError, LabelCommand
 import os
 
+from django.core.management.base import copy_helper, CommandError, LabelCommand
+
 class Command(LabelCommand):
-    help = "Creates a Django app directory structure for the given app name in 
the current directory."
+    help = ("Creates a Django app directory structure for the given app name"
+            " in the current directory.")
     args = "[appname]"
     label = 'application name'
 
@@ -14,17 +16,18 @@
     def handle_label(self, app_name, directory=None, **options):
         if directory is None:
             directory = os.getcwd()
-        # Determine the project_name a bit naively -- by looking at the name of
-        # the parent directory.
-        project_dir = os.path.normpath(os.path.join(directory, os.pardir))
-        parent_dir = os.path.basename(project_dir)
+        # Determine the project_name by using the basename of directory,
+        # which should be the full path of the project directory (or the
+        # current directory if no directory was passed).
         project_name = os.path.basename(directory)
         if app_name == project_name:
-            raise CommandError("You cannot create an app with the same name 
(%r) as your project." % app_name)
-        copy_helper(self.style, 'app', app_name, directory, parent_dir)
+            raise CommandError("You cannot create an app with the same name"
+                               " (%r) as your project." % app_name)
+        copy_helper(self.style, 'app', app_name, directory, project_name)
 
 class ProjectCommand(Command):
-    help = "Creates a Django app directory structure for the given app name in 
this project's directory."
+    help = ("Creates a Django app directory structure for the given app name"
+            " in this project's directory.")
 
     def __init__(self, project_directory):
         super(ProjectCommand, self).__init__()

Modified: django/branches/queryset-refactor/django/db/backends/__init__.py
===================================================================
--- django/branches/queryset-refactor/django/db/backends/__init__.py    
2007-11-03 02:04:59 UTC (rev 6636)
+++ django/branches/queryset-refactor/django/db/backends/__init__.py    
2007-11-03 02:15:27 UTC (rev 6637)
@@ -127,6 +127,27 @@
         """
         raise NotImplementedError('Full-text search is not implemented for 
this database backend')
 
+    def last_executed_query(self, cursor, sql, params):
+        """
+        Returns a string of the query last executed by the given cursor, with
+        placeholders replaced with actual values.
+
+        `sql` is the raw query containing placeholders, and `params` is the
+        sequence of parameters. These are used by default, but this method
+        exists for database backends to provide a better implementation
+        according to their own quoting schemes.
+        """
+        from django.utils.encoding import smart_unicode, force_unicode
+
+        # Convert params to contain Unicode values.
+        to_unicode = lambda s: force_unicode(s, strings_only=True)
+        if isinstance(params, (list, tuple)):
+            u_params = tuple([to_unicode(val) for val in params])
+        else:
+            u_params = dict([(to_unicode(k), to_unicode(v)) for k, v in 
params.items()])
+
+        return smart_unicode(sql) % u_params
+
     def last_insert_id(self, cursor, table_name, pk_name):
         """
         Given a cursor object that has just performed an INSERT statement into

Modified: 
django/branches/queryset-refactor/django/db/backends/postgresql_psycopg2/base.py
===================================================================
--- 
django/branches/queryset-refactor/django/db/backends/postgresql_psycopg2/base.py
    2007-11-03 02:04:59 UTC (rev 6636)
+++ 
django/branches/queryset-refactor/django/db/backends/postgresql_psycopg2/base.py
    2007-11-03 02:15:27 UTC (rev 6637)
@@ -5,7 +5,7 @@
 """
 
 from django.db.backends import BaseDatabaseWrapper, BaseDatabaseFeatures
-from django.db.backends.postgresql.operations import DatabaseOperations
+from django.db.backends.postgresql.operations import DatabaseOperations as 
PostgresqlDatabaseOperations
 try:
     import psycopg2 as Database
     import psycopg2.extensions
@@ -21,6 +21,13 @@
 class DatabaseFeatures(BaseDatabaseFeatures):
     needs_datetime_string_cast = False
 
+class DatabaseOperations(PostgresqlDatabaseOperations):
+    def last_executed_query(self, cursor, sql, params):
+        # With psycopg2, cursor objects have a "query" attribute that is the
+        # exact query sent to the database. See docs here:
+        # 
http://www.initd.org/tracker/psycopg/wiki/psycopg2_documentation#postgresql-status-message-and-executed-query
+        return cursor.query
+
 class DatabaseWrapper(BaseDatabaseWrapper):
     features = DatabaseFeatures()
     ops = DatabaseOperations()

Modified: django/branches/queryset-refactor/django/db/backends/util.py
===================================================================
--- django/branches/queryset-refactor/django/db/backends/util.py        
2007-11-03 02:04:59 UTC (rev 6636)
+++ django/branches/queryset-refactor/django/db/backends/util.py        
2007-11-03 02:15:27 UTC (rev 6637)
@@ -1,7 +1,6 @@
 import datetime
 import md5
 from time import time
-from django.utils.encoding import smart_unicode, force_unicode
 
 try:
     import decimal
@@ -11,7 +10,7 @@
 class CursorDebugWrapper(object):
     def __init__(self, cursor, db):
         self.cursor = cursor
-        self.db = db
+        self.db = db # Instance of a BaseDatabaseWrapper subclass
 
     def execute(self, sql, params=()):
         start = time()
@@ -19,8 +18,9 @@
             return self.cursor.execute(sql, params)
         finally:
             stop = time()
+            sql = self.db.ops.last_executed_query(self.cursor, sql, params)
             self.db.queries.append({
-                'sql': smart_unicode(sql) % convert_args(params),
+                'sql': sql,
                 'time': "%.3f" % (stop - start),
             })
 
@@ -31,7 +31,7 @@
         finally:
             stop = time()
             self.db.queries.append({
-                'sql': 'MANY: ' + sql + ' ' + smart_unicode(tuple(param_list)),
+                'sql': '%s times: %s' % (len(param_list), sql),
                 'time': "%.3f" % (stop - start),
             })
 
@@ -41,16 +41,6 @@
         else:
             return getattr(self.cursor, attr)
 
-def convert_args(args):
-    """
-    Convert sequence or dictionary to contain unicode values.
-    """
-    to_unicode = lambda s: force_unicode(s, strings_only=True)
-    if isinstance(args, (list, tuple)):
-        return tuple([to_unicode(val) for val in args])
-    else:
-        return dict([(to_unicode(k), to_unicode(v)) for k, v in args.items()])
-
 ###############################################
 # Converters from database (string) to Python #
 ###############################################

Modified: django/branches/queryset-refactor/django/newforms/fields.py
===================================================================
--- django/branches/queryset-refactor/django/newforms/fields.py 2007-11-03 
02:04:59 UTC (rev 6636)
+++ django/branches/queryset-refactor/django/newforms/fields.py 2007-11-03 
02:15:27 UTC (rev 6637)
@@ -1,11 +1,20 @@
 """
-Field classes
+Field classes.
 """
 
 import copy
 import datetime
 import re
 import time
+# Python 2.3 fallbacks
+try:
+    from decimal import Decimal, DecimalException
+except ImportError:
+    from django.utils._decimal import Decimal, DecimalException
+try:
+    set
+except NameError:
+    from sets import Set as set
 
 from django.utils.translation import ugettext
 from django.utils.encoding import StrAndUnicode, smart_unicode
@@ -13,18 +22,14 @@
 from util import ErrorList, ValidationError
 from widgets import TextInput, PasswordInput, HiddenInput, 
MultipleHiddenInput, FileInput, CheckboxInput, Select, NullBooleanSelect, 
SelectMultiple, DateTimeInput
 
-try:
-    from decimal import Decimal, DecimalException
-except ImportError:
-    from django.utils._decimal import Decimal, DecimalException
 
 __all__ = (
     'Field', 'CharField', 'IntegerField',
     'DEFAULT_DATE_INPUT_FORMATS', 'DateField',
     'DEFAULT_TIME_INPUT_FORMATS', 'TimeField',
     'DEFAULT_DATETIME_INPUT_FORMATS', 'DateTimeField',
-    'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField', 
'BooleanField',
-    'ChoiceField', 'NullBooleanField', 'MultipleChoiceField',
+    'RegexField', 'EmailField', 'FileField', 'ImageField', 'URLField',
+    'BooleanField', 'NullBooleanField', 'ChoiceField', 'MultipleChoiceField',
     'ComboField', 'MultiValueField', 'FloatField', 'DecimalField',
     'SplitDateTimeField', 'IPAddressField',
 )
@@ -32,16 +37,7 @@
 # These values, if given to to_python(), will trigger the self.required check.
 EMPTY_VALUES = (None, '')
 
-try:
-    set
-except NameError:
-    from sets import Set as set   # Python 2.3 fallback
 
-try:
-    from decimal import Decimal
-except ImportError:
-    from django.utils._decimal import Decimal   # Python 2.3 fallback
-
 class Field(object):
     widget = TextInput # Default widget to use when rendering this type of 
Field.
     hidden_widget = HiddenInput # Default widget to use when rendering this as 
"hidden".

Modified: 
django/branches/queryset-refactor/django/utils/translation/trans_real.py
===================================================================
--- django/branches/queryset-refactor/django/utils/translation/trans_real.py    
2007-11-03 02:04:59 UTC (rev 6636)
+++ django/branches/queryset-refactor/django/utils/translation/trans_real.py    
2007-11-03 02:15:27 UTC (rev 6637)
@@ -1,8 +1,12 @@
 "Translation helper functions"
 
-import os, re, sys
+import locale
+import os
+import re
+import sys
 import gettext as gettext_module
 from cStringIO import StringIO
+
 from django.utils.encoding import force_unicode
 
 try:
@@ -25,15 +29,25 @@
 # The default translation is based on the settings file.
 _default = None
 
-# This is a cache for accept-header to translation object mappings to prevent
-# the accept parser to run multiple times for one user.
+# This is a cache for normalised accept-header languages to prevent multiple
+# file lookups when checking the same locale on repeated requests.
 _accepted = {}
 
-def to_locale(language):
+# Format of Accept-Language header values. From RFC 2616, section 14.4 and 3.9.
+accept_language_re = re.compile(r'''
+        ([A-Za-z]{1,8}(?:-[A-Za-z]{1,8})*|\*)   # "en", "en-au", "x-y-z", "*"
+        (?:;q=(0(?:\.\d{,3})?|1(?:.0{,3})?))?   # Optional "q=1.00", "q=0.8"
+        (?:\s*,\s*|$)                            # Multiple accepts per header.
+        ''', re.VERBOSE)
+
+def to_locale(language, to_lower=False):
     "Turns a language name (en-us) into a locale name (en_US)."
     p = language.find('-')
     if p >= 0:
-        return language[:p].lower()+'_'+language[p+1:].upper()
+        if to_lower:
+            return language[:p].lower()+'_'+language[p+1:].lower()
+        else:
+            return language[:p].lower()+'_'+language[p+1:].upper()
     else:
         return language.lower()
 
@@ -334,47 +348,41 @@
         if lang_code in supported and lang_code is not None and 
check_for_language(lang_code):
             return lang_code
 
-    lang_code = request.COOKIES.get('django_language', None)
-    if lang_code in supported and lang_code is not None and 
check_for_language(lang_code):
+    lang_code = request.COOKIES.get('django_language')
+    if lang_code and lang_code in supported and check_for_language(lang_code):
         return lang_code
 
-    accept = request.META.get('HTTP_ACCEPT_LANGUAGE', None)
-    if accept is not None:
+    accept = request.META.get('HTTP_ACCEPT_LANGUAGE', '')
+    for lang, unused in parse_accept_lang_header(accept):
+        if lang == '*':
+            break
 
-        t = _accepted.get(accept, None)
-        if t is not None:
-            return t
+        # We have a very restricted form for our language files (no encoding
+        # specifier, since they all must be UTF-8 and only one possible
+        # language each time. So we avoid the overhead of gettext.find() and
+        # look up the MO file manually.
 
-        def _parsed(el):
-            p = el.find(';q=')
-            if p >= 0:
-                lang = el[:p].strip()
-                order = int(float(el[p+3:].strip())*100)
-            else:
-                lang = el
-                order = 100
-            p = lang.find('-')
-            if p >= 0:
-                mainlang = lang[:p]
-            else:
-                mainlang = lang
-            return (lang, mainlang, order)
+        normalized = locale.locale_alias.get(to_locale(lang, True))
+        if not normalized:
+            continue
 
-        langs = [_parsed(el) for el in accept.split(',')]
-        langs.sort(lambda a,b: -1*cmp(a[2], b[2]))
+        # Remove the default encoding from locale_alias
+        normalized = normalized.split('.')[0]
 
-        for lang, mainlang, order in langs:
-            if lang in supported or mainlang in supported:
-                langfile = gettext_module.find('django', globalpath, 
[to_locale(lang)])
-                if langfile:
-                    # reconstruct the actual language from the language
-                    # filename, because otherwise we might incorrectly
-                    # report de_DE if we only have de available, but
-                    # did find de_DE because of language normalization
-                    lang = langfile[len(globalpath):].split(os.path.sep)[1]
-                    _accepted[accept] = lang
-                    return lang
+        if normalized in _accepted:
+            # We've seen this locale before and have an MO file for it, so no
+            # need to check again.
+            return _accepted[normalized]
 
+        for lang in (normalized, normalized.split('_')[0]):
+            if lang not in supported:
+                continue
+            langfile = os.path.join(globalpath, lang, 'LC_MESSAGES',
+                    'django.mo')
+            if os.path.exists(langfile):
+                _accepted[normalized] = lang
+            return lang
+
     return settings.LANGUAGE_CODE
 
 def get_date_formats():
@@ -505,3 +513,23 @@
                 out.write(blankout(t.contents, 'X'))
     return out.getvalue()
 
+def parse_accept_lang_header(lang_string):
+    """
+    Parses the lang_string, which is the body of an HTTP Accept-Language
+    header, and returns a list of (lang, q-value), ordered by 'q' values.
+
+    Any format errors in lang_string results in an empty list being returned.
+    """
+    result = []
+    pieces = accept_language_re.split(lang_string)
+    if pieces[-1]:
+        return []
+    for i in range(0, len(pieces) - 1, 3):
+        first, lang, priority = pieces[i : i + 3]
+        if first:
+            return []
+        priority = priority and float(priority) or 1.0
+        result.append((lang, priority))
+    result.sort(lambda x, y: -cmp(x[1], y[1]))
+    return result
+

Modified: django/branches/queryset-refactor/docs/django-admin.txt
===================================================================
--- django/branches/queryset-refactor/docs/django-admin.txt     2007-11-03 
02:04:59 UTC (rev 6636)
+++ django/branches/queryset-refactor/docs/django-admin.txt     2007-11-03 
02:15:27 UTC (rev 6637)
@@ -741,22 +741,25 @@
 
 **New in Django development version**
 
-If you want to add an action of your own to ``manage.py``, you can.
-Simply add a ``management/commands`` directory to your application.
-Each python module in that directory will be discovered and registered as
+Applications can register their own actions with ``manage.py``. For example,
+you might want to add a ``manage.py`` action for a Django app that you're
+distributing.
+
+To do this, just add a ``management/commands`` directory to your application.
+Each Python module in that directory will be auto-discovered and registered as
 a command that can be executed as an action when you run ``manage.py``::
 
-    /fancy_blog
+    blog/
         __init__.py
         models.py
-        /management
+        management/
             __init__.py
-            /commands
+            commands/
                 __init__.py
                 explode.py
         views.py
         
-In this example, ``explode`` command will be made available to any project
+In this example, the ``explode`` command will be made available to any project
 that includes the ``fancy_blog`` application in ``settings.INSTALLED_APPS``.
 
 The ``explode.py`` module has only one requirement -- it must define a class

Modified: 
django/branches/queryset-refactor/tests/regressiontests/forms/regressions.py
===================================================================
--- 
django/branches/queryset-refactor/tests/regressiontests/forms/regressions.py    
    2007-11-03 02:04:59 UTC (rev 6636)
+++ 
django/branches/queryset-refactor/tests/regressiontests/forms/regressions.py    
    2007-11-03 02:15:27 UTC (rev 6637)
@@ -26,7 +26,6 @@
 Translations are done at rendering time, so multi-lingual apps can define forms
 early and still send back the right translation.
 
-# XFAIL
 >>> activate('de')
 >>> print f.as_p()
 <p><label for="id_username">Benutzername:</label> <input id="id_username" 
type="text" name="username" maxlength="10" /></p>

Added: django/branches/queryset-refactor/tests/regressiontests/i18n/misc.py
===================================================================
--- django/branches/queryset-refactor/tests/regressiontests/i18n/misc.py        
                        (rev 0)
+++ django/branches/queryset-refactor/tests/regressiontests/i18n/misc.py        
2007-11-03 02:15:27 UTC (rev 6637)
@@ -0,0 +1,57 @@
+tests = """
+>>> from django.utils.translation.trans_real import parse_accept_lang_header
+>>> p = parse_accept_lang_header
+
+Good headers.
+>>> p('de')
+[('de', 1.0)]
+>>> p('en-AU')
+[('en-AU', 1.0)]
+>>> p('*;q=1.00')
+[('*', 1.0)]
+>>> p('en-AU;q=0.123')
+[('en-AU', 0.123)]
+>>> p('en-au;q=0.1')
+[('en-au', 0.10000000000000001)]
+>>> p('en-au;q=1.0')
+[('en-au', 1.0)]
+>>> p('da, en-gb;q=0.25, en;q=0.5')
+[('da', 1.0), ('en', 0.5), ('en-gb', 0.25)]
+>>> p('en-au-xx')
+[('en-au-xx', 1.0)]
+>>> p('de,en-au;q=0.75,en-us;q=0.5,en;q=0.25,es;q=0.125,fa;q=0.125')
+[('de', 1.0), ('en-au', 0.75), ('en-us', 0.5), ('en', 0.25), ('es', 0.125), 
('fa', 0.125)]
+>>> p('*')
+[('*', 1.0)]
+>>> p('de;q=0.')
+[('de', 1.0)]
+>>> p('')
+[]
+
+Bad headers; should always return [].
+>>> p('en-gb;q=1.0000')
+[]
+>>> p('en;q=0.1234')
+[]
+>>> p('en;q=.2')
+[]
+>>> p('abcdefghi-au')
+[]
+>>> p('**')
+[]
+>>> p('en,,gb')
+[]
+>>> p('en-au;q=0.1.0')
+[]
+>>> 
p('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXZ,en')
+[]
+>>> p('da, en-gb;q=0.8, en;q=0.7,#')
+[]
+>>> p('de;q=2.0')
+[]
+>>> p('de;q=0.a')
+[]
+>>> p('')
+[]
+
+"""

Modified: django/branches/queryset-refactor/tests/regressiontests/i18n/tests.py
===================================================================
--- django/branches/queryset-refactor/tests/regressiontests/i18n/tests.py       
2007-11-03 02:04:59 UTC (rev 6636)
+++ django/branches/queryset-refactor/tests/regressiontests/i18n/tests.py       
2007-11-03 02:15:27 UTC (rev 6637)
@@ -1,6 +1,7 @@
 # coding: utf-8
+import misc
 
-ur"""
+regressions = ur"""
 Format string interpolation should work with *_lazy objects.
 
 >>> from django.utils.translation import ugettext_lazy, activate, deactivate, 
 >>> gettext_lazy
@@ -39,3 +40,8 @@
 >>> unicode(django.utils.translation.string_concat("dja", "ngo"))
 u'django'
 """
+
+__test__ = {
+    'regressions': regressions,
+    'misc': misc.tests,
+}

Modified: 
django/branches/queryset-refactor/tests/regressiontests/templates/urls.py
===================================================================
--- django/branches/queryset-refactor/tests/regressiontests/templates/urls.py   
2007-11-03 02:04:59 UTC (rev 6636)
+++ django/branches/queryset-refactor/tests/regressiontests/templates/urls.py   
2007-11-03 02:15:27 UTC (rev 6637)
@@ -7,7 +7,7 @@
     # Test urls for testing reverse lookups
     (r'^$', views.index),
     (r'^client/(\d+)/$', views.client),
-    (r'^client/(\d+)/(?P<action>[^/]+)/$', views.client_action),
+    (r'^client/(?P<id>\d+)/(?P<action>[^/]+)/$', views.client_action),
     url(r'^named-client/(\d+)/$', views.client, name="named.client"),
 
     # Unicode strings are permitted everywhere.


--~--~---------~--~----~------------~-------~--~----~
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
-~----------~----~----~----~------~----~------~--~---

Reply via email to