我正在尝试实现一个简单的商店,但我有一个问题:
背景:
我正在做表单验证(在表单类中),如果用户试图购买比他们拥有的更多的商品,那么表单将返回错误。
如果在表单验证的时候没有出现这样的问题,那么进一步在我的View类(ProductDetail)的form_valid()方法中,从这个产品中取出用户购买的数量。
而实际上的问题是,是否需要在第 2 点再次检查仓库中是否有足够的货物(直接在视图中),还是仅在第 1 点进行此检查就足够了?
会不会有条件竞争导致货物被双重拿走?还是其他一些与交易相关的问题?假设两个用户尝试同时购买 10 件和 10 件,而库存只有 10 件。
我附上了下面的代码,并用评论标记了有争议的地方。
我的视图类:
class ProductDetail(FormView):
form_class = SaleForm
template_name = "shop/product_detail.html"
success_url = reverse_lazy('product_list')
def get_initial(self):
initial = super(ProductDetail, self).get_initial()
initial.update({'amount': 1, 'product_id': self.kwargs['product_id']})
return initial
def dispatch(self, request, *args, **kwargs):
product = self.check_product_exist(self.request, self.kwargs['product_id'])
if not product: return redirect('product_list')
sellers_qs = self.check_seller_exist(self.request, product)
if not sellers_qs: return redirect('product_list')
self.kwargs['product'] = product
self.kwargs['sellers_qs'] = sellers_qs
return super(ProductDetail, self).dispatch(request, *args, **kwargs)
def get_form_kwargs(self):
form_kwargs = super(ProductDetail, self).get_form_kwargs()
form_kwargs['sellers_qs'] = self.kwargs['sellers_qs']
form_kwargs['max_amount'] = self.kwargs['product'].amount
return form_kwargs
def form_valid(self, form):
product = self.check_product_exist(self.request, form.cleaned_data['product_id'])
"""
(!) Нужно ли здесь повторно делать проверку на то, есть ли на складе достаточное кол-во единиц товара!?
"""
product.amount -= form.cleaned_data['amount']
product.save()
Sale.objects.create(seller=form.cleaned_data['sellers'], product=product, amount_sold=form.cleaned_data['amount'],
purchase_amount=product.price * form.cleaned_data['amount'])
return super(ProductDetail, self).form_valid(form)
def get_context_data(self, **kwargs):
product = self.check_product_exist(self.request, self.kwargs['product_id'])
context = super().get_context_data(**kwargs)
context["product"] = product
return context
def check_product_exist(self, request, product_id):
try:
product = Product.objects.filter(pk=product_id)[0]
return product
except IndexError:
messages.add_message(request, messages.ERROR, 'Нужный товар не был найден, возможно он был удален.')
return None
def check_seller_exist(self, request, product):
sellers_qs = Seller.objects.filter(product=product.pk)
if sellers_qs: return sellers_qs
messages.add_message(request, messages.ERROR, 'Продавцы для данной позиции отсутствуют в базе данных.')
return None
我的表单类:
class SaleForm(forms.Form):
amount = forms.IntegerField(label='Кол-во', min_value=1)
product_id = forms.IntegerField(widget=forms.HiddenInput())
sellers = forms.ModelChoiceField(label='Продавцы', queryset=Seller.objects.none())
def clean(self):
cleaned_data = super().clean()
amount = cleaned_data.get("amount")
product_id = cleaned_data.get("product_id")
seller = cleaned_data.get("sellers")
if amount < 1:
raise forms.ValidationError(
"Указано неверное количесто товара."
)
if not Product.objects.filter(pk=product_id):
raise forms.ValidationError(
"Товар не найден в базе данных."
)
if Product.objects.get(pk=product_id).amount < amount:
raise forms.ValidationError(
"На складе нет столько единиц товара, выберите другое количество."
)
if not Seller.objects.filter(name=seller):
raise forms.ValidationError(
"Продавец не найден в базе данных."
)
if not Seller.objects.filter(product=product_id):
raise forms.ValidationError(
"Продавец не продает данный товар."
)
def __init__(self, *args, **kwargs):
# Устанавливаем в форме продавцов, которые были переданны и максимальное кол-во товара, которое можно выбрать в форме.
qs = kwargs.pop('sellers_qs')
max_amount = kwargs.pop('max_amount')
super(SaleForm, self).__init__(*args, **kwargs)
self.fields['sellers'].queryset = qs
self.fields['amount'].max_value = max_amount
self.fields['amount'].widget.attrs['max'] = max_amount