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
, который возвращает результат вида:
Пытаясь реализовать это в 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')
