mirror of
				https://github.com/django/django.git
				synced 2025-10-31 01:25:32 +00:00 
			
		
		
		
	boulder-oracle-sprint: Merged to [5234]
git-svn-id: http://code.djangoproject.com/svn/django/branches/boulder-oracle-sprint@5235 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
		
							
								
								
									
										2
									
								
								AUTHORS
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								AUTHORS
									
									
									
									
									
								
							| @@ -78,6 +78,7 @@ answer newbie questions, and generally made Django that much better: | |||||||
|     flavio.curella@gmail.com |     flavio.curella@gmail.com | ||||||
|     Jure Cuhalev <gandalf@owca.info> |     Jure Cuhalev <gandalf@owca.info> | ||||||
|     dackze+django@gmail.com |     dackze+django@gmail.com | ||||||
|  |     David Danier <goliath.mailinglist@gmx.de> | ||||||
|     Dirk Datzert <dummy@habmalnefrage.de> |     Dirk Datzert <dummy@habmalnefrage.de> | ||||||
|     Jonathan Daugherty (cygnus) <http://www.cprogrammer.org/> |     Jonathan Daugherty (cygnus) <http://www.cprogrammer.org/> | ||||||
|     dave@thebarproject.com |     dave@thebarproject.com | ||||||
| @@ -85,6 +86,7 @@ answer newbie questions, and generally made Django that much better: | |||||||
|     Alex Dedul |     Alex Dedul | ||||||
|     deric@monowerks.com |     deric@monowerks.com | ||||||
|     Max Derkachev <mderk@yandex.ru> |     Max Derkachev <mderk@yandex.ru> | ||||||
|  |     Jordan Dimov <s3x3y1@gmail.com> | ||||||
|     dne@mayonnaise.net |     dne@mayonnaise.net | ||||||
|     Maximillian Dornseif <md@hudora.de> |     Maximillian Dornseif <md@hudora.de> | ||||||
|     Jeremy Dunck <http://dunck.us/> |     Jeremy Dunck <http://dunck.us/> | ||||||
|   | |||||||
| @@ -38,6 +38,7 @@ LANGUAGE_CODE = 'en-us' | |||||||
| LANGUAGES = ( | LANGUAGES = ( | ||||||
|     ('ar', gettext_noop('Arabic')), |     ('ar', gettext_noop('Arabic')), | ||||||
|     ('bn', gettext_noop('Bengali')), |     ('bn', gettext_noop('Bengali')), | ||||||
|  |     ('bg', gettext_noop('Bulgarian')), | ||||||
|     ('ca', gettext_noop('Catalan')), |     ('ca', gettext_noop('Catalan')), | ||||||
|     ('cs', gettext_noop('Czech')), |     ('cs', gettext_noop('Czech')), | ||||||
|     ('cy', gettext_noop('Welsh')), |     ('cy', gettext_noop('Welsh')), | ||||||
|   | |||||||
							
								
								
									
										
											BIN
										
									
								
								django/conf/locale/bg/LC_MESSAGES/django.mo
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								django/conf/locale/bg/LC_MESSAGES/django.mo
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										2670
									
								
								django/conf/locale/bg/LC_MESSAGES/django.po
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2670
									
								
								django/conf/locale/bg/LC_MESSAGES/django.po
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								django/conf/locale/bg/LC_MESSAGES/djangojs.mo
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								django/conf/locale/bg/LC_MESSAGES/djangojs.mo
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										106
									
								
								django/conf/locale/bg/LC_MESSAGES/djangojs.po
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										106
									
								
								django/conf/locale/bg/LC_MESSAGES/djangojs.po
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,106 @@ | |||||||
|  | # translation of djangojs.po to Bulgarian | ||||||
|  | # | ||||||
|  | msgid "" | ||||||
|  | msgstr "" | ||||||
|  | "Project-Id-Version: djangojs\n" | ||||||
|  | "Report-Msgid-Bugs-To: \n" | ||||||
|  | "POT-Creation-Date: 2005-12-09 11:51+0100\n" | ||||||
|  | "PO-Revision-Date: 2007-05-12 17:51+0300\n" | ||||||
|  | "Last-Translator: Jordan Dimov <s3x3y1@gmail.com>\n" | ||||||
|  | "Language-Team: Bulgarian\n" | ||||||
|  | "MIME-Version: 1.0\n" | ||||||
|  | "Content-Type: text/plain; charset=UTF-8\n" | ||||||
|  | "Content-Transfer-Encoding: 8bit\n" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/SelectFilter2.js:33 | ||||||
|  | #, perl-format | ||||||
|  | msgid "Available %s" | ||||||
|  | msgstr "Налични %s" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/SelectFilter2.js:41 | ||||||
|  | msgid "Choose all" | ||||||
|  | msgstr "Избери всички" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/SelectFilter2.js:46 | ||||||
|  | msgid "Add" | ||||||
|  | msgstr "Добави" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/SelectFilter2.js:48 | ||||||
|  | msgid "Remove" | ||||||
|  | msgstr "Премахни" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/SelectFilter2.js:53 | ||||||
|  | #, perl-format | ||||||
|  | msgid "Chosen %s" | ||||||
|  | msgstr "Избрахме %s" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/SelectFilter2.js:54 | ||||||
|  | msgid "Select your choice(s) and click " | ||||||
|  | msgstr "Направете своя избор и щракнете " | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/SelectFilter2.js:59 | ||||||
|  | msgid "Clear all" | ||||||
|  | msgstr "Изчисти всички" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/dateparse.js:26 | ||||||
|  | #: contrib/admin/media/js/calendar.js:24 | ||||||
|  | msgid "" | ||||||
|  | "January February March April May June July August September October November " | ||||||
|  | "December" | ||||||
|  | msgstr "Януари Февруари Март Април Май Юни Юли Август Септември Октомври Ноември Декември" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/dateparse.js:27 | ||||||
|  | msgid "Sunday Monday Tuesday Wednesday Thursday Friday Saturday" | ||||||
|  | msgstr "Неделя Понеделник Вторник Сряда Четвъртък Петък Събота" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/calendar.js:25 | ||||||
|  | msgid "S M T W T F S" | ||||||
|  | msgstr "Н П В С Ч П С" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/admin/DateTimeShortcuts.js:45 | ||||||
|  | #: contrib/admin/media/js/admin/DateTimeShortcuts.js:80 | ||||||
|  | msgid "Now" | ||||||
|  | msgstr "Сега" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/admin/DateTimeShortcuts.js:48 | ||||||
|  | msgid "Clock" | ||||||
|  | msgstr "Часовник" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/admin/DateTimeShortcuts.js:77 | ||||||
|  | msgid "Choose a time" | ||||||
|  | msgstr "Избери време" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/admin/DateTimeShortcuts.js:81 | ||||||
|  | msgid "Midnight" | ||||||
|  | msgstr "Полунощ" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/admin/DateTimeShortcuts.js:82 | ||||||
|  | msgid "6 a.m." | ||||||
|  | msgstr "6 a.m." | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/admin/DateTimeShortcuts.js:83 | ||||||
|  | msgid "Noon" | ||||||
|  | msgstr "По обяд" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/admin/DateTimeShortcuts.js:87 | ||||||
|  | #: contrib/admin/media/js/admin/DateTimeShortcuts.js:168 | ||||||
|  | msgid "Cancel" | ||||||
|  | msgstr "Отказ" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/admin/DateTimeShortcuts.js:111 | ||||||
|  | #: contrib/admin/media/js/admin/DateTimeShortcuts.js:162 | ||||||
|  | msgid "Today" | ||||||
|  | msgstr "Днес" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/admin/DateTimeShortcuts.js:114 | ||||||
|  | msgid "Calendar" | ||||||
|  | msgstr "Календар" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/admin/DateTimeShortcuts.js:160 | ||||||
|  | msgid "Yesterday" | ||||||
|  | msgstr "Вчера" | ||||||
|  |  | ||||||
|  | #: contrib/admin/media/js/admin/DateTimeShortcuts.js:164 | ||||||
|  | msgid "Tomorrow" | ||||||
|  | msgstr "Утре" | ||||||
|  |  | ||||||
| @@ -41,7 +41,7 @@ class ISIdNumberField(RegexField): | |||||||
|         method is modulo 11. |         method is modulo 11. | ||||||
|         """ |         """ | ||||||
|         check = [3, 2, 7, 6, 5, 4, 3, 2, 1, 0] |         check = [3, 2, 7, 6, 5, 4, 3, 2, 1, 0] | ||||||
|         return sum(int(value[i]) * check[i] for i in range(10)) % 11 == 0 |         return sum([int(value[i]) * check[i] for i in range(10)]) % 11 == 0 | ||||||
|  |  | ||||||
|     def _format(self, value): |     def _format(self, value): | ||||||
|         """ |         """ | ||||||
|   | |||||||
| @@ -1463,21 +1463,25 @@ def load_data(fixture_labels, verbosity=1): | |||||||
|                     if verbosity > 1: |                     if verbosity > 1: | ||||||
|                         print "No %s fixture '%s' in %s." % \ |                         print "No %s fixture '%s' in %s." % \ | ||||||
|                             (format, fixture_name, humanize(fixture_dir)) |                             (format, fixture_name, humanize(fixture_dir)) | ||||||
|     if count[0] == 0: |                              | ||||||
|         if verbosity > 0: |     if count[0] > 0: | ||||||
|             print "No fixtures found." |  | ||||||
|     else: |  | ||||||
|         if verbosity > 0: |  | ||||||
|             print "Installed %d object(s) from %d fixture(s)" % tuple(count) |  | ||||||
|         sequence_sql = backend.get_sql_sequence_reset(style, models) |         sequence_sql = backend.get_sql_sequence_reset(style, models) | ||||||
|         if sequence_sql: |         if sequence_sql: | ||||||
|             if verbosity > 1: |             if verbosity > 1: | ||||||
|                 print "Resetting sequences" |                 print "Resetting sequences" | ||||||
|             for line in sequence_sql: |             for line in sequence_sql: | ||||||
|                 cursor.execute(line) |                 cursor.execute(line) | ||||||
|  |              | ||||||
|     transaction.commit() |     transaction.commit() | ||||||
|     transaction.leave_transaction_management() |     transaction.leave_transaction_management() | ||||||
|      |      | ||||||
|  |     if count[0] == 0: | ||||||
|  |         if verbosity > 0: | ||||||
|  |             print "No fixtures found." | ||||||
|  |     else: | ||||||
|  |         if verbosity > 0: | ||||||
|  |             print "Installed %d object(s) from %d fixture(s)" % tuple(count) | ||||||
|  |  | ||||||
| load_data.help_doc = 'Installs the named fixture(s) in the database' | load_data.help_doc = 'Installs the named fixture(s) in the database' | ||||||
| load_data.args = "[--verbosity] fixture, fixture, ..." | load_data.args = "[--verbosity] fixture, fixture, ..." | ||||||
|  |  | ||||||
|   | |||||||
| @@ -37,7 +37,7 @@ class Serializer(base.Serializer): | |||||||
|     def handle_fk_field(self, obj, field): |     def handle_fk_field(self, obj, field): | ||||||
|         related = getattr(obj, field.name) |         related = getattr(obj, field.name) | ||||||
|         if related is not None: |         if related is not None: | ||||||
|             related = related._get_pk_val() |             related = getattr(related, field.rel.field_name) | ||||||
|         self._current[field.name] = related |         self._current[field.name] = related | ||||||
|      |      | ||||||
|     def handle_m2m_field(self, obj, field): |     def handle_m2m_field(self, obj, field): | ||||||
| @@ -80,7 +80,10 @@ def Deserializer(object_list, **options): | |||||||
|                  |                  | ||||||
|             # Handle FK fields |             # Handle FK fields | ||||||
|             elif field.rel and isinstance(field.rel, models.ManyToOneRel): |             elif field.rel and isinstance(field.rel, models.ManyToOneRel): | ||||||
|                 data[field.attname] = field.rel.to._meta.pk.to_python(field_value) |                 if field_value: | ||||||
|  |                     data[field.attname] = field.rel.to._meta.get_field(field.rel.field_name).to_python(field_value) | ||||||
|  |                 else: | ||||||
|  |                     data[field.attname] = None | ||||||
|                      |                      | ||||||
|             # Handle all other fields |             # Handle all other fields | ||||||
|             else: |             else: | ||||||
|   | |||||||
| @@ -82,7 +82,7 @@ class Serializer(base.Serializer): | |||||||
|         self._start_relational_field(field) |         self._start_relational_field(field) | ||||||
|         related = getattr(obj, field.name) |         related = getattr(obj, field.name) | ||||||
|         if related is not None: |         if related is not None: | ||||||
|             self.xml.characters(str(related._get_pk_val())) |             self.xml.characters(str(getattr(related, field.rel.field_name))) | ||||||
|         else: |         else: | ||||||
|             self.xml.addQuickElement("None") |             self.xml.addQuickElement("None") | ||||||
|         self.xml.endElement("field") |         self.xml.endElement("field") | ||||||
| @@ -181,7 +181,7 @@ class Deserializer(base.Deserializer): | |||||||
|         if len(node.childNodes) == 1 and node.childNodes[0].nodeName == 'None': |         if len(node.childNodes) == 1 and node.childNodes[0].nodeName == 'None': | ||||||
|             return None |             return None | ||||||
|         else: |         else: | ||||||
|             return field.rel.to._meta.pk.to_python( |             return field.rel.to._meta.get_field(field.rel.field_name).to_python( | ||||||
|                        getInnerText(node).strip().encode(self.encoding)) |                        getInnerText(node).strip().encode(self.encoding)) | ||||||
|          |          | ||||||
|     def _handle_m2m_field_node(self, node, field): |     def _handle_m2m_field_node(self, node, field): | ||||||
|   | |||||||
| @@ -242,7 +242,7 @@ def get_sql_sequence_reset(style, model_list): | |||||||
|             if isinstance(f, models.AutoField): |             if isinstance(f, models.AutoField): | ||||||
|                 output.append("%s setval('%s', (%s max(%s) %s %s));" % \ |                 output.append("%s setval('%s', (%s max(%s) %s %s));" % \ | ||||||
|                     (style.SQL_KEYWORD('SELECT'), |                     (style.SQL_KEYWORD('SELECT'), | ||||||
|                     style.SQL_FIELD('%s_%s_seq' % (model._meta.db_table, f.column)), |                     style.SQL_FIELD(quote_name('%s_%s_seq' % (model._meta.db_table, f.column))), | ||||||
|                     style.SQL_KEYWORD('SELECT'), |                     style.SQL_KEYWORD('SELECT'), | ||||||
|                     style.SQL_FIELD(quote_name(f.column)), |                     style.SQL_FIELD(quote_name(f.column)), | ||||||
|                     style.SQL_KEYWORD('FROM'), |                     style.SQL_KEYWORD('FROM'), | ||||||
| @@ -251,7 +251,7 @@ def get_sql_sequence_reset(style, model_list): | |||||||
|         for f in model._meta.many_to_many: |         for f in model._meta.many_to_many: | ||||||
|             output.append("%s setval('%s', (%s max(%s) %s %s));" % \ |             output.append("%s setval('%s', (%s max(%s) %s %s));" % \ | ||||||
|                 (style.SQL_KEYWORD('SELECT'), |                 (style.SQL_KEYWORD('SELECT'), | ||||||
|                 style.SQL_FIELD('%s_id_seq' % f.m2m_db_table()), |                 style.SQL_FIELD(quote_name('%s_id_seq' % f.m2m_db_table())), | ||||||
|                 style.SQL_KEYWORD('SELECT'), |                 style.SQL_KEYWORD('SELECT'), | ||||||
|                 style.SQL_FIELD(quote_name('id')), |                 style.SQL_FIELD(quote_name('id')), | ||||||
|                 style.SQL_KEYWORD('FROM'), |                 style.SQL_KEYWORD('FROM'), | ||||||
|   | |||||||
| @@ -199,7 +199,7 @@ def get_sql_sequence_reset(style, model_list): | |||||||
|             if isinstance(f, models.AutoField): |             if isinstance(f, models.AutoField): | ||||||
|                 output.append("%s setval('%s', (%s max(%s) %s %s));" % \ |                 output.append("%s setval('%s', (%s max(%s) %s %s));" % \ | ||||||
|                     (style.SQL_KEYWORD('SELECT'), |                     (style.SQL_KEYWORD('SELECT'), | ||||||
|                     style.SQL_FIELD('%s_%s_seq' % (model._meta.db_table, f.column)), |                     style.SQL_FIELD(quote_name('%s_%s_seq' % (model._meta.db_table, f.column))), | ||||||
|                     style.SQL_KEYWORD('SELECT'), |                     style.SQL_KEYWORD('SELECT'), | ||||||
|                     style.SQL_FIELD(quote_name(f.column)), |                     style.SQL_FIELD(quote_name(f.column)), | ||||||
|                     style.SQL_KEYWORD('FROM'), |                     style.SQL_KEYWORD('FROM'), | ||||||
| @@ -208,7 +208,7 @@ def get_sql_sequence_reset(style, model_list): | |||||||
|         for f in model._meta.many_to_many: |         for f in model._meta.many_to_many: | ||||||
|             output.append("%s setval('%s', (%s max(%s) %s %s));" % \ |             output.append("%s setval('%s', (%s max(%s) %s %s));" % \ | ||||||
|                 (style.SQL_KEYWORD('SELECT'), |                 (style.SQL_KEYWORD('SELECT'), | ||||||
|                 style.SQL_FIELD('%s_id_seq' % f.m2m_db_table()), |                 style.SQL_FIELD(quote_name('%s_id_seq' % f.m2m_db_table())), | ||||||
|                 style.SQL_KEYWORD('SELECT'), |                 style.SQL_KEYWORD('SELECT'), | ||||||
|                 style.SQL_FIELD(quote_name('id')), |                 style.SQL_FIELD(quote_name('id')), | ||||||
|                 style.SQL_KEYWORD('FROM'), |                 style.SQL_KEYWORD('FROM'), | ||||||
|   | |||||||
| @@ -184,8 +184,8 @@ class BaseForm(StrAndUnicode): | |||||||
|             try: |             try: | ||||||
|                 value = field.clean(value) |                 value = field.clean(value) | ||||||
|                 self.clean_data[name] = value |                 self.clean_data[name] = value | ||||||
|                 if hasattr(self, 'clean_%s' % name): |                 if hasattr(self, 'do_clean_%s' % name): | ||||||
|                     value = getattr(self, 'clean_%s' % name)() |                     value = getattr(self, 'do_clean_%s' % name)() | ||||||
|                 self.clean_data[name] = value |                 self.clean_data[name] = value | ||||||
|             except ValidationError, e: |             except ValidationError, e: | ||||||
|                 errors[name] = e.messages |                 errors[name] = e.messages | ||||||
| @@ -255,6 +255,8 @@ class BoundField(StrAndUnicode): | |||||||
|             attrs['id'] = auto_id |             attrs['id'] = auto_id | ||||||
|         if not self.form.is_bound: |         if not self.form.is_bound: | ||||||
|             data = self.form.initial.get(self.name, self.field.initial) |             data = self.form.initial.get(self.name, self.field.initial) | ||||||
|  |             if callable(data): | ||||||
|  |                 data = data() | ||||||
|         else: |         else: | ||||||
|             data = self.data |             data = self.data | ||||||
|         return widget.render(self.html_name, data, attrs=attrs) |         return widget.render(self.html_name, data, attrs=attrs) | ||||||
|   | |||||||
| @@ -12,17 +12,7 @@ from widgets import Select, SelectMultiple, MultipleHiddenInput | |||||||
| __all__ = ('save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields', | __all__ = ('save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields', | ||||||
|            'ModelChoiceField', 'ModelMultipleChoiceField') |            'ModelChoiceField', 'ModelMultipleChoiceField') | ||||||
|  |  | ||||||
| def model_save(self, commit=True): | def save_instance(form, instance, fields=None, fail_message='saved', commit=True): | ||||||
|     """ |  | ||||||
|     Creates and returns model instance according to self.clean_data. |  | ||||||
|  |  | ||||||
|     This method is created for any form_for_model Form. |  | ||||||
|     """ |  | ||||||
|     if self.errors: |  | ||||||
|         raise ValueError("The %s could not be created because the data didn't validate." % self._model._meta.object_name) |  | ||||||
|     return save_instance(self, self._model(), commit) |  | ||||||
|  |  | ||||||
| def save_instance(form, instance, commit=True): |  | ||||||
|     """ |     """ | ||||||
|     Saves bound Form ``form``'s clean_data into model instance ``instance``. |     Saves bound Form ``form``'s clean_data into model instance ``instance``. | ||||||
|  |  | ||||||
| @@ -33,15 +23,19 @@ def save_instance(form, instance, commit=True): | |||||||
|     from django.db import models |     from django.db import models | ||||||
|     opts = instance.__class__._meta |     opts = instance.__class__._meta | ||||||
|     if form.errors: |     if form.errors: | ||||||
|         raise ValueError("The %s could not be changed because the data didn't validate." % opts.object_name) |         raise ValueError("The %s could not be %s because the data didn't validate." % (opts.object_name, fail_message)) | ||||||
|     clean_data = form.clean_data |     clean_data = form.clean_data | ||||||
|     for f in opts.fields: |     for f in opts.fields: | ||||||
|         if not f.editable or isinstance(f, models.AutoField) or not f.name in clean_data: |         if not f.editable or isinstance(f, models.AutoField) or not f.name in clean_data: | ||||||
|             continue |             continue | ||||||
|  |         if fields and f.name not in fields: | ||||||
|  |             continue | ||||||
|         setattr(instance, f.name, clean_data[f.name]) |         setattr(instance, f.name, clean_data[f.name]) | ||||||
|     if commit: |     if commit: | ||||||
|         instance.save() |         instance.save() | ||||||
|         for f in opts.many_to_many: |         for f in opts.many_to_many: | ||||||
|  |             if fields and f.name not in fields: | ||||||
|  |                 continue | ||||||
|             if f.name in clean_data: |             if f.name in clean_data: | ||||||
|                 setattr(instance, f.attname, clean_data[f.name]) |                 setattr(instance, f.attname, clean_data[f.name]) | ||||||
|     # GOTCHA: If many-to-many data is given and commit=False, the many-to-many |     # GOTCHA: If many-to-many data is given and commit=False, the many-to-many | ||||||
| @@ -50,13 +44,19 @@ def save_instance(form, instance, commit=True): | |||||||
|     # exception in that case. |     # exception in that case. | ||||||
|     return instance |     return instance | ||||||
|  |  | ||||||
| def make_instance_save(instance): | def make_model_save(model, fields, fail_message): | ||||||
|     "Returns the save() method for a form_for_instance Form." |     "Returns the save() method for a Form." | ||||||
|     def save(self, commit=True): |     def save(self, commit=True): | ||||||
|         return save_instance(self, instance, commit) |         return save_instance(self, model(), fields, fail_message, commit) | ||||||
|     return save |     return save | ||||||
|      |      | ||||||
| def form_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfield()): | def make_instance_save(instance, fields, fail_message): | ||||||
|  |     "Returns the save() method for a Form." | ||||||
|  |     def save(self, commit=True): | ||||||
|  |         return save_instance(self, instance, fields, fail_message, commit) | ||||||
|  |     return save | ||||||
|  |  | ||||||
|  | def form_for_model(model, form=BaseForm, fields=None, formfield_callback=lambda f: f.formfield()): | ||||||
|     """ |     """ | ||||||
|     Returns a Form class for the given Django model class. |     Returns a Form class for the given Django model class. | ||||||
|  |  | ||||||
| @@ -71,13 +71,16 @@ def form_for_model(model, form=BaseForm, formfield_callback=lambda f: f.formfiel | |||||||
|     for f in opts.fields + opts.many_to_many: |     for f in opts.fields + opts.many_to_many: | ||||||
|         if not f.editable: |         if not f.editable: | ||||||
|             continue |             continue | ||||||
|  |         if fields and not f.name in fields: | ||||||
|  |             continue | ||||||
|         formfield = formfield_callback(f) |         formfield = formfield_callback(f) | ||||||
|         if formfield: |         if formfield: | ||||||
|             field_list.append((f.name, formfield)) |             field_list.append((f.name, formfield)) | ||||||
|     fields = SortedDictFromList(field_list) |     base_fields = SortedDictFromList(field_list) | ||||||
|     return type(opts.object_name + 'Form', (form,), {'base_fields': fields, '_model': model, 'save': model_save}) |     return type(opts.object_name + 'Form', (form,),  | ||||||
|  |         {'base_fields': base_fields, '_model': model, 'save': make_model_save(model, fields, 'created')}) | ||||||
|  |  | ||||||
| def form_for_instance(instance, form=BaseForm, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)): | def form_for_instance(instance, form=BaseForm, fields=None, formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)): | ||||||
|     """ |     """ | ||||||
|     Returns a Form class for the given Django model instance. |     Returns a Form class for the given Django model instance. | ||||||
|  |  | ||||||
| @@ -94,13 +97,15 @@ def form_for_instance(instance, form=BaseForm, formfield_callback=lambda f, **kw | |||||||
|     for f in opts.fields + opts.many_to_many: |     for f in opts.fields + opts.many_to_many: | ||||||
|         if not f.editable: |         if not f.editable: | ||||||
|             continue |             continue | ||||||
|  |         if fields and not f.name in fields: | ||||||
|  |             continue | ||||||
|         current_value = f.value_from_object(instance) |         current_value = f.value_from_object(instance) | ||||||
|         formfield = formfield_callback(f, initial=current_value) |         formfield = formfield_callback(f, initial=current_value) | ||||||
|         if formfield: |         if formfield: | ||||||
|             field_list.append((f.name, formfield)) |             field_list.append((f.name, formfield)) | ||||||
|     fields = SortedDictFromList(field_list) |     base_fields = SortedDictFromList(field_list) | ||||||
|     return type(opts.object_name + 'InstanceForm', (form,), |     return type(opts.object_name + 'InstanceForm', (form,), | ||||||
|         {'base_fields': fields, '_model': model, 'save': make_instance_save(instance)}) |         {'base_fields': base_fields, '_model': model, 'save': make_instance_save(instance, fields, 'changed')}) | ||||||
|  |  | ||||||
| def form_for_fields(field_list): | def form_for_fields(field_list): | ||||||
|     "Returns a Form class for the given list of Django database field instances." |     "Returns a Form class for the given list of Django database field instances." | ||||||
|   | |||||||
| @@ -46,15 +46,15 @@ class TestCase(unittest.TestCase): | |||||||
|             management.load_data(self.fixtures, verbosity=0) |             management.load_data(self.fixtures, verbosity=0) | ||||||
|         mail.outbox = [] |         mail.outbox = [] | ||||||
|  |  | ||||||
|     def run(self, result=None): |     def __call__(self, result=None): | ||||||
|         """Wrapper around default run method to perform common Django test set up. |         """ | ||||||
|         This means that user-defined Test Cases aren't required to include a call  |         Wrapper around default __call__ method to perform common Django test | ||||||
|         to super().setUp(). |         set up. This means that user-defined Test Cases aren't required to | ||||||
|          |         include a call to super().setUp(). | ||||||
|         """ |         """ | ||||||
|         self.client = Client() |         self.client = Client() | ||||||
|         self._pre_setup() |         self._pre_setup() | ||||||
|         super(TestCase, self).run(result) |         super(TestCase, self).__call__(result) | ||||||
|  |  | ||||||
|     def assertRedirects(self, response, expected_path, status_code=302, target_status_code=200): |     def assertRedirects(self, response, expected_path, status_code=302, target_status_code=200): | ||||||
|         """Assert that a response redirected to a specific URL, and that the |         """Assert that a response redirected to a specific URL, and that the | ||||||
| @@ -62,7 +62,7 @@ class TestCase(unittest.TestCase): | |||||||
|          |          | ||||||
|         """ |         """ | ||||||
|         self.assertEqual(response.status_code, status_code,  |         self.assertEqual(response.status_code, status_code,  | ||||||
|             "Response didn't redirect: Reponse code was %d (expected %d)" %  |             "Response didn't redirect as expected: Reponse code was %d (expected %d)" %  | ||||||
|                 (response.status_code, status_code)) |                 (response.status_code, status_code)) | ||||||
|         scheme, netloc, path, params, query, fragment = urlparse(response['Location']) |         scheme, netloc, path, params, query, fragment = urlparse(response['Location']) | ||||||
|         self.assertEqual(path, expected_path,  |         self.assertEqual(path, expected_path,  | ||||||
| @@ -70,7 +70,7 @@ class TestCase(unittest.TestCase): | |||||||
|         redirect_response = self.client.get(path) |         redirect_response = self.client.get(path) | ||||||
|         self.assertEqual(redirect_response.status_code, target_status_code,  |         self.assertEqual(redirect_response.status_code, target_status_code,  | ||||||
|             "Couldn't retrieve redirection page '%s': response code was %d (expected %d)" %  |             "Couldn't retrieve redirection page '%s': response code was %d (expected %d)" %  | ||||||
|                 (path, response.status_code, status_code)) |                 (path, redirect_response.status_code, target_status_code)) | ||||||
|      |      | ||||||
|     def assertContains(self, response, text, count=1, status_code=200): |     def assertContains(self, response, text, count=1, status_code=200): | ||||||
|         """Assert that a response indicates that a page was retreived successfully, |         """Assert that a response indicates that a page was retreived successfully, | ||||||
| @@ -108,7 +108,7 @@ class TestCase(unittest.TestCase): | |||||||
|                 for err in errors: |                 for err in errors: | ||||||
|                     if field: |                     if field: | ||||||
|                         if field in context[form].errors: |                         if field in context[form].errors: | ||||||
|                             self.assertTrue(err in context[form].errors[field],  |                             self.failUnless(err in context[form].errors[field],  | ||||||
|                             "The field '%s' on form '%s' in context %d does not contain the error '%s' (actual errors: %s)" %  |                             "The field '%s' on form '%s' in context %d does not contain the error '%s' (actual errors: %s)" %  | ||||||
|                                 (field, form, i, err, list(context[form].errors[field]))) |                                 (field, form, i, err, list(context[form].errors[field]))) | ||||||
|                         elif field in context[form].fields: |                         elif field in context[form].fields: | ||||||
| @@ -117,7 +117,7 @@ class TestCase(unittest.TestCase): | |||||||
|                         else: |                         else: | ||||||
|                             self.fail("The form '%s' in context %d does not contain the field '%s'" % (form, i, field)) |                             self.fail("The form '%s' in context %d does not contain the field '%s'" % (form, i, field)) | ||||||
|                     else: |                     else: | ||||||
|                         self.assertTrue(err in context[form].non_field_errors(),  |                         self.failUnless(err in context[form].non_field_errors(),  | ||||||
|                             "The form '%s' in context %d does not contain the non-field error '%s' (actual errors: %s)" %  |                             "The form '%s' in context %d does not contain the non-field error '%s' (actual errors: %s)" %  | ||||||
|                                 (form, i, err, list(context[form].non_field_errors()))) |                                 (form, i, err, list(context[form].non_field_errors()))) | ||||||
|         if not found_form: |         if not found_form: | ||||||
| @@ -127,7 +127,7 @@ class TestCase(unittest.TestCase): | |||||||
|         "Assert that the template with the provided name was used in rendering the response" |         "Assert that the template with the provided name was used in rendering the response" | ||||||
|         if isinstance(response.template, list): |         if isinstance(response.template, list): | ||||||
|             template_names = [t.name for t in response.template] |             template_names = [t.name for t in response.template] | ||||||
|             self.assertTrue(template_name in template_names, |             self.failUnless(template_name in template_names, | ||||||
|                 "Template '%s' was not one of the templates used to render the response. Templates used: %s" % |                 "Template '%s' was not one of the templates used to render the response. Templates used: %s" % | ||||||
|                     (template_name, template_names)) |                     (template_name, template_names)) | ||||||
|         elif response.template: |         elif response.template: | ||||||
| @@ -140,7 +140,7 @@ class TestCase(unittest.TestCase): | |||||||
|     def assertTemplateNotUsed(self, response, template_name): |     def assertTemplateNotUsed(self, response, template_name): | ||||||
|         "Assert that the template with the provided name was NOT used in rendering the response" |         "Assert that the template with the provided name was NOT used in rendering the response" | ||||||
|         if isinstance(response.template, list):             |         if isinstance(response.template, list):             | ||||||
|             self.assertFalse(template_name in [t.name for t in response.template], |             self.failIf(template_name in [t.name for t in response.template], | ||||||
|                 "Template '%s' was used unexpectedly in rendering the response" % template_name) |                 "Template '%s' was used unexpectedly in rendering the response" % template_name) | ||||||
|         elif response.template: |         elif response.template: | ||||||
|             self.assertNotEqual(template_name, response.template.name, |             self.assertNotEqual(template_name, response.template.name, | ||||||
|   | |||||||
| @@ -44,7 +44,11 @@ How to use Databrowse | |||||||
|        It doesn't matter where you put this, as long as it gets executed at |        It doesn't matter where you put this, as long as it gets executed at | ||||||
|        some point. A good place for it is in your URLconf file (``urls.py``). |        some point. A good place for it is in your URLconf file (``urls.py``). | ||||||
|  |  | ||||||
|     3. Add the following line to your URLconf:: |     3. Change your URLconf to import the ``databrowse`` module:: | ||||||
|  |  | ||||||
|  |            from django.contrib import databrowse | ||||||
|  |  | ||||||
|  |        ...and add the following line to your URLconf:: | ||||||
|  |  | ||||||
|            (r'^databrowse/(.*)', databrowse.site.root), |            (r'^databrowse/(.*)', databrowse.site.root), | ||||||
|  |  | ||||||
|   | |||||||
| @@ -236,7 +236,7 @@ To pluralize, specify both the singular and plural forms with the | |||||||
| ``{% plural %}`` tag, which appears within ``{% blocktrans %}`` and | ``{% plural %}`` tag, which appears within ``{% blocktrans %}`` and | ||||||
| ``{% endblocktrans %}``. Example:: | ``{% endblocktrans %}``. Example:: | ||||||
|  |  | ||||||
|     {% blocktrans count list|count as counter %} |     {% blocktrans count list|length as counter %} | ||||||
|     There is only one {{ name }} object. |     There is only one {{ name }} object. | ||||||
|     {% plural %} |     {% plural %} | ||||||
|     There are {{ counter }} {{ name }} objects. |     There are {{ counter }} {{ name }} objects. | ||||||
|   | |||||||
| @@ -9,28 +9,30 @@ framework. This document explains how to use this new library. | |||||||
| Migration plan | Migration plan | ||||||
| ============== | ============== | ||||||
|  |  | ||||||
| ``django.newforms`` currently is only available in Django beginning | ``django.newforms`` is new in Django's 0.96 release, but, as it won't be new | ||||||
| with the 0.96 release.  the Django development version -- i.e., it's | forever, we plan to rename it to ``django.forms`` in the future. The current | ||||||
| not available in the Django 0.95 release. For the next Django release, | ``django.forms`` package will be available as ``django.oldforms`` until Django | ||||||
| our plan is to do the following: | 1.0, when we plan to remove it for good. | ||||||
|  |  | ||||||
|     * As of revision [4208], we've copied the current ``django.forms`` to | That has direct repercussions on the forward compatibility of your code. Please | ||||||
|       ``django.oldforms``. This allows you to upgrade your code *now* rather | read the following migration plan and code accordingly: | ||||||
|       than waiting for the backwards-incompatible change and rushing to fix |  | ||||||
|       your code after the fact. Just change your import statements like this:: |     * The old forms framework (the current ``django.forms``) has been copied to | ||||||
|  |       ``django.oldforms``. Thus, you can start upgrading your code *now*, | ||||||
|  |       rather than waiting for the future backwards-incompatible change, by | ||||||
|  |       changing your import statements like this:: | ||||||
|  |  | ||||||
|           from django import forms             # old |           from django import forms             # old | ||||||
|           from django import oldforms as forms # new |           from django import oldforms as forms # new | ||||||
|  |  | ||||||
|     * At an undecided future date, we will move the current ``django.newforms`` |     * In the next Django release (0.97), we will move the current | ||||||
|       to ``django.forms``. This will be a backwards-incompatible change, and |       ``django.newforms`` to ``django.forms``. This will be a | ||||||
|       anybody who is still using the old version of ``django.forms`` at that |       backwards-incompatible change, and anybody who is still using the old | ||||||
|       time will need to change their import statements, as described in the |       version of ``django.forms`` at that time will need to change their import | ||||||
|       previous bullet. |       statements, as described in the previous bullet. | ||||||
|  |  | ||||||
|     * We will remove ``django.oldforms`` in the release *after* the next Django |     * We will remove ``django.oldforms`` in the release *after* the next Django | ||||||
|       release -- the release that comes after the release in which we're |       release -- either 0.98 or 1.0, whichever comes first. | ||||||
|       creating the new ``django.forms``. |  | ||||||
|  |  | ||||||
| With this in mind, we recommend you use the following import statement when | With this in mind, we recommend you use the following import statement when | ||||||
| using ``django.newforms``:: | using ``django.newforms``:: | ||||||
| @@ -184,7 +186,7 @@ e-mail address:: | |||||||
|     >>> f.is_valid() |     >>> f.is_valid() | ||||||
|     False |     False | ||||||
|  |  | ||||||
| Access the ``Form`` attribute ``errors`` to get a dictionary of error messages:: | Access the ``errors`` attribute to get a dictionary of error messages:: | ||||||
|  |  | ||||||
|     >>> f.errors |     >>> f.errors | ||||||
|     {'sender': [u'Enter a valid e-mail address.'], 'subject': [u'This field is required.']} |     {'sender': [u'Enter a valid e-mail address.'], 'subject': [u'This field is required.']} | ||||||
| @@ -197,6 +199,10 @@ You can access ``errors`` without having to call ``is_valid()`` first. The | |||||||
| form's data will be validated the first time either you call ``is_valid()`` or | form's data will be validated the first time either you call ``is_valid()`` or | ||||||
| access ``errors``. | access ``errors``. | ||||||
|  |  | ||||||
|  | The validation routines will only get called once, regardless of how many times | ||||||
|  | you access ``errors`` or call ``is_valid()``. This means that if validation has | ||||||
|  | side effects, those side effects will only be triggered once. | ||||||
|  |  | ||||||
| Behavior of unbound forms | Behavior of unbound forms | ||||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
| @@ -274,6 +280,27 @@ but ``clean_data`` contains only the form's fields:: | |||||||
|     >>> f.clean_data # Doesn't contain extra_field_1, etc. |     >>> f.clean_data # Doesn't contain extra_field_1, etc. | ||||||
|     {'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'} |     {'cc_myself': True, 'message': u'Hi there', 'sender': u'foo@example.com', 'subject': u'hello'} | ||||||
|  |  | ||||||
|  | ``clean_data`` will include a key and value for *all* fields defined in the | ||||||
|  | ``Form``, even if the data didn't include a value for fields that are not | ||||||
|  | required. In this example, the data dictionary doesn't include a value for the | ||||||
|  | ``nick_name`` field, but ``clean_data`` includes it, with an empty value:: | ||||||
|  |  | ||||||
|  |     >>> class OptionalPersonForm(Form): | ||||||
|  |     ...     first_name = CharField() | ||||||
|  |     ...     last_name = CharField() | ||||||
|  |     ...     nick_name = CharField(required=False) | ||||||
|  |     >>> data = {'first_name': u'John', 'last_name': u'Lennon'} | ||||||
|  |     >>> f = OptionalPersonForm(data) | ||||||
|  |     >>> f.is_valid() | ||||||
|  |     True | ||||||
|  |     >>> f.clean_data | ||||||
|  |     {'nick_name': u'', 'first_name': u'John', 'last_name': u'Lennon'} | ||||||
|  |  | ||||||
|  | In this above example, the ``clean_data`` value for ``nick_name`` is set to an | ||||||
|  | empty string, because ``nick_name`` is ``CharField``, and ``CharField``\s treat | ||||||
|  | empty values as an empty string. Each field type knows what its "blank" value | ||||||
|  | is -- e.g., for ``DateField``, it's ``None`` instead of the empty string. | ||||||
|  |  | ||||||
| Behavior of unbound forms | Behavior of unbound forms | ||||||
| ~~~~~~~~~~~~~~~~~~~~~~~~~ | ~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
| @@ -454,7 +481,7 @@ field:: | |||||||
| If ``auto_id`` is set to a string containing the format character ``'%s'``, | If ``auto_id`` is set to a string containing the format character ``'%s'``, | ||||||
| then the form output will include ``<label>`` tags, and will generate ``id`` | then the form output will include ``<label>`` tags, and will generate ``id`` | ||||||
| attributes based on the format string. For example, for a format string | attributes based on the format string. For example, for a format string | ||||||
| ``'field_%s'``, a field named ``subject`` will get the ``id`` | ``'field_%s'``, a field named ``subject`` will get the ``id`` value | ||||||
| ``'field_subject'``. Continuing our example:: | ``'field_subject'``. Continuing our example:: | ||||||
|  |  | ||||||
|     >>> f = ContactForm(auto_id='id_for_%s') |     >>> f = ContactForm(auto_id='id_for_%s') | ||||||
| @@ -493,8 +520,9 @@ How errors are displayed | |||||||
|  |  | ||||||
| If you render a bound ``Form`` object, the act of rendering will automatically | If you render a bound ``Form`` object, the act of rendering will automatically | ||||||
| run the form's validation if it hasn't already happened, and the HTML output | run the form's validation if it hasn't already happened, and the HTML output | ||||||
| will include the validation errors as a ``<ul>`` near the field. The particular | will include the validation errors as a ``<ul class="errorlist">`` near the | ||||||
| positioning of the error messages depends on the output method you're using:: | field. The particular positioning of the error messages depends on the output | ||||||
|  | method you're using:: | ||||||
|  |  | ||||||
|     >>> data = {'subject': '', |     >>> data = {'subject': '', | ||||||
|     ...         'message': 'Hi there', |     ...         'message': 'Hi there', | ||||||
| @@ -556,7 +584,8 @@ The field-specific output honors the form object's ``auto_id`` setting:: | |||||||
|     <input type="text" name="message" id="id_message" /> |     <input type="text" name="message" id="id_message" /> | ||||||
|  |  | ||||||
| For a field's list of errors, access the field's ``errors`` attribute. This | For a field's list of errors, access the field's ``errors`` attribute. This | ||||||
| is a list-like object that is displayed as an HTML ``<ul>`` when printed:: | is a list-like object that is displayed as an HTML ``<ul class="errorlist">`` | ||||||
|  | when printed:: | ||||||
|  |  | ||||||
|     >>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''} |     >>> data = {'subject': 'hi', 'message': '', 'sender': '', 'cc_myself': ''} | ||||||
|     >>> f = ContactForm(data, auto_id=False) |     >>> f = ContactForm(data, auto_id=False) | ||||||
| @@ -646,7 +675,7 @@ Core field arguments | |||||||
|  |  | ||||||
| Each ``Field`` class constructor takes at least these arguments. Some | Each ``Field`` class constructor takes at least these arguments. Some | ||||||
| ``Field`` classes take additional, field-specific arguments, but the following | ``Field`` classes take additional, field-specific arguments, but the following | ||||||
| should *always* be available: | should *always* be accepted: | ||||||
|  |  | ||||||
| ``required`` | ``required`` | ||||||
| ~~~~~~~~~~~~ | ~~~~~~~~~~~~ | ||||||
| @@ -704,7 +733,7 @@ field.) | |||||||
| The ``label`` argument lets you specify the "human-friendly" label for this | The ``label`` argument lets you specify the "human-friendly" label for this | ||||||
| field. This is used when the ``Field`` is displayed in a ``Form``. | field. This is used when the ``Field`` is displayed in a ``Form``. | ||||||
|  |  | ||||||
| As explained in _`Outputting forms as HTML` above, the default label for a | As explained in "Outputting forms as HTML" above, the default label for a | ||||||
| ``Field`` is generated from the field name by converting all underscores to | ``Field`` is generated from the field name by converting all underscores to | ||||||
| spaces and upper-casing the first letter. Specify ``label`` if that default | spaces and upper-casing the first letter. Specify ``label`` if that default | ||||||
| behavior doesn't result in an adequate label. | behavior doesn't result in an adequate label. | ||||||
| @@ -779,14 +808,15 @@ validation if a particular field's value is not given. ``initial`` values are | |||||||
| ~~~~~~~~~~ | ~~~~~~~~~~ | ||||||
|  |  | ||||||
| The ``widget`` argument lets you specify a ``Widget`` class to use when | The ``widget`` argument lets you specify a ``Widget`` class to use when | ||||||
| rendering this ``Field``. See _`Widgets` below for more information. | rendering this ``Field``. See "Widgets" below for more information. | ||||||
|  |  | ||||||
| ``help_text`` | ``help_text`` | ||||||
| ~~~~~~~~~~~~~ | ~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
| The ``help_text`` argument lets you specify descriptive text for this | The ``help_text`` argument lets you specify descriptive text for this | ||||||
| ``Field``. If you provide ``help_text``, it will be displayed next to the | ``Field``. If you provide ``help_text``, it will be displayed next to the | ||||||
| ``Field`` when the ``Field`` is rendered in a ``Form``. | ``Field`` when the ``Field`` is rendered by one of the convenience ``Form`` | ||||||
|  | methods (e.g., ``as_ul()``). | ||||||
|  |  | ||||||
| Here's a full example ``Form`` that implements ``help_text`` for two of its | Here's a full example ``Form`` that implements ``help_text`` for two of its | ||||||
| fields. We've specified ``auto_id=False`` to simplify the output:: | fields. We've specified ``auto_id=False`` to simplify the output:: | ||||||
| @@ -860,6 +890,212 @@ level and at the form instance level, and the latter gets precedence:: | |||||||
|     <tr><th>Url:</th><td><input type="text" name="url" /></td></tr> |     <tr><th>Url:</th><td><input type="text" name="url" /></td></tr> | ||||||
|     <tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr> |     <tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr> | ||||||
|  |  | ||||||
|  | Built-in ``Field`` classes | ||||||
|  | -------------------------- | ||||||
|  |  | ||||||
|  | Naturally, the ``newforms`` library comes with a set of ``Field`` classes that | ||||||
|  | represent common validation needs. This section documents each built-in field. | ||||||
|  |  | ||||||
|  | For each field, we describe the default widget used if you don't specify | ||||||
|  | ``widget``. We also specify the value returned when you provide an empty value | ||||||
|  | (see the section on ``required`` above to understand what that means). | ||||||
|  |  | ||||||
|  | ``BooleanField`` | ||||||
|  | ~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  |     * Default widget: ``CheckboxInput`` | ||||||
|  |     * Empty value: ``None`` | ||||||
|  |     * Normalizes to: A Python ``True`` or ``False`` value. | ||||||
|  |     * Validates nothing (i.e., it never raises a ``ValidationError``). | ||||||
|  |  | ||||||
|  | ``CharField`` | ||||||
|  | ~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  |     * Default widget: ``TextInput`` | ||||||
|  |     * Empty value: ``''`` (an empty string) | ||||||
|  |     * Normalizes to: A Unicode object. | ||||||
|  |     * Validates nothing, unless ``max_length`` or ``min_length`` is provided. | ||||||
|  |  | ||||||
|  | Has two optional arguments for validation, ``max_length`` and ``min_length``. | ||||||
|  | If provided, these arguments ensure that the string is at most or at least the | ||||||
|  | given length. | ||||||
|  |  | ||||||
|  | ``ChoiceField`` | ||||||
|  | ~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  |     * Default widget: ``Select`` | ||||||
|  |     * Empty value: ``''`` (an empty string) | ||||||
|  |     * Normalizes to: A Unicode object. | ||||||
|  |     * Validates that the given value exists in the list of choices. | ||||||
|  |  | ||||||
|  | Takes one extra argument, ``choices``, which is an iterable (e.g., a list or | ||||||
|  | tuple) of 2-tuples to use as choices for this field. | ||||||
|  |  | ||||||
|  | ``DateField`` | ||||||
|  | ~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  |     * Default widget: ``TextInput`` | ||||||
|  |     * Empty value: ``None`` | ||||||
|  |     * Normalizes to: A Python ``datetime.date`` object. | ||||||
|  |     * Validates that the given value is either a ``datetime.date``, | ||||||
|  |       ``datetime.datetime`` or string formatted in a particular date format. | ||||||
|  |  | ||||||
|  | Takes one optional argument, ``input_formats``, which is a list of formats used | ||||||
|  | to attempt to convert a string to a valid ``datetime.date`` object. | ||||||
|  |  | ||||||
|  | If no ``input_formats`` argument is provided, the default input formats are:: | ||||||
|  |  | ||||||
|  |     '%Y-%m-%d', '%m/%d/%Y', '%m/%d/%y', # '2006-10-25', '10/25/2006', '10/25/06' | ||||||
|  |     '%b %d %Y', '%b %d, %Y',            # 'Oct 25 2006', 'Oct 25, 2006' | ||||||
|  |     '%d %b %Y', '%d %b, %Y',            # '25 Oct 2006', '25 Oct, 2006' | ||||||
|  |     '%B %d %Y', '%B %d, %Y',            # 'October 25 2006', 'October 25, 2006' | ||||||
|  |     '%d %B %Y', '%d %B, %Y',            # '25 October 2006', '25 October, 2006' | ||||||
|  |  | ||||||
|  | ``DateTimeField`` | ||||||
|  | ~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  |     * Default widget: ``TextInput`` | ||||||
|  |     * Empty value: ``None`` | ||||||
|  |     * Normalizes to: A Python ``datetime.datetime`` object. | ||||||
|  |     * Validates that the given value is either a ``datetime.datetime``, | ||||||
|  |       ``datetime.date`` or string formatted in a particular datetime format. | ||||||
|  |  | ||||||
|  | Takes one optional argument, ``input_formats``, which is a list of formats used | ||||||
|  | to attempt to convert a string to a valid ``datetime.datetime`` object. | ||||||
|  |  | ||||||
|  | If no ``input_formats`` argument is provided, the default input formats are:: | ||||||
|  |  | ||||||
|  |     '%Y-%m-%d %H:%M:%S',     # '2006-10-25 14:30:59' | ||||||
|  |     '%Y-%m-%d %H:%M',        # '2006-10-25 14:30' | ||||||
|  |     '%Y-%m-%d',              # '2006-10-25' | ||||||
|  |     '%m/%d/%Y %H:%M:%S',     # '10/25/2006 14:30:59' | ||||||
|  |     '%m/%d/%Y %H:%M',        # '10/25/2006 14:30' | ||||||
|  |     '%m/%d/%Y',              # '10/25/2006' | ||||||
|  |     '%m/%d/%y %H:%M:%S',     # '10/25/06 14:30:59' | ||||||
|  |     '%m/%d/%y %H:%M',        # '10/25/06 14:30' | ||||||
|  |     '%m/%d/%y',              # '10/25/06' | ||||||
|  |  | ||||||
|  | ``EmailField`` | ||||||
|  | ~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  |     * Default widget: ``TextInput`` | ||||||
|  |     * Empty value: ``''`` (an empty string) | ||||||
|  |     * Normalizes to: A Unicode object. | ||||||
|  |     * Validates that the given value is a valid e-mail address, using a | ||||||
|  |       moderately complex regular expression. | ||||||
|  |  | ||||||
|  | Has two optional arguments for validation, ``max_length`` and ``min_length``. | ||||||
|  | If provided, these arguments ensure that the string is at most or at least the | ||||||
|  | given length. | ||||||
|  |  | ||||||
|  | ``IntegerField`` | ||||||
|  | ~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  |     * Default widget: ``TextInput`` | ||||||
|  |     * Empty value: ``None`` | ||||||
|  |     * Normalizes to: A Python integer or long integer. | ||||||
|  |     * Validates that the given value is an integer. Leading and trailing | ||||||
|  |       whitespace is allowed, as in Python's ``int()`` function. | ||||||
|  |  | ||||||
|  | ``MultipleChoiceField`` | ||||||
|  | ~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  |     * Default widget: ``SelectMultiple`` | ||||||
|  |     * Empty value: ``[]`` (an empty list) | ||||||
|  |     * Normalizes to: A list of Unicode objects. | ||||||
|  |     * Validates that every value in the given list of values exists in the list | ||||||
|  |       of choices. | ||||||
|  |  | ||||||
|  | Takes one extra argument, ``choices``, which is an iterable (e.g., a list or | ||||||
|  | tuple) of 2-tuples to use as choices for this field. | ||||||
|  |  | ||||||
|  | ``NullBooleanField`` | ||||||
|  | ~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  |     * Default widget: ``NullBooleanSelect`` | ||||||
|  |     * Empty value: ``None`` | ||||||
|  |     * Normalizes to: A Python ``True``, ``False`` or ``None`` value. | ||||||
|  |     * Validates nothing (i.e., it never raises a ``ValidationError``). | ||||||
|  |  | ||||||
|  | ``RegexField`` | ||||||
|  | ~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  |     * Default widget: ``TextInput`` | ||||||
|  |     * Empty value: ``''`` (an empty string) | ||||||
|  |     * Normalizes to: A Unicode object. | ||||||
|  |     * Validates that the given value matches against a certain regular | ||||||
|  |       expression. | ||||||
|  |  | ||||||
|  | Takes one required argument, ``regex``, which is a regular expression specified | ||||||
|  | either as a string or a compiled regular expression object. | ||||||
|  |  | ||||||
|  | Also takes the following optional arguments: | ||||||
|  |  | ||||||
|  |     ======================  ===================================================== | ||||||
|  |     Argument                Description | ||||||
|  |     ======================  ===================================================== | ||||||
|  |     ``max_length``          Ensures the string has at most this many characters. | ||||||
|  |     ``min_length``          Ensures the string has at least this many characters. | ||||||
|  |     ``error_message``       Error message to return for failed validation. If no | ||||||
|  |                             message is provided, a generic error message will be | ||||||
|  |                             used. | ||||||
|  |     ======================  ===================================================== | ||||||
|  |  | ||||||
|  | ``TimeField`` | ||||||
|  | ~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  |     * Default widget: ``TextInput`` | ||||||
|  |     * Empty value: ``None`` | ||||||
|  |     * Normalizes to: A Python ``datetime.time`` object. | ||||||
|  |     * Validates that the given value is either a ``datetime.time`` or string | ||||||
|  |       formatted in a particular time format. | ||||||
|  |  | ||||||
|  | Takes one optional argument, ``input_formats``, which is a list of formats used | ||||||
|  | to attempt to convert a string to a valid ``datetime.time`` object. | ||||||
|  |  | ||||||
|  | If no ``input_formats`` argument is provided, the default input formats are:: | ||||||
|  |  | ||||||
|  |     '%H:%M:%S',     # '14:30:59' | ||||||
|  |     '%H:%M',        # '14:30' | ||||||
|  |  | ||||||
|  | ``URLField`` | ||||||
|  | ~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  |     * Default widget: ``TextInput`` | ||||||
|  |     * Empty value: ``''`` (an empty string) | ||||||
|  |     * Normalizes to: A Unicode object. | ||||||
|  |     * Validates that the given value is a valid URL. | ||||||
|  |  | ||||||
|  | Takes the following optional arguments: | ||||||
|  |  | ||||||
|  |     ========================  ===================================================== | ||||||
|  |     Argument                  Description | ||||||
|  |     ========================  ===================================================== | ||||||
|  |     ``max_length``            Ensures the string has at most this many characters. | ||||||
|  |     ``min_length``            Ensures the string has at least this many characters. | ||||||
|  |     ``verify_exists``         If ``True``, the validator will attempt to load the | ||||||
|  |                               given URL, raising ``ValidationError`` if the page | ||||||
|  |                               gives a 404. Defaults to ``False``. | ||||||
|  |     ``validator_user_agent``  String used as the user-agent used when checking for | ||||||
|  |                               a URL's existence. Defaults to the value of the | ||||||
|  |                               ``URL_VALIDATOR_USER_AGENT`` setting. | ||||||
|  |     ========================  ===================================================== | ||||||
|  |  | ||||||
|  | Slightly complex built-in ``Field`` classes | ||||||
|  | ------------------------------------------- | ||||||
|  |  | ||||||
|  | The following are not yet documented here. See the unit tests, linked-to from | ||||||
|  | the bottom of this document, for examples of their use. | ||||||
|  |  | ||||||
|  | ``ComboField`` | ||||||
|  | ~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  | ``MultiValueField`` | ||||||
|  | ~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  | ``SplitDateTimeField`` | ||||||
|  | ~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
| Creating custom fields | Creating custom fields | ||||||
| ---------------------- | ---------------------- | ||||||
|  |  | ||||||
| @@ -870,6 +1106,308 @@ custom ``Field`` classes. To do this, just create a subclass of | |||||||
| mentioned above (``required``, ``label``, ``initial``, ``widget``, | mentioned above (``required``, ``label``, ``initial``, ``widget``, | ||||||
| ``help_text``). | ``help_text``). | ||||||
|  |  | ||||||
|  | Generating forms for models | ||||||
|  | =========================== | ||||||
|  |  | ||||||
|  | If you're building a database-driven app, chances are you'll have forms that | ||||||
|  | map closely to Django models. For instance, you might have a ``BlogComment`` | ||||||
|  | model, and you want to create a form that lets people submit comments. In this | ||||||
|  | case, it would be redundant to define the field types in your form, because | ||||||
|  | you've already defined the fields in your model. | ||||||
|  |  | ||||||
|  | For this reason, Django provides a few helper functions that let you create a | ||||||
|  | ``Form`` class from a Django model. | ||||||
|  |  | ||||||
|  | ``form_for_model()`` | ||||||
|  | -------------------- | ||||||
|  |  | ||||||
|  | The method ``django.newforms.form_for_model()`` creates a form based on the | ||||||
|  | definition of a specific model. Pass it the model class, and it will return a | ||||||
|  | ``Form`` class that contains a form field for each model field. | ||||||
|  |  | ||||||
|  | For example:: | ||||||
|  |  | ||||||
|  |     >>> from django.newforms import form_for_model | ||||||
|  |  | ||||||
|  |     # Create the form class. | ||||||
|  |     >>> ArticleForm = form_for_model(Article) | ||||||
|  |  | ||||||
|  |     # Create an empty form instance. | ||||||
|  |     >>> f = ArticleForm() | ||||||
|  |  | ||||||
|  | It bears repeating that ``form_for_model()`` takes the model *class*, not a | ||||||
|  | model instance, and it returns a ``Form`` *class*, not a ``Form`` instance. | ||||||
|  |  | ||||||
|  | Field types | ||||||
|  | ~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  | The generated ``Form`` class will have a form field for every model field. Each | ||||||
|  | model field has a corresponding default form field. For example, a | ||||||
|  | ``CharField`` on a model is represented as a ``CharField`` on a form. A | ||||||
|  | model ``ManyToManyField`` is represented as a ``MultipleChoiceField``. Here is | ||||||
|  | the full list of conversions: | ||||||
|  |  | ||||||
|  |     ===============================  ======================================== | ||||||
|  |     Model field                      Form field | ||||||
|  |     ===============================  ======================================== | ||||||
|  |     ``AutoField``                    Not represented in the form | ||||||
|  |     ``BooleanField``                 ``BooleanField`` | ||||||
|  |     ``CharField``                    ``CharField`` with ``max_length`` set to | ||||||
|  |                                      the model field's ``maxlength`` | ||||||
|  |     ``CommaSeparatedIntegerField``   ``CharField`` | ||||||
|  |     ``DateField``                    ``DateField`` | ||||||
|  |     ``DateTimeField``                ``DateTimeField`` | ||||||
|  |     ``EmailField``                   ``EmailField`` | ||||||
|  |     ``FileField``                    ``CharField`` | ||||||
|  |     ``FilePathField``                ``CharField`` | ||||||
|  |     ``FloatField``                   ``CharField`` | ||||||
|  |     ``ForeignKey``                   ``ModelChoiceField`` (see below) | ||||||
|  |     ``ImageField``                   ``CharField`` | ||||||
|  |     ``IntegerField``                 ``IntegerField`` | ||||||
|  |     ``IPAddressField``               ``CharField`` | ||||||
|  |     ``ManyToManyField``              ``ModelMultipleChoiceField`` (see | ||||||
|  |                                      below) | ||||||
|  |     ``NullBooleanField``             ``CharField`` | ||||||
|  |     ``PhoneNumberField``             ``USPhoneNumberField`` | ||||||
|  |                                      (from ``django.contrib.localflavor.us``) | ||||||
|  |     ``PositiveIntegerField``         ``IntegerField`` | ||||||
|  |     ``PositiveSmallIntegerField``    ``IntegerField`` | ||||||
|  |     ``SlugField``                    ``CharField`` | ||||||
|  |     ``SmallIntegerField``            ``IntegerField`` | ||||||
|  |     ``TextField``                    ``CharField`` with ``widget=Textarea`` | ||||||
|  |     ``TimeField``                    ``TimeField`` | ||||||
|  |     ``URLField``                     ``URLField`` with ``verify_exists`` set | ||||||
|  |                                      to the model field's ``verify_exists`` | ||||||
|  |     ``USStateField``                 ``CharField`` with | ||||||
|  |                                      ``widget=USStateSelect`` | ||||||
|  |                                      (``USStateSelect`` is from | ||||||
|  |                                      ``django.contrib.localflavor.us``) | ||||||
|  |     ``XMLField``                     ``CharField`` with ``widget=Textarea`` | ||||||
|  |     ===============================  ======================================== | ||||||
|  |  | ||||||
|  | As you might expect, the ``ForeignKey`` and ``ManyToManyField`` model field | ||||||
|  | types are special cases: | ||||||
|  |  | ||||||
|  |     * ``ForeignKey`` is represented by ``django.newforms.ModelChoiceField``, | ||||||
|  |       which is a ``ChoiceField`` whose choices are a model ``QuerySet``. | ||||||
|  |  | ||||||
|  |     * ``ManyToManyField`` is represented by | ||||||
|  |       ``django.newforms.ModelMultipleChoiceField``, which is a | ||||||
|  |       ``MultipleChoiceField`` whose choices are a model ``QuerySet``. | ||||||
|  |  | ||||||
|  | In addition, each generated form field has attributes set as follows: | ||||||
|  |  | ||||||
|  |     * If the model field has ``blank=True``, then ``required`` is set to | ||||||
|  |       ``False`` on the form field. Otherwise, ``required=True``. | ||||||
|  |  | ||||||
|  |     * The form field's ``label`` is set to the ``verbose_name`` of the model | ||||||
|  |       field, with the first character capitalized. | ||||||
|  |  | ||||||
|  |     * The form field's ``help_text`` is set to the ``help_text`` of the model | ||||||
|  |       field. | ||||||
|  |  | ||||||
|  |     * If the model field has ``choices`` set, then the form field's ``widget`` | ||||||
|  |       will be set to ``Select``, with choices coming from the model field's | ||||||
|  |       ``choices``. | ||||||
|  |  | ||||||
|  | Finally, note that you can override the form field used for a given model | ||||||
|  | field. See "Overriding the default field types" below. | ||||||
|  |  | ||||||
|  | A full example | ||||||
|  | ~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  | Consider this set of models:: | ||||||
|  |  | ||||||
|  |     from django.db import models | ||||||
|  |  | ||||||
|  |     TITLE_CHOICES = ( | ||||||
|  |         ('MR', 'Mr.'), | ||||||
|  |         ('MRS', 'Mrs.'), | ||||||
|  |         ('MS', 'Ms.'), | ||||||
|  |     ) | ||||||
|  |  | ||||||
|  |     class Author(models.Model): | ||||||
|  |         name = models.CharField(maxlength=100) | ||||||
|  |         title = models.CharField(maxlength=3, choices=TITLE_CHOICES) | ||||||
|  |         birth_date = models.DateField(blank=True, null=True) | ||||||
|  |  | ||||||
|  |         def __str__(self): | ||||||
|  |             return self.name | ||||||
|  |  | ||||||
|  |     class Book(models.Model): | ||||||
|  |         name = models.CharField(maxlength=100) | ||||||
|  |         authors = models.ManyToManyField(Author) | ||||||
|  |  | ||||||
|  | With these models, a call to ``form_for_model(Author)`` would return a ``Form`` | ||||||
|  | class equivalent to this:: | ||||||
|  |  | ||||||
|  |     class AuthorForm(forms.Form): | ||||||
|  |         name = forms.CharField(max_length=100) | ||||||
|  |         title = forms.CharField(max_length=3, | ||||||
|  |                     widget=forms.Select(choices=TITLE_CHOICES)) | ||||||
|  |         birth_date = forms.DateField(required=False) | ||||||
|  |  | ||||||
|  | A call to ``form_for_model(Book)`` would return a ``Form`` class equivalent to | ||||||
|  | this:: | ||||||
|  |  | ||||||
|  |     class BookForm(forms.Form): | ||||||
|  |         name = forms.CharField(max_length=100) | ||||||
|  |         authors = forms.ModelMultipleChoiceField(queryset=Author.objects.all()) | ||||||
|  |  | ||||||
|  | The ``save()`` method | ||||||
|  | ~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  | Every form produced by ``form_for_model()`` also has a ``save()`` method. This | ||||||
|  | method creates and saves a database object from the data bound to the form. For | ||||||
|  | example:: | ||||||
|  |  | ||||||
|  |     # Create a form instance from POST data. | ||||||
|  |     >>> f = ArticleForm(request.POST) | ||||||
|  |  | ||||||
|  |     # Save a new Article object from the form's data. | ||||||
|  |     >>> new_article = f.save() | ||||||
|  |  | ||||||
|  | Note that ``save()`` will raise a ``ValueError`` if the data in the form | ||||||
|  | doesn't validate -- i.e., ``if form.errors``. | ||||||
|  |  | ||||||
|  | Using an alternate base class | ||||||
|  | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  | If you want to add custom methods to the form generated by | ||||||
|  | ``form_for_model()``, write a class that extends ``django.newforms.BaseForm`` | ||||||
|  | and contains your custom methods. Then, use the ``form`` argument to | ||||||
|  | ``form_for_model()`` to tell it to use your custom form as its base class. | ||||||
|  | For example:: | ||||||
|  |  | ||||||
|  |     # Create the new base class. | ||||||
|  |     >>> class MyBase(BaseForm): | ||||||
|  |     ...     def my_method(self): | ||||||
|  |     ...         # Do whatever the method does | ||||||
|  |  | ||||||
|  |     # Create the form class with a different base class. | ||||||
|  |     >>> ArticleForm = form_for_model(Article, form=MyBase) | ||||||
|  |  | ||||||
|  |     # Instantiate the form. | ||||||
|  |     >>> f = ArticleForm() | ||||||
|  |  | ||||||
|  |     # Use the base class method. | ||||||
|  |     >>> f.my_method() | ||||||
|  |  | ||||||
|  | Using a subset of fields on the form | ||||||
|  | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  | **New in Django development version** | ||||||
|  |  | ||||||
|  | In some cases, you may not want all the model fields to appear on the generated | ||||||
|  | form. There are two ways of telling ``form_for_model()`` to use only a subset | ||||||
|  | of the model fields: | ||||||
|  |  | ||||||
|  |     1. Set ``editable=False`` on the model field. As a result, *any* form | ||||||
|  |        created from the model via ``form_for_model()`` will not include that | ||||||
|  |        field. | ||||||
|  |  | ||||||
|  |     2. Use the ``fields`` argument to ``form_for_model()``. This argument, if | ||||||
|  |        given, should be a list of field names to include in the form. | ||||||
|  |  | ||||||
|  |        For example, if you want a form for the ``Author`` model (defined above) | ||||||
|  |        that includes only the ``name`` and ``title`` fields, you would specify | ||||||
|  |        ``fields`` like this:: | ||||||
|  |  | ||||||
|  |            PartialArticleForm = form_for_model(Author, fields=('name', 'title')) | ||||||
|  |  | ||||||
|  | .. note:: | ||||||
|  |  | ||||||
|  |     If you specify ``fields`` when creating a form with ``form_for_model()``, | ||||||
|  |     make sure that the fields that are *not* specified can provide default | ||||||
|  |     values, or are allowed to have a value of ``None``. If a field isn't | ||||||
|  |     specified on a form, the object created from the form can't provide | ||||||
|  |     a value for that attribute, which will prevent the new instance from | ||||||
|  |     being saved. | ||||||
|  |  | ||||||
|  | Overriding the default field types | ||||||
|  | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  | The default field types, as described in the "Field types" table above, are | ||||||
|  | sensible defaults; if you have a ``DateField`` in your model, chances are you'd | ||||||
|  | want that to be represented as a ``DateField`` in your form. But | ||||||
|  | ``form_for_model()`` gives you the flexibility of changing the form field type | ||||||
|  | for a given model field. You do this by specifying a **formfield callback**. | ||||||
|  |  | ||||||
|  | A formfield callback is a function that, when provided with a model field, | ||||||
|  | returns a form field instance. When constructing a form, ``form_for_model()`` | ||||||
|  | asks the formfield callback to provide form field types. | ||||||
|  |  | ||||||
|  | By default, ``form_for_model()`` calls the ``formfield()`` method on the model | ||||||
|  | field:: | ||||||
|  |  | ||||||
|  |     def default_callback(field, **kwargs): | ||||||
|  |         return field.formfield(**kwargs) | ||||||
|  |  | ||||||
|  | The ``kwargs`` are any keyword arguments that might be passed to the form | ||||||
|  | field, such as ``required=True`` or ``label='Foo'``. | ||||||
|  |  | ||||||
|  | For example, if you wanted to use ``MyDateFormField`` for any ``DateField`` | ||||||
|  | field on the model, you could define the callback:: | ||||||
|  |  | ||||||
|  |     >>> def my_callback(field, **kwargs): | ||||||
|  |     ...     if isinstance(field, models.DateField): | ||||||
|  |     ...         return MyDateFormField(**kwargs) | ||||||
|  |     ...     else: | ||||||
|  |     ...         return field.formfield(**kwargs) | ||||||
|  |  | ||||||
|  |     >>> ArticleForm = form_for_model(formfield_callback=my_callback) | ||||||
|  |  | ||||||
|  | Note that your callback needs to handle *all* possible model field types, not | ||||||
|  | just the ones that you want to behave differently to the default. That's why | ||||||
|  | this example has an ``else`` clause that implements the default behavior. | ||||||
|  |  | ||||||
|  | Finding the model associated with a form | ||||||
|  | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  | The model class that was used to construct the form is available | ||||||
|  | using the ``_model`` property of the generated form:: | ||||||
|  |  | ||||||
|  |     >>> ArticleForm = form_for_model(Article) | ||||||
|  |     >>> ArticleForm._model | ||||||
|  |     <class 'myapp.models.Article'> | ||||||
|  |  | ||||||
|  | ``form_for_instance()`` | ||||||
|  | ----------------------- | ||||||
|  |  | ||||||
|  | ``form_for_instance()`` is like ``form_for_model()``, but it takes a model | ||||||
|  | instance instead of a model class:: | ||||||
|  |  | ||||||
|  |     # Create an Author. | ||||||
|  |     >>> a = Author(name='Joe Smith', title='MR', birth_date=None) | ||||||
|  |     >>> a.save() | ||||||
|  |  | ||||||
|  |     # Create a form for this particular Author. | ||||||
|  |     >>> AuthorForm = form_for_instance(a) | ||||||
|  |  | ||||||
|  |     # Instantiate the form. | ||||||
|  |     >>> f = AuthorForm() | ||||||
|  |  | ||||||
|  | When a form created by ``form_for_instance()`` is created, the initial | ||||||
|  | data values for the form fields are drawn from the instance. However, | ||||||
|  | this data is not bound to the form. You will need to bind data to the | ||||||
|  | form before the form can be saved. | ||||||
|  |  | ||||||
|  | When you call ``save()`` on a form created by ``form_for_instance()``, | ||||||
|  | the database instance will be updated. As in ``form_for_model()``, ``save()`` | ||||||
|  | will raise ``ValueError`` if the data doesn't validate. | ||||||
|  |  | ||||||
|  | ``form_for_instance()`` has ``form``, ``fields`` and ``formfield_callback`` | ||||||
|  | arguments that behave the same way as they do for ``form_for_model()``. | ||||||
|  |  | ||||||
|  | When should you use ``form_for_model()`` and ``form_for_instance()``? | ||||||
|  | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||||||
|  |  | ||||||
|  | The ``form_for_model()`` and ``form_for_instance()`` functions are meant to be | ||||||
|  | shortcuts for the common case. If you want to create a form whose fields map to | ||||||
|  | more than one model, or a form that contains fields that *aren't* on a model, | ||||||
|  | you shouldn't use these shortcuts. Creating a ``Form`` class the "long" way | ||||||
|  | isn't that difficult, after all. | ||||||
|  |  | ||||||
| More coming soon | More coming soon | ||||||
| ================ | ================ | ||||||
|  |  | ||||||
| @@ -880,6 +1418,3 @@ what's possible. | |||||||
|  |  | ||||||
| If you're really itching to learn and use this library, please be patient. | If you're really itching to learn and use this library, please be patient. | ||||||
| We're working hard on finishing both the code and documentation. | We're working hard on finishing both the code and documentation. | ||||||
|  |  | ||||||
| Widgets |  | ||||||
| ======= |  | ||||||
|   | |||||||
| @@ -177,7 +177,7 @@ tools that can be used to establish tests and test conditions. | |||||||
|  |  | ||||||
| * `Test Client`_ | * `Test Client`_ | ||||||
| * `TestCase`_ | * `TestCase`_ | ||||||
| * `Email services`_ | * `E-mail services`_ | ||||||
|  |  | ||||||
| Test Client | Test Client | ||||||
| ----------- | ----------- | ||||||
| @@ -459,9 +459,9 @@ Emptying the test outbox | |||||||
| **New in Django development version** | **New in Django development version** | ||||||
|  |  | ||||||
| At the start of each test case, in addition to installing fixtures, | At the start of each test case, in addition to installing fixtures, | ||||||
| Django clears the contents of the test email outbox. | Django clears the contents of the test e-mail outbox. | ||||||
|  |  | ||||||
| For more detail on email services during tests, see `Email services`_. | For more detail on e-mail services during tests, see `E-mail services`_. | ||||||
|  |  | ||||||
| Assertions | Assertions | ||||||
| ~~~~~~~~~~ | ~~~~~~~~~~ | ||||||
| @@ -502,16 +502,17 @@ that can be useful in testing the behavior of web sites. | |||||||
|     Assert that the template with the given name was used in rendering the |     Assert that the template with the given name was used in rendering the | ||||||
|     response. |     response. | ||||||
|  |  | ||||||
| Email services | E-mail services | ||||||
| -------------- | --------------- | ||||||
|  |  | ||||||
| **New in Django development version** | **New in Django development version** | ||||||
|  |  | ||||||
| If your view makes use of the `Django email services`_, you don't really | If your view makes use of the `Django e-mail services`_, you don't really | ||||||
| want email to be sent every time you run a test using that view. | want e-mail to be sent every time you run a test using that view. | ||||||
|  |  | ||||||
| When the Django test framework is initialized, it transparently replaces the | When the Django test framework is initialized, it transparently replaces the | ||||||
| normal `SMTPConnection`_ class with a dummy implementation that redirects all | normal `SMTPConnection`_ class with a dummy implementation that redirects all | ||||||
| email to a dummy outbox. This outbox, stored as ``django.core.mail.outbox``, | e-mail to a dummy outbox. This outbox, stored as ``django.core.mail.outbox``, | ||||||
| is a simple list of all `EmailMessage`_ instances that have been sent. | is a simple list of all `EmailMessage`_ instances that have been sent. | ||||||
| For example, during test conditions, it would be possible to run the following | For example, during test conditions, it would be possible to run the following | ||||||
| code:: | code:: | ||||||
| @@ -541,7 +542,7 @@ to mail.outbox:: | |||||||
|     # Empty the test outbox |     # Empty the test outbox | ||||||
|     mail.outbox = [] |     mail.outbox = [] | ||||||
|  |  | ||||||
| .. _`Django email services`: ../email/ | .. _`Django e-mail services`: ../email/ | ||||||
| .. _`SMTPConnection`: ../email/#the-emailmessage-and-smtpconnection-classes | .. _`SMTPConnection`: ../email/#the-emailmessage-and-smtpconnection-classes | ||||||
| .. _`EmailMessage`: ../email/#the-emailmessage-and-smtpconnection-classes | .. _`EmailMessage`: ../email/#the-emailmessage-and-smtpconnection-classes | ||||||
| .. _`previously`: #emptying-the-test-outbox | .. _`previously`: #emptying-the-test-outbox | ||||||
| @@ -669,7 +670,7 @@ a number of utility methods in the ``django.test.utils`` module. | |||||||
|  |  | ||||||
| ``teardown_test_environment()`` | ``teardown_test_environment()`` | ||||||
|     Performs any global post-test teardown, such as removing the instrumentation |     Performs any global post-test teardown, such as removing the instrumentation | ||||||
|     of the template rendering system and restoring normal email services. |     of the template rendering system and restoring normal e-mail services. | ||||||
|  |  | ||||||
| ``create_test_db(verbosity=1, autoclobber=False)`` | ``create_test_db(verbosity=1, autoclobber=False)`` | ||||||
|     Creates a new test database, and run ``syncdb`` against it. |     Creates a new test database, and run ``syncdb`` against it. | ||||||
|   | |||||||
| @@ -179,6 +179,18 @@ fields with the 'choices' attribute are represented by a ChoiceField. | |||||||
| <option value="3">Third test</option> | <option value="3">Third test</option> | ||||||
| </select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr> | </select><br /> Hold down "Control", or "Command" on a Mac, to select more than one.</td></tr> | ||||||
|  |  | ||||||
|  | You can restrict a form to a subset of the complete list of fields | ||||||
|  | by providing a 'fields' argument. If you try to save a | ||||||
|  | model created with such a form, you need to ensure that the fields | ||||||
|  | that are _not_ on the form have default values, or are allowed to have | ||||||
|  | a value of None. If a field isn't specified on a form, the object created | ||||||
|  | from the form can't provide a value for that field! | ||||||
|  | >>> PartialArticleForm = form_for_model(Article, fields=('headline','pub_date')) | ||||||
|  | >>> f = PartialArticleForm(auto_id=False) | ||||||
|  | >>> print f | ||||||
|  | <tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" /></td></tr> | ||||||
|  | <tr><th>Pub date:</th><td><input type="text" name="pub_date" /></td></tr> | ||||||
|  |  | ||||||
| You can pass a custom Form class to form_for_model. Make sure it's a | You can pass a custom Form class to form_for_model. Make sure it's a | ||||||
| subclass of BaseForm, not Form. | subclass of BaseForm, not Form. | ||||||
| >>> class CustomForm(BaseForm): | >>> class CustomForm(BaseForm): | ||||||
| @@ -224,7 +236,23 @@ current values are inserted as 'initial' data in each Field. | |||||||
| <option value="2">It's a test</option> | <option value="2">It's a test</option> | ||||||
| <option value="3">Third test</option> | <option value="3">Third test</option> | ||||||
| </select>  Hold down "Control", or "Command" on a Mac, to select more than one.</li> | </select>  Hold down "Control", or "Command" on a Mac, to select more than one.</li> | ||||||
| >>> f = TestArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04', 'writer': u'1', 'article': 'Hello.'}) | >>> f = TestArticleForm({'headline': u'Test headline', 'pub_date': u'1984-02-06', 'writer': u'1', 'article': 'Hello.'}) | ||||||
|  | >>> f.is_valid() | ||||||
|  | True | ||||||
|  | >>> test_art = f.save() | ||||||
|  | >>> test_art.id | ||||||
|  | 1 | ||||||
|  | >>> test_art = Article.objects.get(id=1) | ||||||
|  | >>> test_art.headline | ||||||
|  | 'Test headline' | ||||||
|  |  | ||||||
|  | You can create a form over a subset of the available fields  | ||||||
|  | by specifying a 'fields' argument to form_for_instance.  | ||||||
|  | >>> PartialArticleForm = form_for_instance(art, fields=('headline','pub_date')) | ||||||
|  | >>> f = PartialArticleForm({'headline': u'New headline', 'pub_date': u'1988-01-04'}, auto_id=False) | ||||||
|  | >>> print f.as_ul() | ||||||
|  | <li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" /></li> | ||||||
|  | <li>Pub date: <input type="text" name="pub_date" value="1988-01-04" /></li> | ||||||
| >>> f.is_valid() | >>> f.is_valid() | ||||||
| True | True | ||||||
| >>> new_art = f.save() | >>> new_art = f.save() | ||||||
|   | |||||||
| @@ -34,4 +34,18 @@ Unicode decoding problems... | |||||||
| >>> f = SomeForm() | >>> f = SomeForm() | ||||||
| >>> f.as_p() | >>> f.as_p() | ||||||
| u'<p><label for="id_somechoice_0">Somechoice:</label> <ul>\n<li><label><input type="radio" id="id_somechoice_0" value="0" name="somechoice" /> En tied\xe4</label></li>\n<li><label><input type="radio" id="id_somechoice_1" value="1" name="somechoice" /> Mies</label></li>\n<li><label><input type="radio" id="id_somechoice_2" value="2" name="somechoice" /> Nainen</label></li>\n</ul></p>' | u'<p><label for="id_somechoice_0">Somechoice:</label> <ul>\n<li><label><input type="radio" id="id_somechoice_0" value="0" name="somechoice" /> En tied\xe4</label></li>\n<li><label><input type="radio" id="id_somechoice_1" value="1" name="somechoice" /> Mies</label></li>\n<li><label><input type="radio" id="id_somechoice_2" value="2" name="somechoice" /> Nainen</label></li>\n</ul></p>' | ||||||
|  |  | ||||||
|  | ####################### | ||||||
|  | # Miscellaneous Tests # | ||||||
|  | ####################### | ||||||
|  |  | ||||||
|  | There once was a problem with Form fields called "data". Let's make sure that | ||||||
|  | doesn't come back. | ||||||
|  | >>> class DataForm(Form): | ||||||
|  | ...     data = CharField(max_length=10) | ||||||
|  | >>> f = DataForm({'data': 'xyzzy'}) | ||||||
|  | >>> f.is_valid() | ||||||
|  | True | ||||||
|  | >>> f.clean_data | ||||||
|  | {'data': u'xyzzy'} | ||||||
| """ | """ | ||||||
|   | |||||||
| @@ -1916,6 +1916,34 @@ True | |||||||
| >>> p.clean_data | >>> p.clean_data | ||||||
| {'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} | {'first_name': u'John', 'last_name': u'Lennon', 'birthday': datetime.date(1940, 10, 9)} | ||||||
|  |  | ||||||
|  | clean_data will include a key and value for *all* fields defined in the Form, | ||||||
|  | even if the Form's data didn't include a value for fields that are not | ||||||
|  | required. In this example, the data dictionary doesn't include a value for the | ||||||
|  | "nick_name" field, but clean_data includes it. For CharFields, it's set to the | ||||||
|  | empty string. | ||||||
|  | >>> class OptionalPersonForm(Form): | ||||||
|  | ...     first_name = CharField() | ||||||
|  | ...     last_name = CharField() | ||||||
|  | ...     nick_name = CharField(required=False) | ||||||
|  | >>> data = {'first_name': u'John', 'last_name': u'Lennon'} | ||||||
|  | >>> f = OptionalPersonForm(data) | ||||||
|  | >>> f.is_valid() | ||||||
|  | True | ||||||
|  | >>> f.clean_data | ||||||
|  | {'nick_name': u'', 'first_name': u'John', 'last_name': u'Lennon'} | ||||||
|  |  | ||||||
|  | For DateFields, it's set to None. | ||||||
|  | >>> class OptionalPersonForm(Form): | ||||||
|  | ...     first_name = CharField() | ||||||
|  | ...     last_name = CharField() | ||||||
|  | ...     birth_date = DateField(required=False) | ||||||
|  | >>> data = {'first_name': u'John', 'last_name': u'Lennon'} | ||||||
|  | >>> f = OptionalPersonForm(data) | ||||||
|  | >>> f.is_valid() | ||||||
|  | True | ||||||
|  | >>> f.clean_data | ||||||
|  | {'birth_date': None, 'first_name': u'John', 'last_name': u'Lennon'} | ||||||
|  |  | ||||||
| "auto_id" tells the Form to add an "id" attribute to each form element. | "auto_id" tells the Form to add an "id" attribute to each form element. | ||||||
| If it's a string that contains '%s', Django will use that as a format string | If it's a string that contains '%s', Django will use that as a format string | ||||||
| into which the field's name will be inserted. It will also put a <label> around | into which the field's name will be inserted. It will also put a <label> around | ||||||
| @@ -2275,7 +2303,7 @@ returns a list of input. | |||||||
| Validation errors are HTML-escaped when output as HTML. | Validation errors are HTML-escaped when output as HTML. | ||||||
| >>> class EscapingForm(Form): | >>> class EscapingForm(Form): | ||||||
| ...     special_name = CharField() | ...     special_name = CharField() | ||||||
| ...     def clean_special_name(self): | ...     def do_clean_special_name(self): | ||||||
| ...         raise ValidationError("Something's wrong with '%s'" % self.clean_data['special_name']) | ...         raise ValidationError("Something's wrong with '%s'" % self.clean_data['special_name']) | ||||||
|  |  | ||||||
| >>> f = EscapingForm({'special_name': "Nothing to escape"}, auto_id=False) | >>> f = EscapingForm({'special_name': "Nothing to escape"}, auto_id=False) | ||||||
| @@ -2298,7 +2326,7 @@ including the current field (e.g., the field XXX if you're in clean_XXX()). | |||||||
| ...    username = CharField(max_length=10) | ...    username = CharField(max_length=10) | ||||||
| ...    password1 = CharField(widget=PasswordInput) | ...    password1 = CharField(widget=PasswordInput) | ||||||
| ...    password2 = CharField(widget=PasswordInput) | ...    password2 = CharField(widget=PasswordInput) | ||||||
| ...    def clean_password2(self): | ...    def do_clean_password2(self): | ||||||
| ...        if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']: | ...        if self.clean_data.get('password1') and self.clean_data.get('password2') and self.clean_data['password1'] != self.clean_data['password2']: | ||||||
| ...            raise ValidationError(u'Please make sure your passwords match.') | ...            raise ValidationError(u'Please make sure your passwords match.') | ||||||
| ...        return self.clean_data['password2'] | ...        return self.clean_data['password2'] | ||||||
| @@ -2752,6 +2780,64 @@ then the latter will get precedence. | |||||||
| <li>Username: <input type="text" name="username" value="babik" maxlength="10" /></li> | <li>Username: <input type="text" name="username" value="babik" maxlength="10" /></li> | ||||||
| <li>Password: <input type="password" name="password" /></li> | <li>Password: <input type="password" name="password" /></li> | ||||||
|  |  | ||||||
|  | # Callable initial data ######################################################## | ||||||
|  |  | ||||||
|  | The previous technique dealt with raw values as initial data, but it's also | ||||||
|  | possible to specify callable data. | ||||||
|  |  | ||||||
|  | >>> class UserRegistration(Form): | ||||||
|  | ...    username = CharField(max_length=10) | ||||||
|  | ...    password = CharField(widget=PasswordInput) | ||||||
|  |  | ||||||
|  | We need to define functions that get called later. | ||||||
|  | >>> def initial_django(): | ||||||
|  | ...     return 'django' | ||||||
|  | >>> def initial_stephane(): | ||||||
|  | ...     return 'stephane' | ||||||
|  |  | ||||||
|  | Here, we're not submitting any data, so the initial value will be displayed. | ||||||
|  | >>> p = UserRegistration(initial={'username': initial_django}, auto_id=False) | ||||||
|  | >>> print p.as_ul() | ||||||
|  | <li>Username: <input type="text" name="username" value="django" maxlength="10" /></li> | ||||||
|  | <li>Password: <input type="password" name="password" /></li> | ||||||
|  |  | ||||||
|  | The 'initial' parameter is meaningless if you pass data. | ||||||
|  | >>> p = UserRegistration({}, initial={'username': initial_django}, auto_id=False) | ||||||
|  | >>> print p.as_ul() | ||||||
|  | <li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> | ||||||
|  | <li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> | ||||||
|  | >>> p = UserRegistration({'username': u''}, initial={'username': initial_django}, auto_id=False) | ||||||
|  | >>> print p.as_ul() | ||||||
|  | <li><ul class="errorlist"><li>This field is required.</li></ul>Username: <input type="text" name="username" maxlength="10" /></li> | ||||||
|  | <li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> | ||||||
|  | >>> p = UserRegistration({'username': u'foo'}, initial={'username': initial_django}, auto_id=False) | ||||||
|  | >>> print p.as_ul() | ||||||
|  | <li>Username: <input type="text" name="username" value="foo" maxlength="10" /></li> | ||||||
|  | <li><ul class="errorlist"><li>This field is required.</li></ul>Password: <input type="password" name="password" /></li> | ||||||
|  |  | ||||||
|  | A callable 'initial' value is *not* used as a fallback if data is not provided. | ||||||
|  | In this example, we don't provide a value for 'username', and the form raises a | ||||||
|  | validation error rather than using the initial value for 'username'. | ||||||
|  | >>> p = UserRegistration({'password': 'secret'}, initial={'username': initial_django}) | ||||||
|  | >>> p.errors | ||||||
|  | {'username': [u'This field is required.']} | ||||||
|  | >>> p.is_valid() | ||||||
|  | False | ||||||
|  |  | ||||||
|  | If a Form defines 'initial' *and* 'initial' is passed as a parameter to Form(), | ||||||
|  | then the latter will get precedence. | ||||||
|  | >>> class UserRegistration(Form): | ||||||
|  | ...    username = CharField(max_length=10, initial=initial_django) | ||||||
|  | ...    password = CharField(widget=PasswordInput) | ||||||
|  | >>> p = UserRegistration(auto_id=False) | ||||||
|  | >>> print p.as_ul() | ||||||
|  | <li>Username: <input type="text" name="username" value="django" maxlength="10" /></li> | ||||||
|  | <li>Password: <input type="password" name="password" /></li> | ||||||
|  | >>> p = UserRegistration(initial={'username': initial_stephane}, auto_id=False) | ||||||
|  | >>> print p.as_ul() | ||||||
|  | <li>Username: <input type="text" name="username" value="stephane" maxlength="10" /></li> | ||||||
|  | <li>Password: <input type="password" name="password" /></li> | ||||||
|  |  | ||||||
| # Help text ################################################################### | # Help text ################################################################### | ||||||
|  |  | ||||||
| You can specify descriptive text for a field by using the 'help_text' argument | You can specify descriptive text for a field by using the 'help_text' argument | ||||||
|   | |||||||
| @@ -101,6 +101,12 @@ class Anchor(models.Model): | |||||||
|      |      | ||||||
|     data = models.CharField(maxlength=30) |     data = models.CharField(maxlength=30) | ||||||
|  |  | ||||||
|  | class UniqueAnchor(models.Model): | ||||||
|  |     """This is a model that can be used as  | ||||||
|  |     something for other models to point at""" | ||||||
|  |  | ||||||
|  |     data = models.CharField(unique=True, maxlength=30) | ||||||
|  |      | ||||||
| class FKData(models.Model): | class FKData(models.Model): | ||||||
|     data = models.ForeignKey(Anchor, null=True) |     data = models.ForeignKey(Anchor, null=True) | ||||||
|      |      | ||||||
| @@ -116,6 +122,10 @@ class FKSelfData(models.Model): | |||||||
| class M2MSelfData(models.Model): | class M2MSelfData(models.Model): | ||||||
|     data = models.ManyToManyField('self', null=True, symmetrical=False) |     data = models.ManyToManyField('self', null=True, symmetrical=False) | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class FKDataToField(models.Model): | ||||||
|  |     data = models.ForeignKey(UniqueAnchor, null=True, to_field='data') | ||||||
|  |  | ||||||
| # The following test classes are for validating the | # The following test classes are for validating the | ||||||
| # deserialization of objects that use a user-defined | # deserialization of objects that use a user-defined | ||||||
| # field as the primary key. | # field as the primary key. | ||||||
|   | |||||||
| @@ -159,6 +159,7 @@ The end."""), | |||||||
|  |  | ||||||
|     (data_obj, 300, Anchor, "Anchor 1"), |     (data_obj, 300, Anchor, "Anchor 1"), | ||||||
|     (data_obj, 301, Anchor, "Anchor 2"), |     (data_obj, 301, Anchor, "Anchor 2"), | ||||||
|  |     (data_obj, 302, UniqueAnchor, "UAnchor 1"), | ||||||
|  |  | ||||||
|     (fk_obj, 400, FKData, 300), # Post reference |     (fk_obj, 400, FKData, 300), # Post reference | ||||||
|     (fk_obj, 401, FKData, 500), # Pre reference |     (fk_obj, 401, FKData, 500), # Pre reference | ||||||
| @@ -184,8 +185,13 @@ The end."""), | |||||||
|     (m2m_obj, 445, M2MSelfData, []), |     (m2m_obj, 445, M2MSelfData, []), | ||||||
|     (m2m_obj, 446, M2MSelfData, []), |     (m2m_obj, 446, M2MSelfData, []), | ||||||
|  |  | ||||||
|  |     (fk_obj, 450, FKDataToField, "UAnchor 1"), | ||||||
|  |     (fk_obj, 451, FKDataToField, "UAnchor 2"), | ||||||
|  |     (fk_obj, 452, FKDataToField, None), | ||||||
|  |      | ||||||
|     (data_obj, 500, Anchor, "Anchor 3"), |     (data_obj, 500, Anchor, "Anchor 3"), | ||||||
|     (data_obj, 501, Anchor, "Anchor 4"), |     (data_obj, 501, Anchor, "Anchor 4"), | ||||||
|  |     (data_obj, 502, UniqueAnchor, "UAnchor 2"), | ||||||
|  |  | ||||||
|     (pk_obj, 601, BooleanPKData, True), |     (pk_obj, 601, BooleanPKData, True), | ||||||
|     (pk_obj, 602, BooleanPKData, False), |     (pk_obj, 602, BooleanPKData, False), | ||||||
|   | |||||||
| @@ -61,6 +61,34 @@ class AssertTemplateUsedTests(TestCase): | |||||||
|         except AssertionError, e: |         except AssertionError, e: | ||||||
|             self.assertEquals(str(e), "Template 'Valid POST Template' was not one of the templates used to render the response. Templates used: ['form_view.html', 'base.html']") |             self.assertEquals(str(e), "Template 'Valid POST Template' was not one of the templates used to render the response. Templates used: ['form_view.html', 'base.html']") | ||||||
|  |  | ||||||
|  | class AssertRedirectsTests(TestCase): | ||||||
|  |     def test_redirect_page(self): | ||||||
|  |         "An assertion is raised if the original page couldn't be retrieved as expected"         | ||||||
|  |         # This page will redirect with code 301, not 302 | ||||||
|  |         response = self.client.get('/test_client/permanent_redirect_view/')         | ||||||
|  |         try: | ||||||
|  |             self.assertRedirects(response, '/test_client/get_view/') | ||||||
|  |         except AssertionError, e: | ||||||
|  |             self.assertEquals(str(e), "Response didn't redirect as expected: Reponse code was 301 (expected 302)") | ||||||
|  |  | ||||||
|  |     def test_incorrect_target(self): | ||||||
|  |         "An assertion is raised if the response redirects to another target" | ||||||
|  |         response = self.client.get('/test_client/permanent_redirect_view/')         | ||||||
|  |         try: | ||||||
|  |             # Should redirect to get_view | ||||||
|  |             self.assertRedirects(response, '/test_client/some_view/') | ||||||
|  |         except AssertionError, e: | ||||||
|  |             self.assertEquals(str(e), "Response didn't redirect as expected: Reponse code was 301 (expected 302)") | ||||||
|  |          | ||||||
|  |     def test_target_page(self): | ||||||
|  |         "An assertion is raised if the reponse redirect target cannot be retrieved as expected" | ||||||
|  |         response = self.client.get('/test_client/double_redirect_view/') | ||||||
|  |         try: | ||||||
|  |             # The redirect target responds with a 301 code, not 200 | ||||||
|  |             self.assertRedirects(response, '/test_client/permanent_redirect_view/') | ||||||
|  |         except AssertionError, e: | ||||||
|  |             self.assertEquals(str(e), "Couldn't retrieve redirection page '/test_client/permanent_redirect_view/': response code was 301 (expected 200)") | ||||||
|  |              | ||||||
| class AssertFormErrorTests(TestCase): | class AssertFormErrorTests(TestCase): | ||||||
|     def test_unknown_form(self): |     def test_unknown_form(self): | ||||||
|         "An assertion is raised if the form name is unknown" |         "An assertion is raised if the form name is unknown" | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user