I am using Flask-Marshmallow and Flask-SQLAlchemy to dump data to an API.
I have an option in the API to dump specific fields in the response:
schema = UserSchema()
schema.dump(data, only=fields)
I get an error if one of the field names is derived if it is plucked using the data_key feature:
ValueError: Invalid fields for <UserSchema()>: {'account_name'}.
CLARIFICATION: To correctly filter this field the client has to pass accountname to return account_name. This is undesirable since the client is not exposed to the internal implementation.
Example Code:
class User(db.Model):
__tablename__ = 'users'
user_id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String)
account = db.relationship('Account', backref='user')
class Account(db.Model):
__tablename__ = 'accounts'
account_id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer)
accountname = db.Column(db.String)
class UserSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = User
include_fk = True
sqla_session = db.session
account = ma.Pluck('AccountSchema', 'accountname', data_key='account_name')
class AccountSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Account
include_fk = True
sqla_session = db.session
Is there a trick to get Marshmallow to dump based on the data_key?
UPDATE: I am currently solving this with a brute force method by decorating the controller functions with a mapper. But, it would be nice if Marshmallow could handle this?
I guess the issue here is that you pass user data (name of keys from client side) into method from object world (dump) that is not meant to.
I don't think it would work even if there was no Pluck field involved, would it?
You need a layer translating user field selection into object world (field names).
This is the mapping you're using. Maybe you could generate the mapping automatically by introspecting the schemas. This can be done in a base schema class.
Related
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"
...
I have a model with an id and a label:
class Widget(models.Model):
label = models.TextField()
And I want to serialize the label and id, so I have a serializer like this:
class WidgetSerializer(serializers.ModelSerializer):
class Meta:
model = Widget
fields = ('id', 'label')
This gives me a serialization like {"id": 61, "label": "Super Widget"}. However, I need to give the serialization different labels (e.g. {"widget_id": 61, "widget_name": "Super Widget"}) without changing the model since it's used in different places. Is there a way to tell the serializer to serialize a field using a different label?
I think you can’t achieve that with "serializers.ModelSerializer" but you can achieve it with "serializers.Serializer".
Try something like this:
class CommentSerializer(serializers.Serializer):
id = serializers.IntegerField(label="widget_id")
label = serializers.CharField(label="widget_name")
For more information: http://www.django-rest-framework.org/api-guide/fields/#serializer-fields
And for some examples: http://hackersome.com/p/jezdez/django-serializers
I'm having some trouble to understand how to get field values from another model.
I added a custom field in res.partner module by making a custom module:
class custom_partner_fields(osv.osv):
_inherit = 'res.partner'
_columns = {
'RTN': fields.char('RTN Numerico'),
}
_defaults = {
}
custom_partner_fields()
Then I create a custom xml for the form view when creating a new customer and now I can see RTN field in the customer create form.
Now I want this new field to appear when making a new quotation/sale order.
I would like it to get its value when I select my customer (I believe onchange function should be use but don't know how to use that!),so what I did was create a custom module for it:
class custom_saleorder_fields(osv.osv):
_inherits = 'sale.order'
_columns = {
'partner_rtn': fields.char('RTN'),
}
custom_saleorder_fields()
I believe I need to use something like a function or relational field for this but what I've tried hasn't worked yet.
Then, I created the custom view form the sale order form view and adds my partner_field.
Now, I would like to know how can I access the specific RTN value from res.partner module from custom_saleorder_fields based on the selected customer.
On the other hand, the main purpose of this new value is to displayed in the sale workflow and also print it in a report.
You need to add relational field in sale order model.
Before applying code you should refer that documentation of odoo,
In the Odoo Field Doc you will know how fields.related works.
class custom_saleorder_fields(osv.osv):
_inherits = 'sale.order'
_columns = {
'partner_rtn': fields.related('partner_id','RTN',type="char",relation="res.partner",string="RTN",store=True,readonly=True),
}
custom_saleorder_fields()
bring modelA fields in modelB by relatiional fields
eg use many2one field in another model :
from openerp import models, fields, api
class partsproviderclass(models.Model):
_name='partsprovider.vechicle'
#_rec_name='parts_provider'
id=fields.Integer()
parts_provider=fields.Many2many('supplier.car', string="Parts provider")
parts_name=fields.Many2many('selection.selection',string="Parts Name")
parts_price=fields.Float(string="Price of the Part")
class selectionsxample(models.Model):
_name='selection.selection'
name=fields.Char('name',required=True)
value=fields.Char('value',required=True)
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.
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)....