Filter Data in Serialization Django - python

i have serialization class like this
class MenuSerializer(serializers.ModelSerializer):
data = Menu.objects.raw('''SELECT menu_menu.*, menu_permission.role_id FROM menu_menu
JOIN menu_permission ON menu_menu.id = menu_permission.menu_id
WHERE sub_menu_id IS NULL
ORDER BY menu_menu.id ASC''')
subItems = SubMenuSerializer(data=data,many=True)
class Meta:
model = Menu
fields = ('id',
'label',
'icon',
'link',
'isTitle',
'isMenuCollapse',
'subItems')
how to filter subItems based request header

This is not the way to go.
Serializers serialize data but don't extract it, you "choose" the data to be shown in the controller ( viewset, in DRF lang ).
Check this https://www.django-rest-framework.org/api-guide/viewsets/
Anyway, you can access the request information using the method "save" in the serializer, it is in the var named "context". You can find it in the documentation too.

Related

Django Rest Framework: Create / Update by id, output dictionary

I have a django-rest-framework model viewset (for tests) that is using a serialize like this:
class ProcessSerializer(serializers.Serializer):
class Meta:
model = Process.draft_model
fields = ['id', 'name']
class TestSerializer(serializers.ModelSerializer):
process = ProcessSerializer()
class Meta:
model = ConfigurationTest
fields = [
'id',
'name',
'process',
]
This works great when retrieving tests, but doesn't work for creation / updates, where I would ideally like to just provide the ID with a request like this:
{
process: 1
name: 'A new test'
}
When sending that request to the server I get an error like Invalid data. Expected a dictionary, but got int
What I tried:
Have process and process_id as included fields and just sending process_id in a POST request. In that case I get an error like process_id cannot be null, which is plain confusing.
Use a different serializer for the create action, that uses process as a plain PrimaryKeyRelatedField without a nested serializer. This works nicely for getting the request, but also obviously means the server reponse to that POST request doesn't include the nicely nested serializer.
Models for reference
class ConfigurationTest(...):
name = CharField(max_length=120)
process = ForeignKey(Process)
class Process(...):
name = CharField(max_length=120)
I would give a serializer like this. One serializer field for read_only where it uses ProcessSerializer and process_id for write_only as integer.
class TestSerializer(serializers.ModelSerializer):
process = ProcessSerializer(read_only=True)
process_id = IntegerField(write_only=True)
class Meta:
model = ConfigurationTest
fields = [
'id',
'name',
'process',
'process_id',
]
And POST this:
{
process_id: 1
name: 'A new test'
}
I am not 100% sure you don't need to override create/update but this should work fine.
N.B.:
I see that you tried something with similar logic. Was it the same code though ?

DRF deserializing data from API to django model

I'm using serializers of django-rest-framework (DRK)
I'm fetch data from an external API, and I want to convert this datas into an internal model (here Period)
The thing is that the field's format of the external api are like this :
{"DateFrom": "2020-02-10T00:00:00"}
I want to rename into "date_from" field.
Here what I tried :
Serializer :
class PeriodSerializer(serializers.ModelSerializer):
date_from = serializers.DateTimeField(write_only=True, source='DateFrom')
class Meta:
model = Period
fields = ('date_from',)
Notice that I tried with "write_only=True, source='DateFrom'"
And then in my code :
json = {"DateFrom": "2020-02-10T00:00:00"}
serializer = PeriodSerializer(data=json)
serializer.is_valid() # This is returning False
print(serializer.errors)
And then the output is :
{'date_from': [ErrorDetail(string='This field is required.', code='required')]}
How handle that ? (in the best way (good practice))
I think you have something backwards here. Given your model and what you expose in your API, you would want to do:
class PeriodSerializer(serializers.ModelSerializer):
DateFrom = serializers.DateTimeField(write_only=True, source='date_from')
source specifies the data source on the model, while the name of the serializer field is what the field will be named when serialized.

How to add link fields to serializer dynamically

