Nullable ForeignKey fields in Django REST framework
Asked Answered
K

2

23

In Django REST framework (2.1.16) I have a model with nullable FK field type, but POST creation request gives 400 bad request which says that field is required.

My model is

class Product(Model):
    barcode = models.CharField(max_length=13)
    type = models.ForeignKey(ProdType, null=True, blank=True)

and serializer is:

class ProductSerializer(serializers.ModelSerializer):
    class Meta:
        model = Product
        exclude = ('id')

I've tried to add type explicitly to serializer like

class ProductSerializer(serializers.ModelSerializer):
    type = serializers.PrimaryKeyRelatedField(null=True, source='type')
    class Meta:
        model = Product
        exclude = ('id')

and it has no effect.

From http://django-rest-framework.org/topics/release-notes.html#21x-series I see that there was a bug, but it was fixed in 2.1.7.

How should I change serializer to properly handle my FK field?

Thanks!


UPDATE: from the shell it gives

>>> serializer = ProductSerializer(data={'barcode': 'foo', 'type': None})
>>> print serializer.is_valid()
True
>>> 
>>> print serializer.errors
{}

but without type=None:

>>> serializer = ProductSerializer(data={'barcode': 'foo'})
>>> print serializer.is_valid()
False
>>> print serializer.errors
{'type': [u'This field is required.']}
>>> serializer.fields['type']
<rest_framework.relations.PrimaryKeyRelatedField object at 0x22a6cd0>
>>> print serializer.errors
{'type': [u'This field is required.']}

in both cases it gives

>>> serializer.fields['type'].null
True
>>> serializer.fields['type'].__dict__
{'read_only': False, ..., 'parent': <prodcomp.serializers.ProductSerializer object at   0x22a68d0>, ...'_queryset': <mptt.managers.TreeManager object at 0x21bd1d0>, 'required': True, 
Kinkajou answered 17/1, 2013 at 15:17 Comment(3)
Don't think this is related to your issue, but looks like those exclude options are missing a comma, that'd force them to be treated as tuples. exclude = ('id',)Forecast
Also note that you don't need the source='type', since in this case the field name already matches the source you want to use.Forecast
@TomChristie yes, I've tried firstly without source='type'Kinkajou
F
8

I'm not sure what's going on there, we've got coverage for that case and similar cases work fine for me.

Perhaps try dropping into the shell and inspecting the serializer directly.

For example if you instantiate the serializer, what does serializer.fields return? How about serializer.field['type'].null? If you pass data to the serializer directly in the shell what results do you get?

For example:

serializer = ProductSerializer(data={'barcode': 'foo', 'type': None})
print serializer.is_valid()
print serializer.errors

If you get some answers to those, update the question and we'll see if we can get it sorted.

Edit

Okay, that explains things better. The 'type' field is nullable, so it may be None, but it's still a required field. If you want it to be null you have to explicitly set it to None.

If you really do want to be able to exclude the field when POSTing the data, you can include the required=False flag on the serializer field.

Forecast answered 17/1, 2013 at 16:13 Comment(6)
thanks, I've updated the question with the output from the shell.Kinkajou
thank you, adding type = serializers.PrimaryKeyRelatedField( required=False) to serializer helps. (Thought null=True means the same)Kinkajou
Just in case somebody is searching to null a field and also stumbles upon this thread: If you want to explicitly enable a field to be nullable for a PrimaryKeyRelatedField, you should add: allow_null=True :)Dg
I'm getting "Relational field must provide a queryset argument, or set read_only=True" error...Valverde
What's the difference between required=False, null=True, and allow_null=True in DRF? The documentation doesn't mention it.Arte
@Arte required=False will use default value if set, and if you send an empty value (depending on field type) it might raise a validation error. null=True means it allows null values at database level. allow_null=True means field can be set to None, and it's similar to null=True but this is at a serializer level instead of db level.Flophouse
B
17

Add the kwarg allow_null when initializing the serializer:

class ProductSerializer(serializers.ModelSerializer):
    type = serializers.PrimaryKeyRelatedField(null=True, source='type', allow_null=True)

As already mentioned in the comment of @gabn88 but I think it warrants its own answer. (Cost me some time because I only read that comment after finding it out by myself.)

See http://www.django-rest-framework.org/api-guide/relations/

Beulabeulah answered 4/8, 2015 at 17:23 Comment(0)
F
8

I'm not sure what's going on there, we've got coverage for that case and similar cases work fine for me.

Perhaps try dropping into the shell and inspecting the serializer directly.

For example if you instantiate the serializer, what does serializer.fields return? How about serializer.field['type'].null? If you pass data to the serializer directly in the shell what results do you get?

For example:

serializer = ProductSerializer(data={'barcode': 'foo', 'type': None})
print serializer.is_valid()
print serializer.errors

If you get some answers to those, update the question and we'll see if we can get it sorted.

Edit

Okay, that explains things better. The 'type' field is nullable, so it may be None, but it's still a required field. If you want it to be null you have to explicitly set it to None.

If you really do want to be able to exclude the field when POSTing the data, you can include the required=False flag on the serializer field.

Forecast answered 17/1, 2013 at 16:13 Comment(6)
thanks, I've updated the question with the output from the shell.Kinkajou
thank you, adding type = serializers.PrimaryKeyRelatedField( required=False) to serializer helps. (Thought null=True means the same)Kinkajou
Just in case somebody is searching to null a field and also stumbles upon this thread: If you want to explicitly enable a field to be nullable for a PrimaryKeyRelatedField, you should add: allow_null=True :)Dg
I'm getting "Relational field must provide a queryset argument, or set read_only=True" error...Valverde
What's the difference between required=False, null=True, and allow_null=True in DRF? The documentation doesn't mention it.Arte
@Arte required=False will use default value if set, and if you send an empty value (depending on field type) it might raise a validation error. null=True means it allows null values at database level. allow_null=True means field can be set to None, and it's similar to null=True but this is at a serializer level instead of db level.Flophouse

© 2022 - 2024 — McMap. All rights reserved.