Changeset 5475
- Timestamp:
- 06/14/07 23:02:33 (2 years ago)
- Files:
-
- django/branches/newforms-admin (modified) (1 prop)
- django/branches/newforms-admin/AUTHORS (modified) (5 diffs)
- django/branches/newforms-admin/django/conf/global_settings.py (modified) (1 diff)
- django/branches/newforms-admin/django/conf/locale/nl/LC_MESSAGES/django.mo (modified) (previous)
- django/branches/newforms-admin/django/conf/locale/nl/LC_MESSAGES/django.po (modified) (1 diff)
- django/branches/newforms-admin/django/contrib/auth/__init__.py (modified) (2 diffs)
- django/branches/newforms-admin/django/contrib/sessions/models.py (modified) (2 diffs)
- django/branches/newforms-admin/django/core/management.py (modified) (2 diffs)
- django/branches/newforms-admin/django/db/backends/postgresql/base.py (modified) (1 diff)
- django/branches/newforms-admin/django/db/backends/postgresql_psycopg2/base.py (modified) (1 diff)
- django/branches/newforms-admin/django/db/backends/util.py (modified) (1 diff)
- django/branches/newforms-admin/django/__init__.py (modified) (1 diff)
- django/branches/newforms-admin/django/newforms/models.py (modified) (1 diff)
- django/branches/newforms-admin/django/template/defaulttags.py (modified) (8 diffs)
- django/branches/newforms-admin/django/test/client.py (modified) (1 diff)
- django/branches/newforms-admin/django/views/debug.py (modified) (3 diffs)
- django/branches/newforms-admin/docs/authentication.txt (modified) (1 diff)
- django/branches/newforms-admin/docs/db-api.txt (modified) (3 diffs)
- django/branches/newforms-admin/docs/man (copied) (copied from django/trunk/docs/man)
- django/branches/newforms-admin/docs/man/django-admin.1 (copied) (copied from django/trunk/docs/man/django-admin.1)
- django/branches/newforms-admin/docs/model-api.txt (modified) (1 diff)
- django/branches/newforms-admin/docs/newforms.txt (modified) (6 diffs)
- django/branches/newforms-admin/docs/templates.txt (modified) (2 diffs)
- django/branches/newforms-admin/scripts/rpm-install.sh (modified) (1 diff)
- django/branches/newforms-admin/tests/regressiontests/serializers_regress/tests.py (modified) (2 diffs)
- django/branches/newforms-admin/tests/regressiontests/templates/tests.py (modified) (1 diff)
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 74 74 Ian Clelland <clelland@gmail.com> 75 75 crankycoder@gmail.com 76 Pete Crosier <pete.crosier@gmail.com> 76 77 Matt Croydon <http://www.postneo.com/> 77 78 flavio.curella@gmail.com … … 131 132 Antti Kaihola <http://akaihola.blogspot.com/> 132 133 Ben Dean Kawamura <ben.dean.kawamura@gmail.com> 134 ian.g.kelly@gmail.com 133 135 Garth Kidd <http://www.deadlybloodyserious.com/> 134 136 kilian <kilian.cavalotti@lip6.fr> … … 170 172 mmarshall 171 173 Eric Moritz <http://eric.themoritzfamily.com/> 174 mrmachine <real.human@mrmachine.net> 172 175 Robin Munn <http://www.geekforgod.com/> 173 176 Robert Myers <myer0052@gmail.com> … … 219 222 Ville Säävuori <http://www.unessa.net/> 220 223 Tyson Tate <tyson@fallingbullets.com> 224 Frank Tegtmeyer <fte@fte.to> 221 225 thebjorn <bp@datakortet.no> 222 226 Zach Thompson <zthompson47@gmail.com> … … 235 239 Dan Watson <http://theidioteque.net/> 236 240 Chris Wesseling <Chris.Wesseling@cwi.nl> 241 James Wheare <django@sparemint.com> 237 242 charly.wilhelm@gmail.com 238 243 Rachel Willmer <http://www.willmer.com/kb/> django/branches/newforms-admin/django/conf/global_settings.py
r5440 r5475 242 242 # The User-Agent string to use when checking for URL validity through the 243 243 # isExistingURL validator. 244 URL_VALIDATOR_USER_AGENT = "Django/0.96pre (http://www.djangoproject.com)" 244 from django import get_version 245 URL_VALIDATOR_USER_AGENT = "Django/%s (http://www.djangoproject.com)" % get_version() 245 246 246 247 ############## django/branches/newforms-admin/django/conf/locale/nl/LC_MESSAGES/django.po
r4546 r5475 2203 2203 2204 2204 #: utils/timesince.py:16 2205 # In the timesince context it is stilistically wrong to use the plural for hour in Dutch. 2205 2206 msgid "hour" 2206 2207 msgid_plural "hours" 2207 2208 msgstr[0] "uur" 2208 msgstr[1] "u ren"2209 msgstr[1] "uur" 2209 2210 2210 2211 #: utils/timesince.py:17 django/branches/newforms-admin/django/contrib/auth/__init__.py
r5195 r5475 54 54 request.session[SESSION_KEY] = user.id 55 55 request.session[BACKEND_SESSION_KEY] = user.backend 56 if hasattr(request, 'user'): 57 request.user = user 56 58 57 59 def logout(request): … … 67 69 except KeyError: 68 70 pass 71 if hasattr(request, 'user'): 72 from django.contrib.auth.models import AnonymousUser 73 request.user = AnonymousUser() 69 74 70 75 def get_user(request): django/branches/newforms-admin/django/contrib/sessions/models.py
r4824 r5475 1 import base64, md5, random, sys, datetime 1 import base64, md5, random, sys, datetime, os, time 2 2 import cPickle as pickle 3 3 from django.db import models … … 15 15 "Returns session key that isn't being used." 16 16 # The random module is seeded when this Apache child is created. 17 # Use person_id andSECRET_KEY as added salt.17 # Use SECRET_KEY as added salt. 18 18 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() 20 20 try: 21 21 self.get(session_key=session_key) django/branches/newforms-admin/django/core/management.py
r5440 r5475 4 4 import django 5 5 from django.core.exceptions import ImproperlyConfigured 6 import os, re, shutil, sys, textwrap7 6 from optparse import OptionParser 8 7 from django.utils import termcolors 8 import os, re, shutil, sys, textwrap 9 9 10 10 # For Python 2.3 11 11 if not hasattr(__builtins__, 'set'): 12 12 from sets import Set as set 13 14 # For backwards compatibility: get_version() used to be in this module. 15 get_version = django.get_version 13 16 14 17 MODULE_TEMPLATE = ''' {%% if perms.%(app)s.%(addperm)s or perms.%(app)s.%(changeperm)s %%} … … 93 96 # field as the field to which it points. 94 97 get_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 VERSION99 v = '.'.join([str(i) for i in VERSION[:-1]])100 if VERSION[-1]:101 v += '-' + VERSION[-1]102 return v103 98 104 99 def get_sql_create(app): django/branches/newforms-admin/django/db/backends/postgresql/base.py
r5326 r5475 220 220 output = [] 221 221 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. 222 225 for f in model._meta.fields: 223 226 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;" % \ 225 228 (style.SQL_KEYWORD('SELECT'), 226 229 style.SQL_FIELD(quote_name('%s_%s_seq' % (model._meta.db_table, f.column))), 227 style.SQL_KEYWORD('SELECT'),228 230 style.SQL_FIELD(quote_name(f.column)), 231 style.SQL_FIELD(quote_name(f.column)), 232 style.SQL_KEYWORD('IS NOT'), 229 233 style.SQL_KEYWORD('FROM'), 230 234 style.SQL_TABLE(quote_name(model._meta.db_table)))) 231 235 break # Only one AutoField is allowed per model, so don't bother continuing. 232 236 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;" % \ 234 238 (style.SQL_KEYWORD('SELECT'), 235 239 style.SQL_FIELD(quote_name('%s_id_seq' % f.m2m_db_table())), 236 style.SQL_KEYWORD('SELECT'),237 240 style.SQL_FIELD(quote_name('id')), 241 style.SQL_FIELD(quote_name('id')), 242 style.SQL_KEYWORD('IS NOT'), 238 243 style.SQL_KEYWORD('FROM'), 239 244 style.SQL_TABLE(f.m2m_db_table()))) django/branches/newforms-admin/django/db/backends/postgresql_psycopg2/base.py
r5244 r5475 177 177 output = [] 178 178 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. 179 182 for f in model._meta.fields: 180 183 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;" % \ 182 185 (style.SQL_KEYWORD('SELECT'), 183 186 style.SQL_FIELD(quote_name('%s_%s_seq' % (model._meta.db_table, f.column))), 184 style.SQL_KEYWORD('SELECT'),185 187 style.SQL_FIELD(quote_name(f.column)), 188 style.SQL_FIELD(quote_name(f.column)), 189 style.SQL_KEYWORD('IS NOT'), 186 190 style.SQL_KEYWORD('FROM'), 187 191 style.SQL_TABLE(quote_name(model._meta.db_table)))) 188 192 break # Only one AutoField is allowed per model, so don't bother continuing. 189 193 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;" % \ 191 195 (style.SQL_KEYWORD('SELECT'), 192 196 style.SQL_FIELD(quote_name('%s_id_seq' % f.m2m_db_table())), 193 style.SQL_KEYWORD('SELECT'),194 197 style.SQL_FIELD(quote_name('id')), 198 style.SQL_FIELD(quote_name('id')), 199 style.SQL_KEYWORD('IS NOT'), 195 200 style.SQL_KEYWORD('FROM'), 196 201 style.SQL_TABLE(f.m2m_db_table()))) django/branches/newforms-admin/django/db/backends/util.py
r5326 r5475 92 92 93 93 def typecast_decimal(s): 94 if s is None :94 if s is None or s == '': 95 95 return None 96 96 return decimal.Decimal(s) django/branches/newforms-admin/django/__init__.py
r4940 r5475 1 1 VERSION = (0, 97, 'newforms-admin') 2 3 def 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 21 21 Saves bound Form ``form``'s cleaned_data into model instance ``instance``. 22 22 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``. 26 25 """ 27 26 from django.db import models django/branches/newforms-admin/django/template/defaulttags.py
r5440 r5475 6 6 from django.conf import settings 7 7 import sys 8 import re 9 10 if 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 8 17 9 18 register = Library() … … 62 71 63 72 class ForNode(Node): 64 def __init__(self, loopvar , sequence, reversed, nodelist_loop):65 self.loopvar , self.sequence = loopvar, sequence73 def __init__(self, loopvars, sequence, reversed, nodelist_loop): 74 self.loopvars, self.sequence = loopvars, sequence 66 75 self.reversed = reversed 67 76 self.nodelist_loop = nodelist_loop … … 73 82 reversed = '' 74 83 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) 76 85 77 86 def __iter__(self): … … 103 112 len_values = len(values) 104 113 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 110 116 for i, item in enumerate(values): 111 117 context['forloop'] = { … … 121 127 'parentloop': parentloop, 122 128 } 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 124 134 for node in self.nodelist_loop: 125 135 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() 126 143 context.pop() 127 144 return nodelist.render(context) … … 487 504 parser.delete_first_token() 488 505 return FilterNode(filter_expr, nodelist) 489 filter = register.tag("filter", do_filter)506 do_filter = register.tag("filter", do_filter) 490 507 491 508 #@register.tag … … 531 548 </ul> 532 549 533 You can alsoloop over a list in reverse by using550 You can loop over a list in reverse by using 534 551 ``{% 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 %} 535 558 536 559 The for loop sets a number of variables available within the loop: … … 553 576 """ 554 577 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]) 564 592 nodelist_loop = parser.parse(('endfor',)) 565 593 parser.delete_first_token() 566 return ForNode(loopvar , sequence, reversed, nodelist_loop)594 return ForNode(loopvars, sequence, reversed, nodelist_loop) 567 595 do_for = register.tag("for", do_for) 568 596 django/branches/newforms-admin/django/test/client.py
r5195 r5475 66 66 lines.extend([ 67 67 '--' + 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), 72 69 'Content-Type: application/octet-stream', 73 70 '', django/branches/newforms-admin/django/views/debug.py
r5195 r5475 3 3 from django.utils.html import escape 4 4 from django.http import HttpResponseServerError, HttpResponseNotFound 5 import os, re 5 import os, re, sys 6 6 7 7 HIDDEN_SETTINGS = re.compile('SECRET|PASSWORD|PROFANITIES_LIST') … … 132 132 'request_protocol': request.is_secure() and "https" or "http", 133 133 'settings': get_safe_settings(), 134 'sys_executable' : sys.executable, 135 'sys_version_info' : '%d.%d.%d' % sys.version_info[0:3], 134 136 'template_info': template_info, 135 137 'template_does_not_exist': template_does_not_exist, … … 335 337 <td>{{ lastframe.filename|escape }} in {{ lastframe.function|escape }}, line {{ lastframe.lineno }}</td> 336 338 </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> 337 347 </table> 338 348 </div> django/branches/newforms-admin/docs/authentication.txt
r5195 r5475 731 731 ------------------- 732 732 733 Three basic permissions -- add, c reate and delete -- are automatically created733 Three basic permissions -- add, change and delete -- are automatically created 734 734 for each Django model that has a ``class Admin`` set. Behind the scenes, these 735 735 permissions are added to the ``auth_permission`` database table when you run django/branches/newforms-admin/docs/db-api.txt
r5440 r5475 389 389 underlying SQL statement, and the whole thing is enclosed in a ``NOT()``. 390 390 391 This example excludes all entries whose ``pub_date`` is the current date/time391 This example excludes all entries whose ``pub_date`` is later than 2005-1-3 392 392 AND whose ``headline`` is "Hello":: 393 393 … … 399 399 WHERE NOT (pub_date > '2005-1-3' AND headline = 'Hello') 400 400 401 This example excludes all entries whose ``pub_date`` is the current date/time402 OR whose ``headline`` is"Hello"::401 This example excludes all entries whose ``pub_date`` is later than 2005-1-3 402 AND whose headline is NOT "Hello":: 403 403 404 404 Entry.objects.exclude(pub_date__gt=datetime.date(2005, 1, 3)).exclude(headline='Hello') … … 599 599 c = p.hometown # Doesn't hit the database. 600 600 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. 602 602 p = b.author # Hits the database. 603 603 c = p.hometown # Hits the database. django/branches/newforms-admin/docs/model-api.txt
r5440 r5475 447 447 448 448 The admin represents this as an ``<input type="text">`` (a single-line input). 449 450 ``URLField`` takes an optional argument, ``maxlength``, the maximum length (in 451 characters) of the field. The maxlength is enforced at the database level and 452 in Django's validation. If you don't specify ``maxlength``, a default of 200 453 is used. 449 454 450 455 ``USStateField`` django/branches/newforms-admin/docs/newforms.txt
r5440 r5475 300 300 empty string, because ``nick_name`` is ``CharField``, and ``CharField``\s treat 301 301 empty 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. 302 is -- e.g., for ``DateField``, it's ``None`` instead of the empty string. For 303 full details on each field's behavior in this case, see the "Empty value" note 304 for each field in the "Built-in ``Field`` classes" section below. 305 306 You can write code to perform validation for particular form fields (based on 307 their name) or for the form as a whole (considering combinations of various 308 fields). More information about this is in the `Custom form and field 309 validation`_ section, below. 303 310 304 311 Behavior of unbound forms 305 312 ~~~~~~~~~~~~~~~~~~~~~~~~~ 306 313 307 It's meaningless to request "clean " data in a form with no data, but, for the314 It's meaningless to request "cleaned" data in a form with no data, but, for the 308 315 record, here's what happens with unbound forms:: 309 316 … … 607 614 608 615 Let'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:: 616 view and template. 617 618 Simple view example 619 ~~~~~~~~~~~~~~~~~~~ 620 621 This example view displays the contact form by default and validates/processes 622 it if accessed via a POST request:: 611 623 612 624 def contact(request): … … 620 632 return render_to_response('contact.html', {'form': form}) 621 633 622 Simple template output623 ~~~~~~~~~~~~~~~~~~~~~~ 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.634 Simple template example 635 ~~~~~~~~~~~~~~~~~~~~~~~ 636 637 The template in the above view example, ``contact.html``, is responsible for 638 displaying the form as HTML. To do this, we can use the techniques outlined in 639 the "Outputting forms as HTML" section above. 628 640 629 641 The simplest way to display a form's HTML is to use the variable on its own, … … 678 690 This iteration technique is useful if you want to apply the same HTML 679 691 formatting 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 listedin the order in which692 ahead of time. Note that the fields will be iterated over in the order in which 681 693 they're defined in the ``Form`` class. 682 694 … … 702 714 ----------------- 703 715 704 If you subclass a custom ``Form`` class, the resulting ``Form`` class will 716 If you have multiple ``Form`` classes that share fields, you can use 717 subclassing to remove redundancy. 718 719 When you subclass a custom ``Form`` class, the resulting subclass will 705 720 include all fields of the parent class(es), followed by the fields you define 706 721 in the subclass. … … 1202 1217 mentioned above (``required``, ``label``, ``initial``, ``widget``, 1203 1218 ``help_text``). 1219 1220 Custom form and field validation 1221 --------------------------------- 1222 1223 Form validation happens when the data is cleaned. If you want to customise 1224 this process, there are various places you can change, each one serving a 1225 different purpose. Thee types of cleaning methods are run during form 1226 processing. These are normally executed when you call the ``is_valid()`` 1227 method on a form. There are other things that can trigger cleaning and 1228 validation (accessing the ``errors`` attribute or calling ``full_clean()`` 1229 directly), but normally they won't be needed. 1230 1231 In general, any cleaning method can raise ``ValidationError`` if there is a 1232 problem with the data it is processing, passing the relevant error message to 1233 the ``ValidationError`` constructor. If no ``ValidationError`` is raised, the 1234 method should return the cleaned (normalised) data as a Python object. 1235 1236 If you detect multiple errors during a cleaning method and wish to signal all 1237 of them to the form submitter, it is possible to pass a list of errors to the 1238 ``ValidationError`` constructor. 1239 1240 The 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 1278 These methods are run in the order given above, one field at a time. That is, 1279 for each field in the form (in the order they are declared in the form 1280 definition), the ``Field.clean()`` method (or it's override) is run, then 1281 ``clean_<fieldname>()``. Finally, once those two methods are run for every 1282 field, the ``Form.clean()`` method, or it's override, is executed. 1283 1284 As mentioned above, any of these methods can raise a ``ValidationError``. For 1285 any field, if the ``Field.clean()`` method raises a ``ValidationError``, any 1286 field-specific cleaning method is not called. However, the cleaning methods 1287 for all remaining fields are still executed. 1288 1289 The ``clean()`` method for the ``Form`` class or subclass is always run. If 1290 that method raises a ``ValidationError``, ``cleaned_data`` will be an empty 1291 dictionary. 1292 1293 The previous paragraph means that if you are overriding ``Form.clean()``, you 1294 should iterate through ``self.cleaned_data.items()``, possibly considering the 1295 ``_errors`` dictionary attribute on the form as well. In this way, you will 1296 already know which fields have passed their individual validation requirements. 1297 1298 A simple example 1299 ~~~~~~~~~~~~~~~~ 1300 1301 Here's a simple example of a custom field that validates its input is a string 1302 containing comma-separated e-mail addresses, with at least one address. We'll 1303 keep 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 1318 Let's alter the ongoing ``ContactForm`` example to demonstrate how you'd use 1319 this in a form. Simply use ``MultiEmailField`` instead of ``forms.EmailField``, 1320 like 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() 1204 1327 1205 1328 Generating forms for models django/branches/newforms-admin/docs/templates.txt
r5440 r5475 448 448 449 449 Loop over each item in an array. For example, to display a list of athletes 450 given ``athlete_list``::450 provided in ``athlete_list``:: 451 451 452 452 <ul> … … 456 456 </ul> 457 457 458 You can also loop over a list in reverse by using ``{% for obj in list reversed %}``. 458 You can loop over a list in reverse by using ``{% for obj in list reversed %}``. 459 460 **New in Django development version** 461 If you need to loop over a list of lists, you can unpack the values 462 in eachs sub-list into a set of known names. For example, if your context contains 463 a list of (x,y) coordinates called ``points``, you could use the following 464 to output the list of points:: 465 466 {% for x, y in points %} 467 There is a point at {{ x }},{{ y }} 468 {% endfor %} 469 470 This can also be useful if you need to access the items in a dictionary. 471 For example, if your context contained a dictionary ``data``, the following 472 would display the keys and values of the dictionary:: 473 474 {% for key, value in data.items %} 475 {{ key }}: {{ value }} 476 {% endfor %} 459 477 460 478 The for loop sets a number of variables available within the loop: django/branches/newforms-admin/scripts/rpm-install.sh
r4940 r5475 22 22 sed -e "/\.py[co]$/d" -e "s/\.py$/.py*/" DIRS FILES >INSTALLED_FILES 23 23 24 mkdir -p ${RPM_BUILD_ROOT}/%{_mandir}/man1/ 25 cp docs/man/* ${RPM_BUILD_ROOT}/%{_mandir}/man1/ 26 cat << EOF >> INSTALLED_FILES 27 %doc %{_mandir}/man1/*" 28 EOF django/branches/newforms-admin/tests/regressiontests/serializers_regress/tests.py
r5440 r5475 286 286 obj = ComplexModel(field1='first',field2='second',field3='third') 287 287 obj.save() 288 288 289 289 # Serialize then deserialize the test database 290 290 serialized_data = serializers.serialize(format, [obj], indent=2, fields=('field1','field3')) 291 291 result = serializers.deserialize(format, serialized_data).next() 292 292 293 293 # Check that the deserialized object contains data in only the serialized fields. 294 294 self.assertEqual(result.object.field1, 'first') … … 302 302 obj = ComplexModel(field1='first',field2='second',field3='third') 303 303 obj.save() 304 304 305 305 # Serialize the test database to a stream 306 stream = StringIO() 306 stream = StringIO() 307 307 serializers.serialize(format, [obj], indent=2, stream=stream) 308 308 309 309 # Serialize normally for a comparison 310 310 string_data = serializers.serialize(format, [obj], indent=2) 311 311 312 312 # Check that the two are the same 313 self.assertEqual(string_data, stream. buffer())313 self.assertEqual(string_data, stream.getvalue()) 314 314 stream.close() 315 315 316 316 for format in serializers.get_serializer_formats(): 317 317 setattr(SerializerTests, 'test_'+format+'_serializer', curry(serializerTest, format)) 318 318 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 290 290 'for-tag-vars03': ("{% for val in values %}{{ forloop.revcounter }}{% endfor %}", {"values": [6, 6, 6]}, "321"), 291 291 '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/")), 292 306 293 307 ### IF TAG ################################################################
