Closed legshort closed 2 years ago
Hi @legshort - thanks for this. Would you be able to provide a short reproducible set of what you did to get this error? From the looks of it you're using Generic Foreign Keys?
Hi @evenicoulddoit - yeah that's right. My code is just like a django example This is how my models and serializers look like.
class Photo(Model):
file = models.ImageField(upload_to=photo_directory)
content_type = models.ForeignKey(ContentType, on_delete=models.PROTECT)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
class Post(BaseModel):
photos = GenericRelation(Photo)
class PostSerializer(SerializerExtensionsMixin, ModelSerializer):
id = HashIdField(model=models.Post, read_only=True)
class Meta:
model = models.Post
fields = ('id', 'created', 'contents', 'like_count', 'comment_count', 'photos',)
read_only_fields = ('like_count', 'comment_count',)
This is the error.
Error
Traceback (most recent call last):
File "/Users/user/repo/drf-server/drf/posts/tests/test_view.py", line 64, in test_should_retrieve_post
response = self.client.get('/api/posts/{post_id}'.format(post_id=self.encode_id(Post, self.post.id)))
File "/Users/user/.pyenv/versions/drf-server-3.6.2/lib/python3.6/site-packages/rest_framework/test.py", line 291, in get
response = super(APIClient, self).get(path, data=data, **extra)
File "/Users/user/.pyenv/versions/drf-server-3.6.2/lib/python3.6/site-packages/rest_framework/test.py", line 208, in get
return self.generic('GET', path, **r)
File "/Users/user/.pyenv/versions/drf-server-3.6.2/lib/python3.6/site-packages/rest_framework/test.py", line 237, in generic
method, path, data, content_type, secure, **extra)
File "/Users/user/.pyenv/versions/drf-server-3.6.2/lib/python3.6/site-packages/django/test/client.py", line 404, in generic
return self.request(**r)
File "/Users/user/.pyenv/versions/drf-server-3.6.2/lib/python3.6/site-packages/rest_framework/test.py", line 288, in request
return super(APIClient, self).request(**kwargs)
File "/Users/user/.pyenv/versions/drf-server-3.6.2/lib/python3.6/site-packages/rest_framework/test.py", line 240, in request
request = super(APIRequestFactory, self).request(**kwargs)
File "/Users/user/.pyenv/versions/drf-server-3.6.2/lib/python3.6/site-packages/django/test/client.py", line 485, in request
raise exc_value
File "/Users/user/.pyenv/versions/drf-server-3.6.2/lib/python3.6/site-packages/django/core/handlers/exception.py", line 35, in inner
response = get_response(request)
File "/Users/user/.pyenv/versions/drf-server-3.6.2/lib/python3.6/site-packages/django/core/handlers/base.py", line 158, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/Users/user/.pyenv/versions/drf-server-3.6.2/lib/python3.6/site-packages/django/core/handlers/base.py", line 156, in _get_response
response = response.render()
File "/Users/user/.pyenv/versions/drf-server-3.6.2/lib/python3.6/site-packages/django/template/response.py", line 106, in render
self.content = self.rendered_content
File "/Users/user/.pyenv/versions/drf-server-3.6.2/lib/python3.6/site-packages/rest_framework/response.py", line 72, in rendered_content
ret = renderer.render(self.data, accepted_media_type, context)
File "/Users/user/.pyenv/versions/drf-server-3.6.2/lib/python3.6/site-packages/rest_framework/renderers.py", line 105, in render
allow_nan=not self.strict, separators=separators
File "/Users/user/.pyenv/versions/drf-server-3.6.2/lib/python3.6/site-packages/rest_framework/utils/json.py", line 28, in dumps
return json.dumps(*args, **kwargs)
File "/Users/user/.pyenv/versions/3.6.2/lib/python3.6/json/__init__.py", line 238, in dumps
**kw).encode(obj)
File "/Users/user/.pyenv/versions/3.6.2/lib/python3.6/json/encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/Users/user/.pyenv/versions/3.6.2/lib/python3.6/json/encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "/Users/user/.pyenv/versions/drf-server-3.6.2/lib/python3.6/site-packages/rest_framework/utils/encoders.py", line 68, in default
return super(JSONEncoder, self).default(obj)
File "/Users/user/.pyenv/versions/3.6.2/lib/python3.6/json/encoder.py", line 180, in default
o.__class__.__name__)
TypeError: Object of type 'GenericRelatedObjectManager' is not JSON serializable
Thanks for the raid response!
Thanks for the confirmation. I can't see anywhere in the stack-trace which suggests an error in serializer extensions? Were you trying to expand something at the time? I've not serialized a GenericRelation before, but you'll need to provide a serializer explicitly (otherwise, what should it print out), and you also may need to change the source="photos.all"
I wasn't trying to add expandable_fields
, I was preparing it but I think the above code should work fine because it has nothing configuration yet. However, I think I made some mistake and below code works like a charm now!!
class PostSerializer(SerializerExtensionsMixin, serializers.ModelSerializer):
id = HashIdField(model=models.Post, read_only=True)
class Meta:
model = models.Post
fields = ('id', 'created', 'contents', 'like_count', 'comment_count',)
read_only_fields = ('like_count', 'comment_count',)
expandable_fields = {
'photos': {
'serializer': PhotoSerializer,
'source': 'photos.all',
'many': True,
},
}
I almost switched to drf-tweaks but I like your library much more because it's simple and has more features.
In addition, I think it would be more helpful if source.all
and ExternalIdViewMixin
are documented otherwise users have to look for them from source code just like me. I will try to help to make the docs as well.
Thanks for the great library, it makes DRF so much easier, looking forward even better and nicer!
Interesting problem pops up with above code, validated_data
has photos
automatically.
POST: /posts?expand=photos
This is my form data looks like {'contents': 'xWczeXyUVKhS'}
and this is validated_data looks like {'contents': 'xWczeXyUVKhS', 'photos': {'all': []}
.
It seems like read_only=True
is not working as expected.
I found the default value for read_only
is False so I tested with read_only=True
but still validated_data
has photos
.
When it comes to _writable_fields()
, ListSerializer
of PhotoSerializer
has read_only=False
. I guess read_only=True
from expandable_fiedls
hasn't effected to ListSerializer
Hey, thanks for these - a couple of things:
.all
?read_only
option on the expandable fields which it doesn't look like you're specifying?'source':'photos'
Failure
Traceback (most recent call last):
File "/Users/user/repo/server/server/posts/tests/test_view.py", line 65, in test_should_retrieve_post
response = self.client.get('/api/posts/{post_id}?expand=photos'.format(post_id=self.encode_id(Post, self.post.id)))
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/test.py", line 291, in get
response = super(APIClient, self).get(path, data=data, **extra)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/test.py", line 208, in get
return self.generic('GET', path, **r)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/test.py", line 237, in generic
method, path, data, content_type, secure, **extra)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/django/test/client.py", line 404, in generic
return self.request(**r)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/test.py", line 288, in request
return super(APIClient, self).request(**kwargs)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/test.py", line 240, in request
request = super(APIRequestFactory, self).request(**kwargs)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/django/test/client.py", line 485, in request
raise exc_value
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/django/core/handlers/exception.py", line 35, in inner
response = get_response(request)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/django/core/handlers/base.py", line 128, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/django/core/handlers/base.py", line 126, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/viewsets.py", line 95, in view
return self.dispatch(request, *args, **kwargs)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/views.py", line 494, in dispatch
response = self.handle_exception(exc)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/views.py", line 454, in handle_exception
self.raise_uncaught_exception(exc)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/views.py", line 491, in dispatch
response = handler(request, *args, **kwargs)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/mixins.py", line 58, in retrieve
return Response(serializer.data)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/serializers.py", line 537, in data
ret = super(Serializer, self).data
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/serializers.py", line 262, in data
self._data = self.to_representation(self.instance)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/serializers.py", line 487, in to_representation
fields = self._readable_fields
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/django/utils/functional.py", line 36, in __get__
res = instance.__dict__[self.name] = self.func(instance)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/serializers.py", line 377, in _readable_fields
field for field in self.fields.values()
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/serializers.py", line 364, in fields
self._fields[key] = value
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/utils/serializer_helpers.py", line 148, in __setitem__
field.bind(field_name=key, parent=self.serializer)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/serializers.py", line 572, in bind
super(ListSerializer, self).bind(field_name, parent)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/fields.py", line 366, in bind
(field_name, self.__class__.__name__, parent.__class__.__name__)
AssertionError: It is redundant to specify `source='photos'` on field 'ListSerializer' in serializer 'PostSerializer', because it is the same as the field name. Remove the `source` keyword argument.
'source': 'photos.all'
Failure
Traceback (most recent call last):
File "/Users/user/repo/server/posts/tests/test_view.py", line 21, in test_should_create_post
response = self.client.post('/api/posts?expand=photos', form, format='multipart')
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/test.py", line 299, in post
path, data=data, format=format, content_type=content_type, **extra)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/test.py", line 212, in post
return self.generic('POST', path, data, content_type, **extra)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/test.py", line 237, in generic
method, path, data, content_type, secure, **extra)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/django/test/client.py", line 404, in generic
return self.request(**r)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/test.py", line 288, in request
return super(APIClient, self).request(**kwargs)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/test.py", line 240, in request
request = super(APIRequestFactory, self).request(**kwargs)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/django/test/client.py", line 485, in request
raise exc_value
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/django/core/handlers/exception.py", line 35, in inner
response = get_response(request)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/django/core/handlers/base.py", line 128, in _get_response
response = self.process_exception_by_middleware(e, request)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/django/core/handlers/base.py", line 126, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/viewsets.py", line 95, in view
return self.dispatch(request, *args, **kwargs)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/views.py", line 494, in dispatch
response = self.handle_exception(exc)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/views.py", line 454, in handle_exception
self.raise_uncaught_exception(exc)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/views.py", line 491, in dispatch
response = handler(request, *args, **kwargs)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/mixins.py", line 21, in create
self.perform_create(serializer)
File "/Users/user/repo/server/posts/views.py", line 14, in perform_create
serializer.save(user=self.request.user)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/serializers.py", line 214, in save
self.instance = self.create(validated_data)
File "/Users/user/repo/server/posts/serializers.py", line 30, in create
post = super().create(validated_data)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/serializers.py", line 903, in create
raise_errors_on_nested_writes('create', self, validated_data)
File "/Users/user/.pyenv/versions/server-3.6.2/lib/python3.6/site-packages/rest_framework/serializers.py", line 818, in raise_errors_on_nested_writes
class_name=serializer.__class__.__name__
AssertionError: The `.create()` method does not support writable dotted-source fields by default.
Write an explicit `.create()` method for serializer `posts.serializers.PostSerializer`, or set `read_only=True` on dotted-source serializer fields.
read_only=True/False
and these are my models and serializers and views, I hope these code can help you to debug, thanks for your help.from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.utils import timezone
class Photo(models.Model):
user = models.ForeignKey('users.User', on_delete=models.PROTECT, related_name='photos')
content_type = models.ForeignKey(ContentType, on_delete=models.PROTECT)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
from django.contrib.contenttypes.fields import GenericRelation
from django.db import models
class Post(models.Model):
contents = models.TextField()
like_count = models.PositiveIntegerField(default=0)
comment_count = models.PositiveIntegerField(default=0)
user = models.ForeignKey('users.User', on_delete=models.PROTECT, related_name='posts')
photos = GenericRelation('photos.Photo')
from rest_framework import serializers
from rest_framework_serializer_extensions.fields import HashIdField
from rest_framework_serializer_extensions.serializers import SerializerExtensionsMixin
from photos.serializers import PhotoSerializer
from posts import models
class PostSerializer(SerializerExtensionsMixin, serializers.ModelSerializer):
id = HashIdField(model=models.Post, read_only=True)
class Meta:
model = models.Post
fields = ('id', 'contents', 'like_count', 'comment_count', )
read_only_fields = ('like_count', 'comment_count',)
expandable_fields = {
'photos': {
'serializer': PhotoSerializer,
'source': 'photos.all',
'many': True,
'read_only': True
},
}
def create(self, validated_data):
print(validated_data)
return super().create(validated_data)
from rest_framework import mixins, viewsets
from rest_framework_serializer_extensions.views import SerializerExtensionsAPIViewMixin, ExternalIdViewMixin
from posts.models import Post
from posts.serializers import PostSerializer
class PostViewSet(ExternalIdViewMixin, SerializerExtensionsAPIViewMixin,
mixins.CreateModelMixin, mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user)
@legshort did you ever find a solution to this?
it's been a while, I don't quite remember how I overcome this issue but I will look into it and let you know! thanks for paying attention.
Great, thanks. Sorry I've been out of the loop for so long
I'm going to close this for now, as I've nothing more to go off.
...but the problem is not solved
Hi @nicbou - in which case I'll reopen the issue, but I can't give you any concrete guidance as to when I'll get time to look into this further at the minute. Did you do any debugging yourself?
Hello @evenicoulddoit , I realise now that I'm commenting in a different repository, so my answer probably won't make any sense.
For anyone trying to figure out how to serialize a GenericRelation and getting errors, this is how I got it to work: https://github.com/nicbou/timeline/blob/master/backend/source/archive/serializers.py#L24
@nicbou thanks for this - I'm sure it'll be helpful to help me diagnose the issue :)
@nicbou - just had a chance to parse this. The link you post to doesn't even use the serializer extensions? So, I'm not sure I understand how this relates in any way specifically to this package?
I guess generic relation is not supported yet.