peewee orm,如何将 SQL 转换为 orm

peewee orm, How to convert SQL to orm

小便:3.14.4 databse:postgresql

table定义

from peewee import *
from peewee import TextField
from playhouse.postgres_ext import DateTimeTZField

from model import BaseModel as  Model, database


class AdAccount(Model):
    account_id = BigIntegerField(primary_key=True)
    bm_id = BigIntegerField(null=True, verbose_name="bm账号id", index=True)
    bm_name = TextField(null=True, verbose_name="bm账号名称")
    account_name = TextField(null=True, verbose_name="广告账号名称")
    status = IntegerField(default=0, verbose_name="广告账号状态")


class Campaign(Model):
    campaign_id = BigIntegerField(primary_key=True, verbose_name="广告系列Id")
    campaign_name = CharField(null=True, verbose_name="广告系列名称")
    status = CharField(null=True, verbose_name="广告系列状态")
    effective_status = CharField(null=True, verbose_name="广告系列有效状态")
    daily_budget = CharField(null=True, verbose_name="广告系列预算")

    account_id = BigIntegerField(verbose_name="广告账号Id")

class AdSet(Model):
    adset_id = BigIntegerField(null=True, verbose_name="广告组Id", index=True)
    adset_name = CharField(verbose_name="广告组名称")
    status = CharField(verbose_name="广告组状态")
    effective_status = CharField(verbose_name="广告组有效状态")
    daily_budget = CharField(null=True, verbose_name="每日预算")
    optimization_goal = CharField(null=True, verbose_name="优化目标")

    campaign_id = BigIntegerField(verbose_name="广告系列Id")
    account_id = BigIntegerField(verbose_name="广告账号Id")

class Ad(Model):
    ad_id = BigIntegerField(verbose_name="广告Id", index=True)
    ad_name = CharField(verbose_name="广告名称")
    # 广告
    link = CharField(null=True, verbose_name="落地页链接")

    # 状态
    status = CharField(verbose_name="广告状态")
    effective_status = CharField(verbose_name="广告有效状态")
    adset_id = BigIntegerField(verbose_name="广告组Id")
    account_id = BigIntegerField(verbose_name="广告账号Id")


# 成效数据表
class AdInsights(Model):
    id = PrimaryKeyField()
    ad_id = BigIntegerField(verbose_name="广告Id", index=True)
    adset_id = BigIntegerField(verbose_name="广告组Id",index=True)
    campaign_id = BigIntegerField(verbose_name="广告系列Id",index=True)
    account_id = BigIntegerField(verbose_name="账号Id")

    # 基础数据
    impressions = FloatField(null=True, verbose_name="展示次数")
    clicks = FloatField(null=True, verbose_name="链接点击次数")
    unique_clicks = FloatField(null=True, verbose_name="链接点击人数")
    spend = FloatField(null=True, verbose_name="花费")
    reach = FloatField(null=True, verbose_name="覆盖人数")
    frequency = FloatField(null=True, verbose_name="频次")
    cpc = FloatField(null=True, verbose_name="单次点击成本")
    cpm = FloatField(null=True, verbose_name="千人展现成本")
    ctr = FloatField(null=True, verbose_name="点击率")
    purchase_roas = FloatField(null=True, verbose_name="广告花费回报roas")
    cpp = FloatField(null=True)

    date = DateField(null=True, index=True)  # 时间
    create_time = DateTimeTZField()
    update_time = DateTimeTZField()

    # 成效  actions中获取
    purchase = FloatField(null=True, verbose_name="购买")
    add_to_cart = FloatField(null=True, verbose_name="加入购物车")
    landing_page_view = FloatField(null=True, verbose_name="落地页浏览量")
    omni_complete_registration = FloatField(null=True, verbose_name="注册")
    app_install = FloatField(null=True, verbose_name="应用安装量")

    # # 手动计算
    # purchase_rate = FloatField(null=True, verbose_name="购买转化率")  # 购买/点击量
    # purchase_cost = FloatField(null=True, verbose_name="购买成本")  # 花费/购买
    # add_to_chart_rate = FloatField(null=True, verbose_name="加入购物车转化率")  # 加入购物车/点击量
    # add_to_cart_cost = FloatField(null=True, verbose_name="加入购物车成本")  # 花费/加入购物车
    # app_installs_rate = FloatField(null=True, verbose_name="应用安装转化率")  # 应用安装量/点击量
    # app_install_cost = FloatField(null=True, verbose_name="应用安装成本")  # 花费/应用安装量

    # # 其他
    # cost_per_unique_click = TextField(null=True)
    # cost_per_unique_action_type = TextField(null=True)
    # cost_per_action_type = TextField(null=True)
    # actions = TextField(null=True)
    # unique_actions = TextField(null=True)
    # action_values = TextField(null=True)
    # cost_per_conversion = TextField(null=True)
    # conversions = TextField(null=True)
    # conversion_values = TextField(null=True)

