How can I get computed elements of a table in a django queryset?

后端 未结 5 1520
野的像风
野的像风 2021-01-17 20:53

I\'m trying to use django\'s queryset API to emulate the following query:

SELECT EXTRACT(year FROM chosen_date) AS year, 
EXTRACT(month FROM chosen_date) AS          


        
5条回答
  •  陌清茗
    陌清茗 (楼主)
    2021-01-17 21:03

    Well here're some workarounds

    1. In your particular case you could do it with one extra:

    if use_date_due:
        sum_qs = sum_qs.extra(select={
                              'year': 'EXTRACT(year FROM coalesce(date_due, date))',
                              'month': 'EXTRACT(month FROM coalesce(date_due, date))',
                              'is_paid':'date_paid IS NOT NULL'
                            })
    

    2. It's also possible to use plain python to get data you need:

    for x in sum_qs:
        chosen_date = x.date_due if use_date_due and x.date_due else x.date
        print chosen_date.year, chosen_date.month
    

    or

    [(y.year, y.month) for y in (x.date_due if use_date_due and x.date_due else x.date for x in sum_qs)]
    

    3. In the SQL world this type of calculating new fields is usually done by uing subquery or common table expression. I like cte more because of it's readability. It could be like:

    with cte1 as (
        select
            *, coalesce(date_due, date) as chosen_date
        from polls_invoice
    )
    select
        *,
        extract(year from chosen_date) as year,
        extract(month from chosen_date) as month,
        case when date_paid is not null then 1 else 0 end as is_paid
    from cte1
    

    you can also chain as many cte as you want:

    with cte1 as (
        select
            *, coalesce(date_due, date) as chosen_date
        from polls_invoice
    ), cte2 as (
        select
            extract(year from chosen_date) as year,
            extract(month from chosen_date) as month,
            case when date_paid is not null then 1 else 0 end as is_paid
        from cte2
    )
    select
        year, month, sum(is_paid) as paid_count
    from cte2
    group by year, month
    

    so in django you can use raw query like:

    Invoice.objects.raw('
         with cte1 as (
            select
                *, coalesce(date_due, date) as chosen_date
            from polls_invoice
        )
        select
            *,
            extract(year from chosen_date) as year,
            extract(month from chosen_date) as month,
            case when date_paid is not null then 1 else 0 end as is_paid
        from cte1')
    

    and you will have Invoice objects with some additional properties.

    4. Or you can simply substitute fields in your query with plain python

    if use_date_due:
        chosen_date = 'coalesce(date_due, date)'
    else: 
        chosen_date = 'date'
    
    year = 'extract(year from {})'.format(chosen_date)
    month = 'extract(month from {})'.format(chosen_date)
    fields = {'year': year, 'month': month, 'is_paid':'date_paid is not null'}, 'chosen_date':chosen_date)
    sum_qs = sum_qs.extra(select = fields)
    

提交回复
热议问题