Ticket #12521: 12521.2.diff
File 12521.2.diff, 24.4 KB (added by , 15 years ago) |
---|
-
django/contrib/admin/options.py
diff --git a/django/contrib/admin/options.py b/django/contrib/admin/options.py index 6dc707e..8f68eb1 100644
a b class ModelAdmin(BaseModelAdmin): 579 579 """ 580 580 messages.info(request, message) 581 581 582 def save_form(self, request, form, change , commit=False):582 def save_form(self, request, form, change): 583 583 """ 584 584 Given a ModelForm return an unsaved instance. ``change`` is True if 585 585 the object is being changed, and False if it's being added. 586 586 """ 587 return form.save(commit= commit)587 return form.save(commit=False) 588 588 589 589 def save_model(self, request, obj, form, change): 590 590 """ … … class ModelAdmin(BaseModelAdmin): 758 758 if request.method == 'POST': 759 759 form = ModelForm(request.POST, request.FILES) 760 760 if form.is_valid(): 761 # Save the object, even if inline formsets haven't been 762 # validated yet. We need to pass the valid model to the 763 # formsets for validation. If the formsets do not validate, we 764 # will delete the object. 765 new_object = self.save_form(request, form, change=False, commit=True) 761 new_object = self.save_form(request, form, change=False) 766 762 form_validated = True 767 763 else: 768 764 form_validated = False … … class ModelAdmin(BaseModelAdmin): 779 775 prefix=prefix, queryset=inline.queryset(request)) 780 776 formsets.append(formset) 781 777 if all_valid(formsets) and form_validated: 778 self.save_model(request, new_object, form, change=False) 779 form.save_m2m() 782 780 for formset in formsets: 783 781 self.save_formset(request, form, formset, change=False) 784 782 785 783 self.log_addition(request, new_object) 786 784 return self.response_add(request, new_object) 787 elif form_validated:788 # The form was valid, but formsets were not, so delete the789 # object we saved above.790 new_object.delete()791 785 else: 792 786 # Prepare the dict of initial data from the request. 793 787 # We have to special-case M2Ms as a list of comma-separated PKs. -
django/contrib/auth/forms.py
diff --git a/django/contrib/auth/forms.py b/django/contrib/auth/forms.py index dbc55ca..e9a7fb2 100644
a b 1 from django.contrib.auth.models import User , UNUSABLE_PASSWORD1 from django.contrib.auth.models import User 2 2 from django.contrib.auth import authenticate 3 3 from django.contrib.auth.tokens import default_token_generator 4 4 from django.contrib.sites.models import Site … … class UserCreationForm(forms.ModelForm): 21 21 model = User 22 22 fields = ("username",) 23 23 24 def clean(self):25 # Fill the password field so model validation won't complain about it26 # being blank. We'll set it with the real value below.27 self.instance.password = UNUSABLE_PASSWORD28 super(UserCreationForm, self).clean()29 30 24 def clean_username(self): 31 25 username = self.cleaned_data["username"] 32 26 try: … … class UserCreationForm(forms.ModelForm): 43 37 self.instance.set_password(password1) 44 38 return password2 45 39 40 def save(self, *args, **kwargs): 41 self.instance.set_password(self.cleaned_data["password1"]) 42 return super(UserCreationForm, self).save(*args, **kwargs) 43 46 44 class UserChangeForm(forms.ModelForm): 47 45 username = forms.RegexField(label=_("Username"), max_length=30, regex=r'^\w+$', 48 46 help_text = _("Required. 30 characters or fewer. Alphanumeric characters only (letters, digits and underscores)."), -
django/core/exceptions.py
diff --git a/django/core/exceptions.py b/django/core/exceptions.py index fee7db4..bc599df 100644
a b class FieldError(Exception): 33 33 pass 34 34 35 35 NON_FIELD_ERRORS = '__all__' 36 class BaseValidationError(Exception):36 class ValidationError(Exception): 37 37 """An error while validating data.""" 38 38 def __init__(self, message, code=None, params=None): 39 39 import operator … … class BaseValidationError(Exception): 64 64 return repr(self.message_dict) 65 65 return repr(self.messages) 66 66 67 class ValidationError(BaseValidationError): 68 pass 69 70 class UnresolvableValidationError(BaseValidationError): 71 """Validation error that cannot be resolved by the user.""" 72 pass 67 def update_errors(errors, e): 68 if hasattr(e, 'message_dict'): 69 if errors: 70 for k, v in e.message_dict.items(): 71 errors.setdefault(k, []).extend(v) 72 else: 73 errors = e.message_dict 74 else: 75 errors[NON_FIELD_ERRORS] = e.messages 76 return errors 73 77 -
django/db/models/base.py
diff --git a/django/db/models/base.py b/django/db/models/base.py index 06db7cc..6a1960b 100644
a b import sys 3 3 import os 4 4 from itertools import izip 5 5 import django.db.models.manager # Imported to register signal handler. 6 from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError, ValidationError, NON_FIELD_ERRORS 6 from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError, ValidationError, NON_FIELD_ERRORS, update_errors 7 7 from django.core import validators 8 8 from django.db.models.fields import AutoField, FieldDoesNotExist 9 9 from django.db.models.fields.related import OneToOneRel, ManyToOneRel, OneToOneField … … class Model(object): 642 642 def prepare_database_save(self, unused): 643 643 return self.pk 644 644 645 def validate(self):645 def full_validate(self): 646 646 """ 647 Hook for doing any extra model-wide validation after clean() has been 648 called on every field. Any ValidationError raised by this method will 649 not be associated with a particular field; it will have a special-case 650 association with the field defined by NON_FIELD_ERRORS. 647 Calls validate_fields, validate_unique, and validate on the model, 648 and combines and reraises any ``ValidationError``s that occur. 651 649 """ 652 self.validate_unique() 650 errors = {} 651 652 try: 653 self.validate_fields() 654 except ValidationError, e: 655 errors = update_errors(errors, e) 653 656 654 def validate_unique(self): 655 unique_checks, date_checks = self._get_unique_checks() 657 try: 658 self.validate_unique() 659 except ValidationError, e: 660 errors = update_errors(errors, e) 661 662 # Form.clean() is run even if other validation fails, so do the 663 # same with Model.validate() for consistency. 664 try: 665 self.validate() 666 except ValidationError, e: 667 errors = update_errors(errors, e) 668 669 if errors: 670 raise ValidationError(errors) 671 672 def validate_fields(self, fields=None): 673 """ 674 Cleans all fields and raises a ValidationError containing message_dict 675 of all validation errors if any occur. 676 677 If ``fields`` is supplied, only validate the specified fields. 678 """ 679 errors = {} 680 if fields is None: 681 fields = self._meta.fields 682 else: 683 fields = [self._meta.get_field_by_name(f)[0] for f in fields] 684 for f in fields: 685 try: 686 setattr(self, f.attname, f.clean(getattr(self, f.attname), self)) 687 except ValidationError, e: 688 errors[f.name] = e.messages 689 if errors: 690 raise ValidationError(errors) 691 692 def validate_unique(self, fields=None): 693 unique_checks, date_checks = self._get_unique_checks(fields=fields) 656 694 657 695 errors = self._perform_unique_checks(unique_checks) 658 696 date_errors = self._perform_date_checks(date_checks) … … class Model(object): 663 701 if errors: 664 702 raise ValidationError(errors) 665 703 666 def _get_unique_checks(self): 667 from django.db.models.fields import FieldDoesNotExist, Field as ModelField 704 def _get_unique_checks(self, fields=None): 705 if fields is None: 706 fields = [f.name for f in self._meta.fields] 707 unique_checks = [] 708 # Gather a list of checks to perform. Since validate_unique could be 709 # called from a ModelForm, some fields may have been excluded; we can't 710 # perform a unique check on a model that is missing fields involved 711 # in that check. It also does not make sense to check data that didn't 712 # validate, and since NULL does not equal NULL in SQL we should not do 713 # any unique checking for NULL values. 714 for check in self._meta.unique_together: 715 for name in check: 716 # If this is an excluded field, short circuit and don't a unique_check. 717 if name not in fields: 718 break 719 # Also, Skip fields that don't have a value, we can't check for 720 # their uniqueness. 721 if getattr(self, name, None) is None: 722 break 723 else: 724 unique_checks.append(check) 668 725 669 unique_checks = list(self._meta.unique_together) 670 # these are checks for the unique_for_<date/year/month> 726 # These are checks for the unique_for_<date/year/month>. 671 727 date_checks = [] 672 728 673 729 # Gather a list of checks for fields declared as unique and add them to 674 730 # the list of checks. Again, skip empty fields and any that did not validate. 675 731 for f in self._meta.fields: 676 732 name = f.name 733 # Don't add excluded fields to unique for date checks. 734 if name not in fields: 735 continue 677 736 if f.unique: 678 737 unique_checks.append((name,)) 679 738 if f.unique_for_date: … … class Model(object): 684 743 date_checks.append(('month', name, f.unique_for_month)) 685 744 return unique_checks, date_checks 686 745 687 688 746 def _perform_unique_checks(self, unique_checks): 689 747 errors = {} 690 748 … … class Model(object): 781 839 'field_label': unicode(field_labels) 782 840 } 783 841 784 def full_validate(self, exclude=[]):842 def validate(self): 785 843 """ 786 Cleans all fields and raises ValidationError containing message_dict 787 of all validation errors if any occur. 844 Hook for doing any extra model-wide validation after clean() has been 845 called on every field. Any ValidationError raised by this method will 846 not be associated with a particular field; it will have a special-case 847 association with the field defined by NON_FIELD_ERRORS. 788 848 """ 789 errors = {} 790 for f in self._meta.fields: 791 if f.name in exclude: 792 continue 793 try: 794 setattr(self, f.attname, f.clean(getattr(self, f.attname), self)) 795 except ValidationError, e: 796 errors[f.name] = e.messages 797 798 # Form.clean() is run even if other validation fails, so do the 799 # same with Model.validate() for consistency. 800 try: 801 self.validate() 802 except ValidationError, e: 803 if hasattr(e, 'message_dict'): 804 if errors: 805 for k, v in e.message_dict.items(): 806 errors.set_default(k, []).extend(v) 807 else: 808 errors = e.message_dict 809 else: 810 errors[NON_FIELD_ERRORS] = e.messages 811 812 if errors: 813 raise ValidationError(errors) 849 pass 814 850 815 851 816 852 ############################################ -
django/db/models/fields/related.py
diff --git a/django/db/models/fields/related.py b/django/db/models/fields/related.py index 8fec836..ae6a2f7 100644
a b class ForeignKey(RelatedField, Field): 740 740 def validate(self, value, model_instance): 741 741 if self.rel.parent_link: 742 742 return 743 # Don't validate the field if a value wasn't supplied. This is 744 # generally the case when saving new inlines in the admin. 745 # See #12507. 746 if value is None: 747 return 743 748 super(ForeignKey, self).validate(value, model_instance) 744 749 if not value: 745 750 return -
django/forms/models.py
diff --git a/django/forms/models.py b/django/forms/models.py index ff20c93..58d6a10 100644
a b from django.utils.datastructures import SortedDict 9 9 from django.utils.text import get_text_list, capfirst 10 10 from django.utils.translation import ugettext_lazy as _, ugettext 11 11 12 from django.core.exceptions import ValidationError, NON_FIELD_ERRORS, UnresolvableValidationError12 from django.core.exceptions import ValidationError, update_errors, NON_FIELD_ERRORS 13 13 from django.core.validators import EMPTY_VALUES 14 14 from util import ErrorList 15 15 from forms import BaseForm, get_declared_fields … … class BaseModelForm(BaseForm): 248 248 def clean(self): 249 249 opts = self._meta 250 250 self.instance = construct_instance(self, self.instance, opts.fields, opts.exclude) 251 # For backwards-compatibility, several types of fields need to be 252 # excluded from model validation. See the following tickets for 253 # details: #12507, #12521, #12553 254 errors = {} 255 # Build up a list of fields that should be included in model validation 256 # and unique checks. 257 fields = [] 258 for f in self.instance._meta.fields: 259 field = f.name 260 # Exclude fields that aren't on the form. The developer may be 261 # adding these values to the model after form validation. 262 if field not in self.fields: 263 continue 264 # Exclude fields that failed form validation. There's no need for 265 # the model to validate them as well. 266 elif field in self._errors.keys(): 267 continue 268 # Exclude empty fields that are not required by the form. Model 269 # validation might complain that they are empty, but the 270 # developer is reponsible for setting their value after calling 271 # form validation. 272 elif (not self.fields[field].required) and self.cleaned_data.get(field, None) is None: 273 continue 274 else: 275 fields.append(field) 276 try: 277 self.instance.validate_fields(fields=list(fields)) 278 except ValidationError, e: 279 errors = update_errors(errors, e) 251 280 try: 252 self.instance. full_validate(exclude=self._errors.keys())281 self.instance.validate_unique(fields=fields) 253 282 except ValidationError, e: 254 for k, v in e.message_dict.items(): 283 errors = update_errors(errors, e) 284 if errors: 285 for k, v in errors.items(): 255 286 if k != NON_FIELD_ERRORS: 256 287 self._errors.setdefault(k, ErrorList()).extend(v) 257 258 288 # Remove the data from the cleaned_data dict since it was invalid 259 289 if k in self.cleaned_data: 260 290 del self.cleaned_data[k] 261 262 if NON_FIELD_ERRORS in e.message_dict: 263 raise ValidationError(e.message_dict[NON_FIELD_ERRORS]) 264 265 # If model validation threw errors for fields that aren't on the 266 # form, the the errors cannot be corrected by the user. Displaying 267 # those errors would be pointless, so raise another type of 268 # exception that *won't* be caught and displayed by the form. 269 if set(e.message_dict.keys()) - set(self.fields.keys() + [NON_FIELD_ERRORS]): 270 raise UnresolvableValidationError(e.message_dict) 271 272 291 if NON_FIELD_ERRORS in errors: 292 raise ValidationError(errors[NON_FIELD_ERRORS]) 273 293 return self.cleaned_data 274 294 275 295 def save(self, commit=True): -
tests/modeltests/model_forms/models.py
diff --git a/tests/modeltests/model_forms/models.py b/tests/modeltests/model_forms/models.py index ba59f9a..cb2aa14 100644
a b True 1114 1114 1115 1115 >>> instance.delete() 1116 1116 1117 # Test the non-required FileField 1118 >>> f = TextFileForm(data={'description': u'Assistance'}) 1119 >>> f.fields['file'].required = False 1120 >>> f.is_valid() 1121 True 1122 >>> instance = f.save() 1123 >>> instance.file 1124 <FieldFile: None> 1125 1117 1126 >>> f = TextFileForm(data={'description': u'Assistance'}, files={'file': SimpleUploadedFile('test3.txt', 'hello world')}, instance=instance) 1118 1127 >>> f.is_valid() 1119 1128 True … … True 1152 1161 >>> class BigIntForm(forms.ModelForm): 1153 1162 ... class Meta: 1154 1163 ... model = BigInt 1155 ... 1164 ... 1156 1165 >>> bif = BigIntForm({'biggie': '-9223372036854775808'}) 1157 1166 >>> bif.is_valid() 1158 1167 True … … False 1425 1434 >>> form._errors 1426 1435 {'__all__': [u'Price with this Price and Quantity already exists.']} 1427 1436 1428 # This form is never valid because quantity is blank=False. 1437 This Price instance generated by this form is not valid because the quantity 1438 field is required, but the form is valid because the field is excluded from 1439 the form. This is for backwards compatibility. 1440 1429 1441 >>> class PriceForm(ModelForm): 1430 1442 ... class Meta: 1431 1443 ... model = Price 1432 1444 ... exclude = ('quantity',) 1433 1445 >>> form = PriceForm({'price': '6.00'}) 1434 1446 >>> form.is_valid() 1447 True 1448 >>> price = form.save(commit=False) 1449 >>> price.full_validate() 1435 1450 Traceback (most recent call last): 1436 1451 ... 1437 UnresolvableValidationError: {'quantity': [u'This field cannot be null.']} 1452 ValidationError: {'quantity': [u'This field cannot be null.']} 1453 1454 The form should not validate fields that it doesn't contain even if they are 1455 specified using 'fields', not 'exclude'. 1456 ... class Meta: 1457 ... model = Price 1458 ... fields = ('price',) 1459 >>> form = PriceForm({'price': '6.00'}) 1460 >>> form.is_valid() 1461 True 1462 1463 The form should still have an instance of a model that is not complete and 1464 not saved into a DB yet. 1465 1466 >>> form.instance.price 1467 Decimal('6.00') 1468 >>> form.instance.quantity is None 1469 True 1470 >>> form.instance.pk is None 1471 True 1438 1472 1439 1473 # Unique & unique together with null values 1440 1474 >>> class BookForm(ModelForm): -
tests/modeltests/model_formsets/models.py
diff --git a/tests/modeltests/model_formsets/models.py b/tests/modeltests/model_formsets/models.py index 5eab202..702523e 100644
a b This is used in the admin for save_as functionality. 543 543 ... 'book_set-2-title': '', 544 544 ... } 545 545 546 >>> formset = AuthorBooksFormSet(data, instance=Author(), save_as_new=True) 547 >>> formset.is_valid() 548 True 549 546 550 >>> new_author = Author.objects.create(name='Charles Baudelaire') 547 551 >>> formset = AuthorBooksFormSet(data, instance=new_author, save_as_new=True) 548 552 >>> [book for book in formset.save() if book.author.pk == new_author.pk] … … False 1031 1035 >>> formset._non_form_errors 1032 1036 [u'Please correct the duplicate data for price and quantity, which must be unique.'] 1033 1037 1038 # Only the price field is specified, this should skip any unique checks since 1039 # the unique_together is not fulfilled. This will fail with a KeyError if broken. 1040 >>> FormSet = modelformset_factory(Price, fields=("price",), extra=2) 1041 >>> data = { 1042 ... 'form-TOTAL_FORMS': '2', 1043 ... 'form-INITIAL_FORMS': '0', 1044 ... 'form-0-price': '24', 1045 ... 'form-1-price': '24', 1046 ... } 1047 >>> formset = FormSet(data) 1048 >>> formset.is_valid() 1049 True 1050 1034 1051 >>> FormSet = inlineformset_factory(Author, Book, extra=0) 1035 1052 >>> author = Author.objects.order_by('id')[0] 1036 1053 >>> book_ids = author.book_set.values_list('id', flat=True) -
tests/modeltests/validation/test_unique.py
diff --git a/tests/modeltests/validation/test_unique.py b/tests/modeltests/validation/test_unique.py index cbb56aa..3aea99d 100644
a b 1 1 import unittest 2 import datetime 2 3 from django.conf import settings 3 4 from django.db import connection 4 5 from models import CustomPKModel, UniqueTogetherModel, UniqueFieldsModel, UniqueForDateModel, ModelToValidate … … class GetUniqueCheckTests(unittest.TestCase): 13 14 ) 14 15 15 16 def test_unique_together_gets_picked_up(self): 16 m = UniqueTogetherModel() 17 # Give the fields values, otherwise they won't be picked up in 18 # unique checks. 19 m = UniqueTogetherModel( 20 cfield='test', 21 ifield=1, 22 efield='test@example.com' 23 ) 17 24 self.assertEqual( 18 25 ([('ifield', 'cfield',),('ifield', 'efield'), ('id',), ], []), 19 26 m._get_unique_checks() … … class GetUniqueCheckTests(unittest.TestCase): 24 31 self.assertEqual(([('my_pk_field',)], []), m._get_unique_checks()) 25 32 26 33 def test_unique_for_date_gets_picked_up(self): 27 m = UniqueForDateModel() 34 # Give the fields values, otherwise they won't be picked up in 35 # unique checks. 36 m = UniqueForDateModel( 37 start_date=datetime.date(2010, 1, 9), 38 end_date=datetime.datetime(2010, 1, 9, 9, 0, 0), 39 count=1, 40 order=1, 41 name='test' 42 ) 28 43 self.assertEqual(( 29 30 44 [('id',)], 45 [('date', 'count', 'start_date'), ('year', 'count', 'end_date'), ('month', 'order', 'end_date')] 31 46 ), m._get_unique_checks() 32 47 ) 33 48 … … class PerformUniqueChecksTest(unittest.TestCase): 56 71 mtv = ModelToValidate(number=10, name='Some Name') 57 72 mtv.full_validate() 58 73 self.assertEqual(l, len(connection.queries)) 74 -
tests/modeltests/validation/tests.py
diff --git a/tests/modeltests/validation/tests.py b/tests/modeltests/validation/tests.py index c00070b..002e136 100644
a b class BaseModelValidationTests(ValidationTestCase): 19 19 mtv = ModelToValidate(number=10, name='Some Name') 20 20 self.assertEqual(None, mtv.full_validate()) 21 21 22 def test_custom_validate_method _is_called(self):22 def test_custom_validate_method(self): 23 23 mtv = ModelToValidate(number=11) 24 24 self.assertFailsValidation(mtv.full_validate, [NON_FIELD_ERRORS, 'name']) 25 25 -
tests/regressiontests/admin_views/tests.py
diff --git a/tests/regressiontests/admin_views/tests.py b/tests/regressiontests/admin_views/tests.py index 899fff8..351974b 100644
a b import re 4 4 import datetime 5 5 from django.core.files import temp as tempfile 6 6 from django.test import TestCase 7 from django.contrib.auth.models import User, Permission 7 from django.contrib.auth import admin # Register auth models with the admin. 8 from django.contrib.auth.models import User, Permission, UNUSABLE_PASSWORD 8 9 from django.contrib.contenttypes.models import ContentType 9 10 from django.contrib.admin.models import LogEntry, DELETION 10 11 from django.contrib.admin.sites import LOGIN_FORM_KEY … … class ReadonlyTest(TestCase): 1753 1754 self.assertEqual(Post.objects.count(), 2) 1754 1755 p = Post.objects.order_by('-id')[0] 1755 1756 self.assertEqual(p.posted, datetime.date.today()) 1757 1758 class IncompleteFormTest(TestCase): 1759 """ 1760 Tests validation of a ModelForm that doesn't explicitly have all data 1761 corresponding to model fields. Model validation shouldn't fail 1762 such a forms. 1763 """ 1764 fixtures = ['admin-views-users.xml'] 1765 1766 def setUp(self): 1767 self.client.login(username='super', password='secret') 1768 1769 def tearDown(self): 1770 self.client.logout() 1771 1772 def test_user_creation(self): 1773 response = self.client.post('/test_admin/admin/auth/user/add/', { 1774 'username': 'newuser', 1775 'password1': 'newpassword', 1776 'password2': 'newpassword', 1777 }) 1778 new_user = User.objects.order_by('-id')[0] 1779 self.assertRedirects(response, '/test_admin/admin/auth/user/%s/' % new_user.pk) 1780 self.assertNotEquals(new_user.password, UNUSABLE_PASSWORD) 1781 1782 def test_password_mismatch(self): 1783 response = self.client.post('/test_admin/admin/auth/user/add/', { 1784 'username': 'newuser', 1785 'password1': 'newpassword', 1786 'password2': 'mismatch', 1787 }) 1788 self.assertEquals(response.status_code, 200) 1789 self.assert_('password' not in response.context['form'].errors) 1790 self.assertFormError(response, 'form', 'password2', ["The two password fields didn't match."])