Django - Функция с шаблоном возвращает `TypeError: недостаточно аргументов для строки формата`.

Я пытаюсь использовать функцию FORMAT от PostgreSQL в Django для форматирования строк телефонных номеров.

Я могу сделать это с помощью следующего SQL запроса:

SELECT
       phone_number, FORMAT('(%s) %s-%s', SUBSTRING(phone_number,3,3), SUBSTRING(phone_number,6,3), SUBSTRING(phone_number,9,4))
FROM core_user
WHERE phone_number iS NOT NULL

, который возвращает результат вида:

enter image description here

Пытаясь реализовать это в Django для использования в ORM запросе, я сделал следующее:

class FormatPhoneNumber(Func):
    function = "FORMAT"
    template = "%(function)s('(%s) %s-%s', SUBSTRING(%(expressions)s,3,3), SUBSTRING(%(expressions)s,6,3), SUBSTRING(%(expressions)s,9,4))"

ORM запрос:

User.objects.annotate(phone_number2=FormatPhoneNumber(f"phone_number"))

Возвращает следующую ошибку:

File /venv/lib/python3.10/site-packages/django/db/models/expressions.py:802, in Func.as_sql(self, compiler, connection, function, template, arg_joiner, **extra_context)
    800 arg_joiner = arg_joiner or data.get("arg_joiner", self.arg_joiner)
    801 data["expressions"] = data["field"] = arg_joiner.join(sql_parts)
--> 802 return template % data, params

TypeError: not enough arguments for format string

Я полагаю, что это происходит из-за этой строки '(%s) %s-%s', которая поставляется в функцию FORMAT.

У кого-нибудь есть идеи, как я могу заставить это работать?

Да, вы используете два последовательных процента для получения процента после форматирования, так:

class FormatPhoneNumber(Func):
    function = "FORMAT"
    template = "%(function)s('(%%s) %%s-%%s', SUBSTRING(%(expressions)s,3,3), SUBSTRING(%(expressions)s,6,3), SUBSTRING(%(expressions)s,9,4))"

Но обычно форматирование не выполняется базой данных, обычно вы делаете это в модели, в поле модели, или в представлении или шаблоне.

Я не смог заставить работать решение с использованием функции FORMAT, но я заставил это работать с REGEXP_REPLACE, что дает тот же результат:

class FormatPhoneNumber2(Func):
    function = "REGEXP_REPLACE"
    template = "%(function)s(SUBSTRING(%(expressions)s,3,10), '(\d{3})(\d{3})(\d{4})', '(\\1) \\2-\\3')"


In [27]: result = (
    ...:     User.objects.filter(phone_number__isnull=False)
    ...:     .annotate(phone_number2=FormatPhoneNumber2("phone_number"))
    ...:     .values_list("phone_number", "phone_number2")
    ...: )

In [28]: for user in result:
    ...:     print(user)
('+16502553199', '(650) 255-3199')
('+12147047099', '(214) 704-7099')
('+12147547099', '(214) 754-7099')
Вернуться на верх