In my model:
class HomePageFirstModule(models.Model):
name = models.CharField(max_length=8, unique=True)
is_active = models.BooleanField(default=True) # 是否启用
class HomePageSecondModule(models.Model):
name = models.CharField(max_length=16, unique=True)
is_active = models.BooleanField(default=True) # 是否启用
home_page_first_module = models.ForeignKey(to=HomePageFirstModule) # 所属的第一级模块
class HomePageThridModule(models.Model):
name = models.CharField(max_length=16, unique=True)
url = models.CharField(max_length=128)
is_active = models.BooleanField(default=True) # 是否启用
home_page_second_module = models.ForeignKey(to=HomePageSecondModule) # 所属的第二级模块
Then I use filter method to query out the data:
def get_homepage_module_list():
"""
获取到可以使用的模块信息
:return:
"""
data_query_list = models.HomePageThridModule.objects.filter(
home_page_second_module__home_page_first_module="1"
).values('id', 'name', 'is_active', 'home_page_second_module__name',
'home_page_second_module__home_page_first_module__name',
'home_page_second_module__home_page_first_module__is_active',
'home_page_second_module__is_active'
)
data_list_del = []
data_list = list(data_query_list)
for item in data_list:
if (item['is_active'] == False) or (
item['home_page_second_module__is_active'] == False
) or (
item['home_page_second_module__home_page_first_module__is_active'] == False
):
data_list_del.append(item)
for item_del in data_list_del:
data_list.remove(item_del)
return data_list
========================
How can I convert this list data :
[
{
"home_page_second_module__name": "云主机",
"home_page_second_module__home_page_first_module__name": "产品",
"id": 1,
"name": "云主机子1"
},
{
"home_page_second_module__name": "云主机",
"home_page_second_module__home_page_first_module__name": "产品",
"id": 4,
"name": "云主机子4"
},
{
"home_page_second_module__name": "云硬盘",
"home_page_second_module__home_page_first_module__name": "产品",
"id": 2,
"name": "云硬盘子2"
},
{
"home_page_second_module__name": "云硬盘",
"home_page_second_module__home_page_first_module__name": "产品",
"id": 3,
"name": "云硬盘子3"
}
]
to this:
[
{"name":"产品",
"data":[
{"name":"云主机",
"data":[{"name":"云主机子1",
"data":{"id":1}},
{"name":"云主机子2",
"data":{"id":2}}]},
{"name":"云硬盘",
"data":[{"name":"云硬盘子1",
"data":{"id":3}},
{"name":"云硬盘子2",
"data":{"id":4}}]}
]
}
]
There should has a arithmetic method to do this, but I tried, do not get that.
I only think of this below little things:
home_page_second_module__name_list = []
home_page_second_module__home_page_first_module__name_list = []
id_list = []
name_list = []
for home_page_second_module__name,home_page_second_module__home_page_first_module__name,id,name in ori_list:
if not (home_page_second_module__name_list.__contains__(home_page_second_module__name)):
home_page_second_module__name_list.append(home_page_second_module__name)
if not (home_page_second_module__home_page_first_module__name_list.__contains__(home_page_second_module__home_page_first_module__name_list)):
home_page_second_module__home_page_first_module__name_list.append(home_page_second_module__home_page_first_module__name)
But now I think this is very difficult to do that, and I think mine is wrong way to do that.
Is there a convenient way to realize it?
EDIT
The 产品, 云主机, 云硬盘 may can be deed as parent id.
You can use django-rest-framework, and use related serializers. relations serializers
Outdated code, as the original question just asked how to transform a list of dicts into another list of dicts with different outcome
I bet this can optimized a lot but... assuming your dict is named old, I think this might do it:
new = {'name': i['home_page_second_module__home_page_first_module__name'] for i in old if not i['home_page_second_module__home_page_first_module__name'] in old}
new['data'] = [['name': i['home_page_second_module__name'], 'data':[{'name': i['home_page_second_module__name'], 'data': {'id': i['id']}}]] for i in old]
Related
I'm working in Django 3.2 and graphene-django 2.15.
I'm still learning how to use Graphene by the way.
Note: I'm not allowed to share all the code so I rewrote it for the purpose of this question. Please notify me if you've found any error unrelated to the question.
I have an Team model which has a Many-to-Many relationship with the default Django Group model:
from django.db import models
from django.contrib.auth.models import Group
class Team(models.Model):
team_id = models.IntegerField(unique=True) # Custom ID not related to Django's pk
name = models.CharField(max_length=255)
groups = models.ManyToManyField(Group, blank=True)
Here is my schema:
import graphene
from django.contrib.auth.models import Group
from graphene_django import DjangoObjectType
from .models import Team
class TeamType(DjangoObjectType):
class Meta:
model = Team
class GroupType(DjangoObjectType):
class Meta:
model = Group
class GroupInput(graphene.InputObjectType):
id = graphene.ID(required=True)
class UpdateTeam(graphene.Mutation):
team = graphene.Field(TeamType)
class Arguments:
team_id = graphene.ID(required=True)
name = graphene.String(required=True)
groups_id = graphene.List(GroupInput, required=True)
def mutate(self, info, team_id, name, groups_id):
team = Team.objects.get(pk=team_id)
team.name = name
team.groups = Group.objects.filter(pk__in=groups_id)
team.save()
return UpdateTeam(team=team)
class TeamMutations(graphene.ObjectType):
update_team = UpdateTeam.Field()
class Mutation(TeamMutations, graphene.ObjectType):
pass
schema = graphene.schema(query=Query, mutation=Mutation)
When I perform this query:
mutation{
updateTeam(
teamId: 65961826547,
name: "My Team Name",
groupsId: [{id: 1}, {id: 2}]
) {
team {
team_id,
name,
groups {
name,
}
}
}
}
I get this error:
{
"errors": [
{
"message": "Field 'id' expected a number but got {'id': '1'}.",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"updateTeam"
]
}
],
"data": {
"updateTeam": null
}
}
I don't really understand how Many-to-may relationships are managed by Graphene.
Does someone has a solution and some explanations for me? Thanks a lot.
I found the solution.
At first, I thought there was something related to graphene, especially these InputObjectTypes, I didn't get correctly.
But the issue is actually very simple.
The GroupInput is expecting a single value, which it an ID.
class GroupInput(graphene.InputObjectType):
id = graphene.ID(required=True)
Because I put it in graphene.List(), I'm now expecting a list of ID's:
class UpdateTeam(graphene.Mutation):
...
class Arguments:
...
groups_id = graphene.List(GroupInput, required=True)
But in my API call, instead of giving an actual list, I gave a dict:
mutation{
updateTeam(
...
groupsId: [{id: 1}, {id: 2}]
) {
...
}
So this works:
mutation{
updateTeam(
...
groupsId: [1, 2]
) {
...
}
Note 1:
graphene.ID also accepts ids given as strings:
groupsId: ["1", "2"]
Note 2:
I actually removed my GroupInput and put the graphene.ID field directly in the graphene.List:
class UpdateTeam(graphene.Mutation):
...
class Arguments:
...
groups_id = graphene.List(graphene.ID, required=True)
I have models Software and Domain described loosely as:
class Software(models.Model)
id = models.BigInteger(primary_key=True, db_index=True, null=False)
company = models.ForeignKey('Company')
domain = models.ForeignKey('Domain')
type = models.CharField(null=False)
vendor = models.CharField(null=False)
name = models.CharField(null=False)
class Domain(models.Model):
id = models.BigInteger(primary_key=True, db_index=True, null=False)
type = models.CharField()
importance = models.DecimalField(max_digits=11, decimal_places=10, null=False)
And I get a Software queryset with:
qs = Software.objects.filter(company=c).order_by('vendor')
The desired output should have an aggregated Domain importance with total count for each unique Software, i.e.
[
{
'type': 'type_1', \
'vendor': 'ajwr', | - unique together
'name': 'nginx', /
'domains': {
'total_count': 4,
'importance_counts': [0.1: 1, 0.5: 2, 0.9: 1] # sum of counts = total_count
},
},
{
...
},
]
I feel like the first step here should be to just group the type, vendor, name by Domain so each Software object has a list of Domains instead of just one but I'm not sure how to do that. Doing this in memory would make it a lot easier but it seems like it would be a lot slower than using querysets / SQL.
So I would do it like this:
from django.db.models import Sum
qs = Software.objects.filter(company=c).prefetch_related(
'domain'
).annotate(
total_count=Sum('domain__importance')
).order_by('vendor')
output = []
for obj in qs:
domains = obj.domain.all() # using prefetched domains, no db query
output.append({
# ...
'domains': {
'total_count': obj.total_count,
'importance_counts': [d.importance for d in domains]
}
})
And I belive it should be fast enough. Only if finding that it isn't I would try to improve. Remember "Premature optimization is the root of all evil"
Once again I'm asking for your help. I want to return data in Json format from database and I need 3 values temperature, date and sensor.description. With little to no problem I can query and get 'temperature' and 'date' but I have hard time with getting that sensor description. This gives me temperature and date
sen = S280000020E3120.objects.filter(date__range=[dateo, datee]).values('date', 'temperature')
and this returns the description
desc = S280000020E3120.objects.filter(date__range=[dateo, datee]).last().sensor.description
And If I want to return json from that I have to do sen = list(sen) but when I try to append desc to sen by result = sen.append(desc) I got error
NoneType object is not iterable
Is it possible to get result in similar format?
{
"ds": [
{"date": "2020-06-19T16:23:16", "temperature": "27.4"},
{"date": "2020-06-19T16:26:24", "temperature": "27.4"},
{..rest of data..},
sensor.description
]
}
My view.py
def chartData(request):
fixm = datetime.min.time()
fixd = datetime.max.time()
dateo = '2020-06-19'
datee = '2020-6-20'
dateo = datetime.strptime(dateo, '%Y-%m-%d')
datee = datetime.strptime(datee, '%Y-%m-%d')
dateo = datetime.combine(dateo, fixm)
datee = datetime.combine(datee, fixd)
sen = S280000020E3120.objects.filter(date__range=[dateo, datee])
#date = sen.values('date', )
value = sen.values('date', 'temperature')
lvalue=list(value)
senName = sen.last().sensor.sensor_name
#lvalue = lvalue.append(senName)
result = {
'ds': list(lvalue),
}
return JsonResponse(result, safe=False)
models.py
class S280000020E3120(models.Model):
sensor = models.ForeignKey('Sensors', models.DO_NOTHING)
temprature = models.DecimalField(max_digits=4, decimal_places=1)
date = models.DateTimeField()
class Meta:
managed = False
db_table = '28-0000020e3120'
class Sensors(models.Model):
sensor_name = models.CharField(max_length=20)
description = models.CharField(max_length=255, blank=True, null=True)
class Meta:
managed = False
db_table = 'sensors'
Output I get
{"ds": [{"date": "2020-06-19T16:23:16", "temperature": "27.4"}, {"date": "2020-06-19T16:26:24", "temperature": "27.4"}, {...}]}
Output I want or something similar when I can easily get description because this data I hope will be graph with char.js or something
{"ds": [{"date": "2020-06-19T16:23:16", "temperature": "27.4"}, {"date": "2020-06-19T16:26:24", "temperature": "27.4"}, , {...},sensor.description]}
In this example I query only one table but at the end I need get data from 8 of these if this makes it any different.
I don't know how your sensor model looks like, but in general, this should do the trick:
sen = S280000020E3120.objects.filter(date__range=[dateo, datee]).values('date','temperature','sensor__description')
You can use Djangos field lookup syntax here too. See: https://docs.djangoproject.com/en/3.1/ref/models/querysets/#django.db.models.query.QuerySet.values
models.py
class Keyword(models.Model):
name=models.CharField(max_length=500,unique=True)
image = models.ImageField(upload_to='keywords/', blank=True, null=True)
mood=models.ManyToManyField(Mood,blank=True)
def __str__(self):
return str(self.name)
class ItemVariation(models.Model):
restaurant=models.ForeignKey(Restaurant,on_delete=models.CASCADE)
items_id=models.ForeignKey(Item,on_delete=models.CASCADE)
price=models.IntegerField(blank=True,null=True,default=0)
item_code=models.CharField(max_length=500)
keywords= models.ManyToManyField(Keyword)
image=models.ImageField(upload_to='dishes/', blank=True, null=True)
def __str__(self):
return str(self.id)
views.py
class FoodfeedList(APIView):
def get(self,request,format=None):
keyword = request.GET['keywords'].split(",")
mood=request.GET['mood']
location=request.GET['location'].split(",")
price=request.GET['price']
user_id=request.user.id
items=Item.objects.all()
item_variation=ItemVariation.objects.all()
search_restaurant=SearchRestaurant()
search_result=search_restaurant.searchRes(location[0],location[1],keyword[0],user_id)
all_results=[]
json={}
for i in search_result:
json={}
json['res_id'] = i['restaurant']['id']
# keywords_list = ItemVariation.objects.filter(keywords=keyword[0])
items=ItemVariation.objects.filter(restaurant = request.user.id)
itemserializer=ItemVariationSerializer(items,many =True)
json['itemserializer']=itemserializer.data
all_results.append(json)
# items_serilizer=ItemsSerializer(items,many=True)
return Response(all_results)
Output
"res_id": 2,
"itemserializer": [
{
"id": 1,
"price": 0,
"item_code": "2134ffsd",
"image": null,
"restaurant": 1,
"items_id": 1,
"keywords": [1,2,3]
},
I need fiter query for items where keywords are name from keyword model with query paraters 'Burger' as keyword
my url is like
http://127.0.0.1:8000/api/foodfeeds/?keywords=BURGER,teste&mood=happy&location=2323,7767.323&price=2
if keywords match in ItemVariation model in many to many fields so it should return itemvariation
do the filtering like this:
items=ItemVariation.objects.filter(keywords__name__in=keywords)
or for a single keyword do this:
items=ItemVariation.objects.filter(keywords__name=keywords[0])
Not 100% sure if i understood your question correctly. Here i used a for loop on the list with they keywords, adding each keyword-Entity to the list - selected by name.
class FoodfeedList(APIView):
def get(self,request,format=None):
keywords = request.GET['keywords'].split(",")
mood=request.GET['mood']
location=request.GET['location'].split(",")
price=request.GET['price']
user_id=request.user.id
keyword_names = []
for keyword in keywords:
keyword_names += Item.objects.get(name=keyword)
item_variation=ItemVariation.objects.all()
search_restaurant=SearchRestaurant()
search_result=search_restaurant.searchRes(location[0],location[1],keyword[0],user_id)
all_results=[]
json={}
for i in search_result:
json={}
json['res_id'] = i['restaurant']['id']
json['keywords'] = keyword_names
items=ItemVariation.objects.filter(restaurant = request.user.id)
itemserializer=ItemVariationSerializer(items,many =True)
json['itemserializer']=itemserializer.data
all_results.append(json)
# items_serilizer=ItemsSerializer(items,many=True)
return Response(all_results)
I want to import data from json files into my django db. The json contains nested objects.
Current steps are:
Set up my django object models to match the json schema (done manually - see models.py file below)
Import json file into python dict using mydict = json.loads(file.read()) (done)
Convert dict to django models (done - but solution is not pretty)
Is there a way I can convert my nested dict into django models (i.e. step 3) without hard-coding the data structure into the logic?
Bonus points for automatically generating the django models (i.e. the models.py file) based on an example json file.
Thanks in advance!
How I'm currently doing it
Step 3 is easy if the dict does not contain any nested dicts - just construct a new object from the dict i.e. MyModel.objects.create(**mydict) or use django fixtures.
However, because my json/dict contains nested objects, I'm currently doing step 3 like this:
# read the json file into a python dict
d = json.loads(myfile.read())
# construct top-level object using the top-level dict
# (excluding nested lists of dicts called 'judges' and 'contestants')
c = Contest.objects.create(**{k:v for k,v in d.items() if k not in ('judges', 'contestants')})
# construct nested objects using the nested dicts
for judge in d['judges']:
c.judge_set.create(**judge)
for contestant in d['contestants']:
ct = c.contestant_set.create(**{k:v for k,v in contestant.items() if k not in ('singers', 'songs')})
# all contestants sing songs
for song in contestant['songs']:
ct.song_set.create(**song)
# not all contestants have a list of singers
if 'singers' in contestant:
for singer in contestant['singers']:
ct.singer_set.create(**singer)
This works, but requires the data structure to be hard coded into the logic:
Need to hard code the names of the nested dicts to exclude when calling create() (if you try to pass a nested dict to create() it throws a TypeError). I thought about instead doing **{k:v for k,v in contestant.items() if not hasattr(v, 'pop')} to exclude lists and dicts, but I suspect that won't work 100% of the time.
Need to hard code logic to iteratively create the nested objects
Need to hard code logic to deal with nested objects that are not always present
Data structures
example json looks like this:
{
"assoc": "THE BRITISH ASSOCIATION OF BARBERSHOP SINGERS",
"contest": "QUARTET FINAL (NATIONAL STREAM)",
"location": "CHELTENHAM",
"year": "2007/08",
"date": "25/05/2008",
"type": "quartet final",
"filename": "BABS/2008QF.pdf"
"judges": [
{"cat": "m", "name": "Rod"},
{"cat": "m", "name": "Bob"},
{"cat": "p", "name": "Pat"},
{"cat": "p", "name": "Bob"},
{"cat": "s", "name": "Mark"},
{"cat": "s", "name": "Barry"},
{"cat": "a", "name": "Phil"}
],
"contestants": [
{
"prev_tot_score": "1393",
"tot_score": "2774",
"rank_m": "1",
"rank_s": "1",
"rank_p": "1",
"rank": "1", "name": "Monkey Magic",
"pc_score": "77.1",
"songs": [
{"title": "Undecided Medley","m": "234","s": "226","p": "241"},
{"title": "What Kind Of Fool Am I","m": "232","s": "230","p": "230"},
{"title": "Previous","m": "465","s": "462","p": "454"}
],
"singers": [
{"part": "tenor","name": "Alan"},
{"part": "lead","name": "Zac"},
{"part": "bari","name": "Joe"},
{"part": "bass","name": "Duncan"}
]
},
{
"prev_tot_score": "1342",
"tot_score": "2690",
"rank_m": "2",
"rank_s": "2",
"rank_p": "2",
"rank": "2", "name": "Evolution",
"pc_score": "74.7",
"songs": [
{"title": "It's Impossible","m": "224","s": "225","p": "218"},
{"title": "Come Fly With Me","m": "225","s": "222","p": "228"},
{"title": "Previous","m": "448","s": "453","p": "447"}
],
"singers": [
{"part": "tenor","name": "Tony"},
{"part": "lead","name": "Michael"},
{"part": "bari","name": "Geoff"},
{"part": "bass","name": "Stuart"}
]
},
],
}
My models.py file:
from django.db import models
# Create your models here.
class Contest(models.Model):
assoc = models.CharField(max_length=100)
contest = models.CharField(max_length=100)
date = models.DateField()
filename = models.CharField(max_length=100)
location = models.CharField(max_length=100)
type = models.CharField(max_length=20)
year = models.CharField(max_length=20)
class Judge(models.Model):
contest = models.ForeignKey(Contest, on_delete=models.CASCADE)
name = models.CharField(max_length=60)
cat = models.CharField('Category', max_length=2)
class Contestant(models.Model):
contest = models.ForeignKey(Contest, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
tot_score = models.IntegerField('Total Score')
rank_m = models.IntegerField()
rank_s = models.IntegerField()
rank_p = models.IntegerField()
rank = models.IntegerField()
pc_score = models.DecimalField(max_digits=4, decimal_places=1)
# optional fields
director = models.CharField(max_length=100, blank=True, null=True)
size = models.IntegerField(blank=True, null=True)
prev_tot_score = models.IntegerField(blank=True, null=True)
class Song(models.Model):
contestant = models.ForeignKey(Contestant, on_delete=models.CASCADE)
title = models.CharField(max_length=100)
m = models.IntegerField('Music')
s = models.IntegerField('Singing')
p = models.IntegerField('Performance')
class Singer(models.Model):
contestant = models.ForeignKey(Contestant, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
part = models.CharField('Category', max_length=5)
You could browse the json object recursively and use a key to class mapping to instantiate your models dynamically. Here's an idea (not a working solution!):
key_model = {
"contestants": Contestant,
"singers": Singer
}
def make_sub_model(parent, model, vals):
for v in vals:
child = create_model(model, v)
parent.add_child(child) # or whatever it is with Django Models
def create_model(model, obj):
# model should be the class and obj a dict
# take care of the top lvl object
to_process = [] # store nest models
parent = {} # store parent attributes
for k, v in obj.items():
if isinstance(v, list): # you probably want dict as well
to_process.append((k, v))
else:
parent[k] = v
parent_obj = model.create(**parent)
# now process the chidlrend
for k, v in to_process:
make_sub_model(parent_obj, key_model[k], v)
return parent_obj
But in the end, I would discourage this because you are using a Schema based storage (SQL) so your code should enforce that the input matches your schema (you can't handle anything different on the fly anyway). If you don't care about having a schema at all go for a No-SQL solution and you won't have this problem. Or a hybrid like PostgresSQL.