Django 根据条件获取带注释字段的值?

Django get a value for annotated field based on condition?

我有几个简单的模型,想用基于条件的值的字段注释查询。

    class Book(models.Model):
        price = models.DecimalField('Price', null=True, default=None, max_digits=32, decimal_places=2)
        ...

    class Config(models.Model):
        city_prices = models.JSONField(default={"Paris": 10, "London": 15}, null=True)
        ...

我试过这样查询这个模型:

from django.db.models import F, When, Case, Subquery

config = Config.objects.first()

Book.objects.annotate(custom_price=Case(
    When(price__isnull=False, then=F('price')),
    When(price__isnull=True, then=Subquery(config.city_prices.get(F('city'))))
)

我尝试使用 OuterRef 而不是 F() 但没有成功 either.The 第一个“When”效果很好但第二个让我在两种情况下都使用 F() 或OuterRef()

When(price__isnull=True, then=Subquery(db_conf.get(OuterRef('city'))))

AttributeError: 'NoneType' object has no attribute 'all'

我试图去掉第二个“When”和铁杆城市名称中的“F()”,但又出现另一个错误。

When(price__isnull=True, then=Subquery(config.city_prices.get('London')))

AttributeError: 'int' object has no attribute 'all'

此错误表明我得到了“伦敦”的值,但子查询尝试查询它。所以我得出一个结论,当我在之前的查询中尝试使用“F('city')”时,它返回了 None,我认为这是因为 F('city') refer到 Config 模型而不是 Book。

我尝试了不同的方法,但也不成功。

>>>from django.db.models.expressions import RawSQL
>>>Books.objects.annotate(custom_price=RawSQL('SELECT d.city_prices ->> %s FROM CONFIG_CONFIG d WHERE d.id = 1', (F('price'),)))

ProgrammingError: can't adapt type 'F'

我在这里读到 F() 无法与 RawSQL 协作。认为问题是我在查询结束时得到 dict 并想从中提取价值。那么我怎样才能实现我的目标,即根据条件为带注释的字段获取值?

第一种方法:在申请时计算 custom_price。它更简单,但您不能按 custom_price

订购图书
from django.db.models import F, When, Case, Subquery

config = Config.objects.first()


books = list(Book.objects.all())

for book in books:
    if book.price is not None:
        book.custom_price = book.price
    else:
         book.custom_price = config.city_prices.get(book.city)
         

第二种方法:计算 config.city_prices 的 Case-When 表达式并注释它:

from django.db.models import F, When, Case, Subquery, Value

config = Config.objects.first()

whens = []

for city, price in config.city_prices.items():
    whens.append(When(city=city, then=Value(price)) 

Book.objects.annotate(custom_price=Case(*whens))