Django-rest-framework, nested objects in Serializers - serialization

I would like to have a nested object inside a serializer instead of just the foreignkey (or url).
As this documentation says, I just had to specify the serializer class of the nested object in the parent serializer:
# Models
class NestedSample(models.Model):
something = models.CharField(max_length=255)
class Sample(models.Model):
thing = models.BooleanField()
nested = models.ForeignKey(NestedSample)
# Serializers
class NestedSampleSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = api_models.NestedSample
class SampleSerializer(serializers.HyperlinkedModelSerializer):
nested = NestedSampleSerializer() # HERE!
class Meta:
model = api_models.Sample
# Views
class NestedSampleViewSet(viewsets.ModelViewSet):
queryset = api_models.NestedSample.objects.all()
serializer_class = api_serializers.NestedSampleSerializer
class SampleViewSet(viewsets.ModelViewSet):
queryset = api_models.Sample.objects.all()
serializer_class = api_serializers.SampleSerializer
This works very well when I get the objects, but it is not possible to create (=POST) Sample objects anymore, I get the error:
{u'non_field_errors': [u'Invalid data']}
I tried to overwrite the create method in the viewset to get the object using the pk:
class SampleViewSet(viewsets.ModelViewSet):
queryset = api_models.Sample.objects.all()
serializer_class = api_serializers.SampleSerializer
def create(self, request):
request.DATA['nested'] = get_object_or_404(api_models.NestedSample, pk=request.DATA['nested'])
return super(SampleViewSet, self).create(request)
But it doesn't work as well.
Any idea?
I also found this question I can relate with which of course solves the problem but do not let me expose the full nested object, so back to the beginning.
Thanks,

I can think of two solutions to this problem. I prefer the first one.
First solution:
Use a django model form to create objects. Override the create and update methods. A sample create method:
def create(self, request):
form = SampleForm(data=request.POST)
if form.is_valid():
instance = form.save(commit=False)
instance.save()
return Response(dict(id=instance.pk), status=status.HTTP_201_CREATED)
return Response(form.errors, status=status.HTTP_400_BAD_REQUEST)
this way you can create Sample objects with any kind of validation you like.
Second solution:
Override get_serializer_class method and return serializer class based on request method. Define two serializers one for post and put and one for list and retrieve.

Can you confirm that you're sending a JSON encoded request - i.e. the request has the content type set to JSON ?
If not, the post is most probably send using form format which doesn't support nested.

Related

Django rest framework - model serializer doesn't serialize all fields

I'm using Django rest framework 3.7.7
I want to serialize an object instance and then deserialize it (without saving to DB again).
So I created a serializer that inherits from serializers.ModelSerializer.
To this model instance I sometimes add a field within the request that is not part of the model, using property setter. I did add it to the serializer as well, but when serializing - I don't see that field in the serialized data.
This is the model:
class MyModel(models.Model):
manager = models.CharField(max_length=200)
reviewer = models.CharField(max_length=200)
#property
def note(self):
return getattr(self, '_note', None)
#note.setter
def note(self, value):
self._note = value
This is the model serializer:
class MyModelSerializer(serializers.ModelSerializer):
note = serializers.CharField(write_only=True, required=False, max_length=1000, allow_null=True)
class Meta:
model = MyModel
fields = [
'manager',
'reviewer',
'note'
]
This is how I use the serializer:
ser_instance = MyModelSerializer(self.instance)
ser_data = ser_instance.data
seems like the ser_instance.data contains only the model fields data (without the 'note')
From the write_only--[DRF-Doc], it states that
Set this to True to ensure that the field may be used when updating or creating an instance, but is not included when serializing the representation.
Defaults to False
So, if you need to serialize the note field, remove the write_only=true from serializer.
#serializers.py
class MyModelSerializer(serializers.ModelSerializer):
note = serializers.CharField(required=False, max_length=1000, allow_null=True) # removed "write_only=True"
...

