2
votes

This is probably easy to solve. I created a form which use forms.ModelForm. My model has ForeignKey field. Form creates a select field for foreignKey, but but does not display value correctly.

models.py

from django.db import models

# Create your models here.

class Gender(models.Model):

  name = models.CharField(max_length=8, null=True)

  class Meta:
    db_table='gender'

class UserExample(models.Model):

  name     = models.CharField(max_length=16,null=True)
  age      = models.IntegerField(blank=True, null=True)
  gender   = models.ForeignKey('Gender', on_delete=models.SET_NULL, null=True)

  class Meta:
      db_table='userExample'

  def __str__(self):
      return ""

forms.py

from django import forms

from .models import UserExample, Gender

class UserForm(forms.ModelForm):

    class Meta:
        model = UserExample
        fields = ('name', 'age', 'gender')

views.py

from django.shortcuts import render    
from .forms import UserForm

# Create your views here.

def index(request):

    form = UserForm        
    return render(
        request,
        'index.html',
        {'form': form}
    )

urls.py

from django.conf.urls import url
from . import views

urlpatterns = [
    url(r'^$', views.index, name='index'),
]

index.html

<html>
    <head>
    <title>test </title>
    </head>
<body>


<form action="formreturn/" method="post">
    {{ form.as_p }}
    <input type="submit" value="Submit" />
</form>
</body>
</html>

And after I launch my app. In select box I get only selection for gender objects but not for gender names.

enter image description here

Optional I used to add values using sqllite3 like this:
sqlite> insert into gender values(1,"Male");
sqlite> insert into gender values(2,"Female");

3
define __str__ returning self.name in Gender class - Paweł Kordowski

3 Answers

7
votes

Implement __unicode__ or __str__ method in Gender model,

def __unicode__(self):
    return '%s' % self.name

And it will display gender names in your option choices.

How to customize the default form field behavior of ForeignKey Model field

ForeignKey maps to ModelChoiceField and you can override the default behavior of the same. You can override the 'Select' option field value using 'to_field_name' parameter (useful when you have multiple unique fields in your related model) otherwise option field values are defaulted to pk field.

'empty_label' will change the default "--------" with the empty_label atrribute

forms.py

class UserForm(forms.ModelForm):
    gender = forms.ModelChoiceField(queryset=Gender.objects.all(),
                                    to_field_name = 'name',
                                    empty_label="Select Gender")

    class Meta:
        model = UserExample
        fields = ('name', 'age', 'gender')

Option display names are defaulted to __str__ method output. You can override the default behavior by writing a custom choice field class (inherited from ModelChoiceField) and override the label_from_instance() method.

1
votes
def__str__(self):
      return self.name

You will add this on your gender class

0
votes

I already gave this answer in another place, hope it's not bad to copy paste. In my case, I didn't wanna go make an str for my billion models, so I just did this:

You can make one custom ModelChoiceField to which you can pass a function. That way if you have different fields for which you want different attributes to be displayed, you can have only 1 class:

class CustomModelChoiceField(forms.ModelChoiceField):
name_function = staticmethod(lambda obj: obj)

def __init__(self, name_function, *args, **kwargs):
    if not name_function is None: self.name_function = name_function
    super(CustomModelChoiceField, self).__init__(*args, **kwargs)

def label_from_instance(self, obj):
     return self.name_function(obj);

You can then call it as simply as this:

form_field = CustomModelChoiceField(
    lambda obj: obj.get_full_name(),
    queryset=Whatever.objects.all(),
)

You can also pass None in case you're doing some dynamic stuff and it'll just basically default to a regular ModelChoiceField. I'm not too much of a python guy but this works for me.