在GAE + WTForms中处理价格数值

0 投票
1 回答
597 浏览
提问于 2025-04-17 08:11

我在使用谷歌应用引擎(Python)和Jinja2,想要利用webapp2的功能来处理货币的国际化和本地化,也就是把价格存储为数字,并通过webapp2内置的i18n方法进行正确的格式化。因此,我首先需要确保我的价格数据类型是正确的。

我正在从一个原型迁移过来,之前是用字符串来表示价格,现在想用更实际的实现方式,使用某种数字数据类型来表示价格,同时再加一个变量来表示货币类型。所以在迁移的过程中,我会同时使用这两个变量,逐步将价格从字符串转换为数字,去掉字符串数据类型,并进行一个mapreduce任务来更新旧的数据实体,把价格从文本/字符串转换为基于数字的变量,比如整数或小数。

我的环境(谷歌应用引擎)不支持小数数据类型,我也可以只使用整数,因为我的使用场景是买卖比较贵的物品,比如房子、车子等,这些情况下美元是重要的,而分通常只显示为00。所以我放弃了实现小数属性的项目,反正也有点麻烦,现在我想知道设置价格的正确方式是什么,是否在保存之前必须进行类型转换?

也就是说,这样做是否正确:

form = EntityForm(self.request.params)
if form.validate():
    entity.title = form.title.data
    entity.name = form.name.data
    entity.email = form.email.data
    entity.text = form.text.data
    entity.set_password(form.password.data)
    entity.price = form.price.data
    try:
      if form.price.data:
        form.price.data=form.price.data.replace(',','.').replace(' ', '')
        entity.integer_price = int(form.price.data)
    except:
      pass

或者我不需要将变量转换为整数,类型转换会在后台自动处理?我认为表单变量是字符串,而价格必须是数字才能正确排序和过滤。

当我使用自定义的小数属性时,开始出现了这个错误:

Traceback (most recent call last):
  File "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.3/webapp2.py", line 545, in dispatch
    return method(*args, **kwargs)
  File "/base/data/home/apps/s~montaoproject/validation.355292363398040911/i18n.py", line 653, in get
    ads = paginator.page(page)
  File "/base/data/home/apps/s~montaoproject/validation.355292363398040911/paginator.py", line 42, in page
    return Page(self.object_list[bottom:top], number, self)
  File "/base/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 2189, in __getitem__
    return self.fetch(limit, start)
  File "/base/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 2084, in fetch
    return list(self.run(limit=limit, offset=offset, **kwargs))
  File "/base/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 2237, in next
    return self.__model_class.from_entity(self.__iterator.next())
  File "/base/python27_runtime/python27_lib/versions/1/google/appengine/ext/search/__init__.py", line 463, in from_entity
    return super(SearchableModel, cls).from_entity(entity)
  File "/base/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 1411, in from_entity
    entity_values = cls._load_entity_values(entity)
  File "/base/python27_runtime/python27_lib/versions/1/google/appengine/ext/db/__init__.py", line 1387, in _load_entity_values
    value = prop.make_value_from_datastore(entity[prop.name])
  File "/base/data/home/apps/s~montaoproject/validation.355292363398040911/main.py", line 98, in make_value_from_datastore
    return decimal.Decimal(value)
  File "/base/python27_runtime/python27_dist/lib/python2.7/decimal.py", line 548, in __new__
    "Invalid literal for Decimal: %r" % value)
  File "/base/python27_runtime/python27_dist/lib/python2.7/decimal.py", line 3844, in _raise_error
    raise error(explanation)
InvalidOperation: Invalid literal for Decimal: u''

我使用的代码是:

class DecimalProperty(db.Property):
  data_type = decimal.Decimal

  def get_value_for_datastore(self, model_instance):
    return str(super(DecimalProperty, self).get_value_for_datastore(model_instance))

  def make_value_from_datastore(self, value):
    return decimal.Decimal(value)

  def validate(self, value):
    value = super(DecimalProperty, self).validate(value)
    if value is None or isinstance(value, decimal.Decimal):
      return value
    elif isinstance(value, basestring):
      return decimal.Decimal(value)
    raise db.BadValueError("Property %s must be a Decimal or string." % self.name)

但这对我来说不管用,我开始得到“None”这个字符串,表示没有价格的物品,这导致我的应用无法正常加载,所以我完全放弃了小数的尝试,转而使用整数属性,并验证输入要么为空,要么是整数,这可以通过wtforms来实现。

所以我不再使用自定义的小数属性,我们决定使用整数属性,最坏的情况是如果需要处理货币的分数,我可以使用另一个变量“cents”,这样我就可以用整数表示美元和分,而后者很少或几乎不用。

所以我现在计划的解决方案很简单:

class Article(GeoModel, search.SearchableModel):

integer_price = IntegerProperty() #store dollars as dollars
currency = db.StringProperty(choices=(
    'EUR',
    'ARS',
    'AUD',
    'BRL',
    'GBP',
    'CAD',
    'CZK',
    'DKK',
    'HKD',
    'HUF',
    'ILS',
    'INR',
    'JPY',
    'MXN',
    'NZD',
    'NOK',
    'PLN',
    'PHP',
    'SGD',
    'SEK',
    'SGD',
    'CHF',
    'USD',
    'THB',
    'TWB',
    ), verbose_name='Currency')
price = db.StringProperty(verbose_name='price')  #quit using this and use a number instead

你知道我是否真的需要进行类型转换,或者还有其他可能会遇到的陷阱吗?你同意我在这种情况下不需要小数吗(比如广告一辆车、广告一栋房子),尽管在更复杂的定价情况下,我可能需要禁止某些情况,比如“每加仑20美元”这种价格我无法处理,但可以在文本中说明,因为这是特殊的定价(按单位体积),同样如果价格是以分为单位存储。将价格存储为整数属性将允许更好的价格格式化,尤其是因为我的应用是国际化和本地化的。

感谢任何回答或评论。

1 个回答

1

请查看一下问题编号 6595这个。如果你的问题和这些内容有关,可能可以通过补丁来解决。如果不相关,我建议你在代码中使用一个“except”块来捕获错误,并打印出相关的变量,特别是每个单独的序号,以确保传给Decimal的字符是合法的、可以接受的。对我来说,系统报错的字符是一个空字符串,这看起来有点奇怪,所以我们要确认所有的字符都是有效的,因为我们在处理的是Unicode字符。

撰写回答