#32432: ModelForm does not respect ModelChoiceField's to_field_name attribute
-------------------------------+--------------------------------------
Reporter: gopackgo90 | Owner: nobody
Type: Uncategorized | Status: new
Component: Forms | Version: 3.1
Severity: Normal | Resolution:
Keywords: | Triage Stage: Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------+--------------------------------------
Description changed by gopackgo90:
Old description:
> This is the same issue mentioned in #17657 but for ModelChoiceField
> instead of ModelMultipleChoiceField. This bug is present in Django 2.2.18
> and Django 3.1.6. The first two tests were taken directly from #17657
> just to show that ModelMultipleChoiceField still works as expected and
> the equivalent ModelChoiceField tests are added after, the last of which
> fails:
>
> {{{
> # models.py
>
> from django.db import models
>
> class Foo(models.Model):
>
> slug = models.CharField(max_length=40, unique=True)
> title = models.CharField(max_length=40, unique=True)
>
> def __str__(self):
> return self.title
>
> class Bar(models.Model):
>
> foos = models.ManyToManyField(Foo)
>
> class Baz(models.Model):
>
> foo = models.ForeignKey(Foo, on_delete=models.CASCADE)
> }}}
>
> {{{
> # tests.py
>
> from django.test.testcases import TestCase
> from django import forms
> from .models import Foo, Bar, Baz
>
> class TestModelRelationshipChoiceWithFieldName(TestCase):
>
> @classmethod
> def setUpTestData(cls):
> spam = Foo.objects.create(title="Spam", slug="spam")
> ham = Foo.objects.create(title="Ham", slug="ham")
> eggs = Foo.objects.create(title="Eggs", slug="eggs")
> cls.m2m_instance = Bar.objects.create()
>
> cls.m2m_instance.foos.add(spam)
> cls.m2m_instance.foos.add(ham)
> cls.m2m_instance.foos.add(eggs)
>
> cls.fk_instance = Baz.objects.create(foo=eggs)
>
> def test_multiple_without_field_name(self):
>
> class Form(forms.ModelForm):
>
> foos = forms.ModelMultipleChoiceField(Foo.objects.all())
>
> class Meta:
> model = Bar
> fields = '__all__'
>
> form = Form(instance=self.m2m_instance)
>
> self.assertEquals(
> str(form["foos"]),
> '<select name="foos" required id="id_foos" multiple>\n'
> ' <option value="1" selected>Spam</option>\n\n'
> ' <option value="2" selected>Ham</option>\n\n'
> ' <option value="3" selected>Eggs</option>\n\n'
> '</select>'
> )
>
> def test_multiple_with_field_name(self):
>
> class Form(forms.ModelForm):
>
> foos = forms.ModelMultipleChoiceField(Foo.objects.all(),
> to_field_name="slug")
>
> class Meta:
> model = Bar
> fields = '__all__'
>
> form = Form(instance=self.m2m_instance)
>
> # Fixed in #17657, options weren't selected.
> self.assertEquals(
> str(form["foos"]),
> '<select name="foos" required id="id_foos" multiple>\n'
> ' <option value="spam" selected>Spam</option>\n\n'
> ' <option value="ham" selected>Ham</option>\n\n'
> ' <option value="eggs" selected>Eggs</option>\n\n'
> '</select>'
> )
>
> def test_one_without_field_name(self):
>
> class Form(forms.ModelForm):
>
> foo = forms.ModelChoiceField(Foo.objects.all())
>
> class Meta:
> model = Baz
> fields = '__all__'
>
> form = Form(instance=self.fk_instance)
>
> self.assertEquals(
> str(form["foo"]),
> '<select name="foo" required id="id_foo">\n'
> ' <option value="">---------</option>\n\n'
> ' <option value="1">Spam</option>\n\n'
> ' <option value="2">Ham</option>\n\n'
> ' <option value="3" selected>Eggs</option>\n\n'
> '</select>'
> )
>
> def test_one_with_field_name(self):
>
> class Form(forms.ModelForm):
>
> foo = forms.ModelChoiceField(Foo.objects.all(),
> to_field_name="slug")
>
> class Meta:
> model = Baz
> fields = '__all__'
>
> form = Form(instance=self.fk_instance)
>
> # Fails! Option isn't selected.
> self.assertEquals(
> str(form["foo"]),
> '<select name="foo" required id="id_foo">\n'
> ' <option value="">---------</option>\n\n'
> ' <option value="spam">Spam</option>\n\n'
> ' <option value="ham">Ham</option>\n\n'
> ' <option value="eggs" selected>Eggs</option>\n\n'
> '</select>'
> )
> }}}
New description:
This is the same issue mentioned in #17657 but for ModelChoiceField
instead of ModelMultipleChoiceField. This bug is present in Django 2.2.18
and Django 3.1.6. The first two tests were taken directly from #17657 just
to show that ModelMultipleChoiceField still works as expected and the
equivalent ModelChoiceField tests are added after, the last of which
fails:
{{{#!python
# models.py
from django.db import models
class Foo(models.Model):
slug = models.CharField(max_length=40, unique=True)
title = models.CharField(max_length=40, unique=True)
def __str__(self):
return self.title
class Bar(models.Model):
foos = models.ManyToManyField(Foo)
class Baz(models.Model):
foo = models.ForeignKey(Foo, on_delete=models.CASCADE)
}}}
{{{#!python
# tests.py
from django.test.testcases import TestCase
from django import forms
from .models import Foo, Bar, Baz
class TestModelRelationshipChoiceWithFieldName(TestCase):
@classmethod
def setUpTestData(cls):
spam = Foo.objects.create(title="Spam", slug="spam")
ham = Foo.objects.create(title="Ham", slug="ham")
eggs = Foo.objects.create(title="Eggs", slug="eggs")
cls.m2m_instance = Bar.objects.create()
cls.m2m_instance.foos.add(spam)
cls.m2m_instance.foos.add(ham)
cls.m2m_instance.foos.add(eggs)
cls.fk_instance = Baz.objects.create(foo=eggs)
def test_multiple_without_field_name(self):
class Form(forms.ModelForm):
foos = forms.ModelMultipleChoiceField(Foo.objects.all())
class Meta:
model = Bar
fields = '__all__'
form = Form(instance=self.m2m_instance)
self.assertEquals(
str(form["foos"]),
'<select name="foos" required id="id_foos" multiple>\n'
' <option value="1" selected>Spam</option>\n\n'
' <option value="2" selected>Ham</option>\n\n'
' <option value="3" selected>Eggs</option>\n\n'
'</select>'
)
def test_multiple_with_field_name(self):
class Form(forms.ModelForm):
foos = forms.ModelMultipleChoiceField(Foo.objects.all(),
to_field_name="slug")
class Meta:
model = Bar
fields = '__all__'
form = Form(instance=self.m2m_instance)
# Fixed in #17657, options weren't selected.
self.assertEquals(
str(form["foos"]),
'<select name="foos" required id="id_foos" multiple>\n'
' <option value="spam" selected>Spam</option>\n\n'
' <option value="ham" selected>Ham</option>\n\n'
' <option value="eggs" selected>Eggs</option>\n\n'
'</select>'
)
def test_one_without_field_name(self):
class Form(forms.ModelForm):
foo = forms.ModelChoiceField(Foo.objects.all())
class Meta:
model = Baz
fields = '__all__'
form = Form(instance=self.fk_instance)
self.assertEquals(
str(form["foo"]),
'<select name="foo" required id="id_foo">\n'
' <option value="">---------</option>\n\n'
' <option value="1">Spam</option>\n\n'
' <option value="2">Ham</option>\n\n'
' <option value="3" selected>Eggs</option>\n\n'
'</select>'
)
def test_one_with_field_name(self):
class Form(forms.ModelForm):
foo = forms.ModelChoiceField(Foo.objects.all(),
to_field_name="slug")
class Meta:
model = Baz
fields = '__all__'
form = Form(instance=self.fk_instance)
# Fails! Option isn't selected.
self.assertEquals(
str(form["foo"]),
'<select name="foo" required id="id_foo">\n'
' <option value="">---------</option>\n\n'
' <option value="spam">Spam</option>\n\n'
' <option value="ham">Ham</option>\n\n'
' <option value="eggs" selected>Eggs</option>\n\n'
'</select>'
)
}}}
--
--
Ticket URL: <https://code.djangoproject.com/ticket/32432#comment:1>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/068.14c0d6eaa29065deaf149b6112e184ae%40djangoproject.com.