如何将视图中获取的Product实例传入Django表单进行库存校验?
Solution: Pass Product Instance to Form for Quantity Validation
Got it, let's fix this. The core issue is getting access to the Product instance from your make_sale view inside the form's clean_quantity method. Here's a straightforward way to do it:
Step 1: Modify the Form to Accept a Product Instance
Update your AddSaleForm in forms.py to accept a product parameter in its constructor, then store it as an instance attribute so the clean method can use it:
from django import forms from .models import Sale, Product from django.forms.widgets import NumberInput class AddSaleForm(forms.ModelForm): def __init__(self, *args, **kwargs): # Extract the product from kwargs before calling parent __init__ self.product = kwargs.pop('product', None) super().__init__(*args, **kwargs) class Meta: model = Sale fields = ['quantity', 'selling_price'] widgets = { 'quantity': NumberInput(attrs={'class': 'form-control', 'placeholder': 'items number'}), 'selling_price': NumberInput(attrs={'class': 'form-control', 'placeholder': '1000.00'}), } def clean_quantity(self, *args, **kwargs): sale_quantity = self.cleaned_data.get('quantity') if sale_quantity == 0: raise forms.ValidationError('The sale quantity cannot be zero (0)') # Now we can access the product's quantity directly if self.product and sale_quantity > self.product.quantity: raise forms.ValidationError( f'The sale quantity cannot exceed the available quantity. The available quantity for this product is {self.product.quantity}' ) return sale_quantity
Key Notes for the Form:
- We use
kwargs.pop('product', None)to safely retrieve the product parameter without breaking the parent class's initialization (since the baseModelFormdoesn't expect this parameter). TheNonedefault prevents errors if the product isn't passed accidentally. - In
clean_quantity, we first check ifself.productexists before validating the quantity—this adds a safety net. - F-strings make the error message cleaner and easier to read compared to string concatenation.
Step 2: Pass the Product Instance When Initializing the Form
Update your make_sale view in views.py to pass the product instance when creating the form (for both POST and GET requests):
from django.shortcuts import render, get_object_or_404, redirect, reverse from django.contrib import messages from .models import Product, Sale from .forms import AddSaleForm def make_sale(request, id): # Use get_object_or_404 instead of get() to handle missing products gracefully product = get_object_or_404(Product, id=id) if request.method == 'POST': # Pass the product to the form here form = AddSaleForm(request.POST, product=product) if form.is_valid(): quantity = form.cleaned_data['quantity'] selling_price = form.cleaned_data['selling_price'] Sale.objects.create( product=product, quantity=quantity, selling_price=selling_price, sold_by=request.user ) # Update product stock and sale count new_quantity = product.quantity - quantity new_sale_count = product.number_of_sales + 1 Product.objects.filter(id=product.id).update( quantity=new_quantity, number_of_sales=new_sale_count ) messages.success( request, f'{quantity} item(s) sold for {product.name} at Kshs. {selling_price} successfully!' ) return redirect(reverse('products:todays_sales')) else: # Pass product to the form for GET requests too (for consistent behavior) form = AddSaleForm(product=product) # Assume you're rendering a template with the form and product return render(request, 'sales/make_sale.html', {'form': form, 'product': product})
Key Notes for the View:
- I replaced
Product.objects.get(id=id)withget_object_or_404(Product, id=id)—this returns a 404 page if the product doesn't exist, which is better than throwing an uncaughtDoesNotExisterror. - We pass
product=productwhen initializing the form for both POST and GET requests. This ensures the form always has access to the product instance, even if the user submits invalid data and needs to see the form again with error messages. - Cleaned up the stock update logic to be more concise.
How It Works
- When the view initializes the form, it passes the
productinstance as a keyword argument. - The form's
__init__method extracts this instance and stores it asself.product. - When
clean_quantityruns during form validation, it usesself.product.quantityto check if the submitted quantity exceeds available stock.
内容的提问来源于stack exchange,提问作者Kevin_Ravasco
相关产品推荐
相关产品推荐

