Django rest - convert field into a list? - python

I have a Textfield in my model which stores dictionaries, I would like to convert this field into a dictionary if possible in a rest serializer.
at the moment the field returns the dictionary but backslashes all the quotes, it is possible to convert the strings into a nested list of dicts?
Thanks
api currently returns below:
{
"id": 3,
"hostname": "WAN-EDGE",
"timestamp": "2019-04-12T11:34:36.654521",
"routing_table": "[{\"route\": \"0.0.0.0\", \"subnet_mask\": \"0.0.0.0\", \"next_hop\": \"172.16.66.193\"}, {\"route\": \"10.10.21.0\", \"subnet_mask\": \"255.255.255.0\", \"next_hop\": \"172.16.67.146\"}, {\"route\": \"10.22.0.0\", \"subnet_mask\": \"255.255.0.0\", \"next_hop\": \"172.18.1.5\"}, {\"route\": \"10.31.0.0\", \"subnet_mask\": \"255.255.0.0\", \"next_hop\": \"172.16.67.146\"},...]"
},...
}
desired result a nested list of dicts
{
"id": 3,
"hostname": "WAN-EDGE",
"timestamp": "2019-04-12T11:34:36.654521",
"routing_table": [
{
"route": "0.0.0.0",
"subnet_mask": "0.0.0.0",
"next_hop": "172.16.66.193"
},
{
"route": "10.10.21.0",
"subnet_mask": "255.255.255.0",
"next_hop": "172.16.67.146"
},
{
"route": "10.22.0.0",
"subnet_mask": "255.255.0.0",
"next_hop": "172.18.1.5"
},
{
"route": "10.31.0.0",
"subnet_mask": "255.255.0.0",
"next_hop": "172.16.67.146"
},...
]
},...
}
Current serialiser:
class RoutingTableSerializer(serializers.ModelSerializer):
hostname = serializers.ReadOnlyField(
source='device.hostname',
)
rt = serializers.JSONField(
source='routing_table'
)
class Meta:
model = DeviceData
fields = ('id','hostname','timestamp','rt')

You may need serializers.JSONField()
Update-1
You could also try with SerializerMethodField() as
import json
class RoutingTableSerializer(serializers.ModelSerializer):
hostname = serializers.ReadOnlyField(source='device.hostname', )
rt = serializers.SerializerMethodField(source='routing_table', read_only=True)
def get_routing_table(self, instance):
return json.loads(instance.routing_table)
class Meta:
model = DeviceData
fields = ('id', 'hostname', 'timestamp', 'rt')

Related

search/filter with list of string graphene-django

I am looking to search for list of different characters/string using graphene-django with single search query run.
class Query(graphene.ObjectType):
candidateInfo = graphene.List(CandidateType, search=graphene.String(),
first=graphene.Int(),
skip=graphene.Int(), last=graphene.Int(),)
def resolve_candidateInfo(self, info, search=None, first=None, last=None, skip=None,
**kwargs):
qs = Candidate.objects.all()
if search:
filter = (
Q(candidateName__icontains=search)|
Q(candidateEmail__icontains=search)|
Q(candidateSkills__icontains=search)
)
qs = qs.filter(filter)
return qs
Here the candidateName, candidateSkills, candidateEmail are in Candidate class with models.CharField
With a single string/character search i am getting correct output. But it fails with list of sting/characters.
Edited: Adding json sample:
[
{
"model": "api.candidate",
"pk": 1,
"fields": {
"candidateName" : "Jack",
"candidateEmail" : "Jack#gmail.com",
"candidateSkills" : ["machine learning", "Artificial Intelligence"]
}
},
{
"model": "api.candidate",
"pk": 2,
"fields":{
"candidateName" : "John",
"candidateEmail" : "John#gmail.com",
"candidateSkills" : ["python", "machine learning"]
}
},
{
"model": "api.candidate",
"pk": 3,
"fields":{
"candidateName" : "Smith",
"candidateEmail" : "Smith#gmail.com",
"candidateSkills" : ["python"]
}
}
]
If query goes in:
query{
candidateInfo(search: "python")
{
candidateName
candidateEmail
}
}
# output must contain data of John and Smith (from sample json)
Also if query is
query{
candidateInfo(search: ["python","artificial intelligence"])
{
candidateName
candidateEmail
}
}
#output must contain data of Jack, John and smith
Adding models of candidate
from django.db import models
class Candidate(models.Model):
candidateName = models.CharField(max_length=100)
candidateEmail = models.CharField(max_length=100)
candidateSkills = models.CharField(max_length=100)
def __str__(self):
return self.candidateSkills
You can run a for loop as such:
qs = Candidate.objects.all()
if search:
if type(search) == list:
qs_l = []
for search_item in search:
filter = (
Q(candidateName__icontains=search_item)|
Q(candidateEmail__icontains=search_item)|
Q(candidateSkills__icontains=search_item)
)
qs_l.append(qs.filter(filter))
qs = qs_l
else:
filter = (
Q(candidateName__icontains=search)|
Q(candidateEmail__icontains=search)|
Q(candidateSkills__icontains=search)
)
qs = qs.filter(filter)
return qs

