django 根据为单独模型设置的值验证模型

lbsnaicq  于 2023-06-25  发布在  Go
关注(0)|答案(1)|浏览(144)
后台

我正在创建一个电子商务应用程序,它允许用户对任何活跃的物品列表提交出价-其中BidListing是**models.py(* below *)中不同的Django模型。
对于每个新的投标,通过模型表单设置
投标金额**(Bid.amount)。如果出价金额大于当前出价Listing.current_bid),则:

  1. Bid被认为是有效的,对象被保存。
  2. Listing.current_bid被更新为等于新的Bid.amount
问题

目前,我的解决方案(* 见***views.py***below *)达到了预期的结果-它成功地验证和更新了每个新的出价,使用条件连接调用is_valid()

if new_bid_form.is_valid() and new_bid_form.cleaned_data["current_bid"] > previous_bid:

然而,即使这种设计可以工作,它也让人感觉像是“* hackish *”,因为验证逻辑并没有发生在模型中。因此,在调用clean()时不会隐式检查条件,我可以预见这会导致问题的发生。相反,我希望在模型中实现验证逻辑,以便用Django的内置验证调用它,并将引发ValidationError

问题

1.我说我目前的“验证”解决方案设计得很差,对吗?
1.如果是这样,我如何在Bid模型中验证输入到表单中的Bid.amount的值是否大于Listing.current_bid?(* 请参见下面的解决方案尝试 *)

www.example.com models.py

    • Listing Model**
class Listing(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="usernames")
    title = models.CharField(max_length=80)
    description = models.CharField(max_length=800)
    starting_bid = models.DecimalField(max_digits=11, decimal_places=2,validators=[MinValueValidator(Decimal('0.00'))])
    current_bid = models.DecimalField(default=0, max_digits=11, decimal_places=2,validators=[MinValueValidator(Decimal('0.00'))])
    image = models.URLField(blank=True)
    • Bid Model**
class Bid(models.Model):
        owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="owners")
        listing = models.ForeignKey(Listing, on_delete=models.CASCADE, related_name="listings")
        amount = models.DecimalField(max_digits=11, decimal_places=2,validators=[MinValueValidator(Decimal('0.00'))])
views. py
    • listing() Function**:为每个列表定义一个视图并实现一个ModelForm来提交出价。
def listing(request, listing_id):
    class NewBidForm(ModelForm):
        template_name = "auctions/form_template.html"
        class Meta:
            model = Bid
            fields = ["amount"]
            widgets = {
                "amount":forms.NumberInput(attrs={"placeholder": "Submit an offer greater than the current listing price"})
            }
            labels = {
                "amount":_("Submit an Offer:")
            }
    
    # Get the current bid for the current listing
    listing = Listing.objects.get(pk=listing_id)
    current_bid = listing.current_bid

    # If the submitted form data is valid, update & save new bid
    if request.method == "POST":
        new_bid_form = NewBidForm(request.POST)
        if new_bid_form.is_valid() and new_bid_form.cleaned_data["amount"] > current_bid:
            new_bid_form.instance.amount = new_bid_form.cleaned_data["amount"]
            new_bid_form.instance.owner = request.user
            new_bid_form.instance.listing = listing
            new_bid_form.save()

            # Set the listing's current bid to the new bid amount
            listing.current_bid = new_bid_form.instance.amount
            listing.save()
            return HttpResponseRedirect(reverse("listing", args=[listing_id]))
        else:
            return render(request, "auctions/listing.html", {
                "listing": listing,
                "form": new_bid_form,
                "message": messages.add_message(request, messages.ERROR, "Bid must be greater than the current highest bid."),
            })
解决尝试

到目前为止,我还没有找到一个类似的例子,它提供了任何可以迭代的解决方案的提示。从Django文档中,似乎覆盖clean()方法可能 * 是实现这一目标的一种方法,但我还没有找到一种方法,当一个(* 未保存 *)模型示例依赖于另一个不同模型的示例时。
我尝试了一个解决方案,在我的Bid模型中使用了下面的clean()方法:

def clean(self):
        super().clean()
        if self.amount < self.listing.current_bid:
            raise ValidationError("Bid must be greater than the previous current bid")

但是,这返回了一个错误,我认为这是因为新的Bid尚未保存,因此没有对.listing的引用:

RelatedObjectDoesNotExist at /listing/1. Bid has no listing.

任何洞察力都将不胜感激!

pobjuy32

pobjuy321#

对于其他可能有类似问题的人,我能够找到一个解决方案(这是盯着我的脸),通过覆盖clean()方法,因为我已经猜到了。
引发此错误的原因是,在调用重写的clean()方法时,Bid对象尚未引用Listing
因此,通过引用清单对象示例化Bid,我能够解决这个问题:

def listing(request, listing_id):
    class NewBidForm(ModelForm):
        template_name = "auctions/form_template.html"
        class Meta:
            model = Bid
            fields = ["amount"]
            widgets = {
                "amount":forms.NumberInput(attrs={"placeholder": "Submit an offer greater than the current listing price"})
            }
            labels = {
                "amount":_("Submit an Offer:")
            }
    
    # Get the current listing to bid on.
    listing = Listing.objects.get(pk=listing_id)

    # Validate that the new bid is greater than the current highest bid.
    if request.method == "POST":
        new_bid_form = NewBidForm(request.POST, instance=Bid(listing=listing))
        if new_bid_form.is_valid():
            new_bid_form.instance.amount = new_bid_form.cleaned_data["amount"]
            new_bid_form.instance.owner = request.user
            new_bid_form.save()

            # Set the listing's current bid to the new bid amount.
            listing.current_bid = new_bid_form.instance.amount
            listing.save()
            return HttpResponseRedirect(reverse("listing", args=[listing_id]))
        else:
            return render(request, "auctions/listing.html", {
                "listing": listing,
                "form": new_bid_form,
            })

作为参考,对Bid模型中的clean()方法进行了轻微的修改:

class Bid(models.Model):
    owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="owners")
    listing = models.ForeignKey(Listing, on_delete=models.CASCADE, related_name="listings")
    amount = models.DecimalField(max_digits=11, decimal_places=2,validators=[MinValueValidator(Decimal('0.00'))])

    def clean(self):
        super().clean()
        if self.amount <= self.listing.current_bid:
            raise ValidationError(
                _("Unable to place new bid! Please enter a minimum bid of at least $%(value)s!"), 
                params={"value":f"{float(self.listing.current_bid) + 1.0:.2f}"})

相关问题