How does Django query the tree structure - django

I am new to Django and I am creating a blog and comment function
from django.db import models
from django.contrib.auth import get_user_model
class Blog(models.Model):
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
title = models.CharField(max_length=200)
content = models.TextField(max_length=200)
class Comment(models.Model):
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name="children")
content = models.CharField(max_length=200)
This is my current test data
Comment.objects.all().values()
<QuerySet [
{'id': 1, 'user_id': 1, 'blog_id': 1, 'parent_id': None, 'content': '1'},
{'id': 2, 'user_id': 1, 'blog_id': 1, 'parent_id': None, 'content': '2'},
{'id': 3, 'user_id': 1, 'blog_id': 1, 'parent_id': 1, 'content': '1-1'},
{'id': 4, 'user_id': 1, 'blog_id': 1, 'parent_id': 1, 'content': '1-2'}
]>
But now I want to find out such a tree structure, what should I do?
[
{'id': 1, 'user_id': 1, 'blog_id': 1, 'parent_id': None, 'content': '1', 'children': [
{'id': 3, 'user_id': 1, 'blog_id': 1, 'parent_id': 1, 'content': '1-1'},
{'id': 4, 'user_id': 1, 'blog_id': 1, 'parent_id': 1, 'content': '1-2'}
]},
{'id': 2, 'user_id': 1, 'blog_id': 1, 'parent_id': None, 'content': '2', 'children': [] },
]

You can' t obtain a nested dictionary with values.
You could write this:
Comment.objects.all().values('id', 'user_id', 'blog_id', 'parent_id', 'content', 'children')
But the result will not be as you expected, the list will contain a dict for every parent-child combination, for example:
[
{'id': 1, 'user_id': 1, 'blog_id': 1, 'parent_id': None, 'content': '1', children: 2},
{'id': 1, 'user_id': 1, 'blog_id': 1, 'parent_id': None, 'content': '1', children: 3},
{'id': 2, 'user_id': 1, 'blog_id': 1, 'parent_id': 1, 'content': '2', children: None},
{'id': 3, 'user_id': 1, 'blog_id': 1, 'parent_id': 1, 'content': '2', children: None},
]
You can deal directly with the queryset, for example:
for comment in Comment.objects.all().prefetch_related('children'):
for child in comment.children.all():
print(child.id)
Or you can use a nested list comprehension (the result will be as you expected):
print([{'id': comment.id, 'user_id': comment.user.id, 'blog_id': comment.blog.id, 'parent_id': comment.parent.id if comment.parent != None else None, 'content': comment.content, 'children': [{'id':
child.id, 'user_id': child.user.id, 'blog_id': child.blog.id, 'parent_id': child.parent.id, 'content': child.content} for child in comment.children.all()]} for comment in Comment.objects.all()])

I use recursion to query
def get_children(c):
return {
'id': c.id,
'user_id': c.user_id,
'blog_id': c.blog_id,
'parent_id': c.parent_id,
'content': c.content,
'children': list(map(get_children, c.children.all())),
}
def get_comment_tree():
return list(map(get_children, Comment.objects.filter(parent=None).prefetch_related('children')))
Output json
[{
"id": 1,
"user_id": 1,
"blog_id": 1,
"parent_id": null,
"content": "1",
"children": [{
"id": 3,
"user_id": 1,
"blog_id": 1,
"parent_id": 1,
"content": "1-1",
"children": [{
"id": 5,
"user_id": 1,
"blog_id": 1,
"parent_id": 3,
"content": "1-1-1",
"children": []
}]
}, {
"id": 4,
"user_id": 1,
"blog_id": 1,
"parent_id": 1,
"content": "1-2",
"children": []
}]
}, {
"id": 2,
"user_id": 1,
"blog_id": 1,
"parent_id": null,
"content": "2",
"children": []
}]

Related

Django Annotate with one model and include "annotated" records in queryset

