Django Rest fields grouping and customising - python

I have a Django models like so:
class Floor(models.Model):
name = models.CharField(max_lenght =100)
class Point(models.Model):
created_at = fields.UnixDateTimeField(auto_now_add=True)
updated_at = fields.UnixDateTimeField(auto_now=True)
floor = models.ForeignKey(Floor)
device = models.CharField(max_lenght=100)
creates_at and updated_at are just custom fields with timestamps.
So, i need to send request like points/?start=x,end=y,timeslice=z, where x -
is start timestamp, y - end timestamp, z - timeslice in this period. For example, if x is start of the day, and y is and of the day, and z is 3600? i will have 24 slices and want to have JSON like so:
{
floor_id: floor_id
slice: first timestamp of first slice
count: count of devices in this slice
},
{
floor_id: floor_id
slice: first timestamp of second slice
count: count of devices in this slice
},
...
Propably, i need to customise my serializers, using django-filters and write spetial view for this purpose, but i have no ideas how to put it together
UPD: Ok, i customise my serializer for Floor model, and now it looks like:
class FloorWithTPCountSerializer(serializers.ModelSerializer):
class Meta:
model = Floor
fields = ('id', 'count')
count = serializers.SerializerMethodField('get_tp_count')
def get_tp_count(self, obj):
return obj.trackpoint_set.values('tag').distinct().count()
And no i recive JSON like:
{
"id": 28,
"count": 3
},
{
"id": 35,
"count": 1
},
I can suggest, that i need to get querystring params in this serialize class and declarate a method for counting points within timeslice. So, how can i get querydict in serializer class?

Ok, as i suggested early the key was in the serializer customisation. I have to declarete custom method for needed JSON structure.
Here is my solution:
class FloorWithTPCountSerializer(serializers.ModelSerializer):
class Meta:
model = Floor
fields = ('id', 'results')
results = serializers.SerializerMethodField('get_tp_start')
def get_tp_start(self, obj):
query_params = self.context.get("request").query_params
aggregations = {'hour':3600, 'day':86400, 'week':604800}
if 'start' in query_params and 'end' in query_params:
st_tm = int(query_params.get('start'))
en_tm = int(query_params.get('end'))
if 'aggregation' in query_params:
aggregation_value = query_params.get('aggregation')
aggregation = aggregations.get(aggregation_value)
else:
aggregation = en_tm - st_tm
trackpoint_set = obj.trackpoint_set
st = [{'count': trackpoint_set.filter(created_at__gte=ts, created_at__lt=ts + aggregation).values(
'tag').distinct().count(), 'timestamp': ts} for ts in range(st_tm, en_tm, aggregation)]
else:
st = None
return st
Ofcourse, it still lacks some checks by querstring consistency, but i can fetch JSON form, as i needed.
For example, requesting
tpfloors/?start=1496188800&end=1496275199&aggregation=day
i can get something like this:
{
"id": 49,
"results": [
{
"count": 3,
"timestamp": 1496188800
}
]
},
Best regards.

Related

How can I define custom output types for mutations with graphene-django?

Create/remove/update/delete (CRUD) mutations usually return the corresponding database model instance as output type of the mutation. However for non-CRUD mutations I'd like to define business logic specific mutation output types. E.g. returning the count of list elements + a list of IDs which cannot be mapped 1-to-1 between graphql type and db models. How can I achieve this with graphene-django?
List not related to Models
As you want to return both a count and a list of elements, you can create a custom type:
class ListWithCountType(graphene.Scalar):
#staticmethod
def serialize(some_argument):
# make computation here
count = ...
some_list = ...
return { "count": count, "list": some_list }
Then on your mutation you use it like this:
class MyMutation(graphene.Mutation):
list_with_count = graphene.Field(ListWithCountType)
#classmethod
def mutate(cls, root, info, **kwargs):
some_argument = kwargs.pop("some_argument")
return cls(list_with_count=some_argument)
Add to your schema:
class Query(graphene.ObjectType):
my_mutation = MyMutation.Field()
Should return something like:
{
"data": {
"list_with_count": {
"count": <COUNT VALUE>,
"list": <SOME_LIST VALUE>
}
}
}
*PS: if this is only an output, ok. But if you want this type to be an argument, you should also implement "parse_literal" and "parse_value", besides the "serialize".
Here is an example with a custom ErrorType used with forms.
List related to Models
From the docs:
# cookbook/ingredients/schema.py
import graphene
from graphene_django.types import DjangoObjectType
from cookbook.ingredients.models import Category
class CategoryType(DjangoObjectType):
class Meta:
model = Category
class Query(object):
all_categories = graphene.List(CategoryType)
def resolve_all_categories(self, info, **kwargs):
return Category.objects.all()
On your schema:
import graphene
import cookbook.ingredients.schema
class Query(cookbook.ingredients.schema.Query, graphene.ObjectType):
pass
schema = graphene.Schema(query=Query)
Then you can query like:
query {
allCategories {
id
}
}
Should return something like:
{
"data": {
"allCategories": [
{
"id": "1",
},
{
"id": "2",
},
{
"id": "3",
},
{
"id": "4",
}
]
}
}
Here is an example with user model.

