Django

Code

Changeset 5475

Show
Ignore:
Timestamp:
06/14/07 23:02:33 (2 years ago)
Author:
jkocherhans
Message:

newforms-admin: Merged to trunk [5473].

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • django/branches/newforms-admin

    • Property svnmerge-integrated changed from /django/trunk:1-4345,4350-4357,4359-4365,4371-4372,4374-4377,4380-4386,4388,4390-4391,4400-4402,4404-4408,4410,4412-4419,4426-4427,4430-4432,4434,4441,4443-4444,4446-4447,4450,4452-4453,4455-4458,4476,4503,4546,4564-4569,4580-4586,4617,4630,4641-5439 to /django/trunk:1-4345,4350-4357,4359-4365,4371-4372,4374-4377,4380-4386,4388,4390-4391,4400-4402,4404-4408,4410,4412-4419,4426-4427,4430-4432,4434,4441,4443-4444,4446-4447,4450,4452-4453,4455-4458,4476,4503,4546,4564-4569,4580-4586,4617,4630,4641-5473
  • django/branches/newforms-admin/AUTHORS

    r5440 r5475  
    7474    Ian Clelland <clelland@gmail.com> 
    7575    crankycoder@gmail.com 
     76    Pete Crosier <pete.crosier@gmail.com> 
    7677    Matt Croydon <http://www.postneo.com/> 
    7778    flavio.curella@gmail.com 
     
    131132    Antti Kaihola <http://akaihola.blogspot.com/> 
    132133    Ben Dean Kawamura <ben.dean.kawamura@gmail.com> 
     134    ian.g.kelly@gmail.com 
    133135    Garth Kidd <http://www.deadlybloodyserious.com/> 
    134136    kilian <kilian.cavalotti@lip6.fr> 
     
    170172    mmarshall 
    171173    Eric Moritz <http://eric.themoritzfamily.com/> 
     174    mrmachine <real.human@mrmachine.net> 
    172175    Robin Munn <http://www.geekforgod.com/> 
    173176    Robert Myers <myer0052@gmail.com> 
     
    219222    Ville Säävuori <http://www.unessa.net/> 
    220223    Tyson Tate <tyson@fallingbullets.com> 
     224    Frank Tegtmeyer <fte@fte.to> 
    221225    thebjorn <bp@datakortet.no> 
    222226    Zach Thompson <zthompson47@gmail.com> 
     
    235239    Dan Watson <http://theidioteque.net/> 
    236240    Chris Wesseling <Chris.Wesseling@cwi.nl> 
     241    James Wheare <django@sparemint.com> 
    237242    charly.wilhelm@gmail.com 
    238243    Rachel Willmer <http://www.willmer.com/kb/> 
  • django/branches/newforms-admin/django/conf/global_settings.py

    r5440 r5475  
    242242# The User-Agent string to use when checking for URL validity through the 
    243243# isExistingURL validator. 
    244 URL_VALIDATOR_USER_AGENT = "Django/0.96pre (http://www.djangoproject.com)" 
     244from django import get_version 
     245URL_VALIDATOR_USER_AGENT = "Django/%s (http://www.djangoproject.com)" % get_version() 
    245246 
    246247############## 
  • django/branches/newforms-admin/django/conf/locale/nl/LC_MESSAGES/django.po

    r4546 r5475  
    22032203 
    22042204#: utils/timesince.py:16 
     2205# In the timesince context it is stilistically wrong to use the plural for hour in Dutch. 
    22052206msgid "hour" 
    22062207msgid_plural "hours" 
    22072208msgstr[0] "uur" 
    2208 msgstr[1] "uren
     2209msgstr[1] "uur
    22092210 
    22102211#: utils/timesince.py:17 
  • django/branches/newforms-admin/django/contrib/auth/__init__.py

    r5195 r5475  
    5454    request.session[SESSION_KEY] = user.id 
    5555    request.session[BACKEND_SESSION_KEY] = user.backend 
     56    if hasattr(request, 'user'): 
     57        request.user = user 
    5658 
    5759def logout(request): 
     
    6769    except KeyError: 
    6870        pass 
     71    if hasattr(request, 'user'): 
     72        from django.contrib.auth.models import AnonymousUser 
     73        request.user = AnonymousUser() 
    6974 
    7075def get_user(request): 
  • django/branches/newforms-admin/django/contrib/sessions/models.py

    r4824 r5475  
    1 import base64, md5, random, sys, datetime 
     1import base64, md5, random, sys, datetime, os, time 
    22import cPickle as pickle 
    33from django.db import models 
     
    1515        "Returns session key that isn't being used." 
    1616        # The random module is seeded when this Apache child is created. 
    17         # Use person_id and SECRET_KEY as added salt. 
     17        # Use SECRET_KEY as added salt. 
    1818        while 1: 
    19             session_key = md5.new(str(random.randint(0, sys.maxint - 1)) + str(random.randint(0, sys.maxint - 1)) + settings.SECRET_KEY).hexdigest() 
     19            session_key = md5.new("%s%s%s%s" % (random.randint(0, sys.maxint - 1), os.getpid(), time.time(), settings.SECRET_KEY)).hexdigest() 
    2020            try: 
    2121                self.get(session_key=session_key) 
  • django/branches/newforms-admin/django/core/management.py

    r5440 r5475  
    44import django 
    55from django.core.exceptions import ImproperlyConfigured 
    6 import os, re, shutil, sys, textwrap 
    76from optparse import OptionParser 
    87from django.utils import termcolors 
     8import os, re, shutil, sys, textwrap 
    99 
    1010# For Python 2.3 
    1111if not hasattr(__builtins__, 'set'): 
    1212    from sets import Set as set 
     13 
     14# For backwards compatibility: get_version() used to be in this module. 
     15get_version = django.get_version 
    1316 
    1417MODULE_TEMPLATE = '''    {%% if perms.%(app)s.%(addperm)s or perms.%(app)s.%(changeperm)s %%} 
     
    9396# field as the field to which it points. 
    9497get_rel_data_type = lambda f: (f.get_internal_type() in ('AutoField', 'PositiveIntegerField', 'PositiveSmallIntegerField')) and 'IntegerField' or f.get_internal_type() 
    95  
    96 def get_version(): 
    97     "Returns the version as a human-format string." 
    98     from django import VERSION 
    99     v = '.'.join([str(i) for i in VERSION[:-1]]) 
    100     if VERSION[-1]: 
    101         v += '-' + VERSION[-1] 
    102     return v 
    10398 
    10499def get_sql_create(app): 
  • django/branches/newforms-admin/django/db/backends/postgresql/base.py

    r5326 r5475  
    220220    output = [] 
    221221    for model in model_list: 
     222        # Use `coalesce` to set the sequence for each model to the max pk value if there are records, 
     223        # or 1 if there are none. Set the `is_called` property (the third argument to `setval`) to true 
     224        # if there are records (as the max pk value is already in use), otherwise set it to false. 
    222225        for f in model._meta.fields: 
    223226            if isinstance(f, models.AutoField): 
    224                 output.append("%s setval('%s', (%s max(%s) %s %s));" % \ 
     227                output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ 
    225228                    (style.SQL_KEYWORD('SELECT'), 
    226229                    style.SQL_FIELD(quote_name('%s_%s_seq' % (model._meta.db_table, f.column))), 
    227                     style.SQL_KEYWORD('SELECT'), 
    228230                    style.SQL_FIELD(quote_name(f.column)), 
     231                    style.SQL_FIELD(quote_name(f.column)), 
     232                    style.SQL_KEYWORD('IS NOT'), 
    229233                    style.SQL_KEYWORD('FROM'), 
    230234                    style.SQL_TABLE(quote_name(model._meta.db_table)))) 
    231235                break # Only one AutoField is allowed per model, so don't bother continuing. 
    232236        for f in model._meta.many_to_many: 
    233             output.append("%s setval('%s', (%s max(%s) %s %s));" % \ 
     237            output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ 
    234238                (style.SQL_KEYWORD('SELECT'), 
    235239                style.SQL_FIELD(quote_name('%s_id_seq' % f.m2m_db_table())), 
    236                 style.SQL_KEYWORD('SELECT'), 
    237240                style.SQL_FIELD(quote_name('id')), 
     241                style.SQL_FIELD(quote_name('id')), 
     242                style.SQL_KEYWORD('IS NOT'), 
    238243                style.SQL_KEYWORD('FROM'), 
    239244                style.SQL_TABLE(f.m2m_db_table()))) 
  • django/branches/newforms-admin/django/db/backends/postgresql_psycopg2/base.py

    r5244 r5475  
    177177    output = [] 
    178178    for model in model_list: 
     179        # Use `coalesce` to set the sequence for each model to the max pk value if there are records, 
     180        # or 1 if there are none. Set the `is_called` property (the third argument to `setval`) to true 
     181        # if there are records (as the max pk value is already in use), otherwise set it to false. 
    179182        for f in model._meta.fields: 
    180183            if isinstance(f, models.AutoField): 
    181                 output.append("%s setval('%s', (%s max(%s) %s %s));" % \ 
     184                output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ 
    182185                    (style.SQL_KEYWORD('SELECT'), 
    183186                    style.SQL_FIELD(quote_name('%s_%s_seq' % (model._meta.db_table, f.column))), 
    184                     style.SQL_KEYWORD('SELECT'), 
    185187                    style.SQL_FIELD(quote_name(f.column)), 
     188                    style.SQL_FIELD(quote_name(f.column)), 
     189                    style.SQL_KEYWORD('IS NOT'), 
    186190                    style.SQL_KEYWORD('FROM'), 
    187191                    style.SQL_TABLE(quote_name(model._meta.db_table)))) 
    188192                break # Only one AutoField is allowed per model, so don't bother continuing. 
    189193        for f in model._meta.many_to_many: 
    190             output.append("%s setval('%s', (%s max(%s) %s %s));" % \ 
     194            output.append("%s setval('%s', coalesce(max(%s), 1), max(%s) %s null) %s %s;" % \ 
    191195                (style.SQL_KEYWORD('SELECT'), 
    192196                style.SQL_FIELD(quote_name('%s_id_seq' % f.m2m_db_table())), 
    193                 style.SQL_KEYWORD('SELECT'), 
    194197                style.SQL_FIELD(quote_name('id')), 
     198                style.SQL_FIELD(quote_name('id')), 
     199                style.SQL_KEYWORD('IS NOT'), 
    195200                style.SQL_KEYWORD('FROM'), 
    196201                style.SQL_TABLE(f.m2m_db_table()))) 
  • django/branches/newforms-admin/django/db/backends/util.py

    r5326 r5475  
    9292 
    9393def typecast_decimal(s): 
    94     if s is None
     94    if s is None or s == ''
    9595        return None 
    9696    return decimal.Decimal(s) 
  • django/branches/newforms-admin/django/__init__.py

    r4940 r5475  
    11VERSION = (0, 97, 'newforms-admin') 
     2 
     3def get_version(): 
     4    "Returns the version as a human-format string." 
     5    v = '.'.join([str(i) for i in VERSION[:-1]]) 
     6    if VERSION[-1]: 
     7        v += '-' + VERSION[-1] 
     8    return v 
  • django/branches/newforms-admin/django/newforms/models.py

    r5473 r5475  
    2121    Saves bound Form ``form``'s cleaned_data into model instance ``instance``. 
    2222 
    23     Assumes ``form`` has a field for every non-AutoField database field in 
    24     ``instance``. If commit=True, then the changes to ``instance`` will be 
    25     saved to the database. Returns ``instance``. 
     23    If commit=True, then the changes to ``instance`` will be saved to the 
     24    database. Returns ``instance``. 
    2625    """ 
    2726    from django.db import models 
  • django/branches/newforms-admin/django/template/defaulttags.py

    r5440 r5475  
    66from django.conf import settings 
    77import sys 
     8import re 
     9 
     10if not hasattr(__builtins__, 'reversed'): 
     11    # For Python 2.3. 
     12    # From http://www.python.org/doc/current/tut/node11.html 
     13    def reversed(data): 
     14        for index in xrange(len(data)-1, -1, -1): 
     15            yield data[index] 
     16 
    817 
    918register = Library() 
     
    6271 
    6372class ForNode(Node): 
    64     def __init__(self, loopvar, sequence, reversed, nodelist_loop): 
    65         self.loopvar, self.sequence = loopvar, sequence 
     73    def __init__(self, loopvars, sequence, reversed, nodelist_loop): 
     74        self.loopvars, self.sequence = loopvars, sequence 
    6675        self.reversed = reversed 
    6776        self.nodelist_loop = nodelist_loop 
     
    7382            reversed = '' 
    7483        return "<For Node: for %s in %s, tail_len: %d%s>" % \ 
    75             (self.loopvar, self.sequence, len(self.nodelist_loop), reversed) 
     84            (', '.join( self.loopvars ), self.sequence, len(self.nodelist_loop), reversed) 
    7685 
    7786    def __iter__(self): 
     
    103112        len_values = len(values) 
    104113        if self.reversed: 
    105             # From http://www.python.org/doc/current/tut/node11.html 
    106             def reverse(data): 
    107                 for index in range(len(data)-1, -1, -1): 
    108                     yield data[index] 
    109             values = reverse(values) 
     114            values = reversed(values) 
     115        unpack = len(self.loopvars) > 1 
    110116        for i, item in enumerate(values): 
    111117            context['forloop'] = { 
     
    121127                'parentloop': parentloop, 
    122128            } 
    123             context[self.loopvar] = item 
     129            if unpack: 
     130                # If there are multiple loop variables, unpack the item into them. 
     131                context.update(dict(zip(self.loopvars, item))) 
     132            else: 
     133                context[self.loopvars[0]] = item 
    124134            for node in self.nodelist_loop: 
    125135                nodelist.append(node.render(context)) 
     136            if unpack: 
     137                # The loop variables were pushed on to the context so pop them 
     138                # off again. This is necessary because the tag lets the length 
     139                # of loopvars differ to the length of each set of items and we 
     140                # don't want to leave any vars from the previous loop on the 
     141                # context. 
     142                context.pop() 
    126143        context.pop() 
    127144        return nodelist.render(context) 
     
    487504    parser.delete_first_token() 
    488505    return FilterNode(filter_expr, nodelist) 
    489 filter = register.tag("filter", do_filter) 
     506do_filter = register.tag("filter", do_filter) 
    490507 
    491508#@register.tag 
     
    531548        </ul> 
    532549 
    533     You can also loop over a list in reverse by using 
     550    You can loop over a list in reverse by using 
    534551    ``{% for obj in list reversed %}``. 
     552     
     553    You can also unpack multiple values from a two-dimensional array:: 
     554     
     555        {% for key,value in dict.items %} 
     556            {{ key }}: {{ value }} 
     557        {% endfor %} 
    535558 
    536559    The for loop sets a number of variables available within the loop: 
     
    553576    """ 
    554577    bits = token.contents.split() 
    555     if len(bits) == 5 and bits[4] != 'reversed': 
    556         raise TemplateSyntaxError, "'for' statements with five words should end in 'reversed': %s" % token.contents 
    557     if len(bits) not in (4, 5): 
    558         raise TemplateSyntaxError, "'for' statements should have either four or five words: %s" % token.contents 
    559     if bits[2] != 'in': 
    560         raise TemplateSyntaxError, "'for' statement must contain 'in' as the second word: %s" % token.contents 
    561     loopvar = bits[1] 
    562     sequence = parser.compile_filter(bits[3]) 
    563     reversed = (len(bits) == 5) 
     578    if len(bits) < 4: 
     579        raise TemplateSyntaxError, "'for' statements should have at least four words: %s" % token.contents 
     580 
     581    reversed = bits[-1] == 'reversed' 
     582    in_index = reversed and -3 or -2 
     583    if bits[in_index] != 'in': 
     584        raise TemplateSyntaxError, "'for' statements should use the format 'for x in y': %s" % token.contents 
     585 
     586    loopvars = re.sub(r' *, *', ',', ' '.join(bits[1:in_index])).split(',') 
     587    for var in loopvars: 
     588        if not var or ' ' in var: 
     589            raise TemplateSyntaxError, "'for' tag received an invalid argument: %s" % token.contents 
     590 
     591    sequence = parser.compile_filter(bits[in_index+1]) 
    564592    nodelist_loop = parser.parse(('endfor',)) 
    565593    parser.delete_first_token() 
    566     return ForNode(loopvar, sequence, reversed, nodelist_loop) 
     594    return ForNode(loopvars, sequence, reversed, nodelist_loop) 
    567595do_for = register.tag("for", do_for) 
    568596 
  • django/branches/newforms-admin/django/test/client.py

    r5195 r5475  
    6666            lines.extend([ 
    6767                '--' + boundary, 
    68                 'Content-Disposition: form-data; name="%s"' % key, 
    69                 '', 
    70                 '--' + boundary, 
    71                 'Content-Disposition: form-data; name="%s_file"; filename="%s"' % (key, value.name), 
     68                'Content-Disposition: form-data; name="%s"; filename="%s"' % (key, value.name), 
    7269                'Content-Type: application/octet-stream', 
    7370                '', 
  • django/branches/newforms-admin/django/views/debug.py

    r5195 r5475  
    33from django.utils.html import escape 
    44from django.http import HttpResponseServerError, HttpResponseNotFound 
    5 import os, re 
     5import os, re, sys 
    66 
    77HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST') 
     
    132132        'request_protocol': request.is_secure() and "https" or "http", 
    133133        'settings': get_safe_settings(), 
     134        'sys_executable' : sys.executable, 
     135        'sys_version_info' : '%d.%d.%d' % sys.version_info[0:3], 
    134136        'template_info': template_info, 
    135137        'template_does_not_exist': template_does_not_exist, 
     
    335337      <td>{{ lastframe.filename|escape }} in {{ lastframe.function|escape }}, line {{ lastframe.lineno }}</td> 
    336338    </tr> 
     339    <tr> 
     340      <th>Python Executable:</th> 
     341      <td>{{ sys_executable|escape }}</td> 
     342    </tr> 
     343    <tr> 
     344      <th>Python Version:</th> 
     345      <td>{{ sys_version_info }}</td> 
     346    </tr> 
    337347  </table> 
    338348</div> 
  • django/branches/newforms-admin/docs/authentication.txt

    r5195 r5475  
    731731------------------- 
    732732 
    733 Three basic permissions -- add, create and delete -- are automatically created 
     733Three basic permissions -- add, change and delete -- are automatically created 
    734734for each Django model that has a ``class Admin`` set. Behind the scenes, these 
    735735permissions are added to the ``auth_permission`` database table when you run 
  • django/branches/newforms-admin/docs/db-api.txt

    r5440 r5475  
    389389underlying SQL statement, and the whole thing is enclosed in a ``NOT()``. 
    390390 
    391 This example excludes all entries whose ``pub_date`` is the current date/time 
     391This example excludes all entries whose ``pub_date`` is later than 2005-1-3 
    392392AND whose ``headline`` is "Hello":: 
    393393 
     
    399399    WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello') 
    400400 
    401 This example excludes all entries whose ``pub_date`` is the current date/time 
    402 OR whose ``headline`` is "Hello":: 
     401This example excludes all entries whose ``pub_date`` is later than 2005-1-3 
     402AND whose headline is NOT "Hello":: 
    403403 
    404404    Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline='Hello') 
     
    599599    c = p.hometown       # Doesn't hit the database. 
    600600 
    601     sv = Book.objects.get(id=4) # No select_related() in this example. 
     601    b = Book.objects.get(id=4) # No select_related() in this example. 
    602602    p = b.author         # Hits the database. 
    603603    c = p.hometown       # Hits the database. 
  • django/branches/newforms-admin/docs/model-api.txt

    r5440 r5475  
    447447 
    448448The admin represents this as an ``<input type="text">`` (a single-line input). 
     449 
     450``URLField`` takes an optional argument, ``maxlength``, the maximum length (in 
     451characters) of the field. The maxlength is enforced at the database level and 
     452in Django's validation. If you don't specify ``maxlength``, a default of 200 
     453is used. 
    449454 
    450455``USStateField`` 
  • django/branches/newforms-admin/docs/newforms.txt

    r5440 r5475  
    300300empty string, because ``nick_name`` is ``CharField``, and ``CharField``\s treat 
    301301empty values as an empty string. Each field type knows what its "blank" value 
    302 is -- e.g., for ``DateField``, it's ``None`` instead of the empty string. 
     302is -- e.g., for ``DateField``, it's ``None`` instead of the empty string. For 
     303full details on each field's behavior in this case, see the "Empty value" note 
     304for each field in the "Built-in ``Field`` classes" section below. 
     305 
     306You can write code to perform validation for particular form fields (based on 
     307their name) or for the form as a whole (considering combinations of various 
     308fields). More information about this is in the `Custom form and field 
     309validation`_ section, below. 
    303310 
    304311Behavior of unbound forms 
    305312~~~~~~~~~~~~~~~~~~~~~~~~~ 
    306313 
    307 It's meaningless to request "clean" data in a form with no data, but, for the 
     314It's meaningless to request "cleaned" data in a form with no data, but, for the 
    308315record, here's what happens with unbound forms:: 
    309316 
     
    607614 
    608615Let's put this all together and use the ``ContactForm`` example in a Django 
    609 view and template. This example view displays the contact form by default and 
    610 validates/processes it if accessed via a POST request:: 
     616view and template. 
     617 
     618Simple view example 
     619~~~~~~~~~~~~~~~~~~~ 
     620 
     621This example view displays the contact form by default and validates/processes 
     622it if accessed via a POST request:: 
    611623 
    612624    def contact(request): 
     
    620632        return render_to_response('contact.html', {'form': form}) 
    621633 
    622 Simple template output 
    623 ~~~~~~~~~~~~~~~~~~~~~~ 
    624  
    625 The template, ``contact.html``, is responsible for displaying the form as HTML. 
    626 To do this, we can use the techniques outlined in the "Outputting forms as HTML" 
    627 section above. 
     634Simple template example 
     635~~~~~~~~~~~~~~~~~~~~~~~ 
     636 
     637The template in the above view example, ``contact.html``, is responsible for 
     638displaying the form as HTML. To do this, we can use the techniques outlined in 
     639the "Outputting forms as HTML" section above. 
    628640 
    629641The simplest way to display a form's HTML is to use the variable on its own, 
     
    678690This iteration technique is useful if you want to apply the same HTML 
    679691formatting to each field, or if you don't know the names of the form fields 
    680 ahead of time. Note that the fields will be listed in the order in which 
     692ahead of time. Note that the fields will be iterated over in the order in which 
    681693they're defined in the ``Form`` class. 
    682694 
     
    702714----------------- 
    703715 
    704 If you subclass a custom ``Form`` class, the resulting ``Form`` class will 
     716If you have multiple ``Form`` classes that share fields, you can use 
     717subclassing to remove redundancy. 
     718 
     719When you subclass a custom ``Form`` class, the resulting subclass will 
    705720include all fields of the parent class(es), followed by the fields you define 
    706721in the subclass. 
     
    12021217mentioned above (``required``, ``label``, ``initial``, ``widget``, 
    12031218``help_text``). 
     1219 
     1220Custom form and field validation 
     1221--------------------------------- 
     1222 
     1223Form validation happens when the data is cleaned. If you want to customise 
     1224this process, there are various places you can change, each one serving a 
     1225different purpose. Thee types of cleaning methods are run during form 
     1226processing. These are normally executed when you call the ``is_valid()`` 
     1227method on a form. There are other things that can trigger cleaning and 
     1228validation (accessing the ``errors`` attribute or calling ``full_clean()`` 
     1229directly), but normally they won't be needed. 
     1230 
     1231In general, any cleaning method can raise ``ValidationError`` if there is a 
     1232problem with the data it is processing, passing the relevant error message to 
     1233the ``ValidationError`` constructor. If no ``ValidationError`` is raised, the 
     1234method should return the cleaned (normalised) data as a Python object. 
     1235 
     1236If you detect multiple errors during a cleaning method and wish to signal all 
     1237of them to the form submitter, it is possible to pass a list of errors to the 
     1238``ValidationError`` constructor. 
     1239 
     1240The three types of cleaning methods are: 
     1241 
     1242    * The ``clean()`` method on a Field subclass. This is responsible 
     1243      for cleaning the data in a way that is generic for that type of field. 
     1244      For example, a FloatField will turn the data into a Python ``float`` or 
     1245      raise a ``ValidationError``. 
     1246 
     1247    * The ``clean_<fieldname>()`` method in a form subclass -- where 
     1248      ``<fieldname>`` is replaced with the name of the form field attribute. 
     1249      This method does any cleaning that is specific to that particular 
     1250      attribute, unrelated to the type of field that it is. This method is not 
     1251      passed any parameters. You will need to look up the value of the field 
     1252      in ``self.cleaned_data`` and remember that it will be a Python object 
     1253      at this point, not the original string submitted in the form (it will be 
     1254      in ``cleaned_data`` because the general field ``clean()`` method, above, 
     1255      has already cleaned the data once). 
     1256 
     1257      For example, if you wanted to validate that the contents of a 
     1258      ``CharField`` called ``serialnumber`` was unique, 
     1259      ``clean_serialnumber()`` would be the right place to do this. You don't 
     1260      need a specific field (it's just a ``CharField``), but you want a 
     1261      formfield-specific piece of validation and, possibly, 
     1262      cleaning/normalizing the data. 
     1263 
     1264    * The Form subclass's ``clean()`` method. This method can perform 
     1265      any validation that requires access to multiple fields from the form at 
     1266      once. This is where you might put in things to check that if field ``A`` 
     1267      is supplied, field ``B`` must contain a valid email address and the 
     1268      like. The data that this method returns is the final ``cleaned_data`` 
     1269      attribute for the form, so don't forget to return the full list of 
     1270      cleaned data if you override this method (by default, ``Form.clean()`` 
     1271      just returns ``self.cleaned_data``). 
     1272 
     1273      Note that any errors raised by your ``Form.clean()`` override will not 
     1274      be associated with any field in particular. They go into a special 
     1275      "field" (called ``__all__``, which you can access via the 
     1276      ``non_field_errors()`` method if you need to. 
     1277 
     1278These methods are run in the order given above, one field at a time.  That is, 
     1279for each field in the form (in the order they are declared in the form 
     1280definition), the ``Field.clean()`` method (or it's override) is run, then 
     1281``clean_<fieldname>()``. Finally, once those two methods are run for every 
     1282field, the ``Form.clean()`` method, or it's override, is executed. 
     1283 
     1284As mentioned above, any of these methods can raise a ``ValidationError``. For 
     1285any field, if the ``Field.clean()`` method raises a ``ValidationError``, any 
     1286field-specific cleaning method is not called. However, the cleaning methods 
     1287for all remaining fields are still executed. 
     1288 
     1289The ``clean()`` method for the ``Form`` class or subclass is always run. If 
     1290that method raises a ``ValidationError``, ``cleaned_data`` will be an empty 
     1291dictionary. 
     1292 
     1293The previous paragraph means that if you are overriding ``Form.clean()``, you 
     1294should iterate through ``self.cleaned_data.items()``, possibly considering the 
     1295``_errors`` dictionary attribute on the form as well. In this way, you will 
     1296already know which fields have passed their individual validation requirements. 
     1297 
     1298A simple example 
     1299~~~~~~~~~~~~~~~~ 
     1300 
     1301Here's a simple example of a custom field that validates its input is a string 
     1302containing comma-separated e-mail addresses, with at least one address. We'll 
     1303keep it simple and assume e-mail validation is contained in a function called 
     1304``is_valid_email()``. The full class:: 
     1305 
     1306    from django import newforms as forms 
     1307 
     1308    class MultiEmailField(forms.Field): 
     1309        def clean(self, value): 
     1310            emails = value.split(',') 
     1311            for email in emails: 
     1312                if not is_valid_email(email): 
     1313                    raise forms.ValidationError('%s is not a valid e-mail address.' % email) 
     1314            if not emails: 
     1315                raise forms.ValidationError('Enter at least one e-mail address.') 
     1316            return emails 
     1317 
     1318Let's alter the ongoing ``ContactForm`` example to demonstrate how you'd use 
     1319this in a form. Simply use ``MultiEmailField`` instead of ``forms.EmailField``, 
     1320like so:: 
     1321 
     1322    class ContactForm(forms.Form): 
     1323        subject = forms.CharField(max_length=100) 
     1324        message = forms.CharField() 
     1325        senders = MultiEmailField() 
     1326        cc_myself = forms.BooleanField() 
    12041327 
    12051328Generating forms for models 
  • django/branches/newforms-admin/docs/templates.txt

    r5440 r5475  
    448448 
    449449Loop over each item in an array.  For example, to display a list of athletes 
    450 given ``athlete_list``:: 
     450provided in ``athlete_list``:: 
    451451 
    452452    <ul> 
     
    456456    </ul> 
    457457 
    458 You can also loop over a list in reverse by using ``{% for obj in list reversed %}``. 
     458You can loop over a list in reverse by using ``{% for obj in list reversed %}``. 
     459 
     460**New in Django development version** 
     461If you need to loop over a list of lists, you can unpack the values 
     462in eachs sub-list into a set of known names. For example, if your context contains 
     463a list of (x,y) coordinates called ``points``, you could use the following 
     464to output the list of points::  
     465 
     466    {% for x, y in points %} 
     467        There is a point at {{ x }},{{ y }} 
     468    {% endfor %} 
     469     
     470This can also be useful if you need to access the items in a dictionary.  
     471For example, if your context contained a dictionary ``data``, the following 
     472would display the keys and values of the dictionary:: 
     473 
     474    {% for key, value in data.items %} 
     475        {{ key }}: {{ value }} 
     476    {% endfor %} 
    459477 
    460478The for loop sets a number of variables available within the loop: 
  • django/branches/newforms-admin/scripts/rpm-install.sh

    r4940 r5475  
    2222sed -e "/\.py[co]$/d" -e "s/\.py$/.py*/" DIRS FILES >INSTALLED_FILES 
    2323 
     24mkdir -p ${RPM_BUILD_ROOT}/%{_mandir}/man1/ 
     25cp docs/man/* ${RPM_BUILD_ROOT}/%{_mandir}/man1/ 
     26cat << EOF >> INSTALLED_FILES 
     27%doc %{_mandir}/man1/*" 
     28EOF 
  • django/branches/newforms-admin/tests/regressiontests/serializers_regress/tests.py

    r5440 r5475  
    286286    obj = ComplexModel(field1='first',field2='second',field3='third') 
    287287    obj.save() 
    288      
     288 
    289289    # Serialize then deserialize the test database 
    290290    serialized_data = serializers.serialize(format, [obj], indent=2, fields=('field1','field3')) 
    291291    result = serializers.deserialize(format, serialized_data).next() 
    292      
     292 
    293293    # Check that the deserialized object contains data in only the serialized fields. 
    294294    self.assertEqual(result.object.field1, 'first') 
     
    302302    obj = ComplexModel(field1='first',field2='second',field3='third') 
    303303    obj.save() 
    304      
     304 
    305305    # Serialize the test database to a stream 
    306     stream = StringIO()     
     306    stream = StringIO() 
    307307    serializers.serialize(format, [obj], indent=2, stream=stream) 
    308      
     308 
    309309    # Serialize normally for a comparison 
    310310    string_data = serializers.serialize(format, [obj], indent=2) 
    311311 
    312312    # Check that the two are the same 
    313     self.assertEqual(string_data, stream.buffer()) 
     313    self.assertEqual(string_data, stream.getvalue()) 
    314314    stream.close() 
    315      
     315 
    316316for format in serializers.get_serializer_formats(): 
    317317    setattr(SerializerTests, 'test_'+format+'_serializer', curry(serializerTest, format)) 
    318318    setattr(SerializerTests, 'test_'+format+'_serializer_fields', curry(fieldsTest, format)) 
    319     setattr(SerializerTests, 'test_'+format+'_serializer_stream', curry(fieldsTest, format)) 
     319    if format != 'python': 
     320        setattr(SerializerTests, 'test_'+format+'_serializer_stream', curry(streamTest, format)) 
  • django/branches/newforms-admin/tests/regressiontests/templates/tests.py

    r5195 r5475  
    290290            'for-tag-vars03': ("{% for val in values %}{{ forloop.revcounter }}{% endfor %}", {"values": [6, 6, 6]}, "321"), 
    291291            'for-tag-vars04': ("{% for val in values %}{{ forloop.revcounter0 }}{% endfor %}", {"values": [6, 6, 6]}, "210"), 
     292            'for-tag-unpack01': ("{% for key,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), 
     293            'for-tag-unpack03': ("{% for key, value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), 
     294            'for-tag-unpack04': ("{% for key , value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), 
     295            'for-tag-unpack05': ("{% for key ,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), 
     296            'for-tag-unpack06': ("{% for key value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError), 
     297            'for-tag-unpack07': ("{% for key,,value in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError), 
     298            'for-tag-unpack08': ("{% for key,value, in items %}{{ key }}:{{ value }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, template.TemplateSyntaxError), 
     299            # Ensure that a single loopvar doesn't truncate the list in val. 
     300            'for-tag-unpack09': ("{% for val in items %}{{ val.0 }}:{{ val.1 }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, "one:1/two:2/"), 
     301            # Otherwise, silently truncate if the length of loopvars differs to the length of each set of items. 
     302            'for-tag-unpack10': ("{% for x,y in items %}{{ x }}:{{ y }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2, 'orange'))}, "one:1/two:2/"), 
     303            'for-tag-unpack11': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1), ('two', 2))}, ("one:1,/two:2,/", "one:1,INVALID/two:2,INVALID/")), 
     304            'for-tag-unpack12': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2))}, ("one:1,carrot/two:2,/", "one:1,carrot/two:2,INVALID/")), 
     305            'for-tag-unpack13': ("{% for x,y,z in items %}{{ x }}:{{ y }},{{ z }}/{% endfor %}", {"items": (('one', 1, 'carrot'), ('two', 2, 'cheese'))}, ("one:1,carrot/two:2,cheese/", "one:1,carrot/two:2,cheese/")), 
    292306 
    293307            ### IF TAG ################################################################