I am fairly new to Django and have figured out the "basic" annotate function, but I would like to include the records from the annotated values. For example,
qs = People.objects.values('status').annotate(total=Count('id'),totalScore=Sum('score'),averageAge=Avg('age'))
I would like to include the summarized records in the result, in this case the individual people, like so...
[
{'status': 'One',
'total': 2,
'totalScore': 150,
'averageAge': 36,
'people': [
{
'id': 1,
'status': 'One',
'name': 'Jim',
'score': 80,
'age': 40
},
{
'id': 5,
'status': 'One',
'name': 'Jill',
'score': 70,
'age': 32
}
]},
{'status': 'Two',
'total': 1,
'totalScore': 85,
'averageAge': 42,
'people': [
{
'id': 3,
'status': 'Two',
'name': 'Jack',
'score': 85,
'age': 42
}
]
},...
Thanks for any help.

Merging a list of dictionaries

I have a list of dicts as below.
list = [ {id: 1, s_id:2, class: 'a', teacher: 'b'} ]
list1 = [ {id: 1, c_id:1, rank:2, area: 34}, {id:1, c_id:2, rank:1, area: 21} ]
I want to merge the two lists on the common key-value pairs (in this case 'id:1')
Merged_list = [ {id:1, s_id:2, class: 'a', teacher: 'b', list1: {c_id:1, rank: 2, area: 34}, {c_id:2, rank: 1, area: 21} ]
How do I go about this?
Thanks
You can use
merged_list = [{**d1, **d2} for d1, d2 in zip(list1, list2)]
>>> merged_list
[{'id': 1, 's_id': 2, 'class': 'a', 'teacher': 'b', 'rank': 2, 'area': 34},
{'id': 2, 's_id': 3, 'class': 'c', 'teacher': 'd', 'rank': 1, 'area': 21}]
where {**d1, **d2} is just a neat way to combine 2 dictionaries. Keep in mind this will replace the duplicate keys of the first dictionary. If you're on Python 3.9, you could use d1 | d2.
EDIT: For the edit in your question, you can try this horrible one liner (keep in mind this will create the pair list1: [] if no matching indeces were found on list1):
list_ = [{"id": 1, "s_id": 2, "class": 'a', "teacher": 'b'}]
list1 = [{"id": 1, "c_id": 1, "rank": 2, "area": 34}, {"id": 1, "c_id": 2, "rank": 1, "area": 21}]
merged_list = [{**d, **{"list1": [{k: v for k, v in d1.items() if k != "id"} for d1 in list1 if d1["id"] == d["id"]]}} for d in list_]
>>> merged_list
[{'id': 1,
's_id': 2,
'class': 'a',
'teacher': 'b',
'list1': [{'c_id': 1, 'rank': 2, 'area': 34},
{'c_id': 2, 'rank': 1, 'area': 21}]}]
This is equivalent to (with some added benefits):
merged_list = []
for d in list_:
matched_ids = []
for d1 in list1:
if d["id"] == d1["id"]:
d1.pop("id") # remove id from dictionary before appending
matched_ids.append(d1)
if matched_ids: # added benefit of not showing the 'list1' key if matched_ids is empty
found = {"list1": matched_ids}
else:
found = {}
merged_list.append({**d, **found})
Try this
And don't forget to put " " when declare string
list = [ {"id": 1, "s_id": 2 ," class": 'a', "teacher": 'b'}, {"id": 2, "s_id" : 3, "class" : 'c', "teacher": 'd'} ]
list1 = [ {"id": 1, "rank" :2, "area" : 34}, {"id" :2, "rank" :1, "area": 21} ]
list2 = list1 + list
print(list2)

Dart/Flutter - How to delete key-value-pairs of element of Map inside a List?

How can I can delete a specific key-value-pair in that type of List<Map<String, dynamic>> for each map.
For example:
List<Map<String, dynamic>> before the operation:
List<Map<String, dynamic>> currencies = [
{
'id': 1,
'name': 'coin',
'desc': 'This is just a blindtext.',
'order': 101,
'icon': 'https://icon1.jpg'
},
{
'id': 2,
'name': 'karma',
'desc': 'This is just a blindtext.',
'order': 102,
'icon': 'https://icon2.jpg'
},
{
'id': 3,
'name': 'laurel',
'desc': 'This is just a blindtext.',
'order': 104,
'icon': 'https://icon3.jpg'
},
];
List<Map<String, dynamic>> after the operation I am searching for:
List<Map<String, dynamic>> currencies = [
{
'id': 1,
'name': 'coin',
'icon': 'https://icon1.jpg'
},
{
'id': 2,
'name': 'karma',
'icon': 'https://icon2.jpg'
},
{
'id': 3,
'name': 'laurel',
'icon': 'https://icon3.jpg'
},
];
So basically, I deleted "unwanted" key-value-pairs.
Any suggestions?
Thanks in advance!!
currencies.forEach((item) => item..remove("order")..remove("desc"));

how to count number of occurrences of element in list of maps in dart?

I have a list of maps like this
data = [
{
'id': 100,
'quantity': 20
},
{
'id': 101,
'quantity': 25
},
{
'id': 101,
'quantity': 30
}
{
'id': 105,
'quantity': 50
}
]
I am able to count occurrences of element in a list like this
data.forEach((x) => map[x] = !map.containsValue(x) ? (1) : (map[x] + 1));
print(map);
Output :
{{id: 100, quantity: 20}: 1, {id: 101, quantity: 25}: 1, {id: 101, quantity: 30}: 1, {id: 105, quantity: 50}: 1}
But I want to count how many times id = 101 comes in the list so How can I achieve that?
You can use where to filter your array of maps and then use the getter length :
var data = [
{'id': 100, 'quantity': 20},
{'id': 101, 'quantity': 25},
{'id': 101, 'quantity': 30},
{'id': 105, 'quantity': 50}
];
print(data.where((e) => e['id'] == 101).length); // 2

django serializer grouping by value

I have a django orm queryset like this.
[{"id": 1,"type": "en", "name": "aaa"},
{"id": 3,"type": "en", "name": "ccc"},
{"id": 2, "type": "it", "name": "bbb"}]
I would like to change like this, grouping a list of dictionaries by type using DRF serializer, I tried many possible solutions but could not get the answer.
[{"type": "en", "data": {"id": 1, "name": "aaa"},
{"type": "it", "data": {"id": 2, "name": "bbb"},
{"type": "it", "data": {"id": 2, "name": "ccc"}]
In[1]: from rest_framework import serializers
In[2]: raw_data = [{"id": 1,"type": "en", "name": "aaa"},
...: {"id": 3,"type": "en", "name": "ccc"},
...: {"id": 2, "type": "it", "name": "bbb"}]
In[3]: class DataSerializer(serializers.Serializer):
...: id = serializers.IntegerField()
...: name = serializers.CharField()
...:
In[4]: class MainSerializer(serializers.Serializer):
...: type = serializers.CharField()
...: data = serializers.SerializerMethodField()
...:
...: def get_data(self, obj):
...: return DataSerializer(obj).data
...:
In[5]: MainSerializer(raw_data, many=True).data
Out[5]: [OrderedDict([('type', 'en'), ('data', {'id': 1, 'name': 'aaa'})]), OrderedDict([('type', 'en'), ('data', {'id': 3, 'name': 'ccc'})]), OrderedDict([('type', 'it'), ('data', {'id': 2, 'name': 'bbb'})])]
or easier:
class MainSerializer(serializers.Serializer):
type = serializers.CharField()
data = DataSerializer(source='*')