Luis Masuelli Luis Masuelli - 5 months ago 16
Python Question

Is this behavior documented in Django's field validators for foreign keys?

This is an example Python 2 code:

from django.db import models

def my_validator(value):
assert isinstance(value, (int, long))

class Foo(models.Model):
name = models.CharField(...) # irrelevant here

class Bar(models.Model):
name = models.CharField(...) # irrelevant here
foo = models.ForeignKey(Foo, validators=[my_validator])

If I create a Foo instance, then a Bar instance (assigning the foo instance), and then validate, this code passes: the FK value to validate is not a model instance but an ID (which is an integer, by default):

foo = Foo.objects.create(name='foo')
bar = Bar.objects.create(name='bar', foo=foo)

Edit: I forgot to include the
call. But yes: the troublesome code calls
. In fact, the first time I noticed this behavior was when trying to treat the
in the validator callable, as a model instance instead of a raw value, which triggered a
int value has no attribute xxx
when trying to invoke an instance method inside the validator.


This happens in Django 1.9. Is this documented and expected?


Yes - this is implicitly referred to in the documentation for ForeignKey.to_field:

The field on the related object that the relation is to. By default, Django uses the primary key of the related object.


For fields like ForeignKey that map to model instances, defaults should be the value of the field they reference (pk unless to_field is set) instead of model instances.

That is, by default, the value of the ForeignKey is the primary key of the related object - i.e., an integer.

You can however specify a different to_field, in which case the value would take the type of that field.

In terms of what value is passed to the validators, it seems that the assumption is implicit that this is the to_field (what else would you validate other than the value that is going to be stored in the database? It does not make much sense to pass a model object when validating a foreign key, because the key itself is only a pointer to the object and does not say anything about what that object should be.).

But to answer your question - there doesn't appear to be any explicit documentation stating this.