how to query a one to many relationship in sqlalchemy without duplicates - sqlalchemy

I am trying to query the posts and i want to get all the comments too
this is my model for the parent and child
parent
class Post(Base):
__tablename__ = "posts"
id = Column(Integer, primary_key=True, index=True)
title = Column(String, nullable=False)
content = Column(String, nullable=False)
published = Column(Boolean, default=False)
created_at = Column(TIMESTAMP(timezone=True), nullable=False, default=text("NOW()"))
owner_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False)
comments = relationship("Comment", backref="post")
Child
class Comment(Base):
__tablename__ = "comments"
id = Column(Integer, primary_key=True, nullable=False)
email = Column(String, nullable=True)
name = Column(String, nullable=True)
content = Column(String, nullable=False) commented_at=Column(TIMESTAMP(timezone=True),nullable=False,server_default=text("NOW()"))
post_id = Column(Integer, ForeignKey("posts.id", ondelete="CASCADE"), nullable=False)
and im querying with join like this:
posts = db.query(models.Post, models.User, models.Comment).outerjoin(
models.User).order_by(models.Post.owner_id).outerjoin(models.Comment).order_by(models.Comment.post_id).all()
this is the result but here it is giving me 2 times the post with the id 1 for each comments if the post had 5 comments it will give me 5 times de same post
{
"Post": {
"title": "string",
"id": 1,
"published": false,
"owner_id": 1,
"content": "string",
"created_at": "2022-01-30T21:49:17.851582+02:00"
},
"User": {
"username": "TheBossmanLab",
"created_at": "2022-01-30T21:47:20.416480+02:00",
"email": "super_user#gmail.com",
"is_super_user": true,
"id": 1,
"password": "$2b$12$UEt0mzRp2qiLweJXwTztFuyWGcAfx6h5UFRE3avf3bt82wLzPZxTC",
"is_admin": true
},
"Comment": {
"commented_at": "2022-05-15T21:59:29.830714+02:00",
"id": 1,
"name": "666",
"email": "666#gmail.com",
"content": "granda post",
"post_id": 1
}
},
{
"Post": {
"title": "string",
"id": 1,
"published": false,
"owner_id": 1,
"content": "string",
"created_at": "2022-01-30T21:49:17.851582+02:00"
},
"User": {
"username": "TheBossmanLab",
"created_at": "2022-01-30T21:47:20.416480+02:00",
"email": "super_user#gmail.com",
"is_super_user": true,
"id": 1,
"password": "$2b$12$UEt0mzRp2qiLweJXwTztFuyWGcAfx6h5UFRE3avf3bt82wLzPZxTC",
"is_admin": true
},
"Comment": {
"commented_at": "2022-05-16T11:45:33.501141+02:00",
"id": 2,
"name": "666",
"email": "666#gmail.com",
"content": "nqowrbvowrb",
"post_id": 1
}
},
what i want as a result:
{
"Post": {
"title": "string",
"id": 1,
"published": false,
"owner_id": 1,
"content": "string",
"created_at": "2022-01-30T21:49:17.851582+02:00"
},
"User": {
"username": "TheBossmanLab",
"created_at": "2022-01-30T21:47:20.416480+02:00",
"email": "super_user#gmail.com",
"is_super_user": true,
"id": 1,
"password": "$2b$12$UEt0mzRp2qiLweJXwTztFuyWGcAfx6h5UFRE3avf3bt82wLzPZxTC",
"is_admin": true
},
"Comments": [{
"commented_at": "2022-05-15T21:59:29.830714+02:00",
"id": 1,
"name": "666",
"email": "666#gmail.com",
"content": "granda post",
"post_id": 1},
{"commented_at": "2022-05-16T11:45:33.501141+02:00",
"id": 2,
"name": "666",
"email": "666#gmail.com",
"content": "nqowrbvowrb",
"post_id": 1
}]
},
i think i over explained the situation but really im deseperate i think im blocked i cant think of any solution to this pleasse help me.