if __name__ == '__main__':
    database.create_tables([AdInsights, Ad, AdAccount, AdSet, Campaign])

我写的orm是这样的

ad_insights_alias = AdInsights.alias()
cte = ad_insights_alias.select(ad_insights_alias.adset_id, fn.SUM(ad_insights_alias.impressions),ad_insights_alias.date) \
    .where(ad_insights_alias.date >= start_date, ad_insights_alias.date <= end_date).group_by(ad_insights_alias.adset_id, ad_insights_alias.date)
total = cte.count()
cte = cte.paginate(1, 10).alias('jq')
query = cte.select().join(AdSet,on=AdSet.adset_id==cte.c.adset_id).join(AdAccount,on=AdSet.account_id==AdAccount.account_id)
print(query)


'''
SELECT "t1"."id", "t1"."adset_id", "t1"."adset_name", "t1"."status", 
        "t1"."effective_status", "t1"."daily_budget", "t1"."optimization_goal", 
        "t1"."campaign_id", "t1"."account_id" 
        FROM "ad_set" AS "t1" 
        RIGHT OUTER JOIN 
        (SELECT "t2"."adset_id", SUM("t2"."impressions") 
            FROM "ad_insights" AS "t2" 
            WHERE (("t2"."date" >= '2021-08-04') AND ("t2"."date" <= '2021-08-05')) 
            GROUP BY "t2"."adset_id", "t2"."date" LIMIT 10 OFFSET 0
        ) AS "jq" ON ("jq"."adset_id" = "t1"."adset_id")

'''

我要的SQL是

SELECT * from (SELECT adset_id,date,SUM(clicks) FROM ad_insights GROUP BY adset_id,date limit 2 offset 1)作为 t1 在 t1.adset_id=t2.adset_id 加入 ad_set 作为 t2 在 t2.account_id=ad_account.account_id;

加入 ad_account

如何编写ORM

我已经简化了你的模型,这样我就可以解决这个问题。我相信这是有效的:

class Account(Base):
    account_id = AutoField()
    name = TextField()

class AdSet(Base):
    adset_id = AutoField()
    account = ForeignKeyField(Account)

class AdInsight(Base):
    adset = ForeignKeyField(AdSet)
    date = DateField()
    clicks = IntegerField()

我创建了一些测试数据,以下查询对我来说运行良好:

# First create the subquery we will be joining against.
# You can uncomment the limit/offset as I had commented
# them out to simplify verifying the results.
subq = (AdInsight
        .select(AdInsight.adset, AdInsight.date, fn.SUM(AdInsight.clicks).alias('total_clicks'))
        .group_by(AdInsight.adset, AdInsight.date)
        #.limit(2)
        #.offset(1)
        .alias('subq'))

# Then we'll select from AdSet, Account as well as from
# our subquery:
query = (AdSet
         .select(Account, AdSet, subq.c.date, subq.c.total_clicks)
         .join_from(AdSet, Account, on=(AdSet.account == Account.account_id))
         .join_from(AdSet, subq, on=(AdSet.adset_id == subq.c.adset_id)))

# Now we can retrieve our data:
for row in query:
    print(row.account.name, row.adinsight.date, row.adinsight.total_clicks)

# If you just want the plain row dicts, you can instead:
for row in query.dicts():
    ...

正在生成的 SQL 看起来像:

SELECT "t1"."account_id", "t1"."name", "t2"."adset_id",
       "t2"."account_id", "subq"."date", "subq"."total_clicks" 
FROM "adset" AS "t2" 
INNER JOIN "account" AS "t1" 
    ON ("t2"."account_id" = "t1"."account_id") 
INNER JOIN (
    SELECT "t3"."adset_id", "t3"."date", SUM("t3"."clicks") AS "total_clicks" 
    FROM "adinsight" AS "t3" 
    GROUP BY "t3"."adset_id", "t3"."date" -- limit/offset would be here
) AS "subq" 
    ON ("t2"."adset_id" = "subq"."adset_id")
ad_insights_alias = AdInsights.alias()
cte = ad_insights_alias.select(ad_insights_alias.adset_id,
                               fn.SUM(ad_insights_alias.cliks).alias("cliks"),
                               ad_insights_alias.date).alias("date").where(ad_insights_alias.date >= start_date, ad_insights_alias.date <= end_date).group_by(ad_insights_alias.adset_id, ad_insights_alias.date)
total = cte.count()
cte = cte.paginate(1, 10).alias('jq')
query = cte.select_from(AdSet.adset_name,cte.c.impressions,AdAccount.bm_name).join(AdSet,on=AdSet.adset_id==cte.c.adset_id).join(AdAccount,on=AdSet.account_id==AdAccount.account_id)
print(query)