Django视图getGiftFromFaucet返回None报错与水龙头发币异常求解
问题根因与修复方案
一、ValueError报错原因
Django要求所有视图函数必须返回HttpResponse对象,你的getGiftFromFaucet视图中,POST请求场景下如果表单验证不通过(form.is_valid()返回False),没有任何返回逻辑,函数直接结束返回None,因此触发该报错。
二、水龙头领币逻辑异常原因
1. 填写绑定地址无法收币
你使用的ReceiveTokenFromFaucet是关联Customer模型的ModelForm,而Customer的address字段设置了unique=True的唯一约束。当你提交已经注册过的绑定地址时,表单会触发唯一校验,form.is_valid()直接返回False,后续的转账逻辑完全不会执行,因此无法收到代币。
2. 填写其他地址时代币发到绑定地址
你现有逻辑存在两处不一致:
- 链上转账逻辑:确实是向表单填写的
customerAddress地址转币 - 数据库余额修改:无论填写什么地址,都是给当前登录用户绑定的
customer对象的tokenBalance加余额
你观察到的「发到最初绑定地址」实际是数据库余额加在了绑定用户上,链上代币实际已经转到了你填写的其他地址,二者不一致导致的认知偏差。
另外你代码中冗余的form.save(commit=False)没有任何后续操作,属于无效代码,不会产生实际影响。
三、修复方案
1. 替换ModelForm为普通Form
你不需要通过表单修改Customer对象的地址,仅需要接收地址参数,直接使用普通Form即可规避唯一校验的问题:
from django import forms class ReceiveTokenFromFaucet(forms.Form): address = forms.CharField(max_length=256, label="收款地址")
2. 修复视图缺返回值的问题
补充POST请求表单验证失败后的返回逻辑,同时可根据业务需求调整领币逻辑:如果要求只能向当前用户绑定地址发币,可直接删除表单的地址字段,直接从登录用户信息中取地址即可,避免用户填错。
修改后的视图参考:
def getGiftFromFaucet(request): customer = Customer.objects.get(user=request.user) # 如果要求只能领币到绑定地址,直接用customer.address即可,不需要用户填写 if request.method == 'POST': form = ReceiveTokenFromFaucet(request.POST) if form.is_valid(): # 要校验地址归属的话可在这里加逻辑:判断提交的地址是否等于customer.address customerAddress = form.cleaned_data['address'] # 建议把alreadyRecompensed改成数据库字段存储,不要用全局变量 if customerAddress not in alreadyRecompensed: contract.functions.transfer( customerAddress, 100000000000000000000 ).transact({'from': faucet}) alreadyRecompensed.append(customerAddress) # 如果是转去用户自己的地址再改余额,转其他地址不要改当前用户余额 if customerAddress == customer.address: customer.tokenBalance += 100000000000000000000 customer.save() messages.success(request, '代币已发放到对应地址') return redirect('/homepage/') else: messages.error(request, '该地址已经领取过水龙头代币') return redirect('/homepage/') # 表单验证失败的返回逻辑,修复ValueError else: context = {'form': form, 'alreadyRecompensed': alreadyRecompensed} return render(request, 'requireFromFaucet.html', context) else: form = ReceiveTokenFromFaucet() # 要默认填充绑定地址的话可以初始化表单:form = ReceiveTokenFromFaucet(initial={'address': customer.address}) context = {'form': form, 'alreadyRecompensed': alreadyRecompensed} return render(request, 'requireFromFaucet.html', context)
3. 优化存储方案
全局变量alreadyRecompensed在服务重启、多进程部署时都会失效,建议在Customer模型中新增is_received_faucet = models.BooleanField(default=False)字段来标记用户是否领过币,校验逻辑改成判断当前用户的该字段状态即可,更稳定可靠。
内容的提问来源于stack exchange,提问作者Matteo Gigli
相关产品推荐
相关产品推荐

