I'd like to use the following raw query in a ListAPIView in the Django REST Framework.
I'm not sure how to use a raw SQL query instead of a queryset to support pagination.
I've looked into it and didn't find much that made sense to me.
If I want to use a query set, it would look like this
How do I do this if I want to use raw SQL queries?
class VideoListView(generics.ListAPIView):
queryset = Video.objects.raw(...)
serializer_class = VideoSerializer
SELECT DISTINCT "t"."id",
"t"."title",
"t"."thumbnail_url",
"t"."preview_url",
"t"."embed_url",
"t"."duration",
"t"."views",
"t"."is_public",
"t"."published_at",
"t"."created_at",
"t"."updated_at",
EXISTS
(SELECT (1) AS "a"
FROM "videos_history" U0
WHERE (U0."user_id" IS NULL
AND U0."video_id" = "t"."id")
LIMIT 1) AS "is_viewed",
EXISTS
(SELECT (1) AS "a"
FROM "videos_favorite" U0
WHERE (U0."user_id" IS NULL
AND U0."video_id" = "t"."id")
LIMIT 1) AS "is_favorited",
EXISTS
(SELECT (1) AS "a"
FROM "videos_track" U0
INNER JOIN "videos_playlist" U1 ON (U0."playlist_id" = U1."id")
WHERE (U1."is_wl"
AND U1."user_id" IS NULL
AND U0."video_id" = "t"."id")
LIMIT 1) AS "is_wl"
FROM (
(SELECT "videos_video".*
FROM "videos_video"
WHERE ("videos_video"."is_public"
AND "videos_video"."published_at" <= '2022-01-03 05:20:16.725884+00:00'
AND "videos_video"."title" LIKE '%word')
ORDER BY "videos_video"."published_at" DESC
LIMIT 20)
UNION
(SELECT "videos_video".*
FROM "videos_video"
LEFT OUTER JOIN "videos_video_tags" ON ("videos_video"."id" = "videos_video_tags"."video_id")
LEFT OUTER JOIN "videos_tag" ON ("videos_video_tags"."tag_id" = "videos_tag"."id")
WHERE ("videos_video"."is_public"
AND "videos_video"."published_at" <= '2022-01-03 05:20:16.725884+00:00'
AND "videos_tag"."name" LIKE '%word')
ORDER BY "videos_video"."published_at" DESC
LIMIT 20)
) AS t
ORDER BY "t"."published_at" DESC
LIMIT 20;
Do I need to create a custom paginator or something? I'm even more confused because I'm using LIMIT and other methods for subqueries.
--- Add models.py, etc... ---
# models.py
class Tag(models.Model):
name = models.CharField(unique=True, max_length=30)
created_at = models.DateTimeField(default=timezone.now)
...
class Video(models.Model):
title = models.CharField(max_length=300)
tags = models.ManyToManyField(Tag, blank=True)
updated_at = models.DateTimeField(auto_now=True)
...
class History(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
video = models.ForeignKey(Video, on_delete=models.CASCADE)
...
class Favorite(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE
video = models.ForeignKey(Video, on_delete=models.CASCADE)
...
class Playlist(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
is_wl = models.BooleanField(default=False, editable=False)
...
class Track(models.Model):
playlist = models.ForeignKey(Playlist, on_delete=models.CASCADE, null=True)
video = models.ForeignKey(Video, on_delete=models.CASCADE)
...
The slow queryset and SQL queries that were originally used.
Video.objects.annotate(
is_viewed=Exists(History.objects.filter(user=user, video=OuterRef("pk"))),
is_favorited=Exists(
Favorite.objects.filter(user=user, video=OuterRef("pk"))
),
is_wl=Exists(
Track.objects.filter(
playlist__user=user, playlist__is_wl=True, video=OuterRef("pk")
)
),
).filter(
Q(title__contains=value)
| Q(tags__name__contains=value)),
is_public=True,
published_at__lte=timezone.now(),
).order_by("-published_at").distinct()
SELECT DISTINCT "videos_video"."id",
"videos_video"."published_at",
EXISTS
(SELECT (1) AS "a"
FROM "videos_history" U0
WHERE (U0."user_id" IS NULL
AND U0."video_id" = "videos_video"."id")
LIMIT 1) AS "is_viewed",
EXISTS
(SELECT (1) AS "a"
FROM "videos_favorite" U0
WHERE (U0."user_id" IS NULL
AND U0."video_id" = "videos_video"."id")
LIMIT 1) AS "is_favorited",
EXISTS
(SELECT (1) AS "a"
FROM "videos_track" U0
INNER JOIN "videos_playlist" U1 ON (U0."playlist_id" = U1."id")
WHERE (U1."is_wl"
AND U1."user_id" IS NULL
AND U0."video_id" = "videos_video"."id")
LIMIT 1) AS "is_wl"
FROM "videos_video"
LEFT OUTER JOIN "videos_video_tags" ON ("videos_video"."id" = "videos_video_tags"."video_id")
WHERE ("videos_video"."is_public"
AND "videos_video"."published_at" <= '2021-12-27 13:34:29.103369+00:00'
AND ("videos_video"."title" &#~ 'word'
OR "videos_video_tags"."tag_id" IN
(SELECT U0."id"
FROM "videos_tag" U0
WHERE U0."name" &#~ 'word')))
ORDER BY "videos_video"."published_at" DESC
LIMIT 20;
If you are asking about generic PageNumberPagination from rest_framework.pagination, usually it's done this way. You make your own pagination class with default values:
class VideoPagination(PageNumberPagination):
max_page_size = 100
page_size_query_param = 'page_size'
page_size = 25
Then you just add it to your view:
class VideoListView(generics.ListAPIView):
queryset = Video.objects.all()
serializer_class = VideoSerializer
pagination_class = VideoPagination
That's all, you can use parameters page and page_size.
Related
I am trying to construct a double count left join query in Slick 3.1 similar to this:
SELECT
COUNT(p.id) AS replies,
COUNT(f.id) AS images
FROM posts AS p
LEFT JOIN files AS f
ON p.id = f.post_id
WHERE thread = :thread_id
Join part of the query is quite simple and looks like this:
val joinQ = (threadId: Rep[Long]) =>
(postDAO.posts joinLeft fileRecordDAO.fileRecords on (_.id === _.postId))
.filter(_._1.thread === threadId)
Using joinQ(1L).map(_._1.id).length generates COUNT(1) which counts all rows - not the result I want to obtain. Using joinQ(1L).map(_._1.id).countDistinct generates COUNT(DISTINCT p.id) which is somewhat what I'm looking for, but trying to do two of these generates this monstrosity:
select x2.x3, x4.x5
from (select count(distinct x6.`id`) as x3
from `posts` x6
left outer join `files` x7 on x6.`id` = x7.`post_id`
where x6.`thread` = 1) x2,
(select
count(distinct (case when (x8.`id` is null) then null else 1 end)) as x5
from `posts` x9
left outer join `files` x8 on x9.`id` = x8.`post_id`
where x9.`thread` = 1) x4
Here's the double countDistinct code:
val q = (threadId: Rep[Long]) => {
val join = joinQ(threadId)
val q1 = join.map(_._1.id).countDistinct // map arg type is
val q2 = join.map(_._2).countDistinct // (Post, Rep[Option[FileRecord]])
(q1, q2)
}
Any ideas? :)
Count is aggregation function, so you also need grouping by some field (e.x. id). Try next query:
def joinQ(threadId: Long) = (postDAO.posts joinLeft fileRecordDAO.fileRecords on (_.id === _.postId))
.filter(_._1.thread === threadId)
val q = (threadId: Long) => {
joinQ(threadId).groupBy(_._1.id).map {
case (id, qry) => (id, qry.map(_._1.id).countDistinct, qry.map(_._2.map(_.id)).countDistinct)
}
}
It generates next sql:
SELECT x2.`id`,
Count(DISTINCT x2.`id`),
Count(DISTINCT x3.`id`)
FROM `posts` x2
LEFT OUTER JOIN `files` x3
ON x2.`id` = x3.`post_id`
WHERE x2.`thread` = 10
GROUP BY x2.`id`
I am trying to translate this sql into a slick 3.1 style collection query (single call). This sql (postgres) returns what I am looking for:
select
minDate.min as lastModified,
(select count("id") from "Items" where "orderId" = 1) as totalItemCount,
(select count("id") from "Items" where "orderId" = 1 and "dateModified" >= minDate.min) as addedCount
from
(select min("dateModified") as "min" from "Items" where "orderId" = 1 and "state" = 'new') as minDate
Returns: for a specified set of Items (from orderId), returns:
date of item last modified
total number of items
number of items added since the lastModified
But after many attempts, I can't figure out how to translate this to a single slick-style query
This codes
import scala.slick.driver.PostgresDriver
case class Item(id: Int, orderId: Int, state: String, dateModified: Int)
object SlickComplexQuery {
def main(args: Array[String]) = {
val driver = PostgresDriver
import driver.simple._
class ItemsTable(tag: Tag) extends Table[Item](tag, "Items"){
def id = column[Int]("id")
def orderId = column[Int]("orderId")
def state = column[String]("state")
def dateModified = column[Int]("dateModified")
def * = (id, orderId, state, dateModified) <> (Item.tupled, Item.unapply)
}
val items = TableQuery[ItemsTable]
val query1 = items
.filter(i => i.orderId === 1 && i.state === "new")
.map(_.dateModified)
.min
val query2 = items
.filter(_.orderId === 1)
.map(_.id)
.length
val query3 = items
.filter(i => i.orderId === 1 && i.dateModified >= query1)
.map(_.id)
.length
val query = Query(query1, query2, query3)
results in such query:
select x2.x3, x4.x5, x6.x7
from (select min(x8.x9) as x3
from (select x10."dateModified" as x9
from "Items" x10
where (x10."orderId" = 1) and (x10."state" = 'new')) x8) x2,
(select count(1) as x5
from (select x11."id" as x12
from "Items" x11
where x11."orderId" = 1) x13) x4,
(select count(1) as x7
from (select x14."id" as x15
from "Items" x14, (select min(x16.x17) as x18
from (select x19."dateModified" as x17
from "Items" x19
where (x19."orderId" = 1) and (x19."state" = 'new')) x16) x20
where (x14."orderId" = 1) and (x14."dateModified" >= x20.x18)) x21) x6
This query is much alike yours, slick 2.0 was used.
I have two tables. One is todays prices and one is yesterdays prices. There is a job which updates each table over night. Yesterdays prices is a copy of todays before it gets updated.
I am trying to create a table which shows the differences between the tables.
To do this I am using a full outer join. I am filtering down my criteria to make it faster as both tables are over 48 million rows.
My current code is like this.
WITH differ AS
(
SELECT
tAP.CustomerID, tAP.ProductID, yAP.CustomerID AS 'Yest_CustomerID', yAP.ProductID AS 'Yest_ProductID',
tAP.PDG, tAP.DiscPct1, tAP.DiscPct2,
yAP.DiscPct1 AS 'Yest_DiscPct1',
yAP.DiscPct2 AS 'Yest_DiscPct2',
CONVERT(DECIMAL(18,2),tAP.DiscPct1 - yAP.DiscPct1) AS 'DiscPct1_Difference',
CONVERT(DECIMAL(18,2),tAP.DiscPct2 - yAP.DiscPct2) AS 'DiscPct2_Difference',
tAP.internalSPANumber, yAP.internalSPANumber AS 'Yest_InternalSPANumber', tAP.SPAUniqueReference,
tAP.Project,
tAP.ExpiryDate,tAP.Subaddress, tAP.PriceType, yAP.PriceType AS 'Yest_PriceType', tAP.ListPrice,
tAP.NettPrice, yAP.NettPrice AS 'Yest_NettPrice',
CONVERT(DECIMAL(18,2),tAP.NettPrice- yAP.NettPrice) AS 'NettPrice_Difference'
FROM tbl_Prices tAP FULL OUTER JOIN tbl_Prices_Yesterday yAP ON tAP.CustomerId = yAP.CustomerID AND tAP.ProductID = yAP.ProductID
AND tAP.PDG = yAP.PDG AND tAP.Project = yAP.Project AND tAP.PriceType = yAP.PriceType
WHERE (((tAP.DiscPct1 <> yAP.DiscPct1)
OR (tAP.DiscPct2 <> yAP.DiscPct2)
OR (yAP.CustomerID IS NULL)
OR (tAP.CustomerID IS NULL)
OR (tAP.NettPrice <> yAP.NettPrice)
OR (tAP.InternalSPANumber <> yAP.InternalSPANumber))
AND
(
tAP.ProductID = '10238610' OR tAP.ProductID = '10238620'
OR tAP.ProductID = '10238621' OR tAP.ProductID = '10238687'
OR tAP.ProductID = '10238688' OR yAP.ProductID = '10238610'
OR yAP.ProductID = '10238620' OR yAP.ProductID = '10238621'
OR yAP.ProductID = '10238687' OR yAP.ProductID = '10238688')
)
)
SELECT * INTO tbl_Difference FROM differ
Anyway to make this faster?
Can you filter the 2 large tables before you try to join them using AND filter in your where?
WITH tAPcte AS
(
SELECT * -- fields you need
FROM tbl_Prices
WHERE ProductID IN ('10238610',
'10238620',
'10238621',
'10238687',
'10238688')
),
yAPcte AS
(
SELECT * -- fields you need
FROM tbl_Prices_Yesterday
WHERE ProductID IN ('10238610',
'10238620',
'10238621',
'10238687',
'10238688')
)
differ AS
(
SELECT
tAP.CustomerID, tAP.ProductID, yAP.CustomerID AS 'Yest_CustomerID', yAP.ProductID AS 'Yest_ProductID',
tAP.PDG, tAP.DiscPct1, tAP.DiscPct2,
yAP.DiscPct1 AS 'Yest_DiscPct1',
yAP.DiscPct2 AS 'Yest_DiscPct2',
CONVERT(DECIMAL(18,2),tAP.DiscPct1 - yAP.DiscPct1) AS 'DiscPct1_Difference',
CONVERT(DECIMAL(18,2),tAP.DiscPct2 - yAP.DiscPct2) AS 'DiscPct2_Difference',
tAP.internalSPANumber, yAP.internalSPANumber AS 'Yest_InternalSPANumber', tAP.SPAUniqueReference,
tAP.Project,
tAP.ExpiryDate,tAP.Subaddress, tAP.PriceType, yAP.PriceType AS 'Yest_PriceType', tAP.ListPrice,
tAP.NettPrice, yAP.NettPrice AS 'Yest_NettPrice',
CONVERT(DECIMAL(18,2),tAP.NettPrice- yAP.NettPrice) AS 'NettPrice_Difference'
FROM tAPcte tAP FULL OUTER JOIN yAPcte yAP ON tAP.CustomerId = yAP.CustomerID AND tAP.ProductID = yAP.ProductID
AND tAP.PDG = yAP.PDG AND tAP.Project = yAP.Project AND tAP.PriceType = yAP.PriceType
WHERE (((tAP.DiscPct1 <> yAP.DiscPct1)
OR (tAP.DiscPct2 <> yAP.DiscPct2)
OR (yAP.CustomerID IS NULL)
OR (tAP.CustomerID IS NULL)
OR (tAP.NettPrice <> yAP.NettPrice)
OR (tAP.InternalSPANumber <> yAP.InternalSPANumber))
)
SELECT * INTO tbl_Difference FROM differ
am trying to write join query on two tables using django Queryset,so need suggestions to write
SQL join query using Querysets in Django..
here is My Query and Models of Tables
SELECT t.time, d.id2
FROM disp_b_time t JOIN disp_dispatch d ON t.id1 = d.id2
WHERE t.status = "completed" AND d.status = 0 AND d.vehicle_id =1
class B_Time(models.Model):
id1 = models.ForeignKey(Book)
dispatcher= models.ForeignKey(D)
status = models.CharField(max_length=128, choices=B_STATUS)
time = models.DateTimeField(auto_now=True, auto_now_add=True)
register = models.DateField()
modified = models.DateTimeField(auto_now=True, auto_now_add=True)
class Dispatch(models.Model):
id2 = models.ForeignKey(Book)
vehicle = models.ForeignKey(Vehicle)
driveId = models.ForeignKey(Drive)
dispId = models.ForeignKey(D)
status = models.CharField(max_length=128, choices=STATUS)
register = models.DateTimeField()
modified = models.DateTimeField(auto_now=True, auto_now_add=True)
Thanks in Advance...
d = Dipatch.objects.filter(vehicle__pk=1)\
.filter(status=0).filter(dispID__b_time__status='completed')
Why
d = Dipatch.objects.filter(vehicle__pk=1)\
.filter(status=0).filter(dispID__b_time__status='completed')
?
We can simply write it as
d = Dipatch.objects.filter(vehicle__pk=1, status=0, dispID__b_time__status='completed')
SELECT
PC_SL_ACNO, -- DB ITEM
SLNAME, -- ACCOUNT NAME:
SL_TOTAL_AMOUNT -- TOTAL AMOUNT:
FROM GLAS_PDC_CHEQUES
WHERE PC_COMP_CODE=:parameter.COMP_CODE
AND pc_bank_from = :block02.pb_bank_code
AND pc_due_date between :block01.date_from
AND :block01.date_to
AND nvl(pc_discd,'X') IN(‘X’, 'R')
GROUP BY
pc_comp_code, pc_sl_ldgr_code, pc_sl_acno
ORDER BY pc_sl_acno
ACCOUNT NAME:
BEGIN
SELECT COAD_PTY_FULL_NAME INTO :BLOCK03.SLNAME
FROM GLAS_PTY_ADDRESS,GLAS_SBLGR_MASTERS
WHERE SLMA_COMP_CODE = :PARAMETER.COMP_CODE
AND SLMA_ADDR_ID = COAD_ADDR_ID
AND SLMA_ADDR_TYPE = COAD_ADDR_TYPE
AND SLMA_ACNO = :BLOCK03.PC_SL_ACNO
AND SLMA_COMP_CODE = COAD_COMP_CODE;
EXCEPTION WHEN OTHERS THEN NULL;
END;
TOTAL AMOUNT:
BEGIN
SELECT SUM(PC_AMOUNT) INTO :SL_TOTAL_AMOUNT
FROM GLAS_PDC_CHEQUES
WHERE PC_DUE_DATE BETWEEN :BLOCK01.DATE_FROM AND :BLOCK01.DATE_TO
AND PC_BANK_FROM = :block02.PB_BANK_CODE
AND PC_SL_ACNO = :BLOCK03.PC_SL_ACNO
AND NVL(PC_DISCD,'X') = 'R'
AND PC_COMP_CODE = :PARAMETER.COMP_CODE;
EXCEPTION WHEN OTHERS THEN :block03.SL_TOTAL_AMOUNT := 0;
END;
How can I join the three tables?
You'll have to adjust depending on precisely what criteria and required fields you have for your query or queries.
SELECT
c.PC_SL_ACNO,
a.COAD_PTY_FULL_NAME,
SUM(c.PC_AMOUNT)
FROM
GLAS_PDC_CHEQUES c
LEFT JOIN
GLAS_SBLGR_MASTERS m
ON ( c.PC_SL_ACNO = m.SLMA_ACNO
AND c.PC_COMP_CODE = m.SLMA_COMP_CODE
)
LEFT JOIN
GLAS_PTY_ADDRESS a
ON ( m.SLMA_ADDR_ID = a.COAD_ADDR_ID
AND m.SLMA_COMP_CODE = a.COAD_COMP_CODE
AND m.SLMA_ADDR_TYPE = a.COAD_ADDR_TYPE
)
WHERE
c.PC_COMP_CODE = :PARAMETER.COMP_CODE
AND c.PC_SL_ACNO = :BLOCK03.PC_SL_ACNO
AND c.PC_BANK_FROM = :BLOCK02.PB_BANK_CODE
AND NVL(c.PC_DISCD,'X') IN (‘X’, 'R')
AND c.PC_DUE_DATE BETWEEN :BLOCK01.DATE_FROM AND :BLOCK01.DATE_TO
GROUP BY
c.PC_SL_ACNO, -- not sure which grouping exactly you need.
a.COAD_PTY_FULL_NAME
ORDER BY
c.PC_SL_ACNO
I notice that in the first query you have pc_comp_code as a search criterion, and on the leading edge of your grouping - is that what you need?
This is a bit of an 'estimate' due to the enigmatic nature of your question!