3

tl;dr: How do I make a form output the ‘nice’ name of the choices in a model?

I have a Django model with choices, defined like this:

class City(models.Model):
    AMSTERDAM = 'AMS'
    ROTTERDAM = 'ROT'
    THE_HAGUE = 'THE'
    UTRECHT = 'UTR'
    CITY_CHOICES = (
        (AMSTERDAM, 'Amsterdam'),
        (ROTTERDAM, 'Rotterdam'),
        (THE_HAGUE, 'The Hague'),
        (UTRECHT, 'Utrecht'),
    )
    name = models.CharField(
        max_length=3,
        choices=CITY_CHOICES,
        default=AMSTERDAM,
        blank=False,
    )

In forms.py, the widgets are defined like this:

class ArticleForm(ModelForm):
    class Meta:
        model = Article
        fields = ['title', 'text', 'categories', 'city']
        widgets = {'title': forms.TextInput(attrs={
            'placeholder': 'Enter a descriptive title'}),
            'text': forms.Textarea(attrs={'placeholder': 'The article'}),
            'categories': forms.CheckboxSelectMultiple(),
            'city': forms.RadioSelect(),
        }

The form itself is rendered manually (although the same effect happens with just {{form}}:

<form action="{% url 'article-submit' %}" method="POST">
    {% csrf_token %}
    {{% for field in form %}
        <fieldset class="article-form__field">
        {% if field.name = "categories"%}
            {{ field.label_tag }}
            <ul id={{ field.auto_id }}>
            {% for checkbox in field %}
                <li>
                    <label for="{{ checkbox.id_for_label }}">
                        {{ checkbox.choice_label }}
                    </label>
                    {{ checkbox.tag }}
                </li>
            {% endfor %}
            </ul>
        {% elif field.name = "city" %}
            {{ field.label_tag }}
            <ul id={{ field.auto_id }}>
            {% for radio in field %}
                <li>
                    <label for="{{ radio.id_for_label }}">
                        {{ radio.choice_label }}
                    </label>
                    {{ radio.tag }}
                </li>
            {% endfor %}
            </ul>
        {% else %}
            {{ field.label_tag }} {{ field }}
        {% endif %}
        </fieldset>
    {% endfor %}}
    <fieldset class="article-form__field">
        <input class="button" type="submit" value="submit">
    </fieldset>
</form>

However the output is not Amsterdam, but AMS:

        <fieldset class="article-form__field">

            <label for="city_0">City:</label>
            <ul id=city>

                <li>
                    <label for="city_0">
                        ---------
                    </label>
                    <input checked="checked" id="city_0" name="city" type="radio" value="" />
                </li>

                <li>
                    <label for="city_1">
                        AMS
                    </label>
                    <input id="city_1" name="city" type="radio" value="1" />
                </li>
etc.

In other templates, I could do this: {{city.get_name_display}}, to get the full name. How do I do this in the form?

full code is here

Flobin
  • 626
  • 1
  • 10
  • 26

3 Answers3

3

I got the following answer from reddit user roambe:

Since city is a foreign key, Django will use the __str__ (or __unicode__) method of the City model for the choice label. This should make it start behaving like you want to (substitute for unicode if needed):

def __str__(self):
    return self.get_name_display()
Flobin
  • 626
  • 1
  • 10
  • 26
1

In your line 'city': forms.RadioSelect(),, you need to define the choices for it. It should look like:

from blah.models import City

class ArticleForm(ModelForm):
    class Meta:
        model = Article
        fields = ['title', 'text', 'categories', 'city']
        widgets = {'title': forms.TextInput(attrs={
            'placeholder': 'Enter a descriptive title'}),
            'text': forms.Textarea(attrs={'placeholder': 'The article'}),
            'categories': forms.CheckboxSelectMultiple(),
            'city': forms.RadioSelect(choices=City.CITY_CHOICES),
        }

The radio select widget inherits from the select widget, which has a brief mention of the choices argument in the docs here: https://docs.djangoproject.com/en/1.10/ref/forms/widgets/#django.forms.Select

Titus P
  • 959
  • 1
  • 7
  • 16
0

I'd like to extend the argument a little, with a 'work-around' solution for an inherit problem.

In my situation, the returned value never changes from 'value' to 'human-r'. I believe it depends on my db-django config: on my db the field is a FK, but on Django is a simple CharField. In this way I can't define a str method for it without make changes to the model. I decided, I had only 2-3 choices, to override the get_status function in order to evaluates the status and return a 'constant' output (a simply IF construct).

Gmarra
  • 1