用例中不可出现重复的记录,如:下订单,用户填好表单然后Submit,当用户网速较慢时,很可能会习惯性的刷新当前页,而刷新操作会导致再次POST,此时若不加判断直接入库必然导致用户后台增加N个订单。
解决思路:
实现:
为了方便使用,我们将上述思路写成一个decorator(装饰器). 当然也是为了符合DRY嘛;代码很简单就看中间那几行,需注意唯一的一个参数page_key,为了不跟多个表单页面发生Session key冲突。(补充一点:必须将表单填写页面的view同时使用@never_cache装饰,因为django默认将所有view都做缓存,当再次进入表单页时,就不会重新生成随机串,导致校验无故失败。。。)
# coding:utf-8 |
|
try : |
from functools import wraps |
except ImportError: |
from django.utils.functional import wraps # Python 2.4 fallback. |
import random |
from django.conf import settings |
from django.utils.decorators import available_attrs |
from django.utils.hashcompat import md5_constructor |
|
if hasattr (random, 'SystemRandom' ): |
randrange = random.SystemRandom().randrange |
else : |
randrange = random.randrange |
_MAX_CSRF_KEY = 18446744073709551616L # 2 << 63 |
|
def _get_new_submit_key(): |
return md5_constructor( "%s%s" % (randrange( 0 , _MAX_CSRF_KEY), settings.SECRET_KEY)).hexdigest() |
|
def anti_resubmit(page_key = ''): |
def decorator(view_func): |
@wraps (view_func, assigned = available_attrs(view_func)) |
def _wrapped_view(request, * args, * * kwargs): |
if request.method = = 'GET' : |
request.session[ '%s_submit' % page_key] = _get_new_submit_key() |
print 'session:' + request.session.get( '%s_submit' % page_key) |
elif request.method = = 'POST' : |
old_key = request.session.get( '%s_submit' % page_key, '') |
if old_key = = '': |
from django.http import HttpResponseRedirect |
return HttpResponseRedirect( '/page_expir' ) |
request.session[ '%s_submit' % page_key] = '' |
return view_func(request, * args, * * kwargs) |
return _wrapped_view |
return decorator |
1 @anti_resubmit(page_key='your_view')
2 def your_view(request):
3 '''若是表单填写页和POST的view不是同一个,則需在两个view上都使用anti_resubmit装饰器'''
4 pass #您可别跟着pass噢