Serialize complex fields in django rest framework - python

I have a model that has a JSON field extra_data that contains other fields that might be added to the model. From the beginning it is not known how many fields will be added apart from the compulsory ones, which is why I introduced the extra_data field.
With the usual rest framework serialization I currently have something like this:
[
{
"code": "1",
"name": "Moscow",
"extra_data": {
"type": "Region"
}
},
{
"code": "2",
"name": "Tatarstan",
"extra_data": {
"type": "Republic",
"capital": "Kazan"
}
}
]
But what I need something is like this:
[
{
"code": "1",
"name": "Moscow",
"type": "City"
},
{
"code": "2",
"name": "Tatarstan",
"type": "Republic",
"capital": "Kazan"
}
]
Please I need help, I'm new to django

The serializer itself I don't think can do this, since you do not know how many fields are there. But once you get the serializer.data, you may update your dict like:
serializer_data = serializer.data
extra_data = serializer_data.pop('extra_data')
serializer_data.update(extra_data)
return serializer_data
I'm no expert on Django so I'm not telling you for sure that there is no way of doing that withing the serializer, but none that I can think of

Related

Python + ElasticSearch: Mapper Parsing Exceptions for join field

I'm using ElasticSearch 8.3.2 to store some data I have. The data consists of metabolites and several "studies" for each metabolite, with each study in turn containing concentration values. I am also using the Python ElasticSearch client to communicate with the backend, which works fine.
To associate metabolites with studies, I was considering using a join field as described here.
I have defined this index mapping:
INDEXMAPPING_MET = {
"mappings": {
"properties": {
"id": {"type": "keyword"},
"entry_type": {"type": "text"},
"pc_relation": {
"type": "join",
"relations": {
"metabolite": "study"
}
},
"concentration": {
"type": "nested",
}
}
}
}
pc_relation is the join field here, with metabolites being the parent documents of each study document.
I can create metabolite entries (the parent documents) just fine using the Python client, for example
self.client.index(index="metabolitesv2", id=metabolite, body=json.dumps({
#[... some other fields here]
"pc_relation": {
"name": "metabolite",
},
}))
However, once I try adding child documents, I get a mapping_parser_exception. Notably, I only get this exception when trying to add the pc_relation field, any other fields work just fine and I can create documents if I omit the join field. Here is an example for a study document I am trying to create (on the same index):
self.client.index(index="metabolitesv2", id=study, body=json.dumps({
#[... some other fields here]
"pc_relation": {
"name": "study",
"parent": metabolite_id
},
}))
At first I thought there might be some typing issues, but casting everything to a string sadly does not change the outcome. I would really appreciate any help with regards to where the error could be as I am not really sure what the issue is - From what I can tell from the official ES documentation and other Python+ES projects I am not really doing anything differently.
Tried: Creating an index with a join field, creating a parent document, creating a child document with a join relation to the parent.
Expectation: Documents get created and can be queried using has_child or has_parent tags.
Result: MappingParserException when trying to create the child document
Tldr;
You need to provide a routing value at indexing time for the child document.
The routing value is mandatory because parent and child documents must be indexed on the same shard
By default the routing value of a document is its _id, so in practice you need to provide the _id of the parent document when indexing the child.
Solution
self.client.index(index="metabolitesv2", id=study, routing=metabolite, body=json.dumps({
#[... some other fields here]
"pc_relation": {
"name": "study",
"parent": metabolite_id
},
}))
To reproduce
PUT 75224800
{
"settings": {
"number_of_shards": 4
},
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"pc_relation": {
"type": "join",
"relations": {
"metabolite": "study"
}
}
}
}
}
PUT 75224800/_doc/1
{
"id": "1",
"pc_relation": "metabolite"
}
# No routing Id this is going to fail
PUT 75224800/_doc/2
{
"id": 2,
"pc_relation":{
"name": "study",
"parent": "1"
}
}
PUT 75224800/_doc/3
{
"id": "3",
"pc_relation": "metabolite"
}
PUT 75224800/_doc/4?routing=3
{
"id": 2,
"pc_relation":{
"name": "study",
"parent": "3"
}
}

Troubles with validating nested values