Place Primary key inside JSON response

The JSON response i'm getting is as below.
In my code i'am trying to fetch the list from the db, as per above img. In the result primary key is coming outside of the fields for each record. How can i place it inside the fields for every record like.
"results":[
"fields":{
"pk": "F09087687633",
"company_name": "Tata",
}
]
Below is my code:
views.py (In below code for loop is to remove app name from the results, same i use to remove primary key, it is working but how can i place it inside for each fields.)
#csrf_exempt
def fleet_owner_list(request):
page_number = json.loads(request.body)
records,total_pages = FleetOwner.get_all_owners(page_number)
for data in range(len(records)):
del records[data]['model']
returnObject = {
"page" : page_number,
"total_results":len(records),
"total_pages":total_pages,
"status" : messages.RETRIVAL_SUCCESS,
"message" : messages.FLEETOWNER_DATA_RETRIEVE_SUCCESS,
"results" : records
}
models.py
#classmethod
def get_all_owners(cls,page_number):
data = cls.objects.filter(is_deleted = False)
page_numbers = page_number
pegination_result, total_page_count = list_paginate(data, page_numbers)
data = serializers.serialize("json", pegination_result)
data = json.loads(data)
return data, total_page_count
paginator.py (This is a common function i'm using for all the list functions to perform django pagination.)
def list_paginate(data,page_numbers):
paginator = Paginator(data,10)
page = page_numbers
try :
records = paginator.page(page)
except PageNotAnInteger:
records = paginator.page(1)
except EmptyPage:
records = paginator.page(paginator.num_pages)
return records, paginator.num_pages
pegination_result, total_page_count = list_paginate(data, page_numbers)
data = serializers.serialize("json", pegination_result)
data = json.loads(data)
return data, total_page_count
By default this is how Django serialize a queryset into JSON Objects. The Django documentation also state the same. The whole collection is just represented as an array and the objects are represented by JSON objects with three properties: “pk”, “model” and “fields”. “fields” is again an object containing each field’s name and value as property and property-value respectively. i.e.,
[
{
"pk": "4b678b301dfd8a4e0dad910de3ae245b",
"model": "sessions.session",
"fields": {
"expire_date": "2013-01-16T08:16:59.844Z",
...
}
}
]
If you look at the Serializer class implementation you can find a get_dump_object method which is responsible for the final JSON output for an object. i.e.,
def get_dump_object(self, obj):
data = {'model': str(obj._meta)}
if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'):
data["pk"] = self._value_from_field(obj, obj._meta.pk)
data['fields'] = self._current
return data
In other teams this is the method responsible for the below format,
{
"pk": "pk",
"model": "model",
"fields": {
"field1": "2013-01-16T08:16:59.844Z",
...
}
}
Since you want the pk field inside the fields you should create your own JSON serializer class and override the get_dump_object method like the below one,
>>> import json
>>> from pprint import pprint
>>> from django.core.serializers.json import Serializer as JSONSerializer
>>>
>>> class MyCustomSerializer(JSONSerializer):
... def get_dump_object(self, obj):
... data = {'model': str(obj._meta)}
... data['fields'] = self._current
... if not self.use_natural_primary_keys or not hasattr(obj, 'natural_key'):
... data['fields'].update({'pk': self._value_from_field(obj, obj._meta.pk)})
... return data
...
>>> pprint(json.loads(MyCustomSerializer().serialize(User.objects.all())))
[{'fields': {'date_joined': '2019-07-13T05:52:37.885Z',
'email': 'user1#gmail.com',
'first_name': 'User',
'groups': [],
'is_active': True,
'is_staff': False,
'is_superuser': False,
'last_login': None,
'last_name': '1',
'password': '',
'pk': 1,
'user_permissions': [],
'username': ''},
'model': 'auth.user'}]
# You can see that `pk` is now inside the 'fields' key.
>>> json.loads(MyCustomSerializer().serialize(User.objects.all()))[0]['fields']['pk']
1

I need query to return all document that contains a specific string in list

I need to perform a django query to return all document that contains a specific string "amazon" in the list, and this list is stored in a dict.
The field is a dict and each key in the dictionary contain a list type.
This sample of my document:
{
"collector_id" : 80,
"market_info" : {
"market_name_list" : [
"amazon",
"souq"
],
"key_2" : [
"item1_in_list",
"item2_in_list",
"item3_in_list"
]
}
}
market_info is DictField.
I tried a lot of queries but all of them failed.
query_set = myModel.objects.filter(market_info__contains=
{'market_name_list': ['amazon']})
This is my Model:
from djongo import models as db
class info(db.Model):
collector = db.ForeignKey(Collector, null=True,
on_delete=db.SET_NULL)
market_info = db.DictField(default={'market_name_list': [],
'search_for_list': []
})
found = []
for i in info.objects.all():
for m in i.market_info:
if market_info[m] == 'string':
found.append(i)

How to apply an extra filter to nested serializer with Django REST Framework?

I am building a serializer that has to deal with a nested many-to-many field. I got the chain of serializers set up, but it doesn't quite do what I need. It creates JSON that looks something like this:
"course_section": {
"id": 6,
"periods": [
{
"id": 3,
"coursemeet_set": [
{
"id": 4,
"period": 3,
"course_section": 9,
},
{
"id": 8,
"period": 3,
"course_section": 6
},
{
"id": 9,
"period": 3,
"course_section": 6
},
],
"name": "Second Period"
}
}
}
This is what I'd expect Django REST Framework to output - all CourseMeets that match period 3 are displayed. However, in the case of this JSON output above, the course_section in which the set is nested has the ID of 6, so I'd only need the coursemeets that have "course_section": 6 inside of them.
I spent some time with this question, and I was able to create a custom ListSerializer that almost does what I want:
class FilteredCourseMeetSerializer(serializers.ListSerializer):
def to_representation(self, data):
data = data.filter(period=3, course_section=6)
return super(FilteredCourseMeetSerializer, self).to_representation(data)
class CourseMeetSerializer(serializers.ModelSerializer):
class Meta:
model = CourseMeet
list_serializer_class = FilteredCourseMeetSerializer
class PeriodSerializer(serializers.ModelSerializer):
coursemeet_set = CourseMeetSerializer(many=True)
class Meta:
model = Period
class SectionSerializer(serializers.ModelSerializer):
periods = PeriodSerializer(many=True)
class Meta:
model = CourseSection
But, as you can see, I manually add the period and course_section filters to data.filter, and I would need to be able to pass those based on the id in SectionSerializer as well as the ID in PeriodSerializer.
How do I achieve this? Am I even on the right track with a ListSerializer or is there a simpler way to do this?

JSON to model a class using Django

I'm trying to get a JSON object like:
{
"username": "clelio",
"name": "Clelio de Paula",
}
and transform it in:
class User(models.Model):
name = models.CharField(max_length=30)
username = models.CharField(max_length=20)
def jsonToClass(s):
aux = json.dumps(s, self)
self.name = aux['name']
self.id = aux['id']
So I tried to use the simplejson and one method called jsonToClass():
>>> import simplejson as json
>>> u1 = User()
>>> u1.jsonToClass(face)
>>> u1.save()
This doesn't work. What is the easiest method to do what I want?
You probably want to look at Django's (de)serialization framework. Given JSON like:
[
{
"model": "myapp.user",
"pk": "89900",
"fields": {
"name": "Clelio de Paula"
}
}
]
you can save it like this:
from django.core import serializers
for deserialized_object in serializers.deserialize("json", data):
deserialized_object.save()
Note that I believe you have to use the Django serialization format to use this method, so you might have to adjust your JSON accordingly.
I just realized that
{
"username": "clelio",
"name": "Clelio de Paula",
}
is a dict() object.
So, this is easiest than I thought.
What I need to solve is just
def jsonToClass(self, aux):
self.name = aux['name']
self.username = aux['username']
that's it.
This is simply achieved as follows:
data = {
"username": "stackExchange",
"name": "stack overflow"
}
for obj in serializers.deserialize("json", data):
do_something_with(obj)
Check out django docs for details

Categories