12

Say I have an address table and it has a postal_code field -- ModelChoiceField does not allow me to use something other than PKs to validate existence correct? What would be the way to go? Normal input and use clean_*()?

RS7
  • 2,341
  • 8
  • 34
  • 57
  • Depends on how the relation ship is set up, by default its with the `primary_key` of `postal_code` model. Can you give more details like your `address` and the related `postal_code` models. A `ModelChoiceField` by default creates a dropdown with options as existing instances of the related model. – Pannu Mar 22 '12 at 17:23

3 Answers3

26

What about to_field_name? I'm not sure if it's documented anywhere, but you can find it easily between ModelChoiceField constructor params: https://github.com/django/django/blob/master/django/forms/models.py. It is used to filter field queryset.

For example:

articles = ModelChoiceField(queryset=Articles.objects.all(),
        to_field_name='slug')
user240515
  • 3,056
  • 1
  • 27
  • 34
paluh
  • 2,171
  • 20
  • 14
  • 2
    Beware, in 1.4 (haven't tried 1.5 yet, sorry) it's broken when used with `instance` argument, as `model_to_dict` forcibly uses PK (actually, `value_for_object`). To work around one has to do `YourForm(..., instance=foo, initial={"bar": foo.bar.slug})`. See `BaseModelForm.__init__` implementation for details. – drdaeman Mar 12 '13 at 19:54
  • In a meanwhile Django devs are working on proper fix, I'm using [this quick-and-dirty monkey patch to make `to_field_name` work](https://gist.github.com/drdaeman/5326761). Just thought I'd share it, even though it's a hack. – drdaeman Apr 06 '13 at 16:53
  • Hi guys, anyone have problems recently using slug instead of id for ModelChoiceField and ChoiceField? or the bug mentioned by @drdaeman is now fixed? I'm using Django 1.5.5 btw and so far I haven't experienced any issues. – John Kenn Nov 02 '13 at 07:53
  • Works fine with Django-1.7.1 – Stan Dec 30 '14 at 09:30
1

ModelChoiceFields are meant to be used to select between a choice of existing model instances. This is almost always best represented by some form of Select field.

That said do you really have a FK from address to postal_code as you're implying. What are you storing on a PostalCode table to justify the extra table that will need to be joined in for every address related query?

For most cases postal_code should simply be a CharField and in that case if you want to validate that the value is valid you can use the choices attribute with a list of valid postal codes. Keep in mind that maintaining a list of valid postal codes by hand is a huge hassle.

If you really have a PostalCode table and think it's a good idea (which in some cases it could be) you may want to consider actually using the postal_code as the primary key rather than the default autoincrement since it's necessarily unique, makes your data more exportable, and solves your issue with validation.

John
  • 5,166
  • 27
  • 31
1

If postal_code is a foreign key to a PostalCode model that contains valid postal codes I would just use use a CharField and then do a clean like you suggested. My clean method would look like this:

def clean_postal_code(self):
    try:
        code = PostalCode.objects.get(code_field=self.data['postal_code'])
    except:
        raise forms.ValidationError("Please enter a valid postal code")
    return code
nates
  • 8,312
  • 5
  • 32
  • 28