I would like to create a general serializer for a ManyToMany link, which will include linked models data.
from rest_framework import serializers
def get_model_serializer(model, fields_to_serialize):
class BaseSerializer(serializers.ModelSerializer):
class Meta:
model = model
fields = fields_to_serialize
return BaseSerializer
def get_many_to_many_serializer(many_to_many_model, first_model, second_model, fields_to_serialize)
serializer = get_model_serializer(many_to_many_model, fields_to_serialize)
class BaseSerializer(serializer):
pass
# only when I directly set the attributes upon class definition it works
#attendee = get_model_serializer(first_model)()
#operation = get_model_serializer(second_model)()
# This does not work
setattr(BaseSerializer, first_model.__name__.lower(), get_model_serializer(first_model)())
setattr(BaseSerializer, second_model.__name__.lower(), get_model_serializer(second_model)())
#Or this
#setattr(BaseSerializer, 'operation', get_model_serializer(first_model)())
#setattr(BaseSerializer, 'attendee', get_model_serializer(second_model)())
return BaseSerializer
The problem is, that when I set the attribute using setattr, the linked models are not serialized. I guess there is some magic upon class creation or whatever?
Any ideas, how can I go around this?
You can use the three argument form of type to dynamically create new types/classes
type('M2MSerializer', (serializer, ), {
first_model.__name__.lower(): get_model_serializer(first_model)(),
first_model.__name__.lower(): get_model_serializer(first_model)()
})

Update multiple fields on Google NDB entity

Working with Google App Engine for Python, I am trying to create and then update an ndb entity. To update a single property, you can just access the property using a dot, e.g.
post.body = body
But I would like to know if there is a simple way to update multiple fields within an ndb entity. The following code:
class Create(Handler):
def post(self):
## code to get params
post = Post(author = author,
title = title,
body = body)
post.put()
class Update(Handler):
def post(self, post_id):
post = post.get_by_id(int(post_id))
fields = ['author', 'title', 'body']
data = get_params(self.request, fields)
for field in fields:
post[field] = data[field]
post.put()
The "Create" handler works fine, but the "Update" handler results in:
TypeError: 'Post' object does not support item assignment
So it seems I would need to access the properties using a dot, but that is not going to work when I have a list of properties I want to access.
Can someone provide an alternative way to update multiple properties of an NDB entity after it has been created?
You should use setattr.
for field in fields:
setattr(post, field, data[field])
(Note that GAE objects do actually provide a hidden way of updating them via a dict, but you should use the public interface.)
You can use the populate method:
post.populate(**data)

Django/Tastypie - DELETE requests deleting everything

I have the following model
class Open(models.Model):
name=models.TextField()
opened=models.DateTimeField(auto_now_add=True)
user=models.ForeignKey(User)
and the following resources
class UserResource(ModelResource):
class Meta:
queryset = User.objects.all()
resource_name = 'user'
class OpenResource(ModelResource):
user = fields.ForeignKey(UserResource,'user')
class Meta:
queryset = Open.objects.all()
resource_name = 'open'
I'm trying to delete an Open object from some User's open_set.
For posting, I use the following code (using Requests):
content={"name":file_path,
"user":"/api/v1/user/2/"}
requests.post(
url='http://localhost:8000/api/v1/open/',
data=json.dumps(content),
headers={'content-type':'application/json'},
)
which works perfectly and does exactly what I want.
However, when trying to use similar code for deleting:
content={"name":file_path,
"user":"/api/v1/user/2/"}
requests.delete(
url='http://localhost:8000/api/v1/open/',
data=json.dumps(content),
headers={'content-type':'application/json'},
)
it just deletes all the Open objects from that user (in this case, user with id=2), instead of deleting only the Open objects whose "name" is file_path and whose "user" is "/api/vi/user/2/"
What am I missing?
Distinction between list and detail.
The RESTful methods are split into two kinds:
detail (for GET, PUT and DELETE):
/api/v1/objects/1/
and list (for GET, PUT and DELETE):
/api/v1/objects/
POST and PATCH are bit different.
Means that DELETE /api/v1/objects/ will remove all objects.
To delete one object you have to provide path with id:
DELETE /api/v1/objects/1/
Link to documentation
How filtering works in Tastypie:
You cannot just add things to content and wish to be picked up by Tastypie. All not meant to be there information will be ignored by Tastypie.
If you want to filter your list use queryset parameters:
/api/v1/objects/?name=asdfasdf&user=2
And allow filtering of these:
from tastypie.constants import ALL, ALL_WITH_RELATIONS
class Open(models.Model):
name=models.TextField()
opened=models.DateTimeField(auto_now_add=True)
user=models.ForeignKey(User)
filtering = {'name': ALL, 'user': ALL_WITH_RELATIONS}
After these changes you will be able to delete set of objects:
DELETE /api/v1/objects/?name=asdfasdf&user=5
Link to documentation
Edit:
So your call will look like this:
import urllib
content={"name":file_path,
"user":"/api/v1/user/2/"} # If doesn't work change '/api/v1/user/2/' into 2 I am not sure about this
url = 'http://localhost:8000/api/v1/open/?' + urllib.urlencode(content)
requests.delete(
url=url,
data=None,
headers={'content-type':'application/json'},
)

Categories