Python 一次从网店购买许多产品
通过我的支付系统(api.payson.se)只编写一个产品销售程序非常简单,但同时购买多个不同数量的产品给我带来了麻烦,因为它没有实现,我也不知道如何实现。现在我有了一个解决方案,我刚刚把它放在一起工作,但建模和控制流是一种非常快速和肮脏的,我想知道这是可以接受的,还是需要重写。系统现在可以正常工作,这样我就可以进入商店(步骤1)并输入我想要购买的产品的金额 然后,如果我按Buy(“Köp”)键,我的Python会正确计算总和,无论我所说的总量和产品的组合是什么,这都会起作用,本页还可以列出规范,但尚未实现: 总金额为瑞典货币,且其已向我的数据存储写入状态为“未付”的订单,并包含订购的产品以及数据存储中每个产品的金额: 然后,用户可以取消购买,或继续通过支付系统api.payson.se实际支付: 所以我所需要做的就是倾听Payson的回复,并更新已付款订单的状态。但是我的解决方案看起来不是很干净,我想知道我是否可以继续使用这样的代码,数据模型是两个字符串列表,一个带有数量,一个带有产品(项目ID),因为这是我可以解决它的最简单的方法,但是它不能直接访问,只能从列表中访问。我可以使用更好的数据模型吗 进行处理的代码有点凌乱,可以使用比字符串和列表更好的数据模型和算法:Python 一次从网店购买许多产品,python,google-app-engine,google-cloud-datastore,python-2.7,webshop,Python,Google App Engine,Google Cloud Datastore,Python 2.7,Webshop,通过我的支付系统(api.payson.se)只编写一个产品销售程序非常简单,但同时购买多个不同数量的产品给我带来了麻烦,因为它没有实现,我也不知道如何实现。现在我有了一个解决方案,我刚刚把它放在一起工作,但建模和控制流是一种非常快速和肮脏的,我想知道这是可以接受的,还是需要重写。系统现在可以正常工作,这样我就可以进入商店(步骤1)并输入我想要购买的产品的金额 然后,如果我按Buy(“Köp”)键,我的Python会正确计算总和,无论我所说的总量和产品的组合是什么,这都会起作用,本页还可以列出
class ShopHandler(NewBaseHandler):
@user_required
def get(self):
user = \
auth_models.User.get_by_id(long(self.auth.get_user_by_session()['user_id'
]))
self.render_jinja('shop.htm', items=Item.recent(), user=user)
return ''
@user_required
def post(self, command):
user = \
auth_models.User.get_by_id(long(self.auth.get_user_by_session()['user_id'
]))
logging.info('in shophandler http post item id'+self.request.get('item'))
items = [ self.request.get('items[1]'),self.request.get('items[2]'),self.request.get('items[3]'),self.request.get('items[4]'),self.request.get('items[5]'),self.request.get('items[6]'),self.request.get('items[7]'),self.request.get('items[8]') ]
amounts = [ self.request.get('amounts[1]'),self.request.get('amounts[2]'),self.request.get('amounts[3]'),self.request.get('amounts[4]'),self.request.get('amounts[5]'),self.request.get('amounts[6]'),self.request.get('amounts[7]'),self.request.get('amounts[8]') ]
total = 0
total = int(self.request.get('amounts[1]'))* long(Item.get_by_id(long(self.request.get('items[1]'))).price_fraction()) if self.request.get('amounts[1]') else total
total = total + int(self.request.get('amounts[2]'))* long(Item.get_by_id(long(self.request.get('items[2]'))).price_fraction()) if self.request.get('amounts[2]') else total
total = total + int(self.request.get('amounts[3]'))* long(Item.get_by_id(long(self.request.get('items[3]'))).price_fraction()) if self.request.get('amounts[3]') else total
total = total + int(self.request.get('amounts[4]'))* long(Item.get_by_id(long(self.request.get('items[4]'))).price_fraction()) if self.request.get('amounts[4]') else total
total = total + int(self.request.get('amounts[5]'))* long(Item.get_by_id(long(self.request.get('items[5]'))).price_fraction()) if self.request.get('amounts[5]') else total
total = total + int(self.request.get('amounts[6]'))* long(Item.get_by_id(long(self.request.get('items[6]'))).price_fraction()) if self.request.get('amounts[6]') else total
total = total + int(self.request.get('amounts[7]'))* long(Item.get_by_id(long(self.request.get('items[7]'))).price_fraction()) if self.request.get('amounts[7]') else total
total = total + int(self.request.get('amounts[8]'))* long(Item.get_by_id(long(self.request.get('items[8]'))).price_fraction()) if self.request.get('amounts[8]') else total
logging.info('total:'+str(total))
trimmed = str(total)+',00'
order = model.Order(status='UNPAID')
order.items = items
order.amounts = amounts
order.put()
logging.info('order was written')
ExtraCost = 0
GuaranteeOffered = 2
OkUrl = 'http://' + self.request.host + r'/paysonreceive/'
Key = '3110fb33-6122-4032-b25a-329b430de6b6'
text = 'niklasro@gmail.com' + ':' + str(trimmed) + ':' + str(ExtraCost) \
+ ':' + OkUrl + ':' + str(GuaranteeOffered) + Key
m = hashlib.md5()
BuyerEmail = user.email
AgentID = 11366
self.render_jinja('order.htm', order=order, user=user, total=total, Generated_MD5_Hash_Value = hashlib.md5(text).hexdigest(), BuyerEmail=user.email, Description='Bnano Webshop', trimmed=trimmed, OkUrl=OkUrl, BuyerFirstName=user.firstname, BuyerLastName=user.lastname)
我的订单模型是
class Order(db.Model):
'''a transaction'''
item = db.ReferenceProperty(Item)
items = db.StringListProperty()
amounts = db.StringListProperty()
owner = db.UserProperty()
purchaser = db.UserProperty()
created = db.DateTimeProperty(auto_now_add=True)
status = db.StringProperty( choices=( 'NEW', 'CREATED', 'ERROR', 'CANCELLED', 'RETURNED', 'COMPLETED', 'UNPAID', 'PAID' ) )
status_detail = db.StringProperty()
reference = db.StringProperty()
secret = db.StringProperty() # to verify return_url
debug_request = db.TextProperty()
debug_response = db.TextProperty()
paykey = db.StringProperty()
shipping = db.TextProperty()
产品的型号(即项目)为
class Item(db.Model):
'''an item for sale'''
owner = db.UserProperty() #optional
created = db.DateTimeProperty(auto_now_add=True)
title = db.StringProperty(required=True)
price = db.IntegerProperty() # cents / fractions, use price_decimal to get price in dollar / wholes
image = db.BlobProperty()
enabled = db.BooleanProperty(default=True)
silver = db.IntegerProperty() #number of silver
def price_dollars( self ):
return self.price / 100.0
def price_fraction( self ):
return self.price / 100.0
def price_silver( self ): #number of silvers an item "is worth"
return self.silver / 1000.000
def price_decimal( self ):
return decimal.Decimal( str( self.price / 100.0 ) )
def price_display( self ):
return str(self.price_fraction()).replace('.',',')
@staticmethod
def recent():
return Item.all().filter( "enabled =", True ).order('-created').fetch(10)
我想你现在已经知道发生了什么,这种方法对用户有效,但是代码看起来不太好。你认为我可以这样离开代码,继续保持这个“解决方案”吗?还是我必须重写以使它更合适?商店里只有8种产品,使用此解决方案,很难添加新的销售项目,因为那时我必须重新编程脚本,这并不完美
你能评论一下或者回答一下吗?我很高兴能得到一些关于这个快速而肮脏的用例解决方案的反馈
多谢各位
更新
我做了一次重写,以允许添加新产品,以下内容似乎比以前更好:
class ShopHandler(NewBaseHandler):
@user_required
def get(self):
user = \
auth_models.User.get_by_id(long(self.auth.get_user_by_session()['user_id'
]))
self.render_jinja('shop.htm', items=Item.recent(), user=user)
return ''
@user_required
def post(self, command):
user = \
auth_models.User.get_by_id(long(self.auth.get_user_by_session()['user_id'
]))
logging.info('in shophandler http post')
total = 0
order = model.Order(status='UNPAID')
for item in self.request.POST:
amount = self.request.POST[item]
logging.info('item:'+str(item))
purchase = Item.get_by_id(long(item))
order.items.append(purchase.key())
order.amounts.append(int(amount))
order.put()
price = purchase.price_fraction()
logging.info('amount:'+str(amount))
logging.info('product price:'+str(price))
total = total + price*int(amount)
logging.info('total:'+str(total))
order.total = str(total)
order.put()
trimmed = str(total).replace('.',',') + '0'
ExtraCost = 0
GuaranteeOffered = 2
OkUrl = 'http://' + self.request.host + r'/paysonreceive/'
Key = '6230fb54-7842-3456-b43a-349b340de3b8'
text = 'niklasro@gmail.com' + ':' + str(trimmed) + ':' \
+ str(ExtraCost) + ':' + OkUrl + ':' \
+ str(GuaranteeOffered) + Key
m = hashlib.md5()
BuyerEmail = user.email # if user.email else user.auth_id[0]
AgentID = 11366
self.render_jinja(
'order.htm',
order=order,
user=user,
total=total,
Generated_MD5_Hash_Value=hashlib.md5(text).hexdigest(),
BuyerEmail=user.email,
Description='Bnano Webshop',
trimmed=trimmed,
OkUrl=OkUrl,
BuyerFirstName=user.firstname,
BuyerLastName=user.lastname,
)
伙计,这真是个奇怪的密码。如果你想在你的商店里添加新的物品,你必须重写你的商店脚本。 在第一次从接口取消项目链接时,您必须向控制器发送带有项目ID和数量的POST请求,我不知道gae请求对象是如何工作的,但它必须是这样的: 从您的订单页面发出POST请求,其中包含真正需要{“item_id”:“qnt”}的物品。 在控制器中,您可以获取所有对象,如:
for item, qnt in request.POST:
{do something with each item, for example where you can sum total}
等等
不要直接将控制器与接口链接。若你们想制作一个真正灵活的应用程序,你们必须编写更多的抽象代码 我将试着关注您的代码中一个非常明显的问题,但其中有很多问题我不打算深入讨论。我的建议是马上停止。您正在实施基于web的支付系统。你真的应该把它留给那些有更多技能和经验的人。“基于网络的”在确保安全性的同时很难做到正确,但在线支付系统是一种薪酬丰厚、拥有数十年经验的咨询师所能做到的,而且他们仍然经常出错。你要承担很多法律责任 如果你还执著于此,请从头读到尾,可能会读几遍。Python是一种非常不同的语言,与您在脑海中死记硬背的任何经典OOP语言都不同。之后,至少浏览一下其他文档。如果您在这些方面遇到了问题,请拿起一本关于Python的O'Reilly书;从另一个角度来看应该会有所帮助。在您完成所有这些之后(可能同时),编写尽可能多的代码,如果您做错了,这些代码不会让您被遗忘。然后也许你可以写一个订单/付款系统 如果这听起来很苛刻,我很抱歉,但是这个世界不需要更多的劣质网络商店;1999年为我们解决了这个问题 无论如何,当您编写重复的内容并复制粘贴时,请转到您的代码:D,如下所示:
items = [ self.request.get('items[1]'),self.request.get('items[2]'),self.request.get('items[3]'),self.request.get('items[4]'),self.request.get('items[5]'),self.request.get('items[6]'),self.request.get('items[7]'),self.request.get('items[8]') ]
items = []
i = 0
while True:
try:
items.append(self.request[i + 1]) #attempt to get the next item
except IndexError as exc: #but if it fails...
break #we must be at the last one
i += 1
你应该想一想,“等一下!重复的任务正是计算机设计的目的。”你可以让你的文本编辑器来做这件事(见Vim宏),但简洁(但不要太简洁;)的代码总是比长代码好,因为你可以使它更快地维护,更少地发生程序员错误,更容易调试,更不用说不复制和粘贴节省的时间了,所以让我们改进代码
下面是我将如何在Python中修改这一点(高级程序员会在他们的头脑中进行修改,或者直接跳到最后):
实际上,限制物品的数量是相当业余的,只会让你的用户感到沮丧。您可以这样修复它:
items = [ self.request.get('items[1]'),self.request.get('items[2]'),self.request.get('items[3]'),self.request.get('items[4]'),self.request.get('items[5]'),self.request.get('items[6]'),self.request.get('items[7]'),self.request.get('items[8]') ]
items = []
i = 0
while True:
try:
items.append(self.request[i + 1]) #attempt to get the next item
except IndexError as exc: #but if it fails...
break #we must be at the last one
i += 1
我认为这是你现在应该采用的方式,因为它很清楚,但不重复。但是,您可以使用中的函数进一步缩短它
一些快速提示:
- 避免字符串连接,尤其是在用户提供字符串的情况下,尤其是涉及到用户通过web提供的字符串时。使用str.format和模字符串格式。奖励:您不必将所有内容都转换为字符串李>
- 从中间取出这些常量(例如,
),并将它们放在安全的地方(在模块的顶部,或包中的特殊文件中)ExtraCost=2
- 你太信任用户了