I am looking to add validation to my api endpoint using Marshmallow.
I am running into the problem of how to get this chunk properly validated. The end goal is to make sure impressions is a positive number.
I would greatly appreciate any help or insight you can provide. First time using Marshmallow.
Sample Json:
{
"mode": [
{
"type": "String",
"values": {
"visits": 1000,
"budget": 400
},
"active": true
}
]
}
Sample code attempting to validate
class ValidateValues(BaseSchema):
visits = fields.Int(allow_none=True, validate=[validate.Range(min=0, error="Value must be greater than 0")])
budget = fields.Int(allow_none=True, validate=[validate.Range(min=0, error="Value must be greater than 0")])
class ModeSchema(BaseSchema):
type = fields.String(required=True)
active = fields.Boolean(required=True)
values = fields.Nested(ValidateValues)
class JsonSchema(BaseSchema):
mode = fields.List(fields.Dict(fields.Nested(ModeSchema, many=True)))
Current result
{
"mode": {
"0": {
"type": {
"key": [
"Invalid type."
]
},
"values": {
"key": [
"Invalid type."
]
},
"active": {
"key": [
"Invalid type."
]
}
}
}
}
You're just using a list of Nested fields. No need for Dict, here.
And no need for many=True since you're putting the Nested field in a List field.
Try this:
class JsonSchema(BaseSchema):
mode = fields.List(fields.Nested(ModeSchema))

mongodb data retrieval from nested fields using flask pymongo

I'm passing id and customer id fields as parameters to get the document. With my below code I'm only able to fetch only those fields of a document. How do I get entire document with multiple fields as parameter?
Code:
#reviews.route('/<inp_id>/<cat_id>', methods=['GET'])
def index(inp_id, cat_id):
my_coln = mongo_connection.db.db_name
document = collection.find_one({'id': inp_id}, {'category.id': cat_id})
Result:
{
"category": {
"id": "13"
},
"_id": "5cdd36cd8a348e81d8995d3b"
}
I want:
{
"customer": {
"id": "1",
"name": "Kit Data"
},
"category": {
"id": "13",
"name": "TrainKit"
},
"review_date": "2019-05-06",
"phrases": null,
.....
}
Pass all your filters in the first dict, the second one is for projection.
document = collection.find_one({'id': inp_id, 'category.id': cat_id})
Your original query, collection.find_one({'id': inp_id}, {'category.id': cat_id}) means give me only category.id (and nothing else (well, apart from _id which is returned by default)) of a document in which the value of id equals inp_id.

Django REST Framework: Nested relationships only on read

I have a few related models and I wanted to have related object information nested into the read so I followed the example outlined here:
http://www.django-rest-framework.org/api-guide/relations/#nested-relationships
A couple of my serializers look like this:
class ShowSerializer(serializers.ModelSerializer):
artists = ArtistSerializer(many=True)
venue = VenueSerializer()
class Meta:
model = Show
class GuestSerializer(serializers.ModelSerializer):
show = serializers.RelatedField()
class Meta:
model = Guest
And when I read them (GET), they're perfect, the output is something like:
"results": [
{
"id": 1,
"show": {
"id": 2,
"artists": [
{
"id": 13,
"name": "Some Artist"
}
],
"venue": {
"id": 3,
"name": "Some Venue",
},
"date": "2015-11-20"
},
"first_name": "Amanda",
"last_name": "Hugankiss",
}
]
The problem I'm having is when I POST, this configuration seems to expect an entire nested structure just as above, where I'd prefer to specify an id for a relationship on POST, like so:
{
"show": 16,
"first_name": "Maya",
"last_name": "Normusbutt",
}
Is there a way to configure my serializers/view (by the way, I'm using ModelViewSet) to do this?

Where to change the form of json response in django rest framework?

Lets say I have a model:
class MyModel(models.Model):
name = models.CharField(max_length=100)
description= models.TextField()
...
Then I created ModelViewSet with HyperLinkedSerializer, so when I call my /api/mymodels endpint I get responses like this:
{
"count": 2,
"next": null,
"previous": null,
"results": [
{ "name": "somename", "description": "desc"},
{ "name": "someothername", "description": "asdasd"},
]
}
and when I call /api/mymodels/1 I get:
{ "name": "somename", "description": "asdasd"}
but what I would like to get is:
{
"metadata":{ ...},
"results": { "name": "somename", "description": "desc"}
}
And I would like to use this format for all models at my website, so I dont want to change every viewset, I want to implement it in (most likely) one class and then use it for all my viewsets.
So my question is: which renderer or serializer or other class (Im really not sure) should I alter or create to get this behavior of json response?
The first response appears to be a paginated response, which is determined by the pagination serializer. You can create a custom pagination serializer that will use a custom format. You are looking for something similar to the following:
class MetadataSerialier(pagination.BasePaginationSerializer):
count = serializers.Field(source='paginator.count')
next = NextPageField(source='*')
previous = PreviousPageField(source='*')
class CustomPaginationSerializer(pagination.BasePaginationSerializer):
metadata = MetadataSerializer(source='*')
This should give you an output similar to the following:
{
"metadata": {
"count": 2,
"next": null,
"previous": null
},
"results": [
{ "name": "somename", "description": "desc"},
{ "name": "someothername", "description": "asdasd"}
]
}
The pagination serializer can be set globally through your settings, as described in the documentation.
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_SERIALIZER_CLASS': {
'full.path.to.CustomPaginationSerializer',
}
}

Categories

Resources