Mirage serializer drop Ids when include and embed some model

I have a problem with mirage.
I am defining a serializer extending ActiveModelSerializer with serializeIds: 'always', in this case the model is serialized with the relations models ids.
But when i want include some model in the serialized content i use include: ['someModel'], in that case the someModel is included succesfully, but the ids of the rest of relationships are ignored.
How can i include the ids too?
Hm, I believe this is the default behavior. To override this you could call super on the Serializer's serialize method and ensure the ids are added to the payload:
serialize(post, request) {
let json = Serializer.prototype.serialize.apply(this, arguments);
json.post.commentIds = post.commentIds;
return json;
}

Inheritance of non model, core class in Odoo/OpenERP

In Odoo, I want to modify the addons/web/session/OpenERPSession class without modifying the core code. Meaning I want to subclass this class from my module so that the system will use my version of the OpenERPSession class instead of the core class. And specifically I want to alter only a method's implementation, and I do so by overriding it:
class ExtendedSession(session.OpenERPSession):
def model(self, model):
_logger = logging.getLogger(__name__)
_logger.info('OVERRIDEN ==================== OpenERPSession.model')
if self._db == False:
raise session.SessionExpiredException("Session expired")
return session.Model(self, model)
But unfortunately the 'OVERRIDEN ==================== OpenERPSession.model' statement is not print therefore the system does not call my implementation.
How can I instruct Odoo to use my implementation of the OpenERPSession?
Sorry for answering late...
For any non model class, you can inherit them by using full signature path of that class, for ex.
You can inherit session.OpenERPSession using the full path ...
class ExtendedSession(addons.web.sessions.OpenERPSession):
def model(self, model):
_logger = logging.getLogger(__name__)
_logger.info('OVERRIDEN ==================== OpenERPSession.model')
if self._db == False:
raise session.SessionExpiredException("Session expired")
return session.Model(self, model)
Try this......

See only owner's data in ListAPIView

I have a view deriving from ListAPIView, with the following permissions:
permission_classes = (permissions.IsAuthenticated, IsOwnerOrSuperuser, )
IsOwnerOrSuperuse is defined as such:
class IsOwnerOrSuperuser(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return obj.user == request.user or request.user.is_superuser
(which is very similar to the tutorial)
Now, when a normal user queries my view, it can see everyone's objects. Isn't the permission applied to every single object in the list? How can I enforce this type of behaviour with minimal overhead?
Thanks
No, has_object_permission is not applied to list and create endpoints, only retrieve, update and delete where there is only single instance. To filter lists, you should use get_queryset to filter the objects.
class BlogList(generics.ListAPIView):
serializer_class = BlogSerializer
permission_classes = (IsOwnerOrSuperuser,)
def get_queryset(self):
user = self.request.user
return Blog.objects.filter(user=user)
To apply further permissions, you need to implement .has_permission(self, request, view)....

Serialize and deserialize

I have an active record class with an embedded sample:
class LabResults < ActiveRecord::Base
serialize :sample
end
class Sample
attr_accessor :values # GSL::Vector of responses
def to_yaml
YAML.quick_emit( self, opts ) { |out|
out.map( "!testfile,2012-02-27" ) { |map|
#values.map{|v| v.to_a }
}
}
end
def analyze; end; # do stuff with values
end
I want to serialize and store sample in the database, but GSL::Vector (from gsl gem), does not have a to_yaml method. Defining to_yaml and YAML.quick_emit for Sample is apparently deprecated when using Rails 3.2's default YAML engine Psych.
Any ideas how to serialize and de-serialize this object?
You can write a custom (de)serializer for the column, and pass it as the second argument to "serialize", e.g.:
serialize :sample, SampleSerializer.new
Where SampleSerializer is a class that defines "load" and "dump" methods.
More detail in this answer: ActiveRecord serialize using JSON instead of YAML