My understanding based on your question is that you want to query a list of posts which includes the information about the User and the Comments associated with the post.
If that is the case then instead of loading all three entities you could choose to create a relationship between the Post and User (referenced by the owner_id)
Since you're question does not offer details about whether a Post can exist without a User you can use the following query to fetch posts associated to a user.
from sqlalchemy.orm import joinedload, contains_eager
posts: Post = (
db.query(Post)
.join(Post.user, User.id == Post.owner_id)
.options(contains_eager(Post.user))
.options(joinedload(Post.comments))
.order_by(Post.id.desc())
.all()
)
If an association is created between Post and User then User.id == Post.owner_id becomes redundant and can be skipped.
The comments are simply eager loaded based on the relationship.
The join is only needed if there is no explicit relationships defined.
Both contains_eager and joinedload can be a bit tricky if you're new to sqlalchemy.
The TLDR version is that you'd use contains_eager where you've already instructed SqlAlchemy to load data with a join. In our case that would be the join(Post.user, User.id == Post.owner_id) while joinedload simply uses the defined relationships to eager load the association.
contains_eager
joinedload
Update
I see that you've mentioned the nullable constraint on the foreign_key for the Posts to User relationship. In that case this should be more than adequate.
posts: Post = (
db.query(Post)
.options(
joinedload(Post.user),
joinedload(Post.comments),
)
.order_by(Post.id.desc())
.all()
)

Related

How to create this below JSON response using python DRF [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
This question does not appear to be about a specific programming problem, a software algorithm, or software tools primarily used by programmers. If you believe the question would be on-topic on another Stack Exchange site, you can leave a comment to explain where the question may be able to be answered.
Closed last year.
Improve this question
Since I am new to this DRF I am facing some difficulties creating this JSON. I have created an API endpoint and the output of that API endpoint is like this now as shown below
"meta": {
"limit": 1,
"page_count": 1,
"total_count": 1
},
"results": {
"id": 1234567,
"curriculum": {
"ES Math": [
{
"grade_level": "ES",
"subject": "Math",
"subject_abbr": "Math",
"product_id": 438,
"product_title": "Test1",
"ratings": [
{
"source": "A",
"report_url": "********",
"Org1_rating": [
{
"name": "green_alignment_ratings",
"value": 12,
"label": "Meet Expectations",
"label_color": "Green"
},
{
"name": "green_usability_ratings",
"value": 12,
"label": "Meet Expectations",
"label_color": "Green"
}
],
"Org2_rating": [
{
"name": "Overall",
"value": null,
"label": "Tier 1: Exemplifies Quality",
"label_color": null
}
]
}
]
},
{
"grade_level": "ES",
"subject": "Math",
"subject_abbr": "Math",
"product_id": 2085,
"product_title": "Test2",
"ratings": [
{
"source": "A",
"report_url": "********",
"Org1_rating": [
{
"name": "green_alignment_ratings",
"value": 12,
"label": "Meet Expectations",
"label_color": "Green"
},
{
"name": "green_usability_ratings",
"value": 12,
"label": "Meet Expectations",
"label_color": "Green"
}
],
"Org_rating2": "null"
}
]
}
]
}
}
}
But I want the output to be in this format below
{
"meta": {
"limit": 1,
"page_count": 1,
"total_count": 1
},
"results": {
"id": 1234567,
"curriculum": {
"ES Math": [
{
"grade_level": "ES",
"subject": "Math",
"subject_abbr": "Math",
"product_id": 438,
"product_title": "Test1",
"ratings": [
{
"review_org": "Org1",
"review_url": "url",
"review_items": [
{
"name": "green_alignment_ratings",
"value": 14,
"label": "Meets Expectations",
"label_color": "Green"
},
{
"name": "green_usability_ratings",
"value": 34,
"label": "Green",
"label_color": 38
}
]
},
{
"review_org": "Org2",
"review_url": "url",
"review_items": [
{
"name": "Overall",
"value": null,
"Label": "Tier I, Exemplifies quality",
"scale": null
}
]
}
]
},
{
"grade_level": "ES",
"subject": "Math",
"subject_abbr": "Math",
"product_id": 2085,
"product_title": "Test2",
"ratings": [
{
"review_org": "Org1",
"review_url": "url",
"review_items": [
{
"name":"green_alignment_ratings",
"value": 14,
"label": "Meets Expectations",
"label_color": "Green"
},
{
"name":"green_usability_ratings",
"value": 34,
"label": "Meets Expectations",
"label_color": "Green"
}
]
},
{
"review_org": "Org2",
"review_url": "url",
"review_items": []
}
]
}
]
}
}
}
And I tried something with the serializer below but this is yeilding some different JSON only.
class CurriculumSerializer(ModelSerializer):
grade_level = serializers.CharField(source='grade_level_dim')
subject_abbr = serializers.CharField(source='subject_abbr_dim')
product_id = serializers.IntegerField(source='dim_id')
product_title = serializers.CharField(source='title_dim')
ratings = serializers.SerializerMethodField()
class Meta:
model = Table2
fields = [
'grade_level',
'subject',
'subject_abbr',
'product_id',
'product_title',
'ratings'
]
def get_ratings(self,obj):
queryset = Table2.objects.all()
c = queryset.filter(id=obj.id, title_dim = obj.title_dim, ratings_source__isnull=False).distinct('id',)
if c.exists():
serializer = RatingsSerializer(c, many=True)
return serializer.data
else:
data = 'null'
return data
class RatingsSerializer(ModelSerializer):
review_items = serializers.SerializerMethodField()
Louisiana_rating = serializers.SerializerMethodField()
class Meta:
model = Table3
fields = ['source','report_url','review_items','Louisiana_rating']
def get_review_items(self, obj):
queryset = Table3.objects.all()
c = queryset.filter(source__iexact = 'Org1', title = obj.title_dim, grade_level = obj.grade_level, subject_abbr = obj.subject_abbr_dim)
# print(c.query)
if c.exists():
serializer = reportSerializer(c, many=True)
return serializer.data
else:
data = 'null'
return data
def get_Louisiana_rating(self, obj):
queryset = Table3.objects.all()
c = queryset.filter(source__iexact = 'Org2', title = obj.title_dim, grade_level = obj.grade_level, subject_abbr = obj.subject_abbr_dim)
if c.exists():
serializer = reportSerializer(c, many=True)
return serializer.data
else:
data = 'null'
return data
class reportSerializer(ModelSerializer):
class Meta:
model = Table3
fields = ['name',
'value',
'label',
'label_color']
class DistrictDetailSerializer(ModelSerializer):
curriculum = serializers.SerializerMethodField()
class Meta:
model = Table1
exclude = ('finance_total', 'revenue_total')
def get_curriculum(self, obj):
queryset_list = Table2.objects.all()
c = queryset_list.filter(id=obj.id)\
.distinct('col1','col2')
if c.exists():
serializer = CurriculumSerializer(c, many=True)
curriculum_map = {}
for d in serializer.data:
key = f"{d['grade_level']} {d['subject_abbr']}"
if key in curriculum_map:
curriculum_map[key].append(d)
else:
curriculum_map[key] = [d, ]
return curriculum_map
else:
data = 'null'
return data
The table3 has all details for ratings like source, report_url, name,label,value,label_colour I want this values as show in the JSON below.
This error usually occurs if the user account was not given/assigned to the Virtual Machine User Login role on the VMs.
Please check this https://learn.microsoft.com/en-us/azure/active-directory/devices/howto-vm-sign-in-azure-ad-windows#azure-role-not-assigned to assign required roles and try login.

