diff options
| author | django-bot <ops@djangoproject.com> | 2022-02-03 20:24:19 +0100 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2022-02-07 20:37:05 +0100 |
| commit | 9c19aff7c7561e3a82978a272ecdaad40dda5c00 (patch) | |
| tree | f0506b668a013d0063e5fba3dbf4863b466713ba /tests/model_forms | |
| parent | f68fa8b45dfac545cfc4111d4e52804c86db68d3 (diff) | |
Refs #33476 -- Reformatted code with Black.
Diffstat (limited to 'tests/model_forms')
| -rw-r--r-- | tests/model_forms/models.py | 147 | ||||
| -rw-r--r-- | tests/model_forms/test_modelchoicefield.py | 283 | ||||
| -rw-r--r-- | tests/model_forms/test_uuid.py | 10 | ||||
| -rw-r--r-- | tests/model_forms/tests.py | 1898 |
4 files changed, 1404 insertions, 934 deletions
diff --git a/tests/model_forms/models.py b/tests/model_forms/models.py index c0b3a64148..a6f306c5b2 100644 --- a/tests/model_forms/models.py +++ b/tests/model_forms/models.py @@ -19,7 +19,7 @@ class Person(models.Model): class Category(models.Model): name = models.CharField(max_length=20) slug = models.SlugField(max_length=20) - url = models.CharField('The URL', max_length=40) + url = models.CharField("The URL", max_length=40) def __str__(self): return self.name @@ -35,13 +35,13 @@ class WriterManager(models.Manager): class Writer(models.Model): - name = models.CharField(max_length=50, help_text='Use both first and last names.') + name = models.CharField(max_length=50, help_text="Use both first and last names.") archived = models.BooleanField(default=False, editable=False) objects = WriterManager() class Meta: - ordering = ('name',) + ordering = ("name",) def __str__(self): return self.name @@ -49,9 +49,9 @@ class Writer(models.Model): class Article(models.Model): ARTICLE_STATUS = ( - (1, 'Draft'), - (2, 'Pending'), - (3, 'Live'), + (1, "Draft"), + (2, "Pending"), + (3, "Live"), ) headline = models.CharField(max_length=50) slug = models.SlugField() @@ -92,7 +92,7 @@ class Publication(models.Model): def default_mode(): - return 'di' + return "di" def default_category(): @@ -100,19 +100,21 @@ def default_category(): class PublicationDefaults(models.Model): - MODE_CHOICES = (('di', 'direct'), ('de', 'delayed')) - CATEGORY_CHOICES = ((1, 'Games'), (2, 'Comics'), (3, 'Novel')) + MODE_CHOICES = (("di", "direct"), ("de", "delayed")) + CATEGORY_CHOICES = ((1, "Games"), (2, "Comics"), (3, "Novel")) title = models.CharField(max_length=30) date_published = models.DateField(default=datetime.date.today) datetime_published = models.DateTimeField(default=datetime.datetime(2000, 1, 1)) mode = models.CharField(max_length=2, choices=MODE_CHOICES, default=default_mode) category = models.IntegerField(choices=CATEGORY_CHOICES, default=default_category) active = models.BooleanField(default=True) - file = models.FileField(default='default.txt') + file = models.FileField(default="default.txt") class Author(models.Model): - publication = models.OneToOneField(Publication, models.SET_NULL, null=True, blank=True) + publication = models.OneToOneField( + Publication, models.SET_NULL, null=True, blank=True + ) full_name = models.CharField(max_length=255) @@ -130,12 +132,12 @@ class WriterProfile(models.Model): class Document(models.Model): - myfile = models.FileField(upload_to='unused', blank=True) + myfile = models.FileField(upload_to="unused", blank=True) class TextFile(models.Model): description = models.CharField(max_length=20) - file = models.FileField(storage=temp_storage, upload_to='tests', max_length=15) + file = models.FileField(storage=temp_storage, upload_to="tests", max_length=15) def __str__(self): return self.description @@ -143,17 +145,19 @@ class TextFile(models.Model): class CustomFileField(models.FileField): def save_form_data(self, instance, data): - been_here = getattr(self, 'been_saved', False) + been_here = getattr(self, "been_saved", False) assert not been_here, "save_form_data called more than once" - setattr(self, 'been_saved', True) + setattr(self, "been_saved", True) class CustomFF(models.Model): - f = CustomFileField(upload_to='unused', blank=True) + f = CustomFileField(upload_to="unused", blank=True) class FilePathModel(models.Model): - path = models.FilePathField(path=os.path.dirname(__file__), match='models.py', blank=True) + path = models.FilePathField( + path=os.path.dirname(__file__), match="models.py", blank=True + ) try: @@ -163,8 +167,8 @@ try: class ImageFile(models.Model): def custom_upload_path(self, filename): - path = self.path or 'tests' - return '%s/%s' % (path, filename) + path = self.path or "tests" + return "%s/%s" % (path, filename) description = models.CharField(max_length=20) @@ -172,32 +176,41 @@ try: # trigger the bug in #10404 with width/height not getting assigned. width = models.IntegerField(editable=False) height = models.IntegerField(editable=False) - image = models.ImageField(storage=temp_storage, upload_to=custom_upload_path, - width_field='width', height_field='height') - path = models.CharField(max_length=16, blank=True, default='') + image = models.ImageField( + storage=temp_storage, + upload_to=custom_upload_path, + width_field="width", + height_field="height", + ) + path = models.CharField(max_length=16, blank=True, default="") def __str__(self): return self.description class OptionalImageFile(models.Model): def custom_upload_path(self, filename): - path = self.path or 'tests' - return '%s/%s' % (path, filename) + path = self.path or "tests" + return "%s/%s" % (path, filename) description = models.CharField(max_length=20) - image = models.ImageField(storage=temp_storage, upload_to=custom_upload_path, - width_field='width', height_field='height', - blank=True, null=True) + image = models.ImageField( + storage=temp_storage, + upload_to=custom_upload_path, + width_field="width", + height_field="height", + blank=True, + null=True, + ) width = models.IntegerField(editable=False, null=True) height = models.IntegerField(editable=False, null=True) - path = models.CharField(max_length=16, blank=True, default='') + path = models.CharField(max_length=16, blank=True, default="") def __str__(self): return self.description class NoExtensionImageFile(models.Model): def upload_to(self, filename): - return 'tests/no_extension' + return "tests/no_extension" description = models.CharField(max_length=20) image = models.ImageField(storage=temp_storage, upload_to=upload_to) @@ -225,7 +238,7 @@ class Price(models.Model): quantity = models.PositiveIntegerField() class Meta: - unique_together = (('price', 'quantity'),) + unique_together = (("price", "quantity"),) def __str__(self): return "%s for %s" % (self.quantity, self.price) @@ -237,25 +250,29 @@ class Triple(models.Model): right = models.IntegerField() class Meta: - unique_together = (('left', 'middle'), ('middle', 'right')) + unique_together = (("left", "middle"), ("middle", "right")) class ArticleStatus(models.Model): ARTICLE_STATUS_CHAR = ( - ('d', 'Draft'), - ('p', 'Pending'), - ('l', 'Live'), + ("d", "Draft"), + ("p", "Pending"), + ("l", "Live"), + ) + status = models.CharField( + max_length=2, choices=ARTICLE_STATUS_CHAR, blank=True, null=True ) - status = models.CharField(max_length=2, choices=ARTICLE_STATUS_CHAR, blank=True, null=True) class Inventory(models.Model): barcode = models.PositiveIntegerField(unique=True) - parent = models.ForeignKey('self', models.SET_NULL, to_field='barcode', blank=True, null=True) + parent = models.ForeignKey( + "self", models.SET_NULL, to_field="barcode", blank=True, null=True + ) name = models.CharField(blank=False, max_length=20) class Meta: - ordering = ('name',) + ordering = ("name",) def __str__(self): return self.name @@ -270,7 +287,7 @@ class Book(models.Model): special_id = models.IntegerField(blank=True, null=True, unique=True) class Meta: - unique_together = ('title', 'author') + unique_together = ("title", "author") class BookXtra(models.Model): @@ -279,7 +296,7 @@ class BookXtra(models.Model): suffix2 = models.IntegerField(blank=True, default=0) class Meta: - unique_together = (('suffix1', 'suffix2')) + unique_together = ("suffix1", "suffix2") abstract = True @@ -292,16 +309,16 @@ class ExplicitPK(models.Model): desc = models.CharField(max_length=20, blank=True, unique=True) class Meta: - unique_together = ('key', 'desc') + unique_together = ("key", "desc") def __str__(self): return self.key class Post(models.Model): - title = models.CharField(max_length=50, unique_for_date='posted', blank=True) - slug = models.CharField(max_length=50, unique_for_year='posted', blank=True) - subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True) + title = models.CharField(max_length=50, unique_for_date="posted", blank=True) + slug = models.CharField(max_length=50, unique_for_year="posted", blank=True) + subtitle = models.CharField(max_length=50, unique_for_month="posted", blank=True) posted = models.DateField() def __str__(self): @@ -309,9 +326,9 @@ class Post(models.Model): class DateTimePost(models.Model): - title = models.CharField(max_length=50, unique_for_date='posted', blank=True) - slug = models.CharField(max_length=50, unique_for_year='posted', blank=True) - subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True) + title = models.CharField(max_length=50, unique_for_date="posted", blank=True) + slug = models.CharField(max_length=50, unique_for_year="posted", blank=True) + subtitle = models.CharField(max_length=50, unique_for_month="posted", blank=True) posted = models.DateTimeField(editable=False) def __str__(self): @@ -348,9 +365,9 @@ class CustomFieldForExclusionModel(models.Model): class FlexibleDatePost(models.Model): - title = models.CharField(max_length=50, unique_for_date='posted', blank=True) - slug = models.CharField(max_length=50, unique_for_year='posted', blank=True) - subtitle = models.CharField(max_length=50, unique_for_month='posted', blank=True) + title = models.CharField(max_length=50, unique_for_date="posted", blank=True) + slug = models.CharField(max_length=50, unique_for_year="posted", blank=True) + subtitle = models.CharField(max_length=50, unique_for_month="posted", blank=True) posted = models.DateField(blank=True, null=True) @@ -373,20 +390,24 @@ class CustomErrorMessage(models.Model): name1 = models.CharField( max_length=50, validators=[validators.validate_slug], - error_messages={'invalid': 'Model custom error message.'}, + error_messages={"invalid": "Model custom error message."}, ) name2 = models.CharField( max_length=50, validators=[validators.validate_slug], - error_messages={'invalid': 'Model custom error message.'}, + error_messages={"invalid": "Model custom error message."}, ) def clean(self): - if self.name1 == 'FORBIDDEN_VALUE': - raise ValidationError({'name1': [ValidationError('Model.clean() error messages.')]}) - elif self.name1 == 'FORBIDDEN_VALUE2': - raise ValidationError({'name1': 'Model.clean() error messages (simpler syntax).'}) - elif self.name1 == 'GLOBAL_ERROR': + if self.name1 == "FORBIDDEN_VALUE": + raise ValidationError( + {"name1": [ValidationError("Model.clean() error messages.")]} + ) + elif self.name1 == "FORBIDDEN_VALUE2": + raise ValidationError( + {"name1": "Model.clean() error messages (simpler syntax)."} + ) + elif self.name1 == "GLOBAL_ERROR": raise ValidationError("Global error message.") @@ -411,12 +432,12 @@ class StumpJoke(models.Model): Character, models.CASCADE, limit_choices_to=today_callable_dict, - related_name='jokes', + related_name="jokes", ) has_fooled_today = models.ManyToManyField( Character, limit_choices_to=today_callable_q, - related_name='jokes_today', + related_name="jokes_today", ) funny = models.BooleanField(default=False) @@ -430,7 +451,7 @@ class Student(models.Model): # Model for #639 class Photo(models.Model): title = models.CharField(max_length=30) - image = models.FileField(storage=temp_storage, upload_to='tests') + image = models.FileField(storage=temp_storage, upload_to="tests") # Support code for the tests; this keeps track of how many times save() # gets called on each instance. @@ -455,7 +476,7 @@ class StrictAssignmentFieldSpecific(models.Model): def __setattr__(self, key, value): if self._should_error is True: - raise ValidationError(message={key: "Cannot set attribute"}, code='invalid') + raise ValidationError(message={key: "Cannot set attribute"}, code="invalid") super().__setattr__(key, value) @@ -465,7 +486,7 @@ class StrictAssignmentAll(models.Model): def __setattr__(self, key, value): if self._should_error is True: - raise ValidationError(message="Cannot set attribute", code='invalid') + raise ValidationError(message="Cannot set attribute", code="invalid") super().__setattr__(key, value) @@ -487,8 +508,8 @@ class Number(models.Model): class NumbersToDice(models.Model): - number = models.ForeignKey('Number', on_delete=models.CASCADE) - die = models.ForeignKey('Dice', on_delete=models.CASCADE) + number = models.ForeignKey("Number", on_delete=models.CASCADE) + die = models.ForeignKey("Dice", on_delete=models.CASCADE) class Dice(models.Model): diff --git a/tests/model_forms/test_modelchoicefield.py b/tests/model_forms/test_modelchoicefield.py index 30d1a05821..8c3909f8a8 100644 --- a/tests/model_forms/test_modelchoicefield.py +++ b/tests/model_forms/test_modelchoicefield.py @@ -13,20 +13,25 @@ from .models import Article, Author, Book, Category, Writer class ModelChoiceFieldTests(TestCase): @classmethod def setUpTestData(cls): - cls.c1 = Category.objects.create(name='Entertainment', slug='entertainment', url='entertainment') - cls.c2 = Category.objects.create(name='A test', slug='test', url='test') - cls.c3 = Category.objects.create(name='Third', slug='third-test', url='third') + cls.c1 = Category.objects.create( + name="Entertainment", slug="entertainment", url="entertainment" + ) + cls.c2 = Category.objects.create(name="A test", slug="test", url="test") + cls.c3 = Category.objects.create(name="Third", slug="third-test", url="third") def test_basics(self): f = forms.ModelChoiceField(Category.objects.all()) - self.assertEqual(list(f.choices), [ - ('', '---------'), - (self.c1.pk, 'Entertainment'), - (self.c2.pk, 'A test'), - (self.c3.pk, 'Third'), - ]) + self.assertEqual( + list(f.choices), + [ + ("", "---------"), + (self.c1.pk, "Entertainment"), + (self.c2.pk, "A test"), + (self.c3.pk, "Third"), + ], + ) with self.assertRaises(ValidationError): - f.clean('') + f.clean("") with self.assertRaises(ValidationError): f.clean(None) with self.assertRaises(ValidationError): @@ -34,23 +39,23 @@ class ModelChoiceFieldTests(TestCase): # Invalid types that require TypeError to be caught. with self.assertRaises(ValidationError): - f.clean([['fail']]) + f.clean([["fail"]]) with self.assertRaises(ValidationError): - f.clean([{'foo': 'bar'}]) + f.clean([{"foo": "bar"}]) - self.assertEqual(f.clean(self.c2.id).name, 'A test') - self.assertEqual(f.clean(self.c3.id).name, 'Third') + self.assertEqual(f.clean(self.c2.id).name, "A test") + self.assertEqual(f.clean(self.c3.id).name, "Third") # Add a Category object *after* the ModelChoiceField has already been # instantiated. This proves clean() checks the database during clean() # rather than caching it at instantiation time. - c4 = Category.objects.create(name='Fourth', url='4th') - self.assertEqual(f.clean(c4.id).name, 'Fourth') + c4 = Category.objects.create(name="Fourth", url="4th") + self.assertEqual(f.clean(c4.id).name, "Fourth") # Delete a Category object *after* the ModelChoiceField has already been # instantiated. This proves clean() checks the database during clean() # rather than caching it at instantiation time. - Category.objects.get(url='4th').delete() + Category.objects.get(url="4th").delete() msg = "['Select a valid choice. That choice is not one of the available choices.']" with self.assertRaisesMessage(ValidationError, msg): f.clean(c4.id) @@ -64,69 +69,86 @@ class ModelChoiceFieldTests(TestCase): f.clean(Book.objects.create()) def test_clean_to_field_name(self): - f = forms.ModelChoiceField(Category.objects.all(), to_field_name='slug') + f = forms.ModelChoiceField(Category.objects.all(), to_field_name="slug") self.assertEqual(f.clean(self.c1.slug), self.c1) self.assertEqual(f.clean(self.c1), self.c1) def test_choices(self): - f = forms.ModelChoiceField(Category.objects.filter(pk=self.c1.id), required=False) - self.assertIsNone(f.clean('')) - self.assertEqual(f.clean(str(self.c1.id)).name, 'Entertainment') + f = forms.ModelChoiceField( + Category.objects.filter(pk=self.c1.id), required=False + ) + self.assertIsNone(f.clean("")) + self.assertEqual(f.clean(str(self.c1.id)).name, "Entertainment") with self.assertRaises(ValidationError): - f.clean('100') + f.clean("100") # len() can be called on choices. self.assertEqual(len(f.choices), 2) # queryset can be changed after the field is created. - f.queryset = Category.objects.exclude(name='Third') - self.assertEqual(list(f.choices), [ - ('', '---------'), - (self.c1.pk, 'Entertainment'), - (self.c2.pk, 'A test'), - ]) - self.assertEqual(f.clean(self.c2.id).name, 'A test') + f.queryset = Category.objects.exclude(name="Third") + self.assertEqual( + list(f.choices), + [ + ("", "---------"), + (self.c1.pk, "Entertainment"), + (self.c2.pk, "A test"), + ], + ) + self.assertEqual(f.clean(self.c2.id).name, "A test") with self.assertRaises(ValidationError): f.clean(self.c3.id) # Choices can be iterated repeatedly. gen_one = list(f.choices) gen_two = f.choices - self.assertEqual(gen_one[2], (self.c2.pk, 'A test')) - self.assertEqual(list(gen_two), [ - ('', '---------'), - (self.c1.pk, 'Entertainment'), - (self.c2.pk, 'A test'), - ]) + self.assertEqual(gen_one[2], (self.c2.pk, "A test")) + self.assertEqual( + list(gen_two), + [ + ("", "---------"), + (self.c1.pk, "Entertainment"), + (self.c2.pk, "A test"), + ], + ) # Overriding label_from_instance() to print custom labels. f.queryset = Category.objects.all() - f.label_from_instance = lambda obj: 'category ' + str(obj) - self.assertEqual(list(f.choices), [ - ('', '---------'), - (self.c1.pk, 'category Entertainment'), - (self.c2.pk, 'category A test'), - (self.c3.pk, 'category Third'), - ]) + f.label_from_instance = lambda obj: "category " + str(obj) + self.assertEqual( + list(f.choices), + [ + ("", "---------"), + (self.c1.pk, "category Entertainment"), + (self.c2.pk, "category A test"), + (self.c3.pk, "category Third"), + ], + ) def test_choices_freshness(self): f = forms.ModelChoiceField(Category.objects.all()) self.assertEqual(len(f.choices), 4) - self.assertEqual(list(f.choices), [ - ('', '---------'), - (self.c1.pk, 'Entertainment'), - (self.c2.pk, 'A test'), - (self.c3.pk, 'Third'), - ]) - c4 = Category.objects.create(name='Fourth', slug='4th', url='4th') + self.assertEqual( + list(f.choices), + [ + ("", "---------"), + (self.c1.pk, "Entertainment"), + (self.c2.pk, "A test"), + (self.c3.pk, "Third"), + ], + ) + c4 = Category.objects.create(name="Fourth", slug="4th", url="4th") self.assertEqual(len(f.choices), 5) - self.assertEqual(list(f.choices), [ - ('', '---------'), - (self.c1.pk, 'Entertainment'), - (self.c2.pk, 'A test'), - (self.c3.pk, 'Third'), - (c4.pk, 'Fourth'), - ]) + self.assertEqual( + list(f.choices), + [ + ("", "---------"), + (self.c1.pk, "Entertainment"), + (self.c2.pk, "A test"), + (self.c3.pk, "Third"), + (c4.pk, "Fourth"), + ], + ) def test_choices_bool(self): f = forms.ModelChoiceField(Category.objects.all(), empty_label=None) @@ -135,15 +157,15 @@ class ModelChoiceFieldTests(TestCase): self.assertIs(bool(f.choices), False) def test_choices_bool_empty_label(self): - f = forms.ModelChoiceField(Category.objects.all(), empty_label='--------') + f = forms.ModelChoiceField(Category.objects.all(), empty_label="--------") Category.objects.all().delete() self.assertIs(bool(f.choices), True) def test_choices_radio_blank(self): choices = [ - (self.c1.pk, 'Entertainment'), - (self.c2.pk, 'A test'), - (self.c3.pk, 'Third'), + (self.c1.pk, "Entertainment"), + (self.c2.pk, "A test"), + (self.c3.pk, "Third"), ] categories = Category.objects.all() for widget in [forms.RadioSelect, forms.RadioSelect()]: @@ -156,7 +178,7 @@ class ModelChoiceFieldTests(TestCase): ) self.assertEqual( list(f.choices), - [('', '---------')] + choices if blank else choices, + [("", "---------")] + choices if blank else choices, ) def test_deepcopies_widget(self): @@ -164,10 +186,10 @@ class ModelChoiceFieldTests(TestCase): category = forms.ModelChoiceField(Category.objects.all()) form1 = ModelChoiceForm() - field1 = form1.fields['category'] + field1 = form1.fields["category"] # To allow the widget to change the queryset of field1.widget.choices # without affecting other forms, the following must hold (#11183): - self.assertIsNot(field1, ModelChoiceForm.base_fields['category']) + self.assertIsNot(field1, ModelChoiceForm.base_fields["category"]) self.assertIs(field1.widget.choices.field, field1) def test_result_cache_not_shared(self): @@ -175,9 +197,11 @@ class ModelChoiceFieldTests(TestCase): category = forms.ModelChoiceField(Category.objects.all()) form1 = ModelChoiceForm() - self.assertCountEqual(form1.fields['category'].queryset, [self.c1, self.c2, self.c3]) + self.assertCountEqual( + form1.fields["category"].queryset, [self.c1, self.c2, self.c3] + ) form2 = ModelChoiceForm() - self.assertIsNone(form2.fields['category'].queryset._result_cache) + self.assertIsNone(form2.fields["category"].queryset._result_cache) def test_queryset_none(self): class ModelChoiceForm(forms.Form): @@ -185,24 +209,29 @@ class ModelChoiceFieldTests(TestCase): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['category'].queryset = Category.objects.filter(slug__contains='test') + self.fields["category"].queryset = Category.objects.filter( + slug__contains="test" + ) form = ModelChoiceForm() - self.assertCountEqual(form.fields['category'].queryset, [self.c2, self.c3]) + self.assertCountEqual(form.fields["category"].queryset, [self.c2, self.c3]) def test_no_extra_query_when_accessing_attrs(self): """ ModelChoiceField with RadioSelect widget doesn't produce unnecessary db queries when accessing its BoundField's attrs. """ + class ModelChoiceForm(forms.Form): - category = forms.ModelChoiceField(Category.objects.all(), widget=forms.RadioSelect) + category = forms.ModelChoiceField( + Category.objects.all(), widget=forms.RadioSelect + ) form = ModelChoiceForm() - field = form['category'] # BoundField - template = Template('{{ field.name }}{{ field }}{{ field.help_text }}') + field = form["category"] # BoundField + template = Template("{{ field.name }}{{ field }}{{ field.help_text }}") with self.assertNumQueries(1): - template.render(Context({'field': field})) + template.render(Context({"field": field})) def test_disabled_modelchoicefield(self): class ModelChoiceForm(forms.ModelForm): @@ -210,18 +239,18 @@ class ModelChoiceFieldTests(TestCase): class Meta: model = Book - fields = ['author'] + fields = ["author"] - book = Book.objects.create(author=Writer.objects.create(name='Test writer')) + book = Book.objects.create(author=Writer.objects.create(name="Test writer")) form = ModelChoiceForm({}, instance=book) self.assertEqual( - form.errors['author'], - ['Select a valid choice. That choice is not one of the available choices.'] + form.errors["author"], + ["Select a valid choice. That choice is not one of the available choices."], ) def test_disabled_modelchoicefield_has_changed(self): field = forms.ModelChoiceField(Author.objects.all(), disabled=True) - self.assertIs(field.has_changed('x', 'y'), False) + self.assertIs(field.has_changed("x", "y"), False) def test_disabled_modelchoicefield_initial_model_instance(self): class ModelChoiceForm(forms.Form): @@ -231,36 +260,42 @@ class ModelChoiceFieldTests(TestCase): initial=self.c1, ) - self.assertTrue(ModelChoiceForm(data={'categories': self.c1.pk}).is_valid()) + self.assertTrue(ModelChoiceForm(data={"categories": self.c1.pk}).is_valid()) def test_disabled_multiplemodelchoicefield(self): class ArticleForm(forms.ModelForm): - categories = forms.ModelMultipleChoiceField(Category.objects.all(), required=False) + categories = forms.ModelMultipleChoiceField( + Category.objects.all(), required=False + ) class Meta: model = Article - fields = ['categories'] + fields = ["categories"] - category1 = Category.objects.create(name='cat1') - category2 = Category.objects.create(name='cat2') + category1 = Category.objects.create(name="cat1") + category2 = Category.objects.create(name="cat2") article = Article.objects.create( pub_date=datetime.date(1988, 1, 4), - writer=Writer.objects.create(name='Test writer'), + writer=Writer.objects.create(name="Test writer"), ) article.categories.set([category1.pk]) - form = ArticleForm(data={'categories': [category2.pk]}, instance=article) + form = ArticleForm(data={"categories": [category2.pk]}, instance=article) self.assertEqual(form.errors, {}) - self.assertEqual([x.pk for x in form.cleaned_data['categories']], [category2.pk]) + self.assertEqual( + [x.pk for x in form.cleaned_data["categories"]], [category2.pk] + ) # Disabled fields use the value from `instance` rather than `data`. - form = ArticleForm(data={'categories': [category2.pk]}, instance=article) - form.fields['categories'].disabled = True + form = ArticleForm(data={"categories": [category2.pk]}, instance=article) + form.fields["categories"].disabled = True self.assertEqual(form.errors, {}) - self.assertEqual([x.pk for x in form.cleaned_data['categories']], [category1.pk]) + self.assertEqual( + [x.pk for x in form.cleaned_data["categories"]], [category1.pk] + ) def test_disabled_modelmultiplechoicefield_has_changed(self): field = forms.ModelMultipleChoiceField(Author.objects.all(), disabled=True) - self.assertIs(field.has_changed('x', 'y'), False) + self.assertIs(field.has_changed("x", "y"), False) def test_overridable_choice_iterator(self): """ @@ -281,11 +316,15 @@ class ModelChoiceFieldTests(TestCase): def test_choice_iterator_passes_model_to_widget(self): class CustomCheckboxSelectMultiple(CheckboxSelectMultiple): - def create_option(self, name, value, label, selected, index, subindex=None, attrs=None): - option = super().create_option(name, value, label, selected, index, subindex, attrs) + def create_option( + self, name, value, label, selected, index, subindex=None, attrs=None + ): + option = super().create_option( + name, value, label, selected, index, subindex, attrs + ) # Modify the HTML based on the object being rendered. c = value.instance - option['attrs']['data-slug'] = c.slug + option["attrs"]["data-slug"] = c.slug return option class CustomModelMultipleChoiceField(forms.ModelMultipleChoiceField): @@ -293,16 +332,18 @@ class ModelChoiceFieldTests(TestCase): field = CustomModelMultipleChoiceField(Category.objects.all()) self.assertHTMLEqual( - field.widget.render('name', []), ( - '<div>' + field.widget.render("name", []), + ( + "<div>" '<div><label><input type="checkbox" name="name" value="%d" ' 'data-slug="entertainment">Entertainment</label></div>' '<div><label><input type="checkbox" name="name" value="%d" ' 'data-slug="test">A test</label></div>' '<div><label><input type="checkbox" name="name" value="%d" ' 'data-slug="third-test">Third</label></div>' - '</div>' - ) % (self.c1.pk, self.c2.pk, self.c3.pk), + "</div>" + ) + % (self.c1.pk, self.c2.pk, self.c3.pk), ) def test_custom_choice_iterator_passes_model_to_widget(self): @@ -320,11 +361,15 @@ class ModelChoiceFieldTests(TestCase): return CustomModelChoiceValue(value, obj), label class CustomCheckboxSelectMultiple(CheckboxSelectMultiple): - def create_option(self, name, value, label, selected, index, subindex=None, attrs=None): - option = super().create_option(name, value, label, selected, index, subindex, attrs) + def create_option( + self, name, value, label, selected, index, subindex=None, attrs=None + ): + option = super().create_option( + name, value, label, selected, index, subindex, attrs + ) # Modify the HTML based on the object being rendered. c = value.obj - option['attrs']['data-slug'] = c.slug + option["attrs"]["data-slug"] = c.slug return option class CustomModelMultipleChoiceField(forms.ModelMultipleChoiceField): @@ -333,34 +378,40 @@ class ModelChoiceFieldTests(TestCase): field = CustomModelMultipleChoiceField(Category.objects.all()) self.assertHTMLEqual( - field.widget.render('name', []), + field.widget.render("name", []), """<div> <div><label><input type="checkbox" name="name" value="%d" data-slug="entertainment">Entertainment</label></div> <div><label><input type="checkbox" name="name" value="%d" data-slug="test">A test</label></div> <div><label><input type="checkbox" name="name" value="%d" data-slug="third-test">Third</label></div> -</div>""" % (self.c1.pk, self.c2.pk, self.c3.pk), +</div>""" + % (self.c1.pk, self.c2.pk, self.c3.pk), ) def test_choice_value_hash(self): value_1 = ModelChoiceIteratorValue(self.c1.pk, self.c1) value_2 = ModelChoiceIteratorValue(self.c2.pk, self.c2) - self.assertEqual(hash(value_1), hash(ModelChoiceIteratorValue(self.c1.pk, None))) + self.assertEqual( + hash(value_1), hash(ModelChoiceIteratorValue(self.c1.pk, None)) + ) self.assertNotEqual(hash(value_1), hash(value_2)) def test_choices_not_fetched_when_not_rendering(self): with self.assertNumQueries(1): - field = forms.ModelChoiceField(Category.objects.order_by('-name')) - self.assertEqual('Entertainment', field.clean(self.c1.pk).name) + field = forms.ModelChoiceField(Category.objects.order_by("-name")) + self.assertEqual("Entertainment", field.clean(self.c1.pk).name) def test_queryset_manager(self): f = forms.ModelChoiceField(Category.objects) self.assertEqual(len(f.choices), 4) - self.assertEqual(list(f.choices), [ - ('', '---------'), - (self.c1.pk, 'Entertainment'), - (self.c2.pk, 'A test'), - (self.c3.pk, 'Third'), - ]) + self.assertEqual( + list(f.choices), + [ + ("", "---------"), + (self.c1.pk, "Entertainment"), + (self.c2.pk, "A test"), + (self.c3.pk, "Third"), + ], + ) def test_num_queries(self): """ @@ -370,12 +421,16 @@ class ModelChoiceFieldTests(TestCase): categories = Category.objects.all() class CategoriesForm(forms.Form): - radio = forms.ModelChoiceField(queryset=categories, widget=forms.RadioSelect) - checkbox = forms.ModelMultipleChoiceField(queryset=categories, widget=forms.CheckboxSelectMultiple) + radio = forms.ModelChoiceField( + queryset=categories, widget=forms.RadioSelect + ) + checkbox = forms.ModelMultipleChoiceField( + queryset=categories, widget=forms.CheckboxSelectMultiple + ) template = Template( - '{% for widget in form.checkbox %}{{ widget }}{% endfor %}' - '{% for widget in form.radio %}{{ widget }}{% endfor %}' + "{% for widget in form.checkbox %}{{ widget }}{% endfor %}" + "{% for widget in form.radio %}{{ widget }}{% endfor %}" ) with self.assertNumQueries(2): - template.render(Context({'form': CategoriesForm()})) + template.render(Context({"form": CategoriesForm()})) diff --git a/tests/model_forms/test_uuid.py b/tests/model_forms/test_uuid.py index a0d59c3d03..583b3fea94 100644 --- a/tests/model_forms/test_uuid.py +++ b/tests/model_forms/test_uuid.py @@ -8,7 +8,7 @@ from .models import UUIDPK class UUIDPKForm(forms.ModelForm): class Meta: model = UUIDPK - fields = '__all__' + fields = "__all__" class ModelFormBaseTest(TestCase): @@ -20,7 +20,7 @@ class ModelFormBaseTest(TestCase): form.save() def test_update_save_error(self): - obj = UUIDPK.objects.create(name='foo') + obj = UUIDPK.objects.create(name="foo") form = UUIDPKForm({}, instance=obj) self.assertFalse(form.is_valid()) msg = "The UUIDPK could not be changed because the data didn't validate." @@ -29,5 +29,7 @@ class ModelFormBaseTest(TestCase): def test_model_multiple_choice_field_uuid_pk(self): f = forms.ModelMultipleChoiceField(UUIDPK.objects.all()) - with self.assertRaisesMessage(ValidationError, '“invalid_uuid” is not a valid UUID.'): - f.clean(['invalid_uuid']) + with self.assertRaisesMessage( + ValidationError, "“invalid_uuid” is not a valid UUID." + ): + f.clean(["invalid_uuid"]) diff --git a/tests/model_forms/tests.py b/tests/model_forms/tests.py index db36216d14..dd397785a1 100644 --- a/tests/model_forms/tests.py +++ b/tests/model_forms/tests.py @@ -5,13 +5,19 @@ from unittest import mock, skipUnless from django import forms from django.core.exceptions import ( - NON_FIELD_ERRORS, FieldError, ImproperlyConfigured, ValidationError, + NON_FIELD_ERRORS, + FieldError, + ImproperlyConfigured, + ValidationError, ) from django.core.files.uploadedfile import SimpleUploadedFile from django.db import connection, models from django.db.models.query import EmptyQuerySet from django.forms.models import ( - ModelFormMetaclass, construct_instance, fields_for_model, model_to_dict, + ModelFormMetaclass, + construct_instance, + fields_for_model, + model_to_dict, modelform_factory, ) from django.template import Context, Template @@ -19,15 +25,51 @@ from django.test import SimpleTestCase, TestCase, skipUnlessDBFeature from django.test.utils import isolate_apps from .models import ( - Article, ArticleStatus, Author, Author1, Award, BetterWriter, BigInt, Book, - Category, Character, Colour, ColourfulItem, CustomErrorMessage, CustomFF, - CustomFieldForExclusionModel, DateTimePost, DerivedBook, DerivedPost, Dice, - Document, ExplicitPK, FilePathModel, FlexibleDatePost, Homepage, - ImprovedArticle, ImprovedArticleWithParentLink, Inventory, - NullableUniqueCharFieldModel, Number, Person, Photo, Post, Price, Product, - Publication, PublicationDefaults, StrictAssignmentAll, - StrictAssignmentFieldSpecific, Student, StumpJoke, TextFile, Triple, - Writer, WriterProfile, test_images, + Article, + ArticleStatus, + Author, + Author1, + Award, + BetterWriter, + BigInt, + Book, + Category, + Character, + Colour, + ColourfulItem, + CustomErrorMessage, + CustomFF, + CustomFieldForExclusionModel, + DateTimePost, + DerivedBook, + DerivedPost, + Dice, + Document, + ExplicitPK, + FilePathModel, + FlexibleDatePost, + Homepage, + ImprovedArticle, + ImprovedArticleWithParentLink, + Inventory, + NullableUniqueCharFieldModel, + Number, + Person, + Photo, + Post, + Price, + Product, + Publication, + PublicationDefaults, + StrictAssignmentAll, + StrictAssignmentFieldSpecific, + Student, + StumpJoke, + TextFile, + Triple, + Writer, + WriterProfile, + test_images, ) if test_images: @@ -36,59 +78,62 @@ if test_images: class ImageFileForm(forms.ModelForm): class Meta: model = ImageFile - fields = '__all__' + fields = "__all__" class OptionalImageFileForm(forms.ModelForm): class Meta: model = OptionalImageFile - fields = '__all__' + fields = "__all__" class NoExtensionImageFileForm(forms.ModelForm): class Meta: model = NoExtensionImageFile - fields = '__all__' + fields = "__all__" class ProductForm(forms.ModelForm): class Meta: model = Product - fields = '__all__' + fields = "__all__" class PriceForm(forms.ModelForm): class Meta: model = Price - fields = '__all__' + fields = "__all__" class BookForm(forms.ModelForm): class Meta: model = Book - fields = '__all__' + fields = "__all__" class DerivedBookForm(forms.ModelForm): class Meta: model = DerivedBook - fields = '__all__' + fields = "__all__" class ExplicitPKForm(forms.ModelForm): class Meta: model = ExplicitPK - fields = ('key', 'desc',) + fields = ( + "key", + "desc", + ) class PostForm(forms.ModelForm): class Meta: model = Post - fields = '__all__' + fields = "__all__" class DerivedPostForm(forms.ModelForm): class Meta: model = DerivedPost - fields = '__all__' + fields = "__all__" class CustomWriterForm(forms.ModelForm): @@ -96,89 +141,92 @@ class CustomWriterForm(forms.ModelForm): class Meta: model = Writer - fields = '__all__' + fields = "__all__" class BaseCategoryForm(forms.ModelForm): class Meta: model = Category - fields = '__all__' + fields = "__all__" class ArticleForm(forms.ModelForm): class Meta: model = Article - fields = '__all__' + fields = "__all__" class RoykoForm(forms.ModelForm): class Meta: model = Writer - fields = '__all__' + fields = "__all__" class ArticleStatusForm(forms.ModelForm): class Meta: model = ArticleStatus - fields = '__all__' + fields = "__all__" class InventoryForm(forms.ModelForm): class Meta: model = Inventory - fields = '__all__' + fields = "__all__" class SelectInventoryForm(forms.Form): - items = forms.ModelMultipleChoiceField(Inventory.objects.all(), to_field_name='barcode') + items = forms.ModelMultipleChoiceField( + Inventory.objects.all(), to_field_name="barcode" + ) class CustomFieldForExclusionForm(forms.ModelForm): class Meta: model = CustomFieldForExclusionModel - fields = ['name', 'markup'] + fields = ["name", "markup"] class TextFileForm(forms.ModelForm): class Meta: model = TextFile - fields = '__all__' + fields = "__all__" class BigIntForm(forms.ModelForm): class Meta: model = BigInt - fields = '__all__' + fields = "__all__" class ModelFormWithMedia(forms.ModelForm): class Media: - js = ('/some/form/javascript',) - css = { - 'all': ('/some/form/css',) - } + js = ("/some/form/javascript",) + css = {"all": ("/some/form/css",)} class Meta: model = TextFile - fields = '__all__' + fields = "__all__" class CustomErrorMessageForm(forms.ModelForm): - name1 = forms.CharField(error_messages={'invalid': 'Form custom error message.'}) + name1 = forms.CharField(error_messages={"invalid": "Form custom error message."}) class Meta: - fields = '__all__' + fields = "__all__" model = CustomErrorMessage class ModelFormBaseTest(TestCase): def test_base_form(self): - self.assertEqual(list(BaseCategoryForm.base_fields), ['name', 'slug', 'url']) + self.assertEqual(list(BaseCategoryForm.base_fields), ["name", "slug", "url"]) def test_no_model_class(self): class NoModelModelForm(forms.ModelForm): pass - with self.assertRaisesMessage(ValueError, 'ModelForm has no model class specified.'): + + with self.assertRaisesMessage( + ValueError, "ModelForm has no model class specified." + ): NoModelModelForm() def test_empty_fields_to_fields_for_model(self): @@ -192,6 +240,7 @@ class ModelFormBaseTest(TestCase): """ No fields on a ModelForm should actually result in no fields. """ + class EmptyPersonForm(forms.ModelForm): class Meta: model = Person @@ -204,28 +253,31 @@ class ModelFormBaseTest(TestCase): """ No fields should be set on a model instance if construct_instance receives fields=(). """ - form = modelform_factory(Person, fields="__all__")({'name': 'John Doe'}) + form = modelform_factory(Person, fields="__all__")({"name": "John Doe"}) self.assertTrue(form.is_valid()) instance = construct_instance(form, Person(), fields=()) - self.assertEqual(instance.name, '') + self.assertEqual(instance.name, "") def test_blank_with_null_foreign_key_field(self): """ #13776 -- ModelForm's with models having a FK set to null=False and required=False should be valid. """ + class FormForTestingIsValid(forms.ModelForm): class Meta: model = Student - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['character'].required = False + self.fields["character"].required = False - char = Character.objects.create(username='user', last_action=datetime.datetime.today()) - data = {'study': 'Engineering'} - data2 = {'study': 'Engineering', 'character': char.pk} + char = Character.objects.create( + username="user", last_action=datetime.datetime.today() + ) + data = {"study": "Engineering"} + data2 = {"study": "Engineering", "character": char.pk} # form is valid because required=False for field 'character' f1 = FormForTestingIsValid(data) @@ -242,18 +294,21 @@ class ModelFormBaseTest(TestCase): and the form field set to required=False should allow the field to be unset. """ + class AwardForm(forms.ModelForm): class Meta: model = Award - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['character'].required = False + self.fields["character"].required = False - character = Character.objects.create(username='user', last_action=datetime.datetime.today()) - award = Award.objects.create(name='Best sprinter', character=character) - data = {'name': 'Best tester', 'character': ''} # remove character + character = Character.objects.create( + username="user", last_action=datetime.datetime.today() + ) + award = Award.objects.create(name="Best sprinter", character=character) + data = {"name": "Best tester", "character": ""} # remove character form = AwardForm(data=data, instance=award) self.assertTrue(form.is_valid()) award = form.save() @@ -263,31 +318,34 @@ class ModelFormBaseTest(TestCase): class BookForm(forms.ModelForm): class Meta: model = Book - fields = ['author'] - widgets = {'author': forms.RadioSelect()} + fields = ["author"] + widgets = {"author": forms.RadioSelect()} - writer = Writer.objects.create(name='Joe Doe') + writer = Writer.objects.create(name="Joe Doe") form = BookForm() - self.assertEqual(list(form.fields['author'].choices), [ - ('', '---------'), - (writer.pk, 'Joe Doe'), - ]) + self.assertEqual( + list(form.fields["author"].choices), + [ + ("", "---------"), + (writer.pk, "Joe Doe"), + ], + ) def test_non_blank_foreign_key_with_radio(self): class AwardForm(forms.ModelForm): class Meta: model = Award - fields = ['character'] - widgets = {'character': forms.RadioSelect()} + fields = ["character"] + widgets = {"character": forms.RadioSelect()} character = Character.objects.create( - username='user', + username="user", last_action=datetime.datetime.today(), ) form = AwardForm() self.assertEqual( - list(form.fields['character'].choices), - [(character.pk, 'user')], + list(form.fields["character"].choices), + [(character.pk, "user")], ) def test_save_blank_false_with_required_false(self): @@ -295,20 +353,24 @@ class ModelFormBaseTest(TestCase): A ModelForm with a model with a field set to blank=False and the form field set to required=False should allow the field to be unset. """ - obj = Writer.objects.create(name='test') - form = CustomWriterForm(data={'name': ''}, instance=obj) + obj = Writer.objects.create(name="test") + form = CustomWriterForm(data={"name": ""}, instance=obj) self.assertTrue(form.is_valid()) obj = form.save() - self.assertEqual(obj.name, '') + self.assertEqual(obj.name, "") def test_save_blank_null_unique_charfield_saves_null(self): - form_class = modelform_factory(model=NullableUniqueCharFieldModel, fields='__all__') - empty_value = '' if connection.features.interprets_empty_strings_as_nulls else None + form_class = modelform_factory( + model=NullableUniqueCharFieldModel, fields="__all__" + ) + empty_value = ( + "" if connection.features.interprets_empty_strings_as_nulls else None + ) data = { - 'codename': '', - 'email': '', - 'slug': '', - 'url': '', + "codename": "", + "email": "", + "slug": "", + "url": "", } form = form_class(data=data) self.assertTrue(form.is_valid()) @@ -334,6 +396,7 @@ class ModelFormBaseTest(TestCase): "MissingFieldsForm needs updating." ) with self.assertRaisesMessage(ImproperlyConfigured, message): + class MissingFieldsForm(forms.ModelForm): class Meta: model = Category @@ -342,31 +405,37 @@ class ModelFormBaseTest(TestCase): class ExtraFields(BaseCategoryForm): some_extra_field = forms.BooleanField() - self.assertEqual(list(ExtraFields.base_fields), - ['name', 'slug', 'url', 'some_extra_field']) + self.assertEqual( + list(ExtraFields.base_fields), ["name", "slug", "url", "some_extra_field"] + ) def test_extra_field_model_form(self): - with self.assertRaisesMessage(FieldError, 'no-field'): + with self.assertRaisesMessage(FieldError, "no-field"): + class ExtraPersonForm(forms.ModelForm): - """ ModelForm with an extra field """ + """ModelForm with an extra field""" + age = forms.IntegerField() class Meta: model = Person - fields = ('name', 'no-field') + fields = ("name", "no-field") def test_extra_declared_field_model_form(self): class ExtraPersonForm(forms.ModelForm): - """ ModelForm with an extra field """ + """ModelForm with an extra field""" + age = forms.IntegerField() class Meta: model = Person - fields = ('name', 'age') + fields = ("name", "age") def test_extra_field_modelform_factory(self): - with self.assertRaisesMessage(FieldError, 'Unknown field(s) (no-field) specified for Person'): - modelform_factory(Person, fields=['no-field', 'name']) + with self.assertRaisesMessage( + FieldError, "Unknown field(s) (no-field) specified for Person" + ): + modelform_factory(Person, fields=["no-field", "name"]) def test_replace_field(self): class ReplaceField(forms.ModelForm): @@ -374,9 +443,11 @@ class ModelFormBaseTest(TestCase): class Meta: model = Category - fields = '__all__' + fields = "__all__" - self.assertIsInstance(ReplaceField.base_fields['url'], forms.fields.BooleanField) + self.assertIsInstance( + ReplaceField.base_fields["url"], forms.fields.BooleanField + ) def test_replace_field_variant_2(self): # Should have the same result as before, @@ -386,9 +457,11 @@ class ModelFormBaseTest(TestCase): class Meta: model = Category - fields = ['url'] + fields = ["url"] - self.assertIsInstance(ReplaceField.base_fields['url'], forms.fields.BooleanField) + self.assertIsInstance( + ReplaceField.base_fields["url"], forms.fields.BooleanField + ) def test_replace_field_variant_3(self): # Should have the same result as before, @@ -400,7 +473,9 @@ class ModelFormBaseTest(TestCase): model = Category fields = [] # url will still appear, since it is explicit above - self.assertIsInstance(ReplaceField.base_fields['url'], forms.fields.BooleanField) + self.assertIsInstance( + ReplaceField.base_fields["url"], forms.fields.BooleanField + ) def test_override_field(self): class WriterForm(forms.ModelForm): @@ -408,50 +483,53 @@ class ModelFormBaseTest(TestCase): class Meta: model = Writer - fields = '__all__' + fields = "__all__" - wf = WriterForm({'name': 'Richard Lockridge'}) + wf = WriterForm({"name": "Richard Lockridge"}) self.assertTrue(wf.is_valid()) def test_limit_nonexistent_field(self): - expected_msg = 'Unknown field(s) (nonexistent) specified for Category' + expected_msg = "Unknown field(s) (nonexistent) specified for Category" with self.assertRaisesMessage(FieldError, expected_msg): + class InvalidCategoryForm(forms.ModelForm): class Meta: model = Category - fields = ['nonexistent'] + fields = ["nonexistent"] def test_limit_fields_with_string(self): expected_msg = "CategoryForm.Meta.fields cannot be a string. Did you mean to type: ('url',)?" with self.assertRaisesMessage(TypeError, expected_msg): + class CategoryForm(forms.ModelForm): class Meta: model = Category - fields = ('url') # note the missing comma + fields = "url" # note the missing comma def test_exclude_fields(self): class ExcludeFields(forms.ModelForm): class Meta: model = Category - exclude = ['url'] + exclude = ["url"] - self.assertEqual(list(ExcludeFields.base_fields), ['name', 'slug']) + self.assertEqual(list(ExcludeFields.base_fields), ["name", "slug"]) def test_exclude_nonexistent_field(self): class ExcludeFields(forms.ModelForm): class Meta: model = Category - exclude = ['nonexistent'] + exclude = ["nonexistent"] - self.assertEqual(list(ExcludeFields.base_fields), ['name', 'slug', 'url']) + self.assertEqual(list(ExcludeFields.base_fields), ["name", "slug", "url"]) def test_exclude_fields_with_string(self): expected_msg = "CategoryForm.Meta.exclude cannot be a string. Did you mean to type: ('url',)?" with self.assertRaisesMessage(TypeError, expected_msg): + class CategoryForm(forms.ModelForm): class Meta: model = Category - exclude = ('url') # note the missing comma + exclude = "url" # note the missing comma def test_exclude_and_validation(self): # This Price instance generated by this form is not valid because the quantity @@ -460,9 +538,9 @@ class ModelFormBaseTest(TestCase): class PriceFormWithoutQuantity(forms.ModelForm): class Meta: model = Price - exclude = ('quantity',) + exclude = ("quantity",) - form = PriceFormWithoutQuantity({'price': '6.00'}) + form = PriceFormWithoutQuantity({"price": "6.00"}) self.assertTrue(form.is_valid()) price = form.save(commit=False) msg = "{'quantity': ['This field cannot be null.']}" @@ -474,52 +552,71 @@ class ModelFormBaseTest(TestCase): class PriceFormWithoutQuantity(forms.ModelForm): class Meta: model = Price - fields = ('price',) - form = PriceFormWithoutQuantity({'price': '6.00'}) + fields = ("price",) + + form = PriceFormWithoutQuantity({"price": "6.00"}) self.assertTrue(form.is_valid()) # The form should still have an instance of a model that is not complete and # not saved into a DB yet. - self.assertEqual(form.instance.price, Decimal('6.00')) + self.assertEqual(form.instance.price, Decimal("6.00")) self.assertIsNone(form.instance.quantity) self.assertIsNone(form.instance.pk) def test_confused_form(self): class ConfusedForm(forms.ModelForm): - """ Using 'fields' *and* 'exclude'. Not sure why you'd want to do + """Using 'fields' *and* 'exclude'. Not sure why you'd want to do this, but uh, "be liberal in what you accept" and all. """ + class Meta: model = Category - fields = ['name', 'url'] - exclude = ['url'] + fields = ["name", "url"] + exclude = ["url"] - self.assertEqual(list(ConfusedForm.base_fields), - ['name']) + self.assertEqual(list(ConfusedForm.base_fields), ["name"]) def test_mixmodel_form(self): class MixModelForm(BaseCategoryForm): - """ Don't allow more than one 'model' definition in the + """Don't allow more than one 'model' definition in the inheritance hierarchy. Technically, it would generate a valid form, but the fact that the resulting save method won't deal with multiple objects is likely to trip up people not familiar with the mechanics. """ + class Meta: model = Article - fields = '__all__' + fields = "__all__" + # MixModelForm is now an Article-related thing, because MixModelForm.Meta # overrides BaseCategoryForm.Meta. self.assertEqual( list(MixModelForm.base_fields), - ['headline', 'slug', 'pub_date', 'writer', 'article', 'categories', 'status'] + [ + "headline", + "slug", + "pub_date", + "writer", + "article", + "categories", + "status", + ], ) def test_article_form(self): self.assertEqual( list(ArticleForm.base_fields), - ['headline', 'slug', 'pub_date', 'writer', 'article', 'categories', 'status'] + [ + "headline", + "slug", + "pub_date", + "writer", + "article", + "categories", + "status", + ], ) def test_bad_form(self): @@ -529,7 +626,15 @@ class ModelFormBaseTest(TestCase): self.assertEqual( list(BadForm.base_fields), - ['headline', 'slug', 'pub_date', 'writer', 'article', 'categories', 'status'] + [ + "headline", + "slug", + "pub_date", + "writer", + "article", + "categories", + "status", + ], ) def test_invalid_meta_model(self): @@ -538,7 +643,7 @@ class ModelFormBaseTest(TestCase): pass # no model # Can't create new form - msg = 'ModelForm has no model class specified.' + msg = "ModelForm has no model class specified." with self.assertRaisesMessage(ValueError, msg): InvalidModelForm() @@ -548,13 +653,14 @@ class ModelFormBaseTest(TestCase): def test_subcategory_form(self): class SubCategoryForm(BaseCategoryForm): - """ Subclassing without specifying a Meta on the class will use + """Subclassing without specifying a Meta on the class will use the parent's Meta (or the first parent in the MRO if there are multiple parent classes). """ + pass - self.assertEqual(list(SubCategoryForm.base_fields), ['name', 'slug', 'url']) + self.assertEqual(list(SubCategoryForm.base_fields), ["name", "slug", "url"]) def test_subclassmeta_form(self): class SomeCategoryForm(forms.ModelForm): @@ -562,14 +668,15 @@ class ModelFormBaseTest(TestCase): class Meta: model = Category - fields = '__all__' + fields = "__all__" class SubclassMeta(SomeCategoryForm): - """ We can also subclass the Meta inner class to change the fields + """We can also subclass the Meta inner class to change the fields list. """ + class Meta(SomeCategoryForm.Meta): - exclude = ['url'] + exclude = ["url"] self.assertHTMLEqual( str(SubclassMeta()), @@ -578,33 +685,32 @@ class ModelFormBaseTest(TestCase): <tr><th><label for="id_slug">Slug:</label></th> <td><input id="id_slug" type="text" name="slug" maxlength="20" required></td></tr> <tr><th><label for="id_checkbox">Checkbox:</label></th> -<td><input type="checkbox" name="checkbox" id="id_checkbox" required></td></tr>""" +<td><input type="checkbox" name="checkbox" id="id_checkbox" required></td></tr>""", ) def test_orderfields_form(self): class OrderFields(forms.ModelForm): class Meta: model = Category - fields = ['url', 'name'] + fields = ["url", "name"] - self.assertEqual(list(OrderFields.base_fields), - ['url', 'name']) + self.assertEqual(list(OrderFields.base_fields), ["url", "name"]) self.assertHTMLEqual( str(OrderFields()), """<tr><th><label for="id_url">The URL:</label></th> <td><input id="id_url" type="text" name="url" maxlength="40" required></td></tr> <tr><th><label for="id_name">Name:</label></th> -<td><input id="id_name" type="text" name="name" maxlength="20" required></td></tr>""" +<td><input id="id_name" type="text" name="name" maxlength="20" required></td></tr>""", ) def test_orderfields2_form(self): class OrderFields2(forms.ModelForm): class Meta: model = Category - fields = ['slug', 'url', 'name'] - exclude = ['url'] + fields = ["slug", "url", "name"] + exclude = ["url"] - self.assertEqual(list(OrderFields2.base_fields), ['slug', 'name']) + self.assertEqual(list(OrderFields2.base_fields), ["slug", "name"]) def test_default_populated_on_optional_field(self): class PubForm(forms.ModelForm): @@ -612,20 +718,20 @@ class ModelFormBaseTest(TestCase): class Meta: model = PublicationDefaults - fields = ('mode',) + fields = ("mode",) # Empty data uses the model field default. mf1 = PubForm({}) self.assertEqual(mf1.errors, {}) m1 = mf1.save(commit=False) - self.assertEqual(m1.mode, 'di') - self.assertEqual(m1._meta.get_field('mode').get_default(), 'di') + self.assertEqual(m1.mode, "di") + self.assertEqual(m1._meta.get_field("mode").get_default(), "di") # Blank data doesn't use the model field default. - mf2 = PubForm({'mode': ''}) + mf2 = PubForm({"mode": ""}) self.assertEqual(mf2.errors, {}) m2 = mf2.save(commit=False) - self.assertEqual(m2.mode, '') + self.assertEqual(m2.mode, "") def test_default_not_populated_on_non_empty_value_in_cleaned_data(self): class PubForm(forms.ModelForm): @@ -633,20 +739,20 @@ class ModelFormBaseTest(TestCase): mocked_mode = None def clean(self): - self.cleaned_data['mode'] = self.mocked_mode + self.cleaned_data["mode"] = self.mocked_mode return self.cleaned_data class Meta: model = PublicationDefaults - fields = ('mode',) + fields = ("mode",) pub_form = PubForm({}) - pub_form.mocked_mode = 'de' + pub_form.mocked_mode = "de" pub = pub_form.save(commit=False) - self.assertEqual(pub.mode, 'de') + self.assertEqual(pub.mode, "de") # Default should be populated on an empty value in cleaned_data. - default_mode = 'di' - for empty_value in pub_form.fields['mode'].empty_values: + default_mode = "di" + for empty_value in pub_form.fields["mode"].empty_values: with self.subTest(empty_value=empty_value): pub_form = PubForm({}) pub_form.mocked_mode = empty_value @@ -657,7 +763,7 @@ class ModelFormBaseTest(TestCase): class PubForm(forms.ModelForm): class Meta: model = PublicationDefaults - fields = ('active',) + fields = ("active",) # Empty data doesn't use the model default because CheckboxInput # doesn't have a value in HTML form submission. @@ -665,8 +771,8 @@ class ModelFormBaseTest(TestCase): self.assertEqual(mf1.errors, {}) m1 = mf1.save(commit=False) self.assertIs(m1.active, False) - self.assertIsInstance(mf1.fields['active'].widget, forms.CheckboxInput) - self.assertIs(m1._meta.get_field('active').get_default(), True) + self.assertIsInstance(mf1.fields["active"].widget, forms.CheckboxInput) + self.assertIs(m1._meta.get_field("active").get_default(), True) def test_default_not_populated_on_checkboxselectmultiple(self): class PubForm(forms.ModelForm): @@ -674,15 +780,15 @@ class ModelFormBaseTest(TestCase): class Meta: model = PublicationDefaults - fields = ('mode',) + fields = ("mode",) # Empty data doesn't use the model default because an unchecked # CheckboxSelectMultiple doesn't have a value in HTML form submission. mf1 = PubForm({}) self.assertEqual(mf1.errors, {}) m1 = mf1.save(commit=False) - self.assertEqual(m1.mode, '') - self.assertEqual(m1._meta.get_field('mode').get_default(), 'di') + self.assertEqual(m1.mode, "") + self.assertEqual(m1._meta.get_field("mode").get_default(), "di") def test_default_not_populated_on_selectmultiple(self): class PubForm(forms.ModelForm): @@ -690,28 +796,30 @@ class ModelFormBaseTest(TestCase): class Meta: model = PublicationDefaults - fields = ('mode',) + fields = ("mode",) # Empty data doesn't use the model default because an unselected # SelectMultiple doesn't have a value in HTML form submission. mf1 = PubForm({}) self.assertEqual(mf1.errors, {}) m1 = mf1.save(commit=False) - self.assertEqual(m1.mode, '') - self.assertEqual(m1._meta.get_field('mode').get_default(), 'di') + self.assertEqual(m1.mode, "") + self.assertEqual(m1._meta.get_field("mode").get_default(), "di") def test_prefixed_form_with_default_field(self): class PubForm(forms.ModelForm): - prefix = 'form-prefix' + prefix = "form-prefix" class Meta: model = PublicationDefaults - fields = ('mode',) + fields = ("mode",) - mode = 'de' - self.assertNotEqual(mode, PublicationDefaults._meta.get_field('mode').get_default()) + mode = "de" + self.assertNotEqual( + mode, PublicationDefaults._meta.get_field("mode").get_default() + ) - mf1 = PubForm({'form-prefix-mode': mode}) + mf1 = PubForm({"form-prefix-mode": mode}) self.assertEqual(mf1.errors, {}) m1 = mf1.save(commit=False) self.assertEqual(m1.mode, mode) @@ -726,14 +834,16 @@ class ModelFormBaseTest(TestCase): class Meta: model = PublicationDefaults - fields = ('datetime_published',) + fields = ("datetime_published",) mf1 = PubForm({}) self.assertEqual(mf1.errors, {}) m1 = mf1.save(commit=False) self.assertEqual(m1.datetime_published, datetime.datetime(2000, 1, 1)) - mf2 = PubForm({'datetime_published_0': '2010-01-01', 'datetime_published_1': '0:00:00'}) + mf2 = PubForm( + {"datetime_published_0": "2010-01-01", "datetime_published_1": "0:00:00"} + ) self.assertEqual(mf2.errors, {}) m2 = mf2.save(commit=False) self.assertEqual(m2.datetime_published, datetime.datetime(2010, 1, 1)) @@ -742,32 +852,40 @@ class ModelFormBaseTest(TestCase): class PubForm(forms.ModelForm): class Meta: model = PublicationDefaults - fields = ('file',) + fields = ("file",) mf1 = PubForm({}) self.assertEqual(mf1.errors, {}) m1 = mf1.save(commit=False) - self.assertEqual(m1.file.name, 'default.txt') + self.assertEqual(m1.file.name, "default.txt") - mf2 = PubForm({}, {'file': SimpleUploadedFile('name', b'foo')}) + mf2 = PubForm({}, {"file": SimpleUploadedFile("name", b"foo")}) self.assertEqual(mf2.errors, {}) m2 = mf2.save(commit=False) - self.assertEqual(m2.file.name, 'name') + self.assertEqual(m2.file.name, "name") def test_default_selectdatewidget(self): class PubForm(forms.ModelForm): - date_published = forms.DateField(required=False, widget=forms.SelectDateWidget) + date_published = forms.DateField( + required=False, widget=forms.SelectDateWidget + ) class Meta: model = PublicationDefaults - fields = ('date_published',) + fields = ("date_published",) mf1 = PubForm({}) self.assertEqual(mf1.errors, {}) m1 = mf1.save(commit=False) self.assertEqual(m1.date_published, datetime.date.today()) - mf2 = PubForm({'date_published_year': '2010', 'date_published_month': '1', 'date_published_day': '1'}) + mf2 = PubForm( + { + "date_published_year": "2010", + "date_published_month": "1", + "date_published_day": "1", + } + ) self.assertEqual(mf2.errors, {}) m2 = mf2.save(commit=False) self.assertEqual(m2.date_published, datetime.date(2010, 1, 1)) @@ -776,27 +894,27 @@ class ModelFormBaseTest(TestCase): class FieldOverridesByFormMetaForm(forms.ModelForm): class Meta: model = Category - fields = ['name', 'url', 'slug'] + fields = ["name", "url", "slug"] widgets = { - 'name': forms.Textarea, - 'url': forms.TextInput(attrs={'class': 'url'}) + "name": forms.Textarea, + "url": forms.TextInput(attrs={"class": "url"}), } labels = { - 'name': 'Title', + "name": "Title", } help_texts = { - 'slug': 'Watch out! Letters, numbers, underscores and hyphens only.', + "slug": "Watch out! Letters, numbers, underscores and hyphens only.", } error_messages = { - 'slug': { - 'invalid': ( + "slug": { + "invalid": ( "Didn't you read the help text? " "We said letters, numbers, underscores and hyphens only!" ) } } field_classes = { - 'url': forms.URLField, + "url": forms.URLField, } @@ -804,70 +922,72 @@ class TestFieldOverridesByFormMeta(SimpleTestCase): def test_widget_overrides(self): form = FieldOverridesByFormMetaForm() self.assertHTMLEqual( - str(form['name']), + str(form["name"]), '<textarea id="id_name" rows="10" cols="40" name="name" maxlength="20" required></textarea>', ) self.assertHTMLEqual( - str(form['url']), + str(form["url"]), '<input id="id_url" type="text" class="url" name="url" maxlength="40" required>', ) self.assertHTMLEqual( - str(form['slug']), + str(form["slug"]), '<input id="id_slug" type="text" name="slug" maxlength="20" required>', ) def test_label_overrides(self): form = FieldOverridesByFormMetaForm() self.assertHTMLEqual( - str(form['name'].label_tag()), + str(form["name"].label_tag()), '<label for="id_name">Title:</label>', ) self.assertHTMLEqual( - str(form['url'].label_tag()), + str(form["url"].label_tag()), '<label for="id_url">The URL:</label>', ) self.assertHTMLEqual( - str(form['slug'].label_tag()), + str(form["slug"].label_tag()), '<label for="id_slug">Slug:</label>', ) self.assertHTMLEqual( - form['name'].legend_tag(), + form["name"].legend_tag(), '<legend for="id_name">Title:</legend>', ) self.assertHTMLEqual( - form['url'].legend_tag(), + form["url"].legend_tag(), '<legend for="id_url">The URL:</legend>', ) self.assertHTMLEqual( - form['slug'].legend_tag(), + form["slug"].legend_tag(), '<legend for="id_slug">Slug:</legend>', ) def test_help_text_overrides(self): form = FieldOverridesByFormMetaForm() self.assertEqual( - form['slug'].help_text, - 'Watch out! Letters, numbers, underscores and hyphens only.', + form["slug"].help_text, + "Watch out! Letters, numbers, underscores and hyphens only.", ) def test_error_messages_overrides(self): - form = FieldOverridesByFormMetaForm(data={ - 'name': 'Category', - 'url': 'http://www.example.com/category/', - 'slug': '!%#*@', - }) + form = FieldOverridesByFormMetaForm( + data={ + "name": "Category", + "url": "http://www.example.com/category/", + "slug": "!%#*@", + } + ) form.full_clean() error = [ "Didn't you read the help text? " "We said letters, numbers, underscores and hyphens only!", ] - self.assertEqual(form.errors, {'slug': error}) + self.assertEqual(form.errors, {"slug": error}) def test_field_type_overrides(self): form = FieldOverridesByFormMetaForm() - self.assertIs(Category._meta.get_field('url').__class__, models.CharField) - self.assertIsInstance(form.fields['url'], forms.URLField) + self.assertIs(Category._meta.get_field("url").__class__, models.CharField) + self.assertIsInstance(form.fields["url"], forms.URLField) class IncompleteCategoryFormWithFields(forms.ModelForm): @@ -875,10 +995,11 @@ class IncompleteCategoryFormWithFields(forms.ModelForm): A form that replaces the model's url field with a custom one. This should prevent the model field's validation from being called. """ + url = forms.CharField(required=False) class Meta: - fields = ('name', 'slug') + fields = ("name", "slug") model = Category @@ -887,20 +1008,25 @@ class IncompleteCategoryFormWithExclude(forms.ModelForm): A form that replaces the model's url field with a custom one. This should prevent the model field's validation from being called. """ + url = forms.CharField(required=False) class Meta: - exclude = ['url'] + exclude = ["url"] model = Category class ValidationTest(SimpleTestCase): def test_validates_with_replaced_field_not_specified(self): - form = IncompleteCategoryFormWithFields(data={'name': 'some name', 'slug': 'some-slug'}) + form = IncompleteCategoryFormWithFields( + data={"name": "some name", "slug": "some-slug"} + ) self.assertIs(form.is_valid(), True) def test_validates_with_replaced_field_excluded(self): - form = IncompleteCategoryFormWithExclude(data={'name': 'some name', 'slug': 'some-slug'}) + form = IncompleteCategoryFormWithExclude( + data={"name": "some name", "slug": "some-slug"} + ) self.assertIs(form.is_valid(), True) def test_notrequired_overrides_notblank(self): @@ -912,29 +1038,35 @@ class UniqueTest(TestCase): """ unique/unique_together validation. """ + @classmethod def setUpTestData(cls): - cls.writer = Writer.objects.create(name='Mike Royko') + cls.writer = Writer.objects.create(name="Mike Royko") def test_simple_unique(self): - form = ProductForm({'slug': 'teddy-bear-blue'}) + form = ProductForm({"slug": "teddy-bear-blue"}) self.assertTrue(form.is_valid()) obj = form.save() - form = ProductForm({'slug': 'teddy-bear-blue'}) + form = ProductForm({"slug": "teddy-bear-blue"}) self.assertEqual(len(form.errors), 1) - self.assertEqual(form.errors['slug'], ['Product with this Slug already exists.']) - form = ProductForm({'slug': 'teddy-bear-blue'}, instance=obj) + self.assertEqual( + form.errors["slug"], ["Product with this Slug already exists."] + ) + form = ProductForm({"slug": "teddy-bear-blue"}, instance=obj) self.assertTrue(form.is_valid()) def test_unique_together(self): """ModelForm test of unique_together constraint""" - form = PriceForm({'price': '6.00', 'quantity': '1'}) + form = PriceForm({"price": "6.00", "quantity": "1"}) self.assertTrue(form.is_valid()) form.save() - form = PriceForm({'price': '6.00', 'quantity': '1'}) + form = PriceForm({"price": "6.00", "quantity": "1"}) self.assertFalse(form.is_valid()) self.assertEqual(len(form.errors), 1) - self.assertEqual(form.errors['__all__'], ['Price with this Price and Quantity already exists.']) + self.assertEqual( + form.errors["__all__"], + ["Price with this Price and Quantity already exists."], + ) def test_unique_together_exclusion(self): """ @@ -943,16 +1075,17 @@ class UniqueTest(TestCase): form.save(commit=False) and then assigning the missing field(s) to the model instance. """ + class BookForm(forms.ModelForm): class Meta: model = DerivedBook - fields = ('isbn', 'suffix1') + fields = ("isbn", "suffix1") # The unique_together is on suffix1/suffix2 but only suffix1 is part # of the form. The fields must have defaults, otherwise they'll be # skipped by other logic. - self.assertEqual(DerivedBook._meta.unique_together, (('suffix1', 'suffix2'),)) - for name in ('suffix1', 'suffix2'): + self.assertEqual(DerivedBook._meta.unique_together, (("suffix1", "suffix2"),)) + for name in ("suffix1", "suffix2"): with self.subTest(name=name): field = DerivedBook._meta.get_field(name) self.assertEqual(field.default, 0) @@ -960,8 +1093,8 @@ class UniqueTest(TestCase): # The form fails validation with "Derived book with this Suffix1 and # Suffix2 already exists." if the unique_together validation isn't # skipped. - DerivedBook.objects.create(isbn='12345') - form = BookForm({'isbn': '56789', 'suffix1': '0'}) + DerivedBook.objects.create(isbn="12345") + form = BookForm({"isbn": "56789", "suffix1": "0"}) self.assertTrue(form.is_valid(), form.errors) def test_multiple_field_unique_together(self): @@ -971,131 +1104,176 @@ class UniqueTest(TestCase): before doing all the validation checking (not just failing after the first one). """ + class TripleForm(forms.ModelForm): class Meta: model = Triple - fields = '__all__' + fields = "__all__" Triple.objects.create(left=1, middle=2, right=3) - form = TripleForm({'left': '1', 'middle': '2', 'right': '3'}) + form = TripleForm({"left": "1", "middle": "2", "right": "3"}) self.assertFalse(form.is_valid()) - form = TripleForm({'left': '1', 'middle': '3', 'right': '1'}) + form = TripleForm({"left": "1", "middle": "3", "right": "1"}) self.assertTrue(form.is_valid()) - @skipUnlessDBFeature('supports_nullable_unique_constraints') + @skipUnlessDBFeature("supports_nullable_unique_constraints") def test_unique_null(self): - title = 'I May Be Wrong But I Doubt It' - form = BookForm({'title': title, 'author': self.writer.pk}) + title = "I May Be Wrong But I Doubt It" + form = BookForm({"title": title, "author": self.writer.pk}) self.assertTrue(form.is_valid()) form.save() - form = BookForm({'title': title, 'author': self.writer.pk}) + form = BookForm({"title": title, "author": self.writer.pk}) self.assertFalse(form.is_valid()) self.assertEqual(len(form.errors), 1) - self.assertEqual(form.errors['__all__'], ['Book with this Title and Author already exists.']) - form = BookForm({'title': title}) + self.assertEqual( + form.errors["__all__"], ["Book with this Title and Author already exists."] + ) + form = BookForm({"title": title}) self.assertTrue(form.is_valid()) form.save() - form = BookForm({'title': title}) + form = BookForm({"title": title}) self.assertTrue(form.is_valid()) def test_inherited_unique(self): - title = 'Boss' + title = "Boss" Book.objects.create(title=title, author=self.writer, special_id=1) - form = DerivedBookForm({'title': 'Other', 'author': self.writer.pk, 'special_id': '1', 'isbn': '12345'}) + form = DerivedBookForm( + { + "title": "Other", + "author": self.writer.pk, + "special_id": "1", + "isbn": "12345", + } + ) self.assertFalse(form.is_valid()) self.assertEqual(len(form.errors), 1) - self.assertEqual(form.errors['special_id'], ['Book with this Special id already exists.']) + self.assertEqual( + form.errors["special_id"], ["Book with this Special id already exists."] + ) def test_inherited_unique_together(self): - title = 'Boss' - form = BookForm({'title': title, 'author': self.writer.pk}) + title = "Boss" + form = BookForm({"title": title, "author": self.writer.pk}) self.assertTrue(form.is_valid()) form.save() - form = DerivedBookForm({'title': title, 'author': self.writer.pk, 'isbn': '12345'}) + form = DerivedBookForm( + {"title": title, "author": self.writer.pk, "isbn": "12345"} + ) self.assertFalse(form.is_valid()) self.assertEqual(len(form.errors), 1) - self.assertEqual(form.errors['__all__'], ['Book with this Title and Author already exists.']) + self.assertEqual( + form.errors["__all__"], ["Book with this Title and Author already exists."] + ) def test_abstract_inherited_unique(self): - title = 'Boss' - isbn = '12345' + title = "Boss" + isbn = "12345" DerivedBook.objects.create(title=title, author=self.writer, isbn=isbn) - form = DerivedBookForm({ - 'title': 'Other', 'author': self.writer.pk, 'isbn': isbn, - 'suffix1': '1', 'suffix2': '2', - }) + form = DerivedBookForm( + { + "title": "Other", + "author": self.writer.pk, + "isbn": isbn, + "suffix1": "1", + "suffix2": "2", + } + ) self.assertFalse(form.is_valid()) self.assertEqual(len(form.errors), 1) - self.assertEqual(form.errors['isbn'], ['Derived book with this Isbn already exists.']) + self.assertEqual( + form.errors["isbn"], ["Derived book with this Isbn already exists."] + ) def test_abstract_inherited_unique_together(self): - title = 'Boss' - isbn = '12345' + title = "Boss" + isbn = "12345" DerivedBook.objects.create(title=title, author=self.writer, isbn=isbn) - form = DerivedBookForm({ - 'title': 'Other', - 'author': self.writer.pk, - 'isbn': '9876', - 'suffix1': '0', - 'suffix2': '0' - }) + form = DerivedBookForm( + { + "title": "Other", + "author": self.writer.pk, + "isbn": "9876", + "suffix1": "0", + "suffix2": "0", + } + ) self.assertFalse(form.is_valid()) self.assertEqual(len(form.errors), 1) self.assertEqual( - form.errors['__all__'], - ['Derived book with this Suffix1 and Suffix2 already exists.'], + form.errors["__all__"], + ["Derived book with this Suffix1 and Suffix2 already exists."], ) def test_explicitpk_unspecified(self): """Test for primary_key being in the form and failing validation.""" - form = ExplicitPKForm({'key': '', 'desc': ''}) + form = ExplicitPKForm({"key": "", "desc": ""}) self.assertFalse(form.is_valid()) def test_explicitpk_unique(self): """Ensure keys and blank character strings are tested for uniqueness.""" - form = ExplicitPKForm({'key': 'key1', 'desc': ''}) + form = ExplicitPKForm({"key": "key1", "desc": ""}) self.assertTrue(form.is_valid()) form.save() - form = ExplicitPKForm({'key': 'key1', 'desc': ''}) + form = ExplicitPKForm({"key": "key1", "desc": ""}) self.assertFalse(form.is_valid()) if connection.features.interprets_empty_strings_as_nulls: self.assertEqual(len(form.errors), 1) - self.assertEqual(form.errors['key'], ['Explicit pk with this Key already exists.']) + self.assertEqual( + form.errors["key"], ["Explicit pk with this Key already exists."] + ) else: self.assertEqual(len(form.errors), 3) - self.assertEqual(form.errors['__all__'], ['Explicit pk with this Key and Desc already exists.']) - self.assertEqual(form.errors['desc'], ['Explicit pk with this Desc already exists.']) - self.assertEqual(form.errors['key'], ['Explicit pk with this Key already exists.']) + self.assertEqual( + form.errors["__all__"], + ["Explicit pk with this Key and Desc already exists."], + ) + self.assertEqual( + form.errors["desc"], ["Explicit pk with this Desc already exists."] + ) + self.assertEqual( + form.errors["key"], ["Explicit pk with this Key already exists."] + ) def test_unique_for_date(self): p = Post.objects.create( - title="Django 1.0 is released", slug="Django 1.0", - subtitle="Finally", posted=datetime.date(2008, 9, 3), + title="Django 1.0 is released", + slug="Django 1.0", + subtitle="Finally", + posted=datetime.date(2008, 9, 3), ) - form = PostForm({'title': "Django 1.0 is released", 'posted': '2008-09-03'}) + form = PostForm({"title": "Django 1.0 is released", "posted": "2008-09-03"}) self.assertFalse(form.is_valid()) self.assertEqual(len(form.errors), 1) - self.assertEqual(form.errors['title'], ['Title must be unique for Posted date.']) - form = PostForm({'title': "Work on Django 1.1 begins", 'posted': '2008-09-03'}) + self.assertEqual( + form.errors["title"], ["Title must be unique for Posted date."] + ) + form = PostForm({"title": "Work on Django 1.1 begins", "posted": "2008-09-03"}) self.assertTrue(form.is_valid()) - form = PostForm({'title': "Django 1.0 is released", 'posted': '2008-09-04'}) + form = PostForm({"title": "Django 1.0 is released", "posted": "2008-09-04"}) self.assertTrue(form.is_valid()) - form = PostForm({'slug': "Django 1.0", 'posted': '2008-01-01'}) + form = PostForm({"slug": "Django 1.0", "posted": "2008-01-01"}) self.assertFalse(form.is_valid()) self.assertEqual(len(form.errors), 1) - self.assertEqual(form.errors['slug'], ['Slug must be unique for Posted year.']) - form = PostForm({'subtitle': "Finally", 'posted': '2008-09-30'}) + self.assertEqual(form.errors["slug"], ["Slug must be unique for Posted year."]) + form = PostForm({"subtitle": "Finally", "posted": "2008-09-30"}) self.assertFalse(form.is_valid()) - self.assertEqual(form.errors['subtitle'], ['Subtitle must be unique for Posted month.']) - data = {'subtitle': "Finally", "title": "Django 1.0 is released", "slug": "Django 1.0", 'posted': '2008-09-03'} + self.assertEqual( + form.errors["subtitle"], ["Subtitle must be unique for Posted month."] + ) + data = { + "subtitle": "Finally", + "title": "Django 1.0 is released", + "slug": "Django 1.0", + "posted": "2008-09-03", + } form = PostForm(data, instance=p) self.assertTrue(form.is_valid()) - form = PostForm({'title': "Django 1.0 is released"}) + form = PostForm({"title": "Django 1.0 is released"}) self.assertFalse(form.is_valid()) self.assertEqual(len(form.errors), 1) - self.assertEqual(form.errors['posted'], ['This field is required.']) + self.assertEqual(form.errors["posted"], ["This field is required."]) def test_unique_for_date_in_exclude(self): """ @@ -1103,46 +1281,68 @@ class UniqueTest(TestCase): ModelForm (in this case 'posted' has editable=False, then the constraint should be ignored. """ + class DateTimePostForm(forms.ModelForm): class Meta: model = DateTimePost - fields = '__all__' + fields = "__all__" DateTimePost.objects.create( - title="Django 1.0 is released", slug="Django 1.0", - subtitle="Finally", posted=datetime.datetime(2008, 9, 3, 10, 10, 1), + title="Django 1.0 is released", + slug="Django 1.0", + subtitle="Finally", + posted=datetime.datetime(2008, 9, 3, 10, 10, 1), ) # 'title' has unique_for_date='posted' - form = DateTimePostForm({'title': "Django 1.0 is released", 'posted': '2008-09-03'}) + form = DateTimePostForm( + {"title": "Django 1.0 is released", "posted": "2008-09-03"} + ) self.assertTrue(form.is_valid()) # 'slug' has unique_for_year='posted' - form = DateTimePostForm({'slug': "Django 1.0", 'posted': '2008-01-01'}) + form = DateTimePostForm({"slug": "Django 1.0", "posted": "2008-01-01"}) self.assertTrue(form.is_valid()) # 'subtitle' has unique_for_month='posted' - form = DateTimePostForm({'subtitle': "Finally", 'posted': '2008-09-30'}) + form = DateTimePostForm({"subtitle": "Finally", "posted": "2008-09-30"}) self.assertTrue(form.is_valid()) def test_inherited_unique_for_date(self): p = Post.objects.create( - title="Django 1.0 is released", slug="Django 1.0", - subtitle="Finally", posted=datetime.date(2008, 9, 3), + title="Django 1.0 is released", + slug="Django 1.0", + subtitle="Finally", + posted=datetime.date(2008, 9, 3), + ) + form = DerivedPostForm( + {"title": "Django 1.0 is released", "posted": "2008-09-03"} ) - form = DerivedPostForm({'title': "Django 1.0 is released", 'posted': '2008-09-03'}) self.assertFalse(form.is_valid()) self.assertEqual(len(form.errors), 1) - self.assertEqual(form.errors['title'], ['Title must be unique for Posted date.']) - form = DerivedPostForm({'title': "Work on Django 1.1 begins", 'posted': '2008-09-03'}) + self.assertEqual( + form.errors["title"], ["Title must be unique for Posted date."] + ) + form = DerivedPostForm( + {"title": "Work on Django 1.1 begins", "posted": "2008-09-03"} + ) self.assertTrue(form.is_valid()) - form = DerivedPostForm({'title': "Django 1.0 is released", 'posted': '2008-09-04'}) + form = DerivedPostForm( + {"title": "Django 1.0 is released", "posted": "2008-09-04"} + ) self.assertTrue(form.is_valid()) - form = DerivedPostForm({'slug': "Django 1.0", 'posted': '2008-01-01'}) + form = DerivedPostForm({"slug": "Django 1.0", "posted": "2008-01-01"}) self.assertFalse(form.is_valid()) self.assertEqual(len(form.errors), 1) - self.assertEqual(form.errors['slug'], ['Slug must be unique for Posted year.']) - form = DerivedPostForm({'subtitle': "Finally", 'posted': '2008-09-30'}) + self.assertEqual(form.errors["slug"], ["Slug must be unique for Posted year."]) + form = DerivedPostForm({"subtitle": "Finally", "posted": "2008-09-30"}) self.assertFalse(form.is_valid()) - self.assertEqual(form.errors['subtitle'], ['Subtitle must be unique for Posted month.']) - data = {'subtitle': "Finally", "title": "Django 1.0 is released", "slug": "Django 1.0", 'posted': '2008-09-03'} + self.assertEqual( + form.errors["subtitle"], ["Subtitle must be unique for Posted month."] + ) + data = { + "subtitle": "Finally", + "title": "Django 1.0 is released", + "slug": "Django 1.0", + "posted": "2008-09-03", + } form = DerivedPostForm(data, instance=p) self.assertTrue(form.is_valid()) @@ -1150,20 +1350,26 @@ class UniqueTest(TestCase): class FlexDatePostForm(forms.ModelForm): class Meta: model = FlexibleDatePost - fields = '__all__' + fields = "__all__" p = FlexibleDatePost.objects.create( - title="Django 1.0 is released", slug="Django 1.0", - subtitle="Finally", posted=datetime.date(2008, 9, 3), + title="Django 1.0 is released", + slug="Django 1.0", + subtitle="Finally", + posted=datetime.date(2008, 9, 3), ) - form = FlexDatePostForm({'title': "Django 1.0 is released"}) + form = FlexDatePostForm({"title": "Django 1.0 is released"}) self.assertTrue(form.is_valid()) - form = FlexDatePostForm({'slug': "Django 1.0"}) + form = FlexDatePostForm({"slug": "Django 1.0"}) self.assertTrue(form.is_valid()) - form = FlexDatePostForm({'subtitle': "Finally"}) + form = FlexDatePostForm({"subtitle": "Finally"}) self.assertTrue(form.is_valid()) - data = {'subtitle': "Finally", "title": "Django 1.0 is released", "slug": "Django 1.0"} + data = { + "subtitle": "Finally", + "title": "Django 1.0 is released", + "slug": "Django 1.0", + } form = FlexDatePostForm(data, instance=p) self.assertTrue(form.is_valid()) @@ -1171,36 +1377,38 @@ class UniqueTest(TestCase): class CustomProductForm(ProductForm): class Meta(ProductForm.Meta): error_messages = { - 'slug': { - 'unique': "%(model_name)s's %(field_label)s not unique.", + "slug": { + "unique": "%(model_name)s's %(field_label)s not unique.", } } - Product.objects.create(slug='teddy-bear-blue') - form = CustomProductForm({'slug': 'teddy-bear-blue'}) + Product.objects.create(slug="teddy-bear-blue") + form = CustomProductForm({"slug": "teddy-bear-blue"}) self.assertEqual(len(form.errors), 1) - self.assertEqual(form.errors['slug'], ["Product's Slug not unique."]) + self.assertEqual(form.errors["slug"], ["Product's Slug not unique."]) def test_override_unique_together_message(self): class CustomPriceForm(PriceForm): class Meta(PriceForm.Meta): error_messages = { NON_FIELD_ERRORS: { - 'unique_together': "%(model_name)s's %(field_labels)s not unique.", + "unique_together": "%(model_name)s's %(field_labels)s not unique.", } } Price.objects.create(price=6.00, quantity=1) - form = CustomPriceForm({'price': '6.00', 'quantity': '1'}) + form = CustomPriceForm({"price": "6.00", "quantity": "1"}) self.assertEqual(len(form.errors), 1) - self.assertEqual(form.errors[NON_FIELD_ERRORS], ["Price's Price and Quantity not unique."]) + self.assertEqual( + form.errors[NON_FIELD_ERRORS], ["Price's Price and Quantity not unique."] + ) def test_override_unique_for_date_message(self): class CustomPostForm(PostForm): class Meta(PostForm.Meta): error_messages = { - 'title': { - 'unique_for_date': ( + "title": { + "unique_for_date": ( "%(model_name)s's %(field_label)s not unique " "for %(date_field_label)s date." ), @@ -1208,21 +1416,33 @@ class UniqueTest(TestCase): } Post.objects.create( - title="Django 1.0 is released", slug="Django 1.0", - subtitle="Finally", posted=datetime.date(2008, 9, 3), + title="Django 1.0 is released", + slug="Django 1.0", + subtitle="Finally", + posted=datetime.date(2008, 9, 3), + ) + form = CustomPostForm( + {"title": "Django 1.0 is released", "posted": "2008-09-03"} ) - form = CustomPostForm({'title': "Django 1.0 is released", 'posted': '2008-09-03'}) self.assertEqual(len(form.errors), 1) - self.assertEqual(form.errors['title'], ["Post's Title not unique for Posted date."]) + self.assertEqual( + form.errors["title"], ["Post's Title not unique for Posted date."] + ) class ModelFormBasicTests(TestCase): def create_basic_data(self): - self.c1 = Category.objects.create(name='Entertainment', slug='entertainment', url='entertainment') - self.c2 = Category.objects.create(name="It's a test", slug='its-test', url='test') - self.c3 = Category.objects.create(name='Third test', slug='third-test', url='third') - self.w_royko = Writer.objects.create(name='Mike Royko') - self.w_woodward = Writer.objects.create(name='Bob Woodward') + self.c1 = Category.objects.create( + name="Entertainment", slug="entertainment", url="entertainment" + ) + self.c2 = Category.objects.create( + name="It's a test", slug="its-test", url="test" + ) + self.c3 = Category.objects.create( + name="Third test", slug="third-test", url="third" + ) + self.w_royko = Writer.objects.create(name="Mike Royko") + self.w_woodward = Writer.objects.create(name="Bob Woodward") def test_base_form(self): self.assertEqual(Category.objects.count(), 0) @@ -1234,17 +1454,18 @@ class ModelFormBasicTests(TestCase): <tr><th><label for="id_slug">Slug:</label></th> <td><input id="id_slug" type="text" name="slug" maxlength="20" required></td></tr> <tr><th><label for="id_url">The URL:</label></th> -<td><input id="id_url" type="text" name="url" maxlength="40" required></td></tr>""" +<td><input id="id_url" type="text" name="url" maxlength="40" required></td></tr>""", ) self.assertHTMLEqual( str(f.as_ul()), """<li><label for="id_name">Name:</label> <input id="id_name" type="text" name="name" maxlength="20" required></li> <li><label for="id_slug">Slug:</label> <input id="id_slug" type="text" name="slug" maxlength="20" required></li> -<li><label for="id_url">The URL:</label> <input id="id_url" type="text" name="url" maxlength="40" required></li>""" +<li><label for="id_url">The URL:</label> <input id="id_url" type="text" name="url" maxlength="40" required></li>""", ) self.assertHTMLEqual( str(f["name"]), - """<input id="id_name" type="text" name="name" maxlength="20" required>""") + """<input id="id_name" type="text" name="name" maxlength="20" required>""", + ) def test_auto_id(self): f = BaseCategoryForm(auto_id=False) @@ -1252,7 +1473,7 @@ class ModelFormBasicTests(TestCase): str(f.as_ul()), """<li>Name: <input type="text" name="name" maxlength="20" required></li> <li>Slug: <input type="text" name="slug" maxlength="20" required></li> -<li>The URL: <input type="text" name="url" maxlength="40" required></li>""" +<li>The URL: <input type="text" name="url" maxlength="40" required></li>""", ) def test_initial_values(self): @@ -1261,12 +1482,13 @@ class ModelFormBasicTests(TestCase): f = ArticleForm( auto_id=False, initial={ - 'headline': 'Your headline here', - 'categories': [str(self.c1.id), str(self.c2.id)] - }) + "headline": "Your headline here", + "categories": [str(self.c1.id), str(self.c2.id)], + }, + ) self.assertHTMLEqual( f.as_ul(), - '''<li>Headline: <input type="text" name="headline" value="Your headline here" maxlength="50" required></li> + """<li>Headline: <input type="text" name="headline" value="Your headline here" maxlength="50" required></li> <li>Slug: <input type="text" name="slug" maxlength="50" required></li> <li>Pub date: <input type="text" name="pub_date" required></li> <li>Writer: <select name="writer" required> @@ -1285,30 +1507,32 @@ class ModelFormBasicTests(TestCase): <option value="1">Draft</option> <option value="2">Pending</option> <option value="3">Live</option> -</select></li>''' % (self.w_woodward.pk, self.w_royko.pk, self.c1.pk, self.c2.pk, self.c3.pk)) +</select></li>""" + % (self.w_woodward.pk, self.w_royko.pk, self.c1.pk, self.c2.pk, self.c3.pk), + ) # When the ModelForm is passed an instance, that instance's current values are # inserted as 'initial' data in each Field. f = RoykoForm(auto_id=False, instance=self.w_royko) self.assertHTMLEqual( str(f), - '''<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" required><br> - <span class="helptext">Use both first and last names.</span></td></tr>''' + """<tr><th>Name:</th><td><input type="text" name="name" value="Mike Royko" maxlength="50" required><br> + <span class="helptext">Use both first and last names.</span></td></tr>""", ) art = Article.objects.create( - headline='Test article', - slug='test-article', + headline="Test article", + slug="test-article", pub_date=datetime.date(1988, 1, 4), writer=self.w_royko, - article='Hello.' + article="Hello.", ) art_id_1 = art.id f = ArticleForm(auto_id=False, instance=art) self.assertHTMLEqual( f.as_ul(), - '''<li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" required></li> + """<li>Headline: <input type="text" name="headline" value="Test article" maxlength="50" required></li> <li>Slug: <input type="text" name="slug" value="test-article" maxlength="50" required></li> <li>Pub date: <input type="text" name="pub_date" value="1988-01-04" required></li> <li>Writer: <select name="writer" required> @@ -1327,21 +1551,26 @@ class ModelFormBasicTests(TestCase): <option value="1">Draft</option> <option value="2">Pending</option> <option value="3">Live</option> -</select></li>''' % (self.w_woodward.pk, self.w_royko.pk, self.c1.pk, self.c2.pk, self.c3.pk)) +</select></li>""" + % (self.w_woodward.pk, self.w_royko.pk, self.c1.pk, self.c2.pk, self.c3.pk), + ) - f = ArticleForm({ - 'headline': 'Test headline', - 'slug': 'test-headline', - 'pub_date': '1984-02-06', - 'writer': str(self.w_royko.pk), - 'article': 'Hello.' - }, instance=art) + f = ArticleForm( + { + "headline": "Test headline", + "slug": "test-headline", + "pub_date": "1984-02-06", + "writer": str(self.w_royko.pk), + "article": "Hello.", + }, + instance=art, + ) self.assertEqual(f.errors, {}) self.assertTrue(f.is_valid()) test_art = f.save() self.assertEqual(test_art.id, art_id_1) test_art = Article.objects.get(id=art_id_1) - self.assertEqual(test_art.headline, 'Test headline') + self.assertEqual(test_art.headline, "Test headline") def test_m2m_initial_callable(self): """ @@ -1352,14 +1581,14 @@ class ModelFormBasicTests(TestCase): # Set up a callable initial value def formfield_for_dbfield(db_field, **kwargs): - if db_field.name == 'categories': - kwargs['initial'] = lambda: Category.objects.all().order_by('name')[:2] + if db_field.name == "categories": + kwargs["initial"] = lambda: Category.objects.all().order_by("name")[:2] return db_field.formfield(**kwargs) # Create a ModelForm, instantiate it, and check that the output is as expected ModelForm = modelform_factory( Article, - fields=['headline', 'categories'], + fields=["headline", "categories"], formfield_callback=formfield_for_dbfield, ) form = ModelForm() @@ -1373,19 +1602,22 @@ class ModelFormBasicTests(TestCase): <option value="%d" selected>It's a test</option> <option value="%d">Third test</option> </select></li>""" - % (self.c1.pk, self.c2.pk, self.c3.pk)) + % (self.c1.pk, self.c2.pk, self.c3.pk), + ) def test_basic_creation(self): self.assertEqual(Category.objects.count(), 0) - f = BaseCategoryForm({ - 'name': 'Entertainment', - 'slug': 'entertainment', - 'url': 'entertainment', - }) + f = BaseCategoryForm( + { + "name": "Entertainment", + "slug": "entertainment", + "url": "entertainment", + } + ) self.assertTrue(f.is_valid()) - self.assertEqual(f.cleaned_data['name'], 'Entertainment') - self.assertEqual(f.cleaned_data['slug'], 'entertainment') - self.assertEqual(f.cleaned_data['url'], 'entertainment') + self.assertEqual(f.cleaned_data["name"], "Entertainment") + self.assertEqual(f.cleaned_data["slug"], "entertainment") + self.assertEqual(f.cleaned_data["url"], "entertainment") c1 = f.save() # Testing whether the same object is returned from the # ORM... not the fastest way... @@ -1398,7 +1630,9 @@ class ModelFormBasicTests(TestCase): # If you call save() with commit=False, then it will return an object that # hasn't yet been saved to the database. In this case, it's up to you to call # save() on the resulting model instance. - f = BaseCategoryForm({'name': 'Third test', 'slug': 'third-test', 'url': 'third'}) + f = BaseCategoryForm( + {"name": "Third test", "slug": "third-test", "url": "third"} + ) self.assertTrue(f.is_valid()) c1 = f.save(commit=False) self.assertEqual(c1.name, "Third test") @@ -1408,17 +1642,19 @@ class ModelFormBasicTests(TestCase): def test_save_with_data_errors(self): # If you call save() with invalid data, you'll get a ValueError. - f = BaseCategoryForm({'name': '', 'slug': 'not a slug!', 'url': 'foo'}) - self.assertEqual(f.errors['name'], ['This field is required.']) + f = BaseCategoryForm({"name": "", "slug": "not a slug!", "url": "foo"}) + self.assertEqual(f.errors["name"], ["This field is required."]) self.assertEqual( - f.errors['slug'], - ['Enter a valid “slug” consisting of letters, numbers, underscores or hyphens.'] + f.errors["slug"], + [ + "Enter a valid “slug” consisting of letters, numbers, underscores or hyphens." + ], ) - self.assertEqual(f.cleaned_data, {'url': 'foo'}) + self.assertEqual(f.cleaned_data, {"url": "foo"}) msg = "The Category could not be created because the data didn't validate." with self.assertRaisesMessage(ValueError, msg): f.save() - f = BaseCategoryForm({'name': '', 'slug': '', 'url': 'foo'}) + f = BaseCategoryForm({"name": "", "slug": "", "url": "foo"}) with self.assertRaisesMessage(ValueError, msg): f.save() @@ -1430,7 +1666,7 @@ class ModelFormBasicTests(TestCase): f = ArticleForm(auto_id=False) self.assertHTMLEqual( str(f), - '''<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" required></td></tr> + """<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" required></td></tr> <tr><th>Slug:</th><td><input type="text" name="slug" maxlength="50" required></td></tr> <tr><th>Pub date:</th><td><input type="text" name="pub_date" required></td></tr> <tr><th>Writer:</th><td><select name="writer" required> @@ -1449,18 +1685,24 @@ class ModelFormBasicTests(TestCase): <option value="1">Draft</option> <option value="2">Pending</option> <option value="3">Live</option> -</select></td></tr>''' % (self.w_woodward.pk, self.w_royko.pk, self.c1.pk, self.c2.pk, self.c3.pk)) +</select></td></tr>""" + % (self.w_woodward.pk, self.w_royko.pk, self.c1.pk, self.c2.pk, self.c3.pk), + ) # Add some categories and test the many-to-many form output. new_art = Article.objects.create( - article="Hello.", headline="New headline", slug="new-headline", - pub_date=datetime.date(1988, 1, 4), writer=self.w_royko) - new_art.categories.add(Category.objects.get(name='Entertainment')) + article="Hello.", + headline="New headline", + slug="new-headline", + pub_date=datetime.date(1988, 1, 4), + writer=self.w_royko, + ) + new_art.categories.add(Category.objects.get(name="Entertainment")) self.assertSequenceEqual(new_art.categories.all(), [self.c1]) f = ArticleForm(auto_id=False, instance=new_art) self.assertHTMLEqual( f.as_ul(), - '''<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" required></li> + """<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" required></li> <li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" required></li> <li>Pub date: <input type="text" name="pub_date" value="1988-01-04" required></li> <li>Writer: <select name="writer" required> @@ -1479,7 +1721,9 @@ class ModelFormBasicTests(TestCase): <option value="1">Draft</option> <option value="2">Pending</option> <option value="3">Live</option> -</select></li>''' % (self.w_woodward.pk, self.w_royko.pk, self.c1.pk, self.c2.pk, self.c3.pk)) +</select></li>""" + % (self.w_woodward.pk, self.w_royko.pk, self.c1.pk, self.c2.pk, self.c3.pk), + ) def test_subset_fields(self): # You can restrict a form to a subset of the complete list of fields @@ -1491,59 +1735,70 @@ class ModelFormBasicTests(TestCase): class PartialArticleForm(forms.ModelForm): class Meta: model = Article - fields = ('headline', 'pub_date') + fields = ("headline", "pub_date") f = PartialArticleForm(auto_id=False) self.assertHTMLEqual( str(f), - '''<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" required></td></tr> -<tr><th>Pub date:</th><td><input type="text" name="pub_date" required></td></tr>''') + """<tr><th>Headline:</th><td><input type="text" name="headline" maxlength="50" required></td></tr> +<tr><th>Pub date:</th><td><input type="text" name="pub_date" required></td></tr>""", + ) class PartialArticleFormWithSlug(forms.ModelForm): class Meta: model = Article - fields = ('headline', 'slug', 'pub_date') + fields = ("headline", "slug", "pub_date") - w_royko = Writer.objects.create(name='Mike Royko') + w_royko = Writer.objects.create(name="Mike Royko") art = Article.objects.create( - article="Hello.", headline="New headline", slug="new-headline", - pub_date=datetime.date(1988, 1, 4), writer=w_royko) - f = PartialArticleFormWithSlug({ - 'headline': 'New headline', - 'slug': 'new-headline', - 'pub_date': '1988-01-04' - }, auto_id=False, instance=art) + article="Hello.", + headline="New headline", + slug="new-headline", + pub_date=datetime.date(1988, 1, 4), + writer=w_royko, + ) + f = PartialArticleFormWithSlug( + { + "headline": "New headline", + "slug": "new-headline", + "pub_date": "1988-01-04", + }, + auto_id=False, + instance=art, + ) self.assertHTMLEqual( f.as_ul(), - '''<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" required></li> + """<li>Headline: <input type="text" name="headline" value="New headline" maxlength="50" required></li> <li>Slug: <input type="text" name="slug" value="new-headline" maxlength="50" required></li> -<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" required></li>''' +<li>Pub date: <input type="text" name="pub_date" value="1988-01-04" required></li>""", ) self.assertTrue(f.is_valid()) new_art = f.save() self.assertEqual(new_art.id, art.id) new_art = Article.objects.get(id=art.id) - self.assertEqual(new_art.headline, 'New headline') + self.assertEqual(new_art.headline, "New headline") def test_m2m_editing(self): self.create_basic_data() form_data = { - 'headline': 'New headline', - 'slug': 'new-headline', - 'pub_date': '1988-01-04', - 'writer': str(self.w_royko.pk), - 'article': 'Hello.', - 'categories': [str(self.c1.id), str(self.c2.id)] + "headline": "New headline", + "slug": "new-headline", + "pub_date": "1988-01-04", + "writer": str(self.w_royko.pk), + "article": "Hello.", + "categories": [str(self.c1.id), str(self.c2.id)], } # Create a new article, with categories, via the form. f = ArticleForm(form_data) new_art = f.save() new_art = Article.objects.get(id=new_art.id) art_id_1 = new_art.id - self.assertSequenceEqual(new_art.categories.order_by('name'), [self.c1, self.c2]) + self.assertSequenceEqual( + new_art.categories.order_by("name"), [self.c1, self.c2] + ) # Now, submit form data with no categories. This deletes the existing categories. - form_data['categories'] = [] + form_data["categories"] = [] f = ArticleForm(form_data, instance=new_art) new_art = f.save() self.assertEqual(new_art.id, art_id_1) @@ -1560,7 +1815,7 @@ class ModelFormBasicTests(TestCase): # Create a new article, with categories, via the form, but use commit=False. # The m2m data won't be saved until save_m2m() is invoked on the form. - form_data['categories'] = [str(self.c1.id), str(self.c2.id)] + form_data["categories"] = [str(self.c1.id), str(self.c2.id)] f = ArticleForm(form_data) new_art = f.save(commit=False) @@ -1575,7 +1830,9 @@ class ModelFormBasicTests(TestCase): # Save the m2m data on the form f.save_m2m() - self.assertSequenceEqual(new_art.categories.order_by('name'), [self.c1, self.c2]) + self.assertSequenceEqual( + new_art.categories.order_by("name"), [self.c1, self.c2] + ) def test_custom_form_fields(self): # Here, we define a custom ModelForm. Because it happens to have the same fields as @@ -1588,12 +1845,14 @@ class ModelFormBasicTests(TestCase): class Meta: model = Category - fields = '__all__' + fields = "__all__" - cat = Category.objects.create(name='Third test') - form = ShortCategory({'name': 'Third', 'slug': 'third', 'url': '3rd'}, instance=cat) - self.assertEqual(form.save().name, 'Third') - self.assertEqual(Category.objects.get(id=cat.id).name, 'Third') + cat = Category.objects.create(name="Third test") + form = ShortCategory( + {"name": "Third", "slug": "third", "url": "3rd"}, instance=cat + ) + self.assertEqual(form.save().name, "Third") + self.assertEqual(Category.objects.get(id=cat.id).name, "Third") def test_runtime_choicefield_populated(self): self.maxDiff = None @@ -1604,7 +1863,7 @@ class ModelFormBasicTests(TestCase): f = ArticleForm(auto_id=False) self.assertHTMLEqual( f.as_ul(), - '''<li>Headline: <input type="text" name="headline" maxlength="50" required></li> + """<li>Headline: <input type="text" name="headline" maxlength="50" required></li> <li>Slug: <input type="text" name="slug" maxlength="50" required></li> <li>Pub date: <input type="text" name="pub_date" required></li> <li>Writer: <select name="writer" required> @@ -1623,13 +1882,15 @@ class ModelFormBasicTests(TestCase): <option value="1">Draft</option> <option value="2">Pending</option> <option value="3">Live</option> -</select></li>''' % (self.w_woodward.pk, self.w_royko.pk, self.c1.pk, self.c2.pk, self.c3.pk)) +</select></li>""" + % (self.w_woodward.pk, self.w_royko.pk, self.c1.pk, self.c2.pk, self.c3.pk), + ) - c4 = Category.objects.create(name='Fourth', url='4th') - w_bernstein = Writer.objects.create(name='Carl Bernstein') + c4 = Category.objects.create(name="Fourth", url="4th") + w_bernstein = Writer.objects.create(name="Carl Bernstein") self.assertHTMLEqual( f.as_ul(), - '''<li>Headline: <input type="text" name="headline" maxlength="50" required></li> + """<li>Headline: <input type="text" name="headline" maxlength="50" required></li> <li>Slug: <input type="text" name="slug" maxlength="50" required></li> <li>Pub date: <input type="text" name="pub_date" required></li> <li>Writer: <select name="writer" required> @@ -1650,64 +1911,79 @@ class ModelFormBasicTests(TestCase): <option value="1">Draft</option> <option value="2">Pending</option> <option value="3">Live</option> -</select></li>''' % (self.w_woodward.pk, w_bernstein.pk, self.w_royko.pk, self.c1.pk, self.c2.pk, self.c3.pk, c4.pk)) +</select></li>""" + % ( + self.w_woodward.pk, + w_bernstein.pk, + self.w_royko.pk, + self.c1.pk, + self.c2.pk, + self.c3.pk, + c4.pk, + ), + ) def test_recleaning_model_form_instance(self): """ Re-cleaning an instance that was added via a ModelForm shouldn't raise a pk uniqueness error. """ + class AuthorForm(forms.ModelForm): class Meta: model = Author - fields = '__all__' + fields = "__all__" - form = AuthorForm({'full_name': 'Bob'}) + form = AuthorForm({"full_name": "Bob"}) self.assertTrue(form.is_valid()) obj = form.save() - obj.name = 'Alice' + obj.name = "Alice" obj.full_clean() def test_validate_foreign_key_uses_default_manager(self): class MyForm(forms.ModelForm): class Meta: model = Article - fields = '__all__' + fields = "__all__" # Archived writers are filtered out by the default manager. - w = Writer.objects.create(name='Randy', archived=True) + w = Writer.objects.create(name="Randy", archived=True) data = { - 'headline': 'My Article', - 'slug': 'my-article', - 'pub_date': datetime.date.today(), - 'writer': w.pk, - 'article': 'lorem ipsum', + "headline": "My Article", + "slug": "my-article", + "pub_date": datetime.date.today(), + "writer": w.pk, + "article": "lorem ipsum", } form = MyForm(data) self.assertIs(form.is_valid(), False) self.assertEqual( form.errors, - {'writer': ['Select a valid choice. That choice is not one of the available choices.']}, + { + "writer": [ + "Select a valid choice. That choice is not one of the available choices." + ] + }, ) def test_validate_foreign_key_to_model_with_overridden_manager(self): class MyForm(forms.ModelForm): class Meta: model = Article - fields = '__all__' + fields = "__all__" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # Allow archived authors. - self.fields['writer'].queryset = Writer._base_manager.all() + self.fields["writer"].queryset = Writer._base_manager.all() - w = Writer.objects.create(name='Randy', archived=True) + w = Writer.objects.create(name="Randy", archived=True) data = { - 'headline': 'My Article', - 'slug': 'my-article', - 'pub_date': datetime.date.today(), - 'writer': w.pk, - 'article': 'lorem ipsum', + "headline": "My Article", + "slug": "my-article", + "pub_date": datetime.date.today(), + "writer": w.pk, + "article": "lorem ipsum", } form = MyForm(data) self.assertIs(form.is_valid(), True) @@ -1718,16 +1994,24 @@ class ModelFormBasicTests(TestCase): class ModelMultipleChoiceFieldTests(TestCase): @classmethod def setUpTestData(cls): - cls.c1 = Category.objects.create(name='Entertainment', slug='entertainment', url='entertainment') - cls.c2 = Category.objects.create(name="It's a test", slug='its-test', url='test') - cls.c3 = Category.objects.create(name='Third', slug='third-test', url='third') + cls.c1 = Category.objects.create( + name="Entertainment", slug="entertainment", url="entertainment" + ) + cls.c2 = Category.objects.create( + name="It's a test", slug="its-test", url="test" + ) + cls.c3 = Category.objects.create(name="Third", slug="third-test", url="third") def test_model_multiple_choice_field(self): f = forms.ModelMultipleChoiceField(Category.objects.all()) - self.assertEqual(list(f.choices), [ - (self.c1.pk, 'Entertainment'), - (self.c2.pk, "It's a test"), - (self.c3.pk, 'Third')]) + self.assertEqual( + list(f.choices), + [ + (self.c1.pk, "Entertainment"), + (self.c2.pk, "It's a test"), + (self.c3.pk, "Third"), + ], + ) with self.assertRaises(ValidationError): f.clean(None) with self.assertRaises(ValidationError): @@ -1748,17 +2032,17 @@ class ModelMultipleChoiceFieldTests(TestCase): [self.c1, self.c2], ) with self.assertRaises(ValidationError): - f.clean(['100']) + f.clean(["100"]) with self.assertRaises(ValidationError): - f.clean('hello') + f.clean("hello") with self.assertRaises(ValidationError): - f.clean(['fail']) + f.clean(["fail"]) # Invalid types that require TypeError to be caught (#22808). with self.assertRaises(ValidationError): - f.clean([['fail']]) + f.clean([["fail"]]) with self.assertRaises(ValidationError): - f.clean([{'foo': 'bar'}]) + f.clean([{"foo": "bar"}]) # Add a Category object *after* the ModelMultipleChoiceField has already been # instantiated. This proves clean() checks the database during clean() rather @@ -1766,13 +2050,13 @@ class ModelMultipleChoiceFieldTests(TestCase): # Note, we are using an id of 1006 here since tests that run before # this may create categories with primary keys up to 6. Use # a number that will not conflict. - c6 = Category.objects.create(id=1006, name='Sixth', url='6th') + c6 = Category.objects.create(id=1006, name="Sixth", url="6th") self.assertCountEqual(f.clean([c6.id]), [c6]) # Delete a Category object *after* the ModelMultipleChoiceField has already been # instantiated. This proves clean() checks the database during clean() rather # than caching it at time of instantiation. - Category.objects.get(url='6th').delete() + Category.objects.get(url="6th").delete() with self.assertRaises(ValidationError): f.clean([c6.id]) @@ -1781,17 +2065,18 @@ class ModelMultipleChoiceFieldTests(TestCase): self.assertIsInstance(f.clean([]), EmptyQuerySet) self.assertIsInstance(f.clean(()), EmptyQuerySet) with self.assertRaises(ValidationError): - f.clean(['0']) + f.clean(["0"]) with self.assertRaises(ValidationError): - f.clean([str(self.c3.id), '0']) + f.clean([str(self.c3.id), "0"]) with self.assertRaises(ValidationError): - f.clean([str(self.c1.id), '0']) + f.clean([str(self.c1.id), "0"]) # queryset can be changed after the field is created. - f.queryset = Category.objects.exclude(name='Third') - self.assertEqual(list(f.choices), [ - (self.c1.pk, 'Entertainment'), - (self.c2.pk, "It's a test")]) + f.queryset = Category.objects.exclude(name="Third") + self.assertEqual( + list(f.choices), + [(self.c1.pk, "Entertainment"), (self.c2.pk, "It's a test")], + ) self.assertSequenceEqual(f.clean([self.c2.id]), [self.c2]) with self.assertRaises(ValidationError): f.clean([self.c3.id]) @@ -1800,10 +2085,14 @@ class ModelMultipleChoiceFieldTests(TestCase): f.queryset = Category.objects.all() f.label_from_instance = lambda obj: "multicategory " + str(obj) - self.assertEqual(list(f.choices), [ - (self.c1.pk, 'multicategory Entertainment'), - (self.c2.pk, "multicategory It's a test"), - (self.c3.pk, 'multicategory Third')]) + self.assertEqual( + list(f.choices), + [ + (self.c1.pk, "multicategory Entertainment"), + (self.c2.pk, "multicategory It's a test"), + (self.c3.pk, "multicategory Third"), + ], + ) def test_model_multiple_choice_number_of_queries(self): """ @@ -1826,7 +2115,9 @@ class ModelMultipleChoiceFieldTests(TestCase): def my_validator(value): self._validator_run = True - f = forms.ModelMultipleChoiceField(queryset=Writer.objects.all(), validators=[my_validator]) + f = forms.ModelMultipleChoiceField( + queryset=Writer.objects.all(), validators=[my_validator] + ) f.clean([p.pk for p in Writer.objects.all()[8:9]]) self.assertTrue(self._validator_run) @@ -1834,27 +2125,30 @@ class ModelMultipleChoiceFieldTests(TestCase): """ Test support of show_hidden_initial by ModelMultipleChoiceField. """ + class WriterForm(forms.Form): - persons = forms.ModelMultipleChoiceField(show_hidden_initial=True, queryset=Writer.objects.all()) + persons = forms.ModelMultipleChoiceField( + show_hidden_initial=True, queryset=Writer.objects.all() + ) person1 = Writer.objects.create(name="Person 1") person2 = Writer.objects.create(name="Person 2") form = WriterForm( - initial={'persons': [person1, person2]}, + initial={"persons": [person1, person2]}, data={ - 'initial-persons': [str(person1.pk), str(person2.pk)], - 'persons': [str(person1.pk), str(person2.pk)], + "initial-persons": [str(person1.pk), str(person2.pk)], + "persons": [str(person1.pk), str(person2.pk)], }, ) self.assertTrue(form.is_valid()) self.assertFalse(form.has_changed()) form = WriterForm( - initial={'persons': [person1, person2]}, + initial={"persons": [person1, person2]}, data={ - 'initial-persons': [str(person1.pk), str(person2.pk)], - 'persons': [str(person2.pk)], + "initial-persons": [str(person1.pk), str(person2.pk)], + "persons": [str(person2.pk)], }, ) self.assertTrue(form.is_valid()) @@ -1866,23 +2160,27 @@ class ModelMultipleChoiceFieldTests(TestCase): CheckboxSelectMultiple widget doesn't produce unnecessary db queries when accessing its BoundField's attrs. """ + class ModelMultipleChoiceForm(forms.Form): - categories = forms.ModelMultipleChoiceField(Category.objects.all(), widget=forms.CheckboxSelectMultiple) + categories = forms.ModelMultipleChoiceField( + Category.objects.all(), widget=forms.CheckboxSelectMultiple + ) form = ModelMultipleChoiceForm() - field = form['categories'] # BoundField - template = Template('{{ field.name }}{{ field }}{{ field.help_text }}') + field = form["categories"] # BoundField + template = Template("{{ field.name }}{{ field }}{{ field.help_text }}") with self.assertNumQueries(1): - template.render(Context({'field': field})) + template.render(Context({"field": field})) def test_show_hidden_initial_changed_queries_efficiently(self): class WriterForm(forms.Form): persons = forms.ModelMultipleChoiceField( - show_hidden_initial=True, queryset=Writer.objects.all()) + show_hidden_initial=True, queryset=Writer.objects.all() + ) writers = (Writer.objects.create(name=str(x)) for x in range(0, 50)) writer_pks = tuple(x.pk for x in writers) - form = WriterForm(data={'initial-persons': writer_pks}) + form = WriterForm(data={"initial-persons": writer_pks}) with self.assertNumQueries(1): self.assertTrue(form.has_changed()) @@ -1890,30 +2188,32 @@ class ModelMultipleChoiceFieldTests(TestCase): class PersonForm(forms.Form): persons = forms.ModelMultipleChoiceField(queryset=Person.objects.all()) - person1 = Person.objects.create(name='Person 1') + person1 = Person.objects.create(name="Person 1") form = PersonForm(data={}) - queryset = form.fields['persons'].clean([str(person1.pk)] * 50) + queryset = form.fields["persons"].clean([str(person1.pk)] * 50) sql, params = queryset.query.sql_with_params() self.assertEqual(len(params), 1) def test_to_field_name_with_initial_data(self): class ArticleCategoriesForm(forms.ModelForm): - categories = forms.ModelMultipleChoiceField(Category.objects.all(), to_field_name='slug') + categories = forms.ModelMultipleChoiceField( + Category.objects.all(), to_field_name="slug" + ) class Meta: model = Article - fields = ['categories'] + fields = ["categories"] article = Article.objects.create( - headline='Test article', - slug='test-article', + headline="Test article", + slug="test-article", pub_date=datetime.date(1988, 1, 4), - writer=Writer.objects.create(name='Test writer'), - article='Hello.', + writer=Writer.objects.create(name="Test writer"), + article="Hello.", ) article.categories.add(self.c2, self.c3) form = ArticleCategoriesForm(instance=article) - self.assertCountEqual(form['categories'].value(), [self.c2.slug, self.c3.slug]) + self.assertCountEqual(form["categories"].value(), [self.c2.slug, self.c3.slug]) class ModelOneToOneFieldTests(TestCase): @@ -1921,14 +2221,14 @@ class ModelOneToOneFieldTests(TestCase): class ImprovedArticleForm(forms.ModelForm): class Meta: model = ImprovedArticle - fields = '__all__' + fields = "__all__" class ImprovedArticleWithParentLinkForm(forms.ModelForm): class Meta: model = ImprovedArticleWithParentLink - fields = '__all__' + fields = "__all__" - self.assertEqual(list(ImprovedArticleForm.base_fields), ['article']) + self.assertEqual(list(ImprovedArticleForm.base_fields), ["article"]) self.assertEqual(list(ImprovedArticleWithParentLinkForm.base_fields), []) def test_modelform_subclassed_model(self): @@ -1936,16 +2236,24 @@ class ModelOneToOneFieldTests(TestCase): class Meta: # BetterWriter model is a subclass of Writer with an additional `score` field model = BetterWriter - fields = '__all__' + fields = "__all__" - bw = BetterWriter.objects.create(name='Joe Better', score=10) - self.assertEqual(sorted(model_to_dict(bw)), ['id', 'name', 'score', 'writer_ptr']) + bw = BetterWriter.objects.create(name="Joe Better", score=10) + self.assertEqual( + sorted(model_to_dict(bw)), ["id", "name", "score", "writer_ptr"] + ) self.assertEqual(sorted(model_to_dict(bw, fields=[])), []) - self.assertEqual(sorted(model_to_dict(bw, fields=['id', 'name'])), ['id', 'name']) - self.assertEqual(sorted(model_to_dict(bw, exclude=[])), ['id', 'name', 'score', 'writer_ptr']) - self.assertEqual(sorted(model_to_dict(bw, exclude=['id', 'name'])), ['score', 'writer_ptr']) + self.assertEqual( + sorted(model_to_dict(bw, fields=["id", "name"])), ["id", "name"] + ) + self.assertEqual( + sorted(model_to_dict(bw, exclude=[])), ["id", "name", "score", "writer_ptr"] + ) + self.assertEqual( + sorted(model_to_dict(bw, exclude=["id", "name"])), ["score", "writer_ptr"] + ) - form = BetterWriterForm({'name': 'Some Name', 'score': 12}) + form = BetterWriterForm({"name": "Some Name", "score": 12}) self.assertTrue(form.is_valid()) bw2 = form.save() self.assertEqual(bw2.score, 12) @@ -1955,57 +2263,63 @@ class ModelOneToOneFieldTests(TestCase): class Meta: # WriterProfile has a OneToOneField to Writer model = WriterProfile - fields = '__all__' + fields = "__all__" - self.w_royko = Writer.objects.create(name='Mike Royko') - self.w_woodward = Writer.objects.create(name='Bob Woodward') + self.w_royko = Writer.objects.create(name="Mike Royko") + self.w_woodward = Writer.objects.create(name="Bob Woodward") form = WriterProfileForm() self.assertHTMLEqual( form.as_p(), - '''<p><label for="id_writer">Writer:</label> <select name="writer" id="id_writer" required> + """<p><label for="id_writer">Writer:</label> <select name="writer" id="id_writer" required> <option value="" selected>---------</option> <option value="%s">Bob Woodward</option> <option value="%s">Mike Royko</option> </select></p> -<p><label for="id_age">Age:</label> <input type="number" name="age" id="id_age" min="0" required></p>''' % ( - self.w_woodward.pk, self.w_royko.pk, - ) +<p><label for="id_age">Age:</label> <input type="number" name="age" id="id_age" min="0" required></p>""" + % ( + self.w_woodward.pk, + self.w_royko.pk, + ), ) data = { - 'writer': str(self.w_woodward.pk), - 'age': '65', + "writer": str(self.w_woodward.pk), + "age": "65", } form = WriterProfileForm(data) instance = form.save() - self.assertEqual(str(instance), 'Bob Woodward is 65') + self.assertEqual(str(instance), "Bob Woodward is 65") form = WriterProfileForm(instance=instance) self.assertHTMLEqual( form.as_p(), - '''<p><label for="id_writer">Writer:</label> <select name="writer" id="id_writer" required> + """<p><label for="id_writer">Writer:</label> <select name="writer" id="id_writer" required> <option value="">---------</option> <option value="%s" selected>Bob Woodward</option> <option value="%s">Mike Royko</option> </select></p> <p><label for="id_age">Age:</label> -<input type="number" name="age" value="65" id="id_age" min="0" required></p>''' % ( - self.w_woodward.pk, self.w_royko.pk, - ) +<input type="number" name="age" value="65" id="id_age" min="0" required></p>""" + % ( + self.w_woodward.pk, + self.w_royko.pk, + ), ) def test_assignment_of_none(self): class AuthorForm(forms.ModelForm): class Meta: model = Author - fields = ['publication', 'full_name'] + fields = ["publication", "full_name"] - publication = Publication.objects.create(title="Pravda", date_published=datetime.date(1991, 8, 22)) - author = Author.objects.create(publication=publication, full_name='John Doe') - form = AuthorForm({'publication': '', 'full_name': 'John Doe'}, instance=author) + publication = Publication.objects.create( + title="Pravda", date_published=datetime.date(1991, 8, 22) + ) + author = Author.objects.create(publication=publication, full_name="John Doe") + form = AuthorForm({"publication": "", "full_name": "John Doe"}, instance=author) self.assertTrue(form.is_valid()) - self.assertIsNone(form.cleaned_data['publication']) + self.assertIsNone(form.cleaned_data["publication"]) author = form.save() # author object returned from form still retains original publication object # that's why we need to retrieve it from database again @@ -2016,11 +2330,13 @@ class ModelOneToOneFieldTests(TestCase): class AuthorForm(forms.ModelForm): class Meta: model = Author1 - fields = ['publication', 'full_name'] + fields = ["publication", "full_name"] - publication = Publication.objects.create(title="Pravda", date_published=datetime.date(1991, 8, 22)) - author = Author1.objects.create(publication=publication, full_name='John Doe') - form = AuthorForm({'publication': '', 'full_name': 'John Doe'}, instance=author) + publication = Publication.objects.create( + title="Pravda", date_published=datetime.date(1991, 8, 22) + ) + author = Author1.objects.create(publication=publication, full_name="John Doe") + form = AuthorForm({"publication": "", "full_name": "John Doe"}, instance=author) self.assertFalse(form.is_valid()) @@ -2033,7 +2349,7 @@ class FileAndImageFieldTests(TestCase): """ f = forms.FileField(required=False) self.assertIs(f.clean(False), False) - self.assertIs(f.clean(False, 'initial'), False) + self.assertIs(f.clean(False, "initial"), False) def test_clean_false_required(self): """ @@ -2042,7 +2358,7 @@ class FileAndImageFieldTests(TestCase): otherwise the validation catches the lack of a required value. """ f = forms.FileField(required=True) - self.assertEqual(f.clean(False, 'initial'), 'initial') + self.assertEqual(f.clean(False, "initial"), "initial") with self.assertRaises(ValidationError): f.clean(False) @@ -2051,21 +2367,24 @@ class FileAndImageFieldTests(TestCase): Integration happy-path test that a model FileField can actually be set and cleared via a ModelForm. """ + class DocumentForm(forms.ModelForm): class Meta: model = Document - fields = '__all__' + fields = "__all__" form = DocumentForm() self.assertIn('name="myfile"', str(form)) - self.assertNotIn('myfile-clear', str(form)) - form = DocumentForm(files={'myfile': SimpleUploadedFile('something.txt', b'content')}) + self.assertNotIn("myfile-clear", str(form)) + form = DocumentForm( + files={"myfile": SimpleUploadedFile("something.txt", b"content")} + ) self.assertTrue(form.is_valid()) doc = form.save(commit=False) - self.assertEqual(doc.myfile.name, 'something.txt') + self.assertEqual(doc.myfile.name, "something.txt") form = DocumentForm(instance=doc) - self.assertIn('myfile-clear', str(form)) - form = DocumentForm(instance=doc, data={'myfile-clear': 'true'}) + self.assertIn("myfile-clear", str(form)) + form = DocumentForm(instance=doc, data={"myfile-clear": "true"}) doc = form.save(commit=False) self.assertFalse(doc.myfile) @@ -2075,94 +2394,98 @@ class FileAndImageFieldTests(TestCase): they get a validation error, and the bound redisplay of the form still includes the current file and the clear checkbox. """ + class DocumentForm(forms.ModelForm): class Meta: model = Document - fields = '__all__' + fields = "__all__" - form = DocumentForm(files={'myfile': SimpleUploadedFile('something.txt', b'content')}) + form = DocumentForm( + files={"myfile": SimpleUploadedFile("something.txt", b"content")} + ) self.assertTrue(form.is_valid()) doc = form.save(commit=False) form = DocumentForm( instance=doc, - files={'myfile': SimpleUploadedFile('something.txt', b'content')}, - data={'myfile-clear': 'true'}, + files={"myfile": SimpleUploadedFile("something.txt", b"content")}, + data={"myfile-clear": "true"}, ) self.assertTrue(not form.is_valid()) - self.assertEqual(form.errors['myfile'], - ['Please either submit a file or check the clear checkbox, not both.']) + self.assertEqual( + form.errors["myfile"], + ["Please either submit a file or check the clear checkbox, not both."], + ) rendered = str(form) - self.assertIn('something.txt', rendered) - self.assertIn('myfile-clear', rendered) + self.assertIn("something.txt", rendered) + self.assertIn("myfile-clear", rendered) def test_render_empty_file_field(self): class DocumentForm(forms.ModelForm): class Meta: model = Document - fields = '__all__' + fields = "__all__" doc = Document.objects.create() form = DocumentForm(instance=doc) self.assertHTMLEqual( - str(form['myfile']), - '<input id="id_myfile" name="myfile" type="file">' + str(form["myfile"]), '<input id="id_myfile" name="myfile" type="file">' ) def test_file_field_data(self): # Test conditions when files is either not given or empty. - f = TextFileForm(data={'description': 'Assistance'}) + f = TextFileForm(data={"description": "Assistance"}) self.assertFalse(f.is_valid()) - f = TextFileForm(data={'description': 'Assistance'}, files={}) + f = TextFileForm(data={"description": "Assistance"}, files={}) self.assertFalse(f.is_valid()) # Upload a file and ensure it all works as expected. f = TextFileForm( - data={'description': 'Assistance'}, - files={'file': SimpleUploadedFile('test1.txt', b'hello world')}, + data={"description": "Assistance"}, + files={"file": SimpleUploadedFile("test1.txt", b"hello world")}, ) self.assertTrue(f.is_valid()) - self.assertEqual(type(f.cleaned_data['file']), SimpleUploadedFile) + self.assertEqual(type(f.cleaned_data["file"]), SimpleUploadedFile) instance = f.save() - self.assertEqual(instance.file.name, 'tests/test1.txt') + self.assertEqual(instance.file.name, "tests/test1.txt") instance.file.delete() # If the previous file has been deleted, the file name can be reused f = TextFileForm( - data={'description': 'Assistance'}, - files={'file': SimpleUploadedFile('test1.txt', b'hello world')}, + data={"description": "Assistance"}, + files={"file": SimpleUploadedFile("test1.txt", b"hello world")}, ) self.assertTrue(f.is_valid()) - self.assertEqual(type(f.cleaned_data['file']), SimpleUploadedFile) + self.assertEqual(type(f.cleaned_data["file"]), SimpleUploadedFile) instance = f.save() - self.assertEqual(instance.file.name, 'tests/test1.txt') + self.assertEqual(instance.file.name, "tests/test1.txt") # Check if the max_length attribute has been inherited from the model. f = TextFileForm( - data={'description': 'Assistance'}, - files={'file': SimpleUploadedFile('test-maxlength.txt', b'hello world')}, + data={"description": "Assistance"}, + files={"file": SimpleUploadedFile("test-maxlength.txt", b"hello world")}, ) self.assertFalse(f.is_valid()) # Edit an instance that already has the file defined in the model. This will not # save the file again, but leave it exactly as it is. - f = TextFileForm({'description': 'Assistance'}, instance=instance) + f = TextFileForm({"description": "Assistance"}, instance=instance) self.assertTrue(f.is_valid()) - self.assertEqual(f.cleaned_data['file'].name, 'tests/test1.txt') + self.assertEqual(f.cleaned_data["file"].name, "tests/test1.txt") instance = f.save() - self.assertEqual(instance.file.name, 'tests/test1.txt') + self.assertEqual(instance.file.name, "tests/test1.txt") # Delete the current file since this is not done by Django. instance.file.delete() # Override the file by uploading a new one. f = TextFileForm( - data={'description': 'Assistance'}, - files={'file': SimpleUploadedFile('test2.txt', b'hello world')}, + data={"description": "Assistance"}, + files={"file": SimpleUploadedFile("test2.txt", b"hello world")}, instance=instance, ) self.assertTrue(f.is_valid()) instance = f.save() - self.assertEqual(instance.file.name, 'tests/test2.txt') + self.assertEqual(instance.file.name, "tests/test2.txt") # Delete the current file since this is not done by Django. instance.file.delete() @@ -2170,28 +2493,28 @@ class FileAndImageFieldTests(TestCase): def test_filefield_required_false(self): # Test the non-required FileField - f = TextFileForm(data={'description': 'Assistance'}) - f.fields['file'].required = False + f = TextFileForm(data={"description": "Assistance"}) + f.fields["file"].required = False self.assertTrue(f.is_valid()) instance = f.save() - self.assertEqual(instance.file.name, '') + self.assertEqual(instance.file.name, "") f = TextFileForm( - data={'description': 'Assistance'}, - files={'file': SimpleUploadedFile('test3.txt', b'hello world')}, + data={"description": "Assistance"}, + files={"file": SimpleUploadedFile("test3.txt", b"hello world")}, instance=instance, ) self.assertTrue(f.is_valid()) instance = f.save() - self.assertEqual(instance.file.name, 'tests/test3.txt') + self.assertEqual(instance.file.name, "tests/test3.txt") # Instance can be edited w/out re-uploading the file and existing file should be preserved. - f = TextFileForm({'description': 'New Description'}, instance=instance) - f.fields['file'].required = False + f = TextFileForm({"description": "New Description"}, instance=instance) + f.fields["file"].required = False self.assertTrue(f.is_valid()) instance = f.save() - self.assertEqual(instance.description, 'New Description') - self.assertEqual(instance.file.name, 'tests/test3.txt') + self.assertEqual(instance.description, "New Description") + self.assertEqual(instance.file.name, "tests/test3.txt") # Delete the current file since this is not done by Django. instance.file.delete() @@ -2201,14 +2524,15 @@ class FileAndImageFieldTests(TestCase): """ Regression for #11149: save_form_data should be called only once """ + class CFFForm(forms.ModelForm): class Meta: model = CustomFF - fields = '__all__' + fields = "__all__" # It's enough that the form saves without error -- the custom save routine will # generate an AssertionError if it is called more than once during save. - form = CFFForm(data={'f': None}) + form = CFFForm(data={"f": None}) form.save() def test_file_field_multiple_save(self): @@ -2216,19 +2540,20 @@ class FileAndImageFieldTests(TestCase): Simulate a file upload and check how many times Model.save() gets called. Test for bug #639. """ + class PhotoForm(forms.ModelForm): class Meta: model = Photo - fields = '__all__' + fields = "__all__" # Grab an image for testing. - filename = os.path.join(os.path.dirname(__file__), 'test.png') + filename = os.path.join(os.path.dirname(__file__), "test.png") with open(filename, "rb") as fp: img = fp.read() # Fake a POST QueryDict and FILES MultiValueDict. - data = {'title': 'Testing'} - files = {"image": SimpleUploadedFile('test.png', img, 'image/png')} + data = {"title": "Testing"} + files = {"image": SimpleUploadedFile("test.png", img, "image/png")} form = PhotoForm(data=data, files=files) p = form.save() @@ -2243,13 +2568,16 @@ class FileAndImageFieldTests(TestCase): def test_file_path_field_blank(self): """FilePathField(blank=True) includes the empty option.""" + class FPForm(forms.ModelForm): class Meta: model = FilePathModel - fields = '__all__' + fields = "__all__" form = FPForm() - self.assertEqual([name for _, name in form['path'].field.choices], ['---------', 'models.py']) + self.assertEqual( + [name for _, name in form["path"].field.choices], ["---------", "models.py"] + ) @skipUnless(test_images, "Pillow not installed") def test_image_field(self): @@ -2257,19 +2585,19 @@ class FileAndImageFieldTests(TestCase): # it comes to validation. This specifically tests that #6302 is fixed for # both file fields and image fields. - with open(os.path.join(os.path.dirname(__file__), 'test.png'), 'rb') as fp: + with open(os.path.join(os.path.dirname(__file__), "test.png"), "rb") as fp: image_data = fp.read() - with open(os.path.join(os.path.dirname(__file__), 'test2.png'), 'rb') as fp: + with open(os.path.join(os.path.dirname(__file__), "test2.png"), "rb") as fp: image_data2 = fp.read() f = ImageFileForm( - data={'description': 'An image'}, - files={'image': SimpleUploadedFile('test.png', image_data)}, + data={"description": "An image"}, + files={"image": SimpleUploadedFile("test.png", image_data)}, ) self.assertTrue(f.is_valid()) - self.assertEqual(type(f.cleaned_data['image']), SimpleUploadedFile) + self.assertEqual(type(f.cleaned_data["image"]), SimpleUploadedFile) instance = f.save() - self.assertEqual(instance.image.name, 'tests/test.png') + self.assertEqual(instance.image.name, "tests/test.png") self.assertEqual(instance.width, 16) self.assertEqual(instance.height, 16) @@ -2277,24 +2605,24 @@ class FileAndImageFieldTests(TestCase): # because the dimension fields are not null=True. instance.image.delete(save=False) f = ImageFileForm( - data={'description': 'An image'}, - files={'image': SimpleUploadedFile('test.png', image_data)}, + data={"description": "An image"}, + files={"image": SimpleUploadedFile("test.png", image_data)}, ) self.assertTrue(f.is_valid()) - self.assertEqual(type(f.cleaned_data['image']), SimpleUploadedFile) + self.assertEqual(type(f.cleaned_data["image"]), SimpleUploadedFile) instance = f.save() - self.assertEqual(instance.image.name, 'tests/test.png') + self.assertEqual(instance.image.name, "tests/test.png") self.assertEqual(instance.width, 16) self.assertEqual(instance.height, 16) # Edit an instance that already has the (required) image defined in the model. This will not # save the image again, but leave it exactly as it is. - f = ImageFileForm(data={'description': 'Look, it changed'}, instance=instance) + f = ImageFileForm(data={"description": "Look, it changed"}, instance=instance) self.assertTrue(f.is_valid()) - self.assertEqual(f.cleaned_data['image'].name, 'tests/test.png') + self.assertEqual(f.cleaned_data["image"].name, "tests/test.png") instance = f.save() - self.assertEqual(instance.image.name, 'tests/test.png') + self.assertEqual(instance.image.name, "tests/test.png") self.assertEqual(instance.height, 16) self.assertEqual(instance.width, 16) @@ -2304,13 +2632,13 @@ class FileAndImageFieldTests(TestCase): # Override the file by uploading a new one. f = ImageFileForm( - data={'description': 'Changed it'}, - files={'image': SimpleUploadedFile('test2.png', image_data2)}, + data={"description": "Changed it"}, + files={"image": SimpleUploadedFile("test2.png", image_data2)}, instance=instance, ) self.assertTrue(f.is_valid()) instance = f.save() - self.assertEqual(instance.image.name, 'tests/test2.png') + self.assertEqual(instance.image.name, "tests/test2.png") self.assertEqual(instance.height, 32) self.assertEqual(instance.width, 48) @@ -2320,12 +2648,12 @@ class FileAndImageFieldTests(TestCase): instance.delete() f = ImageFileForm( - data={'description': 'Changed it'}, - files={'image': SimpleUploadedFile('test2.png', image_data2)}, + data={"description": "Changed it"}, + files={"image": SimpleUploadedFile("test2.png", image_data2)}, ) self.assertTrue(f.is_valid()) instance = f.save() - self.assertEqual(instance.image.name, 'tests/test2.png') + self.assertEqual(instance.image.name, "tests/test2.png") self.assertEqual(instance.height, 32) self.assertEqual(instance.width, 48) @@ -2338,11 +2666,11 @@ class FileAndImageFieldTests(TestCase): # Note: In Oracle, we expect a null ImageField to return '' instead of # None. if connection.features.interprets_empty_strings_as_nulls: - expected_null_imagefield_repr = '' + expected_null_imagefield_repr = "" else: expected_null_imagefield_repr = None - f = OptionalImageFileForm(data={'description': 'Test'}) + f = OptionalImageFileForm(data={"description": "Test"}) self.assertTrue(f.is_valid()) instance = f.save() self.assertEqual(instance.image.name, expected_null_imagefield_repr) @@ -2350,23 +2678,23 @@ class FileAndImageFieldTests(TestCase): self.assertIsNone(instance.height) f = OptionalImageFileForm( - data={'description': 'And a final one'}, - files={'image': SimpleUploadedFile('test3.png', image_data)}, + data={"description": "And a final one"}, + files={"image": SimpleUploadedFile("test3.png", image_data)}, instance=instance, ) self.assertTrue(f.is_valid()) instance = f.save() - self.assertEqual(instance.image.name, 'tests/test3.png') + self.assertEqual(instance.image.name, "tests/test3.png") self.assertEqual(instance.width, 16) self.assertEqual(instance.height, 16) # Editing the instance without re-uploading the image should not affect # the image or its width/height properties. - f = OptionalImageFileForm({'description': 'New Description'}, instance=instance) + f = OptionalImageFileForm({"description": "New Description"}, instance=instance) self.assertTrue(f.is_valid()) instance = f.save() - self.assertEqual(instance.description, 'New Description') - self.assertEqual(instance.image.name, 'tests/test3.png') + self.assertEqual(instance.description, "New Description") + self.assertEqual(instance.image.name, "tests/test3.png") self.assertEqual(instance.width, 16) self.assertEqual(instance.height, 16) @@ -2375,75 +2703,91 @@ class FileAndImageFieldTests(TestCase): instance.delete() f = OptionalImageFileForm( - data={'description': 'And a final one'}, - files={'image': SimpleUploadedFile('test4.png', image_data2)} + data={"description": "And a final one"}, + files={"image": SimpleUploadedFile("test4.png", image_data2)}, ) self.assertTrue(f.is_valid()) instance = f.save() - self.assertEqual(instance.image.name, 'tests/test4.png') + self.assertEqual(instance.image.name, "tests/test4.png") self.assertEqual(instance.width, 48) self.assertEqual(instance.height, 32) instance.delete() # Test callable upload_to behavior that's dependent on the value of another field in the model f = ImageFileForm( - data={'description': 'And a final one', 'path': 'foo'}, - files={'image': SimpleUploadedFile('test4.png', image_data)}, + data={"description": "And a final one", "path": "foo"}, + files={"image": SimpleUploadedFile("test4.png", image_data)}, ) self.assertTrue(f.is_valid()) instance = f.save() - self.assertEqual(instance.image.name, 'foo/test4.png') + self.assertEqual(instance.image.name, "foo/test4.png") instance.delete() # Editing an instance that has an image without an extension shouldn't # fail validation. First create: f = NoExtensionImageFileForm( - data={'description': 'An image'}, - files={'image': SimpleUploadedFile('test.png', image_data)}, + data={"description": "An image"}, + files={"image": SimpleUploadedFile("test.png", image_data)}, ) self.assertTrue(f.is_valid()) instance = f.save() - self.assertEqual(instance.image.name, 'tests/no_extension') + self.assertEqual(instance.image.name, "tests/no_extension") # Then edit: - f = NoExtensionImageFileForm(data={'description': 'Edited image'}, instance=instance) + f = NoExtensionImageFileForm( + data={"description": "Edited image"}, instance=instance + ) self.assertTrue(f.is_valid()) class ModelOtherFieldTests(SimpleTestCase): def test_big_integer_field(self): - bif = BigIntForm({'biggie': '-9223372036854775808'}) + bif = BigIntForm({"biggie": "-9223372036854775808"}) self.assertTrue(bif.is_valid()) - bif = BigIntForm({'biggie': '-9223372036854775809'}) + bif = BigIntForm({"biggie": "-9223372036854775809"}) self.assertFalse(bif.is_valid()) self.assertEqual( bif.errors, - {'biggie': ['Ensure this value is greater than or equal to -9223372036854775808.']} + { + "biggie": [ + "Ensure this value is greater than or equal to -9223372036854775808." + ] + }, ) - bif = BigIntForm({'biggie': '9223372036854775807'}) + bif = BigIntForm({"biggie": "9223372036854775807"}) self.assertTrue(bif.is_valid()) - bif = BigIntForm({'biggie': '9223372036854775808'}) + bif = BigIntForm({"biggie": "9223372036854775808"}) self.assertFalse(bif.is_valid()) - self.assertEqual(bif.errors, {'biggie': ['Ensure this value is less than or equal to 9223372036854775807.']}) + self.assertEqual( + bif.errors, + { + "biggie": [ + "Ensure this value is less than or equal to 9223372036854775807." + ] + }, + ) def test_url_on_modelform(self): "Check basic URL field validation on model forms" + class HomepageForm(forms.ModelForm): class Meta: model = Homepage - fields = '__all__' + fields = "__all__" - self.assertFalse(HomepageForm({'url': 'foo'}).is_valid()) - self.assertFalse(HomepageForm({'url': 'http://'}).is_valid()) - self.assertFalse(HomepageForm({'url': 'http://example'}).is_valid()) - self.assertFalse(HomepageForm({'url': 'http://example.'}).is_valid()) - self.assertFalse(HomepageForm({'url': 'http://com.'}).is_valid()) + self.assertFalse(HomepageForm({"url": "foo"}).is_valid()) + self.assertFalse(HomepageForm({"url": "http://"}).is_valid()) + self.assertFalse(HomepageForm({"url": "http://example"}).is_valid()) + self.assertFalse(HomepageForm({"url": "http://example."}).is_valid()) + self.assertFalse(HomepageForm({"url": "http://com."}).is_valid()) - self.assertTrue(HomepageForm({'url': 'http://localhost'}).is_valid()) - self.assertTrue(HomepageForm({'url': 'http://example.com'}).is_valid()) - self.assertTrue(HomepageForm({'url': 'http://www.example.com'}).is_valid()) - self.assertTrue(HomepageForm({'url': 'http://www.example.com:8000'}).is_valid()) - self.assertTrue(HomepageForm({'url': 'http://www.example.com/test'}).is_valid()) - self.assertTrue(HomepageForm({'url': 'http://www.example.com:8000/test'}).is_valid()) - self.assertTrue(HomepageForm({'url': 'http://example.com/foo/bar'}).is_valid()) + self.assertTrue(HomepageForm({"url": "http://localhost"}).is_valid()) + self.assertTrue(HomepageForm({"url": "http://example.com"}).is_valid()) + self.assertTrue(HomepageForm({"url": "http://www.example.com"}).is_valid()) + self.assertTrue(HomepageForm({"url": "http://www.example.com:8000"}).is_valid()) + self.assertTrue(HomepageForm({"url": "http://www.example.com/test"}).is_valid()) + self.assertTrue( + HomepageForm({"url": "http://www.example.com:8000/test"}).is_valid() + ) + self.assertTrue(HomepageForm({"url": "http://example.com/foo/bar"}).is_valid()) def test_modelform_non_editable_field(self): """ @@ -2451,31 +2795,33 @@ class ModelOtherFieldTests(SimpleTestCase): error message should be explicit. """ # 'created', non-editable, is excluded by default - self.assertNotIn('created', ArticleForm().fields) + self.assertNotIn("created", ArticleForm().fields) msg = "'created' cannot be specified for Article model form as it is a non-editable field" with self.assertRaisesMessage(FieldError, msg): + class InvalidArticleForm(forms.ModelForm): class Meta: model = Article - fields = ('headline', 'created') + fields = ("headline", "created") def test_http_prefixing(self): """ If the http:// prefix is omitted on form input, the field adds it again. (Refs #13613) """ + class HomepageForm(forms.ModelForm): class Meta: model = Homepage - fields = '__all__' + fields = "__all__" - form = HomepageForm({'url': 'example.com'}) + form = HomepageForm({"url": "example.com"}) self.assertTrue(form.is_valid()) - self.assertEqual(form.cleaned_data['url'], 'http://example.com') + self.assertEqual(form.cleaned_data["url"], "http://example.com") - form = HomepageForm({'url': 'example.com/test'}) + form = HomepageForm({"url": "example.com/test"}) self.assertTrue(form.is_valid()) - self.assertEqual(form.cleaned_data['url'], 'http://example.com/test') + self.assertEqual(form.cleaned_data["url"], "http://example.com/test") class OtherModelFormTests(TestCase): @@ -2486,25 +2832,25 @@ class OtherModelFormTests(TestCase): self.assertHTMLEqual( str(f.media), '<link href="/some/form/css" media="all" rel="stylesheet">' - '<script src="/some/form/javascript"></script>' + '<script src="/some/form/javascript"></script>', ) def test_choices_type(self): # Choices on CharField and IntegerField f = ArticleForm() with self.assertRaises(ValidationError): - f.fields['status'].clean('42') + f.fields["status"].clean("42") f = ArticleStatusForm() with self.assertRaises(ValidationError): - f.fields['status'].clean('z') + f.fields["status"].clean("z") def test_prefetch_related_queryset(self): """ ModelChoiceField should respect a prefetch_related() on its queryset. """ - blue = Colour.objects.create(name='blue') - red = Colour.objects.create(name='red') + blue = Colour.objects.create(name="blue") + red = Colour.objects.create(name="red") multicolor_item = ColourfulItem.objects.create() multicolor_item.colours.add(blue, red) red_item = ColourfulItem.objects.create() @@ -2512,83 +2858,92 @@ class OtherModelFormTests(TestCase): class ColorModelChoiceField(forms.ModelChoiceField): def label_from_instance(self, obj): - return ', '.join(c.name for c in obj.colours.all()) + return ", ".join(c.name for c in obj.colours.all()) - field = ColorModelChoiceField(ColourfulItem.objects.prefetch_related('colours')) + field = ColorModelChoiceField(ColourfulItem.objects.prefetch_related("colours")) with self.assertNumQueries(3): # would be 4 if prefetch is ignored - self.assertEqual(tuple(field.choices), ( - ('', '---------'), - (multicolor_item.pk, 'blue, red'), - (red_item.pk, 'red'), - )) + self.assertEqual( + tuple(field.choices), + ( + ("", "---------"), + (multicolor_item.pk, "blue, red"), + (red_item.pk, "red"), + ), + ) def test_foreignkeys_which_use_to_field(self): - apple = Inventory.objects.create(barcode=86, name='Apple') - pear = Inventory.objects.create(barcode=22, name='Pear') - core = Inventory.objects.create(barcode=87, name='Core', parent=apple) + apple = Inventory.objects.create(barcode=86, name="Apple") + pear = Inventory.objects.create(barcode=22, name="Pear") + core = Inventory.objects.create(barcode=87, name="Core", parent=apple) - field = forms.ModelChoiceField(Inventory.objects.all(), to_field_name='barcode') - self.assertEqual(tuple(field.choices), ( - ('', '---------'), - (86, 'Apple'), - (87, 'Core'), - (22, 'Pear'))) + field = forms.ModelChoiceField(Inventory.objects.all(), to_field_name="barcode") + self.assertEqual( + tuple(field.choices), + (("", "---------"), (86, "Apple"), (87, "Core"), (22, "Pear")), + ) form = InventoryForm(instance=core) - self.assertHTMLEqual(str(form['parent']), '''<select name="parent" id="id_parent"> + self.assertHTMLEqual( + str(form["parent"]), + """<select name="parent" id="id_parent"> <option value="">---------</option> <option value="86" selected>Apple</option> <option value="87">Core</option> <option value="22">Pear</option> -</select>''') +</select>""", + ) data = model_to_dict(core) - data['parent'] = '22' + data["parent"] = "22" form = InventoryForm(data=data, instance=core) core = form.save() - self.assertEqual(core.parent.name, 'Pear') + self.assertEqual(core.parent.name, "Pear") class CategoryForm(forms.ModelForm): description = forms.CharField() class Meta: model = Category - fields = ['description', 'url'] + fields = ["description", "url"] - self.assertEqual(list(CategoryForm.base_fields), ['description', 'url']) + self.assertEqual(list(CategoryForm.base_fields), ["description", "url"]) self.assertHTMLEqual( str(CategoryForm()), - '''<tr><th><label for="id_description">Description:</label></th> + """<tr><th><label for="id_description">Description:</label></th> <td><input type="text" name="description" id="id_description" required></td></tr> <tr><th><label for="id_url">The URL:</label></th> -<td><input id="id_url" type="text" name="url" maxlength="40" required></td></tr>''' +<td><input id="id_url" type="text" name="url" maxlength="40" required></td></tr>""", ) # to_field_name should also work on ModelMultipleChoiceField ################## - field = forms.ModelMultipleChoiceField(Inventory.objects.all(), to_field_name='barcode') - self.assertEqual(tuple(field.choices), ((86, 'Apple'), (87, 'Core'), (22, 'Pear'))) + field = forms.ModelMultipleChoiceField( + Inventory.objects.all(), to_field_name="barcode" + ) + self.assertEqual( + tuple(field.choices), ((86, "Apple"), (87, "Core"), (22, "Pear")) + ) self.assertSequenceEqual(field.clean([86]), [apple]) - form = SelectInventoryForm({'items': [87, 22]}) + form = SelectInventoryForm({"items": [87, 22]}) self.assertTrue(form.is_valid()) self.assertEqual(len(form.cleaned_data), 1) - self.assertSequenceEqual(form.cleaned_data['items'], [core, pear]) + self.assertSequenceEqual(form.cleaned_data["items"], [core, pear]) def test_model_field_that_returns_none_to_exclude_itself_with_explicit_fields(self): - self.assertEqual(list(CustomFieldForExclusionForm.base_fields), ['name']) + self.assertEqual(list(CustomFieldForExclusionForm.base_fields), ["name"]) self.assertHTMLEqual( str(CustomFieldForExclusionForm()), - '''<tr><th><label for="id_name">Name:</label></th> -<td><input id="id_name" type="text" name="name" maxlength="10" required></td></tr>''' + """<tr><th><label for="id_name">Name:</label></th> +<td><input id="id_name" type="text" name="name" maxlength="10" required></td></tr>""", ) def test_iterable_model_m2m(self): class ColourfulItemForm(forms.ModelForm): class Meta: model = ColourfulItem - fields = '__all__' + fields = "__all__" - colour = Colour.objects.create(name='Blue') + colour = Colour.objects.create(name="Blue") form = ColourfulItemForm() self.maxDiff = 1024 self.assertHTMLEqual( @@ -2598,13 +2953,14 @@ class OtherModelFormTests(TestCase): <select multiple name="colours" id="id_colours" required> <option value="%(blue_pk)s">Blue</option> </select></p>""" - % {'blue_pk': colour.pk}) + % {"blue_pk": colour.pk}, + ) def test_callable_field_default(self): class PublicationDefaultsForm(forms.ModelForm): class Meta: model = PublicationDefaults - fields = ('title', 'date_published', 'mode', 'category') + fields = ("title", "date_published", "mode", "category") self.maxDiff = 2000 form = PublicationDefaultsForm() @@ -2626,16 +2982,18 @@ class OtherModelFormTests(TestCase): <option value="2">Comics</option> <option value="3" selected>Novel</option></select> <input id="initial-id_category" name="initial-category" type="hidden" value="3"> - """.format(today_str) + """.format( + today_str + ), ) empty_data = { - 'title': '', - 'date_published': today_str, - 'initial-date_published': today_str, - 'mode': 'di', - 'initial-mode': 'di', - 'category': '3', - 'initial-category': '3', + "title": "", + "date_published": today_str, + "initial-date_published": today_str, + "mode": "di", + "initial-mode": "di", + "category": "3", + "initial-category": "3", } bound_form = PublicationDefaultsForm(empty_data) self.assertFalse(bound_form.has_changed()) @@ -2643,36 +3001,36 @@ class OtherModelFormTests(TestCase): class ModelFormCustomErrorTests(SimpleTestCase): def test_custom_error_messages(self): - data = {'name1': '@#$!!**@#$', 'name2': '@#$!!**@#$'} + data = {"name1": "@#$!!**@#$", "name2": "@#$!!**@#$"} errors = CustomErrorMessageForm(data).errors self.assertHTMLEqual( - str(errors['name1']), - '<ul class="errorlist"><li>Form custom error message.</li></ul>' + str(errors["name1"]), + '<ul class="errorlist"><li>Form custom error message.</li></ul>', ) self.assertHTMLEqual( - str(errors['name2']), - '<ul class="errorlist"><li>Model custom error message.</li></ul>' + str(errors["name2"]), + '<ul class="errorlist"><li>Model custom error message.</li></ul>', ) def test_model_clean_error_messages(self): - data = {'name1': 'FORBIDDEN_VALUE', 'name2': 'ABC'} + data = {"name1": "FORBIDDEN_VALUE", "name2": "ABC"} form = CustomErrorMessageForm(data) self.assertFalse(form.is_valid()) self.assertHTMLEqual( - str(form.errors['name1']), - '<ul class="errorlist"><li>Model.clean() error messages.</li></ul>' + str(form.errors["name1"]), + '<ul class="errorlist"><li>Model.clean() error messages.</li></ul>', ) - data = {'name1': 'FORBIDDEN_VALUE2', 'name2': 'ABC'} + data = {"name1": "FORBIDDEN_VALUE2", "name2": "ABC"} form = CustomErrorMessageForm(data) self.assertFalse(form.is_valid()) self.assertHTMLEqual( - str(form.errors['name1']), - '<ul class="errorlist"><li>Model.clean() error messages (simpler syntax).</li></ul>' + str(form.errors["name1"]), + '<ul class="errorlist"><li>Model.clean() error messages (simpler syntax).</li></ul>', ) - data = {'name1': 'GLOBAL_ERROR', 'name2': 'ABC'} + data = {"name1": "GLOBAL_ERROR", "name2": "ABC"} form = CustomErrorMessageForm(data) self.assertFalse(form.is_valid()) - self.assertEqual(form.errors['__all__'], ['Global error message.']) + self.assertEqual(form.errors["__all__"], ["Global error message."]) class CustomCleanTests(TestCase): @@ -2681,17 +3039,18 @@ class CustomCleanTests(TestCase): Regression for #12596: Calling super from ModelForm.clean() should be optional. """ + class TripleFormWithCleanOverride(forms.ModelForm): class Meta: model = Triple - fields = '__all__' + fields = "__all__" def clean(self): - if not self.cleaned_data['left'] == self.cleaned_data['right']: - raise ValidationError('Left and right should be equal') + if not self.cleaned_data["left"] == self.cleaned_data["right"]: + raise ValidationError("Left and right should be equal") return self.cleaned_data - form = TripleFormWithCleanOverride({'left': 1, 'middle': 2, 'right': 1}) + form = TripleFormWithCleanOverride({"left": 1, "middle": 2, "right": 1}) self.assertTrue(form.is_valid()) # form.instance.left will be None if the instance was not constructed # by form.full_clean(). @@ -2702,19 +3061,20 @@ class CustomCleanTests(TestCase): Regression test for #12960. Make sure the cleaned_data returned from ModelForm.clean() is applied to the model instance. """ + class CategoryForm(forms.ModelForm): class Meta: model = Category - fields = '__all__' + fields = "__all__" def clean(self): - self.cleaned_data['name'] = self.cleaned_data['name'].upper() + self.cleaned_data["name"] = self.cleaned_data["name"].upper() return self.cleaned_data - data = {'name': 'Test', 'slug': 'test', 'url': '/test'} + data = {"name": "Test", "slug": "test", "url": "/test"} form = CategoryForm(data) category = form.save() - self.assertEqual(category.name, 'TEST') + self.assertEqual(category.name, "TEST") class ModelFormInheritanceTests(SimpleTestCase): @@ -2725,15 +3085,15 @@ class ModelFormInheritanceTests(SimpleTestCase): class ModelForm(forms.ModelForm, Form): class Meta: model = Writer - fields = '__all__' + fields = "__all__" - self.assertEqual(list(ModelForm().fields), ['name', 'age']) + self.assertEqual(list(ModelForm().fields), ["name", "age"]) def test_field_removal(self): class ModelForm(forms.ModelForm): class Meta: model = Writer - fields = '__all__' + fields = "__all__" class Mixin: age = None @@ -2744,39 +3104,51 @@ class ModelFormInheritanceTests(SimpleTestCase): class Form2(forms.Form): foo = forms.IntegerField() - self.assertEqual(list(ModelForm().fields), ['name']) - self.assertEqual(list(type('NewForm', (Mixin, Form), {})().fields), []) - self.assertEqual(list(type('NewForm', (Form2, Mixin, Form), {})().fields), ['foo']) - self.assertEqual(list(type('NewForm', (Mixin, ModelForm, Form), {})().fields), ['name']) - self.assertEqual(list(type('NewForm', (ModelForm, Mixin, Form), {})().fields), ['name']) - self.assertEqual(list(type('NewForm', (ModelForm, Form, Mixin), {})().fields), ['name', 'age']) - self.assertEqual(list(type('NewForm', (ModelForm, Form), {'age': None})().fields), ['name']) + self.assertEqual(list(ModelForm().fields), ["name"]) + self.assertEqual(list(type("NewForm", (Mixin, Form), {})().fields), []) + self.assertEqual( + list(type("NewForm", (Form2, Mixin, Form), {})().fields), ["foo"] + ) + self.assertEqual( + list(type("NewForm", (Mixin, ModelForm, Form), {})().fields), ["name"] + ) + self.assertEqual( + list(type("NewForm", (ModelForm, Mixin, Form), {})().fields), ["name"] + ) + self.assertEqual( + list(type("NewForm", (ModelForm, Form, Mixin), {})().fields), + ["name", "age"], + ) + self.assertEqual( + list(type("NewForm", (ModelForm, Form), {"age": None})().fields), ["name"] + ) def test_field_removal_name_clashes(self): """ Form fields can be removed in subclasses by setting them to None (#22510). """ + class MyForm(forms.ModelForm): media = forms.CharField() class Meta: model = Writer - fields = '__all__' + fields = "__all__" class SubForm(MyForm): media = None - self.assertIn('media', MyForm().fields) - self.assertNotIn('media', SubForm().fields) - self.assertTrue(hasattr(MyForm, 'media')) - self.assertTrue(hasattr(SubForm, 'media')) + self.assertIn("media", MyForm().fields) + self.assertNotIn("media", SubForm().fields) + self.assertTrue(hasattr(MyForm, "media")) + self.assertTrue(hasattr(SubForm, "media")) class StumpJokeForm(forms.ModelForm): class Meta: model = StumpJoke - fields = '__all__' + fields = "__all__" class CustomFieldWithQuerysetButNoLimitChoicesTo(forms.Field): @@ -2795,14 +3167,15 @@ class LimitChoicesToTests(TestCase): """ Tests the functionality of ``limit_choices_to``. """ + @classmethod def setUpTestData(cls): cls.threepwood = Character.objects.create( - username='threepwood', + username="threepwood", last_action=datetime.datetime.today() + datetime.timedelta(days=1), ) cls.marley = Character.objects.create( - username='marley', + username="marley", last_action=datetime.datetime.today() - datetime.timedelta(days=1), ) @@ -2811,14 +3184,18 @@ class LimitChoicesToTests(TestCase): A ForeignKey can use limit_choices_to as a callable (#2554). """ stumpjokeform = StumpJokeForm() - self.assertSequenceEqual(stumpjokeform.fields['most_recently_fooled'].queryset, [self.threepwood]) + self.assertSequenceEqual( + stumpjokeform.fields["most_recently_fooled"].queryset, [self.threepwood] + ) def test_limit_choices_to_callable_for_m2m_rel(self): """ A ManyToManyField can use limit_choices_to as a callable (#2554). """ stumpjokeform = StumpJokeForm() - self.assertSequenceEqual(stumpjokeform.fields['most_recently_fooled'].queryset, [self.threepwood]) + self.assertSequenceEqual( + stumpjokeform.fields["most_recently_fooled"].queryset, [self.threepwood] + ) def test_custom_field_with_queryset_but_no_limit_choices_to(self): """ @@ -2826,15 +3203,15 @@ class LimitChoicesToTests(TestCase): works (#23795). """ f = StumpJokeWithCustomFieldForm() - self.assertEqual(f.fields['custom'].queryset, 42) + self.assertEqual(f.fields["custom"].queryset, 42) def test_fields_for_model_applies_limit_choices_to(self): - fields = fields_for_model(StumpJoke, ['has_fooled_today']) - self.assertSequenceEqual(fields['has_fooled_today'].queryset, [self.threepwood]) + fields = fields_for_model(StumpJoke, ["has_fooled_today"]) + self.assertSequenceEqual(fields["has_fooled_today"].queryset, [self.threepwood]) def test_callable_called_each_time_form_is_instantiated(self): - field = StumpJokeForm.base_fields['most_recently_fooled'] - with mock.patch.object(field, 'limit_choices_to') as today_callable_dict: + field = StumpJokeForm.base_fields["most_recently_fooled"] + with mock.patch.object(field, "limit_choices_to") as today_callable_dict: StumpJokeForm() self.assertEqual(today_callable_dict.call_count, 1) StumpJokeForm() @@ -2842,7 +3219,7 @@ class LimitChoicesToTests(TestCase): StumpJokeForm() self.assertEqual(today_callable_dict.call_count, 3) - @isolate_apps('model_forms') + @isolate_apps("model_forms") def test_limit_choices_to_no_duplicates(self): joke1 = StumpJoke.objects.create( funny=True, @@ -2869,16 +3246,16 @@ class LimitChoicesToTests(TestCase): jokes__funny=True, jokes_today__funny=True, ), - related_name='details_fk_1', + related_name="details_fk_1", ) character2 = models.ForeignKey( Character, models.CASCADE, limit_choices_to={ - 'jokes__funny': True, - 'jokes_today__funny': True, + "jokes__funny": True, + "jokes_today__funny": True, }, - related_name='details_fk_2', + related_name="details_fk_2", ) character3 = models.ManyToManyField( Character, @@ -2886,25 +3263,25 @@ class LimitChoicesToTests(TestCase): jokes__funny=True, jokes_today__funny=True, ), - related_name='details_m2m_1', + related_name="details_m2m_1", ) class CharacterDetailsForm(forms.ModelForm): class Meta: model = CharacterDetails - fields = '__all__' + fields = "__all__" form = CharacterDetailsForm() self.assertCountEqual( - form.fields['character1'].queryset, + form.fields["character1"].queryset, [self.marley, self.threepwood], ) self.assertCountEqual( - form.fields['character2'].queryset, + form.fields["character2"].queryset, [self.marley, self.threepwood], ) self.assertCountEqual( - form.fields['character3'].queryset, + form.fields["character3"].queryset, [self.marley, self.threepwood], ) @@ -2912,18 +3289,17 @@ class LimitChoicesToTests(TestCase): class DiceForm(forms.ModelForm): class Meta: model = Dice - fields = ['numbers'] + fields = ["numbers"] Number.objects.create(value=0) n1 = Number.objects.create(value=1) n2 = Number.objects.create(value=2) form = DiceForm() - self.assertCountEqual(form.fields['numbers'].queryset, [n1, n2]) + self.assertCountEqual(form.fields["numbers"].queryset, [n1, n2]) class FormFieldCallbackTests(SimpleTestCase): - def test_baseform_with_widgets_in_meta(self): """Regression for #13095: Using base forms with widgets defined in Meta should not raise errors.""" widget = forms.Textarea() @@ -2931,28 +3307,28 @@ class FormFieldCallbackTests(SimpleTestCase): class BaseForm(forms.ModelForm): class Meta: model = Person - widgets = {'name': widget} + widgets = {"name": widget} fields = "__all__" Form = modelform_factory(Person, form=BaseForm) - self.assertIsInstance(Form.base_fields['name'].widget, forms.Textarea) + self.assertIsInstance(Form.base_fields["name"].widget, forms.Textarea) def test_factory_with_widget_argument(self): - """ Regression for #15315: modelform_factory should accept widgets - argument + """Regression for #15315: modelform_factory should accept widgets + argument """ widget = forms.Textarea() # Without a widget should not set the widget to textarea Form = modelform_factory(Person, fields="__all__") - self.assertNotEqual(Form.base_fields['name'].widget.__class__, forms.Textarea) + self.assertNotEqual(Form.base_fields["name"].widget.__class__, forms.Textarea) # With a widget should not set the widget to textarea - Form = modelform_factory(Person, fields="__all__", widgets={'name': widget}) - self.assertEqual(Form.base_fields['name'].widget.__class__, forms.Textarea) + Form = modelform_factory(Person, fields="__all__", widgets={"name": widget}) + self.assertEqual(Form.base_fields["name"].widget.__class__, forms.Textarea) def test_modelform_factory_without_fields(self): - """ Regression for #19733 """ + """Regression for #19733""" message = ( "Calling modelform_factory without defining 'fields' or 'exclude' " "explicitly is prohibited." @@ -2961,7 +3337,7 @@ class FormFieldCallbackTests(SimpleTestCase): modelform_factory(Person) def test_modelform_factory_with_all_fields(self): - """ Regression for #19733 """ + """Regression for #19733""" form = modelform_factory(Person, fields="__all__") self.assertEqual(list(form.base_fields), ["name"]) @@ -2978,18 +3354,24 @@ class FormFieldCallbackTests(SimpleTestCase): class BaseForm(forms.ModelForm): class Meta: model = Person - widgets = {'name': widget} + widgets = {"name": widget} fields = "__all__" modelform_factory(Person, form=BaseForm, formfield_callback=callback) id_field, name_field = Person._meta.fields - self.assertEqual(callback_args, [(id_field, {}), (name_field, {'widget': widget})]) + self.assertEqual( + callback_args, [(id_field, {}), (name_field, {"widget": widget})] + ) def test_bad_callback(self): # A bad callback provided by user still gives an error with self.assertRaises(TypeError): - modelform_factory(Person, fields="__all__", formfield_callback='not a function or callable') + modelform_factory( + Person, + fields="__all__", + formfield_callback="not a function or callable", + ) def test_inherit_after_custom_callback(self): def callback(db_field, **kwargs): @@ -3000,7 +3382,7 @@ class FormFieldCallbackTests(SimpleTestCase): class BaseForm(forms.ModelForm): class Meta: model = Person - fields = '__all__' + fields = "__all__" NewForm = modelform_factory(Person, form=BaseForm, formfield_callback=callback) @@ -3010,7 +3392,7 @@ class FormFieldCallbackTests(SimpleTestCase): for name in NewForm.base_fields: self.assertEqual( type(InheritedForm.base_fields[name].widget), - type(NewForm.base_fields[name].widget) + type(NewForm.base_fields[name].widget), ) @@ -3019,27 +3401,30 @@ class LocalizedModelFormTest(TestCase): class PartiallyLocalizedTripleForm(forms.ModelForm): class Meta: model = Triple - localized_fields = ('left', 'right',) - fields = '__all__' + localized_fields = ( + "left", + "right", + ) + fields = "__all__" - f = PartiallyLocalizedTripleForm({'left': 10, 'middle': 10, 'right': 10}) + f = PartiallyLocalizedTripleForm({"left": 10, "middle": 10, "right": 10}) self.assertTrue(f.is_valid()) - self.assertTrue(f.fields['left'].localize) - self.assertFalse(f.fields['middle'].localize) - self.assertTrue(f.fields['right'].localize) + self.assertTrue(f.fields["left"].localize) + self.assertFalse(f.fields["middle"].localize) + self.assertTrue(f.fields["right"].localize) def test_model_form_applies_localize_to_all_fields(self): class FullyLocalizedTripleForm(forms.ModelForm): class Meta: model = Triple - localized_fields = '__all__' - fields = '__all__' + localized_fields = "__all__" + fields = "__all__" - f = FullyLocalizedTripleForm({'left': 10, 'middle': 10, 'right': 10}) + f = FullyLocalizedTripleForm({"left": 10, "middle": 10, "right": 10}) self.assertTrue(f.is_valid()) - self.assertTrue(f.fields['left'].localize) - self.assertTrue(f.fields['middle'].localize) - self.assertTrue(f.fields['right'].localize) + self.assertTrue(f.fields["left"].localize) + self.assertTrue(f.fields["middle"].localize) + self.assertTrue(f.fields["right"].localize) def test_model_form_refuses_arbitrary_string(self): msg = ( @@ -3047,6 +3432,7 @@ class LocalizedModelFormTest(TestCase): "cannot be a string. Did you mean to type: ('foo',)?" ) with self.assertRaisesMessage(TypeError, msg): + class BrokenLocalizedTripleForm(forms.ModelForm): class Meta: model = Triple @@ -3081,41 +3467,47 @@ class StrictAssignmentTests(SimpleTestCase): A model ValidationError using the dict form should put the error message into the correct key of form.errors. """ - form_class = modelform_factory(model=StrictAssignmentFieldSpecific, fields=['title']) - form = form_class(data={'title': 'testing setattr'}, files=None) + form_class = modelform_factory( + model=StrictAssignmentFieldSpecific, fields=["title"] + ) + form = form_class(data={"title": "testing setattr"}, files=None) # This line turns on the ValidationError; it avoids the model erroring # when its own __init__() is called when creating form.instance. form.instance._should_error = True self.assertFalse(form.is_valid()) - self.assertEqual(form.errors, { - 'title': ['Cannot set attribute', 'This field cannot be blank.'] - }) + self.assertEqual( + form.errors, + {"title": ["Cannot set attribute", "This field cannot be blank."]}, + ) def test_setattr_raises_validation_error_non_field(self): """ A model ValidationError not using the dict form should put the error message into __all__ (i.e. non-field errors) on the form. """ - form_class = modelform_factory(model=StrictAssignmentAll, fields=['title']) - form = form_class(data={'title': 'testing setattr'}, files=None) + form_class = modelform_factory(model=StrictAssignmentAll, fields=["title"]) + form = form_class(data={"title": "testing setattr"}, files=None) # This line turns on the ValidationError; it avoids the model erroring # when its own __init__() is called when creating form.instance. form.instance._should_error = True self.assertFalse(form.is_valid()) - self.assertEqual(form.errors, { - '__all__': ['Cannot set attribute'], - 'title': ['This field cannot be blank.'] - }) + self.assertEqual( + form.errors, + { + "__all__": ["Cannot set attribute"], + "title": ["This field cannot be blank."], + }, + ) class ModelToDictTests(TestCase): def test_many_to_many(self): """Data for a ManyToManyField is a list rather than a lazy QuerySet.""" - blue = Colour.objects.create(name='blue') - red = Colour.objects.create(name='red') + blue = Colour.objects.create(name="blue") + red = Colour.objects.create(name="red") item = ColourfulItem.objects.create() item.colours.set([blue]) - data = model_to_dict(item)['colours'] + data = model_to_dict(item)["colours"] self.assertEqual(data, [blue]) item.colours.set([red]) # If data were a QuerySet, it would be reevaluated here and give "red" |