Django ValueError: Cannot assign must be an instance

when i try to insert data to my database through manage.py shell, i got this error
ValueError: Cannot assign "'Ciawigebang'": "Desa.kecamatan" must be a "Kecamatan" instance.
this is my models.py
from django.db import models
class Kecamatan(models.Model):
kecamatan = models.CharField(max_length=30)
def __str__(self):
return self.kecamatan
class Desa(models.Model):
desa = models.CharField(max_length=30)
kecamatan = models.ForeignKey(Kecamatan, null=True, blank=True, on_delete=models.CASCADE)
def __str__(self):
return self.desa
here my json file snippet scrap.json
[
{
"Kecamatan": "Ciawigebang",
"Desa": [
"Ciawigebang",
"Ciawilor",
"Cigarukgak",
"Cihaur",
"Cihirup",
"Cijagamulya",
"Cikubangmulya",
"Ciomas",
"Ciputat",
"Dukuhdalem",
"Geresik",
"Kadurama",
"Kapandayan",
"Karangkamulya",
"Kramatmulya",
"Lebaksiuh",
"Mekarjaya",
"Padarama",
"Pajawanlor",
"Pamijahan",
"Pangkalan",
"Sidaraja",
"Sukadana",
"Sukaraja"
]
},{
"Kecamatan": "Cibeureum",
"Desa": [
"Cibeureum",
"Cimara",
"Kawungsari",
"Randusari",
"Sukadana",
"Sukarapih",
"Sumurwiru",
"Tarikolot"
]
},{
"Kecamatan": "Cibingbin",
"Desa": [
"BANTAR PANJANG",
"CIANGIR",
"Cibingbin",
"Cipondok",
"CISAAT",
"Citenjo",
"DUKUHBADAG",
"Sindang Jawa",
"SUKA MAJU",
"SUKAHARJA"
]
}...
]
this is the code i use to insert the data to database from python manage.py shell
import json
from apps.models import Desa, Kecamatan
with open('scrap.json') as f:
daerahs = json.load(f)
for daerah in daerahs:
for x in daerah['Desa']:
y=Desa(desa=x, kecamatan=daerah['Kecamatan'])
y.save()
how to solve this? there's so many similar question but i can't figure it out how to solve this problem in my case. Thanks for your help...
You need to create a Kecamatan instance. Change your code to this
for daerah in daerahs:
kecamatan, created = Kecamatan.objects.get_or_create(kecamatan=daerah['Kecamatan'])
for x in daerah['Desa']:
y = Desa(desa=x, kecamatan=kecamatan)
y.save()
follow this link to understand get_or_create
you need to use loaddata from your terminal python manage.py loaddata scrap.json
the format for the loaded data should be like this:
[
{
"pk": 1,
"model": "app.desa",
"fields": {
"kecamatan": 1,
"desa": "Ciawigebang"
},
{
"pk": 2,
"model": "app.desa",
"fields": {
"kecamatan": 1,
"desa": "Ciawigebang"
}
]
for every instance of Desa you provide a unique id and for the field kecamatan you use it's id since its a foriegn key (you need to populate the Kecamatan model with values, before populating the Desa models.)
meaning you should initially create another json file for Kecamatan, and load with with loaddata
[
{
"pk": 1,
"model": "app.kecamatan",
"fields": {
"kecamatan": "Ciawigebang",
},
{
"pk": "Cibeureum",
"model": "app.kecamatan",
"fields": {
"kecamatan": 1,
}
]
NOTE: app should be the name of your app that the Desa and Kecamatan models reside.

Saving a JSON list - Django

I am working with a JSON request.get that returns a list. I will like to save each individual object in the response to my models so I did this:
in views.py:
def save_ram_api(request):
r = requests.get('https://ramb.com/ciss-api/v1/')
# data = json.loads(r)
data = r.json()
for x in data:
title = x["title"]
ramyo_donotuse = x["ramyo"]
date = x["date"]
thumbnail = x["thumbnail"]
home_team_name = x["side1"]["name"]
away_team_name = x["side2"]["name"]
competition_name = x["tournament"]["name"]
ramAdd = ramSample.objects.create(title=title, ramyo_donotuse=ramyo_donotuse, date=date, thumbnail=thumbnail, home_team_name=home_team_name, away_team_name=away_team_name, competition_name=competition_name)
ramAdd.save()
return HttpResponse("Successfully submitted!")
This works fine except that it would only save the last objects on the list.
the JSON response list (as a random 60 objects at any time) would look something like:
[
{
"title": "AY - BasketMouth",
"ramyo": "AY de comedian"
"side1": {
"name": "Comedy Central",
"url": "https:\/\/www.rabithole.com\/laugh\/dave-chappel\/"
},
"side2": {
"name": "Basket Mouth",
"url": "https:\/\/www.rabithole.com\/laugh\/chris-rockie\/"
},
"tournament": {
"name": "Night of a thousand laugh",
"id": 15,
"url": "https:\/\/www.rabithole.com\/laugh\/chris-rockie\/"
},
"points": [
{
"nature": "Gentle",
"phrase": "Just stay"
},
{
"nature": "Sarcastic",
"phrase": "Help me"
}
]
},
{
"title": "Dave - Chris",
"ramyo": "Dave Chapelle"
"side1": {
"name": "Comedy Central",
"url": "https:\/\/www.rabithole.com\/laugh\/dave-chappel\/"
},
"side2": {
"name": "Chris Rockie",
"url": "https:\/\/www.rabithole.com\/laugh\/chris-rockie\/"
},
"tournament": {
"name": "Tickle me",
"id": 15,
"url": "https:\/\/www.rabithole.com\/laugh\/chris-rockie\/"
},
"points": [
{
"nature": "Rogue",
"phrase": "Just stay"
}
]
}
]
In this case my views.py will only save the last dictionary on the list, ignoring the other 59.
My question would be:
How do I get the views.py to save the entire objects on the list?
Notice that the "points" is also a list that contains one or more dictionaries, any help how to save this as well?
Your code is saving only the last object in the list because you are creating and saving the object outside of the loop. Try this,
def save_ram_api(request):
r = requests.get('https://ramb.com/ciss-api/v1/')
# data = json.loads(r)
data = r.json()
for x in data:
title = x["title"]
ramyo_donotuse = x["ramyo"]
date = x["date"]
thumbnail = x["thumbnail"]
home_team_name = x["side1"]["name"]
away_team_name = x["side2"]["name"]
competition_name = x["tournament"]["name"]
ramAdd = ramSample.objects.create(title=title, ramyo_donotuse=ramyo_donotuse, date=date, thumbnail=thumbnail, home_team_name=home_team_name, away_team_name=away_team_name, competition_name=competition_name)
ramAdd.save()
return HttpResponse("Successfully submitted!")
How do I get the views.py to save the entire objects on the list?
Notice that the "points" is also a list that contains one or more
dictionaries, any help how to save this as well?
Regarding your those questions
If you are using PostgreSQL as a database then you can use Django's built is JSONField and ArrayField for PostgreSQL database.
And if your database is not PostgreSQL you can use jsonfield library.

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.

Adding additional data to django rest framework viewset response

Curently I have a viewset of an example Warehouse and I want to pass additional 'filter' list to each dictionary that is returned.
My WarehouseViewSet:
class WarehouseViewSet(viewsets.ReadOnlyModelViewSet):
filters = [{'date': 'Date/Time'}]
queryset = Warehouse.objects.all()
serializer_class = WarehouseSerializer
WarehouseSerializer:
class WarehouseSerializer(serializers.ModelSerializer):
class Meta:
model = Warehouse
field = ('name', 'address', 'action_list')
Currently I get an json list response like:
[
{
"id": 1,
"name": "Brameda Warehouse",
"address": "Bergijk"
},
{
"id": 2,
"name": "Amazon",
"address": "UK"
}
]
I would like to get:
[
{
"id": 1,
"name": "Brameda Warehouse",
"address": "Bergijk"
"filters": [
{'date': 'dateTime'}, {'actove': 'ActiveObject'}
]
},
{
"id": 2,
"name": "Amazon",
"address": "UK",
"filters": [
{'date': 'dateTime'}, {'actove': 'ActiveObject'}
]
}
]
I understand that having one filter is enough outside the objects dictionary, but I would like to know how to pass lists inside objects.
Any ideas how I can pass additional lists that would be returned as json object would be appreaciated.
I feel a bit unclear as to what you want, but if you just want to add some read-only computed field to the output, you can use SerializerMethodField:
class WarehouseSerializer(serializers.ModelSerializer):
# your other declared fields here
filters = serializers.SerializerMethodField()
# your Meta options here
def get_filters(self, obj):
return ['some', 'stuff', 'here', {'and': 'more'}]
The method has to be named get_field_name (there is an option to change it but I don't really see any point using it).
You get the instance being serialized as obj.
You may return anything that is made of regular types (numbers, strings, dicts, lists, tuples, booleans, None).
If the data has to come from outside, you should have the caller add it to the context, and it will be available on self.context['foobar'].

Categories