I am creating a REST API in Django using the Django Rest framework and cannot use any other libraries or plugins. I have been having an issue the past few days which I am having trouble solving.
In my seralizer.py I have the following
class BookSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source = 'owner.username')
genres = serializers.ChoiceField(choices=Genre.objects.values_list())
# genres = GenreSerializer()
class Meta:
model = VideoGame
fields = ('title', 'description', 'brief', 'genres', 'owner')
I would like it so when users access the GUI and attempt to make a new book, they can select a genre from a choicefield, this is then translated into its ID. If users access via the rest API, they must also enter the ID.
When the database is blank, I am able to successfully post a new book to the server and select the dropdown using the choicefield. But when the database has an entry, I get the error below. This is solved when i replace the choicefield with the serializers. However then I am left with an input field and not a choice field. Does anyone know how I can solve this?
TypeError at /book/
<Genre: Genre object> is not JSON serializable
You need to look at a) what does ChoiceField
expect to receive as choices and b) what does values_list()
return.
a) from the docs the ChoiceField
expects " A list of valid values, or a list of (key, display_name)
tuples." In this case valid values would be the primary key ids of your Genres.
b) According to the docs values_list()
with no arguments returns "all the fields in the model, in the order they were declared."
I don't think you could have got the error message you show in the question from using choices=Genre.objects.values_list()
...maybe you tried choices=Genre.objects.all()
instead?
Anyway, you could do this:
class BookSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source = 'owner.username')
genres = serializers.ChoiceField(choices=Genre.objects.values_list('pk', flat=True))
If there is a field on your Genre
model that would make a good 'display name' then you could do:
class BookSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source = 'owner.username')
genres = serializers.ChoiceField(choices=Genre.objects.values_list('pk', 'name'))
However there is one problem with the both options above, which is that the queryset will be evaluated as soon as you import the file containing the resource. (because it is immediately converted to a list by the ChoiceField).
It is bad practice to have such side-effects at import time. Also since it is only ever evaluated once each time you start your server, the choices will be out of date when you change the genres.
Fortunately there is a better option:
http://www.django-rest-framework.org/api-guide/relations/#primarykeyrelatedfield
This is analogous to a ModelChoiceField in Django. It has a queryset
argument which is used for validating choices... but unlike the ChoiceField it knows not to immediately evaluate the queryset. It will be evaluated each time choices need to be validated.
So you should do:
class BookSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.ReadOnlyField(source = 'owner.username')
genres = serializers.PrimaryKeyRelatedField(queryset=Genre.objects.all())
You can see the docs here if you need to customise the 'display name' for your choices on the relation field.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With