You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将视图中获取的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 base ModelForm doesn't expect this parameter). The None default prevents errors if the product isn't passed accidentally.
  • In clean_quantity, we first check if self.product exists 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) with get_object_or_404(Product, id=id)—this returns a 404 page if the product doesn't exist, which is better than throwing an uncaught DoesNotExist error.
  • We pass product=product when 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

  1. When the view initializes the form, it passes the product instance as a keyword argument.
  2. The form's __init__ method extracts this instance and stores it as self.product.
  3. When clean_quantity runs during form validation, it uses self.product.quantity to check if the submitted quantity exceeds available stock.

内容的提问来源于stack exchange,提问作者Kevin_Ravasco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 19:22:30