How to group by date in queryset

I need help with writing proper queryset in Django View. I have Post model with created_at (datetime field). I want to group it by date and return in a specific format.
models.py
class Post(TrackableDate):
title = models.CharField(max_length=255)
body = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
views.py
class PostViewSet(mixins.ListModelMixin,
viewsets.GenericViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
serializers.py
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = '__all__'
The usual response looks like:
[
{
"id": 1,
"text": "ok",
"created_at": "2012-12-12T12:30:00"
},
{
"id": 2,
"text": "ok",
"created_at": "2012-12-12T12:30:00"
},
{
"id": 3,
"text": "ok",
"created_at": "2012-12-13T12:30:00"
}
]
How to group and return like that?
{
"2012-12-12": [
{
"id": 1,
"text": "ok",
"created_at": "2012-12-12T12:30:00"
},
{
"id": 2,
"text": "ok",
"created_at": "2012-12-12T12:30:00"
}
],
"2012-12-13": [
{
"id": 3,
"text": "ok",
"created_at": "2012-12-13T12:30:00"
}
]
}
I tried to do
Post.objects.extra(select={'created_at': 'date(created_at)'}).values('created_at').annotate(available=Count('created_at'))
But it returns
<QuerySet [{'created_at': '2020-07-04', 'available': 7}, {'created_at': '2020-07-09', 'available': 2}]>
This is a helper function, you can accomplish your results like this, it may be a better solution. This is the one that I came up with
def group_by_date(mydata):
new_data = {}
for element in mydata:
v = element['created_at'].split('T')[0]
if v not in new_data.keys():
new_data[v] = []
new_data[v].append(element)
return new_data
You can override the get function of your API and rework the output
and override return Response(group_by_date(serializer.data), HTTP_200_OK)

How to merge two object of rest api in django rest framwork?

This my views.py file
class NewsChannelListView(ObjectMultipleModelAPIView):
def get_querylist(self, *args, **kwargs):
userId = self.request.user.id
queryset = [
{'queryset': News_Channel.objects.all(),
'serializer_class': NewsChannelSerializers},
{'queryset': Count.objects.filter(userId=userId),
'serializer_class': CountSerializers},
]
return queryset
I am getting following respose from my this views
{
"News_Channel": [
{
"id": 2,
"name": "Republic",
"info": "Arnab Goswami News channel",
"image": "https://fourthpillar-static.s3.amazonaws.com/media/repiblic_1bRFWNZ.png",
"website": "https://www.repu",
"total_star": 10,
"total_user": 2
},
{
"id": 1,
"name": "NDTV",
"info": "India News Media",
"image": "https://fourthpillar-static.s3.amazonaws.com/media/ndtv_WH67OhA.png",
"website": "https://ndtv.com",
"total_star": 18,
"total_user": 2
}
],
"Count": [
{
"userId": 1,
"channelId": 2,
"rate": 6
},
{
"userId": 1,
"channelId": 1,
"rate": 8
}
]
}
Is there any way I can get single object.Channel id 2 in count merge with the id 2 in news channel and Channel id 1 in count merge with the id 1 in news channel. So final Response shoule be like this
{
"News_Channel": [
{
"id": 2,
"name": "Republic",
"info": "Arnab Goswami News channel",
"image": "https://fourthpillar-static.s3.amazonaws.com/media/repiblic_1bRFWNZ.png",
"website": "https://www.repu",
"total_star": 10,
"total_user": 2,
"userId": 1,
"rate": 6
},
{
"id": 1,
"name": "NDTV",
"info": "India News Media",
"image": "https://fourthpillar-static.s3.amazonaws.com/media/ndtv_WH67OhA.png",
"website": "https://ndtv.com",
"total_star": 18,
"total_user": 2,
"userId": 1,
"rate": 8
}
],
}
Code of the model
class News_Channel(models.Model):
name = models.TextField(blank=False)
info = models.TextField(blank=False)
image = models.FileField()
website = models.TextField()
total_star = models.PositiveIntegerField(default=0)
total_user = models.IntegerField()
class Meta:
ordering = ["-id"]
def __str__(self):
return self.name
class Count(models.Model):
userId = models.ForeignKey(User, on_delete=models.CASCADE)
channelId = models.ForeignKey(News_Channel, on_delete=models.CASCADE)
rate = models.PositiveIntegerField(default=0)
class Meta:
ordering = ["-id"]
I am using django 2.1.7 and the djangorestframework==3.9.2.

Get Count Of Nested Entities using Laravel Eloquent

I have got 3 database tables clients, coupons and categories
clients table
id,
name,
website,
description,
logo,
slug
categories table
id,
name,
slug
coupons table
id,
client_id,
category_id,
type,
coupon,
title,
description,
link,
views,
slug,
expiry
The relationship is
1) many coupons belongs to client ( many to one relationship)
2) many coupons belongs to category ( many to one relationship)
I am using laravel 5.1.
How can i get the unique count of clients with the clients details , number of coupons a client has and the count of total categories an individual client has.
simplified : i need to get the client details and display that xxx number of coupons are available in the xxx number of categories for a particular client.
so far i can get the unique client details and the number of the coupons count.
public function getAvailableClientsWithItemCountList($page = 1)
{
return Client::join('coupons', 'clients.id', '=', 'coupons.client_id')
->join('categories', 'coupons.category_id', '=', 'categories.id')
->where('coupons.expiry', '>', Carbon::today())
->groupBy('clients.id')
->skip(STORES_PER_REQUEST*($page-1))
->take(STORES_PER_REQUEST)
->get(['clients.id', 'clients.name', 'clients.slug', 'clients.logo', DB::raw('count(clients.id) as dealsCount'), DB::raw('count(categories.id) as categoriesCount')]);
}
STORES_PER_REQUEST = 9 (constant) for paginating.
thanks in advance.
If you have your relationships set up you could do something like:
/**
* Mock relationship for eager loading coupon count
*
* #return mixed
*/
public function couponCount()
{
return $this->hasOne(Coupon::class)
->selectRaw('client_id, count(*) as aggregate')
->groupBy('client_id');
}
public function getCouponCountAttribute()
{
// if relation is not loaded already, let's do it first
if (!$this->relationLoaded('couponCount')) {
$this->load('couponCount');
}
$related = $this->getRelation('couponCount');
// then return the count directly
return ($related) ? (int) $related->aggregate : 0;
}
The above can be used in your Client model, and then you can just alter the couponCount relationship method for your Category model (if you wanted to).
Then add the following for your Category count:
/**
* Mock relationship for eager loading category count
*
* #return mixed
*/
public function categoryCount()
{
return $this->hasOne(Coupon::class)
->selectRaw('category_id, count(*) as aggregate')
->groupBy('client_id, category_id');
}
public function getCategoryCountAttribute()
{
// if relation is not loaded already, let's do it first
if (!$this->relationLoaded('categoryCount')) {
$this->load('categoryCount');
}
$related = $this->getRelation('categoryCount');
// then return the count directly
return ($related) ? (int) $related->aggregate : 0;
}
You can then add a query scope in your Coupon model for getting coupons that haven't expired by something like:
public function scopeActive($query)
{
$query->where('expiry', '>', Carbon::today());
}
If you're only ever going to be getting the count for coupons that haven't expired you can add you can add this straight on to the relationship e.g.
groupBy('client)id')->active()
Now you should be able to eager load the relationship like so:
$clients = Client::with('couponCount', 'clientCount')
->skip(STORES_PER_REQUEST * ($page - 1))
->take(STORES_PER_REQUEST)
->get();
Or you could attach the query scope to the eager load i.e.
$clients = Client::with(['couponCount' => function ($q) {$q->active()}, 'clientCount' => function ($q) {$q->active()}]) ...
Hope this helps!
Okay i figured out myself with additional info as coupon type and the categories available.
The key i did was just added the case in count and removed the join of the categories table.
the final code looked like this
return App\Client::join('coupons', 'clients.id', '=', 'coupons.client_id')
->where('coupons.expiry', '>', \Carbon\Carbon::today())
->orderBy('clients.position', 'desc')
->groupBy('clients.id')
->skip(STORES_PER_REQUEST*(1-1))
->take(STORES_PER_REQUEST)
->get(['clients.id', 'clients.name', 'clients.slug', 'clients.logo', DB::raw('count(clients.id) as total'), DB::raw('count(CASE WHEN coupons.type=\'Coupon\' THEN 1 ELSE NULL END) as couponsCount'), DB::raw('count(CASE WHEN coupons.type=\'Deals\' THEN 1 ELSE NULL END) as dealsCount'), DB::raw('count(Distinct category_id) as categoriesCount')]);
The result was
[{
"id": "8",
"name": "Archies Online",
"slug": "archies-online",
"logo": "Archiesonline.jpg",
"total": "22",
"couponsCount": "20",
"dealsCount": "2",
"categoriesCount": "9"
}, {
"id": "5",
"name": "Shop Clues",
"slug": "shop-clues",
"logo": "Shopclues.jpg",
"total": "24",
"couponsCount": "24",
"dealsCount": "0",
"categoriesCount": "9"
}, {
"id": "6",
"name": "Lens Kart",
"slug": "lens-kart",
"logo": "Lenskart.jpg",
"total": "25",
"couponsCount": "25",
"dealsCount": "0",
"categoriesCount": "8"
}, {
"id": "7",
"name": "Amazer",
"slug": "amazer",
"logo": "Amzer.jpg",
"total": "21",
"couponsCount": "21",
"dealsCount": "0",
"categoriesCount": "8"
}, {
"id": "1",
"name": "Flipkart",
"slug": "flipkart",
"logo": "Flipkart.jpg",
"total": "17",
"couponsCount": "17",
"dealsCount": "0",
"categoriesCount": "9"
}, {
"id": "2",
"name": "Make My Trip",
"slug": "make-my-trip",
"logo": "Makemytrip.jpg",
"total": "11",
"couponsCount": "11",
"dealsCount": "0",
"categoriesCount": "8"
}]
This did the trick for now :);

Couchbase N1QL: Update on nested Documents

When having a nested document like this:
{
"blog": "testblog1",
"user_id": 41,
"comments": [
{
"comment": "testcomment1",
"user_id": 883
},
{
"comment": "testcomment2",
"user_id": 790
}
]
}
can one do an Update Operation with N1QL to add an extra field to a subdocument?
I would like to add the field "ranking" : "top comment" to the subdocument with the comment testcomment2:
{
"blog": "testblog1",
"user_id": 41,
"comments": [
{
"comment": "testcomment1",
"user_id": 883
},
{
"comment": "testcomment2",
"user_id": 790,
"ranking" : "top comment"
}
]
}
Site Note: The following statement would add a field to the root document on the Collection Blog:
UPDATE Blog SET rank = "top blog" WHERE blog = "testblog1";
Yes, you can do the following in N1QL:
UPDATE Blog
SET c.ranking = "top comment" FOR c IN comments WHEN c.comment = "testcomment2" END
WHERE blog = "testblog1"