floflo29 floflo29 - 18 days ago 4
Python Question

Serialize a Django model with a ForeignKey

In Django Rest Framework, I have a simple

Present
model which consists in the following fields:

class Present(models.Model):
name = models.CharField(max_length=15)
price = models.FloatField()
link = models.CharField(max_length=15)
isAlreadyBought = models.BooleanField()
user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)


The serializers for the
User
and
Present
models are these ones:

class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('username', 'password', 'email')



class PresentSerializer(serializers.ModelSerializer):

user_id = serializers.RelatedField(source='User', read_only=True)

class Meta:
model = Present
fields = ('name', 'link', 'price', 'isAlreadyBought', 'user_id')


def create(self, validated_data, **kwargs):
print(kwargs)
print(validated_data)
return Present.objects.create(**validated_data)


My goal is, from the following POST request to save a new
Present
. To do so, I've added a
user_id
field to my request to link my
Present
with a given
User
:

import requests

save_present = {'name': 'figurine', 'price': '50.8', 'link': 'fake_link', 'isAlreadyBought': 'True', 'user_id': '22'}
r = requests.post('http://127.0.0.1:8000/present_list', json=save_present)


Here is how I am trying, without success so far, to save my
Present
:

class PresentList(APIView):
# add a new present
def post(self, request, format=None):

# get user id:
# print(request.data['user_id'])
#
# user = User.objects.get(id=request.data['user_id'])

machin = request.data
# machin.pop('user_id', None)
# machin['user'] = UserSerializer(user).data

print('Machin', machin)

serializer = PresentSerializer(data=machin)

if serializer.is_valid():
print('SERIALIZER OK')
serializer.save()

else:
print(serializer.errors())


return Response('')


I do not manage to find out the best Django-based way to achieve this, as I get this error:

November 20, 2016 - 19:55:34
Django version 1.10.3, using settings 'tutorial.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
23
Machin {'name': 'figurine', 'link': 'fake_link', 'isAlreadyBought': 'True', 'price': '50.8', 'user': {'password': 'PASSWORD', 'email': 'WESH@wanadoo.fr', 'username': 'Florian'}}
SERIALIZER OK
Internal Server Error: /present_list
Traceback (most recent call last):
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/db/backends/utils.py", line 64, in execute
return self.cursor.execute(sql, params)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/db/backends/sqlite3/base.py", line 337, in execute
return Database.Cursor.execute(self, query, params)
sqlite3.IntegrityError: NOT NULL constraint failed: blog_present.user_id

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/core/handlers/exception.py", line 39, in inner
response = get_response(request)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/core/handlers/base.py", line 187, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/core/handlers/base.py", line 185, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
return view_func(*args, **kwargs)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/views/generic/base.py", line 68, in view
return self.dispatch(request, *args, **kwargs)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/rest_framework/views.py", line 477, in dispatch
response = self.handle_exception(exc)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/rest_framework/views.py", line 437, in handle_exception
self.raise_uncaught_exception(exc)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/rest_framework/views.py", line 474, in dispatch
response = handler(request, *args, **kwargs)
File "/Users/Florian/PycharmProjects/Backend/tutorial/blog/views.py", line 54, in post
serializer.save()
File "/Users/Florian/anaconda/lib/python3.5/site-packages/rest_framework/serializers.py", line 214, in save
self.instance = self.create(validated_data)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/rest_framework/serializers.py", line 902, in create
instance = ModelClass.objects.create(**validated_data)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/db/models/query.py", line 399, in create
obj.save(force_insert=True, using=self.db)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/db/models/base.py", line 796, in save
force_update=force_update, update_fields=update_fields)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/db/models/base.py", line 824, in save_base
updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/db/models/base.py", line 908, in _save_table
result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/db/models/base.py", line 947, in _do_insert
using=using, raw=raw)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/db/models/query.py", line 1045, in _insert
return query.get_compiler(using=using).execute_sql(return_id)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/db/models/sql/compiler.py", line 1054, in execute_sql
cursor.execute(sql, params)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/db/backends/utils.py", line 79, in execute
return super(CursorDebugWrapper, self).execute(sql, params)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/db/backends/utils.py", line 64, in execute
return self.cursor.execute(sql, params)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/db/utils.py", line 94, in __exit__
six.reraise(dj_exc_type, dj_exc_value, traceback)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/utils/six.py", line 685, in reraise
raise value.with_traceback(tb)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/db/backends/utils.py", line 64, in execute
return self.cursor.execute(sql, params)
File "/Users/Florian/anaconda/lib/python3.5/site-packages/django/db/backends/sqlite3/base.py", line 337, in execute
return Database.Cursor.execute(self, query, params)
django.db.utils.IntegrityError: NOT NULL constraint failed: blog_present.user_id
[20/Nov/2016 19:55:36] "POST /present_list HTTP/1.1" 500 17246


EDIT:

The only way to save my
Present
is to add
blank=True
or/and
null=True
to my
ForeignKey
. However, I would like to assign the
user_id
field while saving my object but this field is not kept in the
validated_data
. How can I solve this?

Answer

You are setting the serializer field to read_only, which means it will not be used when creating or updating an object, only when reading it.

Read-only fields are included in the API output, but should not be included in the input during create or update operations. Any 'read_only' fields that are incorrectly included in the serializer input will be ignored. Set this to True to ensure that the field is used when serializing a representation, but is not used when creating or updating an instance during deserialization.

http://www.django-rest-framework.org/api-guide/fields/#read_only

Also, try using PrimaryKeyRelatedField instead of RelatedField. See the documentation for the field.