Django raises set-returning функции не допускаются в WHERE с аннотированным значением
Мне нужно запросить заказы, в которых почтовый индекс адреса доставки находится в определенном диапазоне. Для этого мне нужно привести почтовый индекс (поле charfield) к целому числу, удалив нечисловые значения.
Я написал эту функцию базы данных для этого, которая работает:
class NumericPostcode(models.Func):
"""
Removes characters from field and converts to integer
"""
function = "REGEXP_MATCHES"
template = "CAST( (%(function)s(%(expressions)s, '\\d+'))[1] as INTEGER )"
output_field = models.IntegerField()
<
orders_qs = Order.objects.annotate(
numeric_postcode=NumericPostcode(models.F("shipping_address__postcode"))
).filter(self.get_range_query())
Метод get_range_query - это метод, который строит динамический запрос:
def get_range_query(self):
"""
Build the range query dynamically based on all postcode ranges linked to this transporter
https://docs.djangoproject.com/en/dev/ref/models/querysets/#range
"""
query = models.Q()
for postcode_range in self.postcode_ranges.all():
query |= models.Q(numeric_postcode__range=(postcode_range.min_postcode, postcode_range.max_postcode))
return query
Однако этот запрос вызывает ошибку:
NotSupportedError at /admin/shop/reports/
set-returning functions are not allowed in WHERE
Без .filter(self.get_range_query())
я могу нормально печатать аннотированное поле, и оно также работает.
Необработанный SQL-запрос выглядит следующим образом:
SELECT "order_order"."id",
"order_order"."number",
"order_order"."site_id",
"order_order"."basket_id",
"order_order"."user_id",
"order_order"."billing_address_id",
"order_order"."currency",
"order_order"."total_incl_tax",
"order_order"."total_excl_tax",
"order_order"."shipping_incl_tax",
"order_order"."shipping_excl_tax",
"order_order"."shipping_address_id",
"order_order"."shipping_method",
"order_order"."shipping_code",
"order_order"."status",
"order_order"."guest_email",
"order_order"."date_placed",
"order_order"."delivery_time_text",
"order_order"."delivery_time_days",
cast( (Regexp_matches("order_shippingaddress"."postcode", '\d+'))[1] AS INTEGER ) AS "numeric_postcode"
FROM "order_order"
LEFT OUTER JOIN "order_shippingaddress"
ON (
"order_order"."shipping_address_id" = "order_shippingaddress"."id")
WHERE (
cast( (regexp_matches("order_shippingaddress"."postcode", '\d+'))[1] AS INTEGER ) BETWEEN 1000 AND 1789
OR cast( (regexp_matches("order_shippingaddress"."postcode", '\d+'))[1] AS INTEGER ) BETWEEN 1798 AND 2099
OR cast( (regexp_matches("order_shippingaddress"."postcode", '\d+'))[1] AS INTEGER ) BETWEEN 3700 AND 3899
OR cast( (regexp_matches("order_shippingaddress"."postcode", '\d+'))[1] AS INTEGER ) BETWEEN 7200 AND 8799
OR cast( (regexp_matches("order_shippingaddress"."postcode", '\d+'))[1] AS INTEGER ) BETWEEN 8898 AND 8898
OR cast( (regexp_matches("order_shippingaddress"."postcode", '\d+'))[1] AS INTEGER ) BETWEEN 9000 AND 9159
OR cast( (regexp_matches("order_shippingaddress"."postcode", '\d+'))[1] AS INTEGER ) BETWEEN 9167 AND 9999)
ORDER BY "order_order"."date_placed" DESC
Мои знания о SQL-запросах не так уж велики; я потратил пару часов, пытаясь понять, могу ли я как-то запросить это значение, но безуспешно.
Если кто-нибудь может мне помочь, было бы здорово!
Заранее спасибо!
В итоге я использовал "regexp_replace", который идеально подходит для того, что я хотел сделать. Похоже, я пропустил эту функцию, когда просматривал документацию postgres.
В моем случае решение было следующим:
def get_order_qs(self, qs):
return qs.annotate(
numeric_postcode=models.functions.Cast(models.Func(
models.F("shipping_address__postcode"),
models.Value("[^0-9.]+"),
models.Value(""), function="regexp_replace"
), output_field=models.IntegerField())
).filter(self.get_range_query())
Обратите внимание, что это не решение для ошибки, которую я получил, но это решение для того, что я пытался решить.