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())

Обратите внимание, что это не решение для ошибки, которую я получил, но это решение для того, что я пытался решить.

Вернуться на верх