Alpine linux で Django の DecimalField の quantize に失敗した場合

投稿者: ytyng 4 年, 8 ヶ月 前

Alpine の Docker 環境で、Django Cartridge のテストに失敗していた

  File "/var/src/venv/lib/python3.6/site-packages/django/db/models/sql/compiler.py", line 958, in prepare_value
value = field.get_db_prep_save(value, connection=self.connection)
File "/var/src/venv/lib/python3.6/site-packages/django/db/models/fields/__init__.py", line 1612, in get_db_prep_save
self.max_digits, self.decimal_places)
File "/var/src/venv/lib/python3.6/site-packages/django/db/backends/base/operations.py", line 493, in adapt_decimalfield_value
return utils.format_number(value, max_digits, decimal_places)
File "/var/src/venv/lib/python3.6/site-packages/django/db/backends/utils.py", line 200, in format_number
value = value.quantize(decimal.Decimal(".1") ** decimal_places, context=context)
decimal.InvalidOperation: [<class 'decimal.InvalidOperation'>]
InvalidOperation: [<class 'decimal.InvalidOperation'>]


が出ていた。

調査すると

decimal_places が 127 になってた。

(別のテスト環境だと0で、テストが成功していた)

decimal_places は、

./manage.py shell

from locale import localeconv; localeconv()
{'currency_symbol': '¥',
'decimal_point': '.',
'frac_digits': 0,
'grouping': [],
'int_curr_symbol': 'JPY ',
'int_frac_digits': 0,
'mon_decimal_point': '.',
'mon_grouping': [3, 3, 0],
'mon_thousands_sep': ',',
'n_cs_precedes': 1,
'n_sep_by_space': 0,
'n_sign_posn': 4,
'negative_sign': '-',
'p_cs_precedes': 1,
'p_sep_by_space': 0,
'p_sign_posn': 1,
'positive_sign': '',
'thousands_sep': ''}

この、frac_digits が使われる。

テストが失敗する環境だと

 {'currency_symbol': '',
'decimal_point': '.',
'frac_digits': 127,
'grouping': [],
'int_curr_symbol': '',
'int_frac_digits': 127,
'mon_decimal_point': '',
'mon_grouping': [],
'mon_thousands_sep': '',
'n_cs_precedes': 127,
'n_sep_by_space': 127,
'n_sign_posn': 127,
'negative_sign': '',
'p_cs_precedes': 127,
'p_sep_by_space': 127,
'p_sign_posn': 127,
'positive_sign': '',
'thousands_sep': ''}


となっている。ロケールが足りないっぽい。



Dockerfile をこのように書いて、ロケール musl-locales を作ってみたが

ENV MUSL_LOCPATH="/usr/share/i18n/locales/musl"

RUN apk --no-cache add libintl && \
apk --no-cache --virtual .locale_build add cmake make musl-dev gcc gettext-dev git && \
git clone https://gitlab.com/ytyng/musl-locales && \
cd musl-locales && cmake -DLOCALE_PROFILE=OFF -DCMAKE_INSTALL_PREFIX:PATH=/usr . && make && make install && \
cd .. && rm -r musl-locales && \
apk del .locale_build


それでも int_frac_digits は変更されなかった。

方法がわからなかったので、
django の settings で

setattr(locale, '_override_localeconv',
{'currency_symbol': '¥',
'decimal_point': '.',
'frac_digits': 0,
'grouping': [],
'int_curr_symbol': 'JPY ',
'int_frac_digits': 0,
'mon_decimal_point': '.',
'mon_grouping': [3, 3, 0],
'mon_thousands_sep': ',',
'n_cs_precedes': 1,
'n_sep_by_space': 0,
'n_sign_posn': 4,
'negative_sign': '-',
'p_cs_precedes': 1,
'p_sep_by_space': 0,
'p_sign_posn': 1,
'positive_sign': '',
'thousands_sep': ''})


こんな感じで、locale._override_localeconv をパッチすると、テストが通るようになった。

ロケールが絡む環境では、Alpine Linux より Debian などを使ったほうがいいかもしれない。

現在未評価

コメント

アーカイブ

2024
2023
2022
2021
2020
2019
2018
2017
2016
2015
2014
2013
2012
2011