Я использую Django createview для создания позиций ставок на сайте аукциона. Createview создаст новый объект, но создаст дополнительный экземпляр объекта без соответствующих внешних ключей. Я использую @staticmethod, чтобы убедиться, действительно ли поданная ставка является самой высокой ставкой, а затем создаю в соответствующем Листинге. Заранее спасибо, если вы могли указать, что я делаю неправильно.

Models.py

class Bid(TimeStampMixin):
    """model representing bid obj"""
    auction = models.ForeignKey(
        Listing, 
        on_delete=models.SET_NULL, 
        related_name='offer', 
        null=True)
    bidder = models.ForeignKey(
        settings.AUTH_USER_MODEL, 
        on_delete=models.SET_NULL, 
        null=True,
        related_name='bid_user')
    amount = models.PositiveIntegerField()

    objects = BidQuerySet.as_manager()

        
    def __str__(self):
        return f"{self.amount} in Listing No: {self.auction.id}"

    class Meta:
        ordering = ['amount']

    @staticmethod
    def high_bid(auction, bidder, bid_amount):
        """util method to ascertain highest bid in auction then create in related auction obj
        :param auction---listing being bid on, bid__auction
        :param bidder---user bidding
        :param amount--- current highest bid
        """
        ###error checks, is auction running? is current bid less than start bid? etc
        if bid_amount < auction.start_bid:
            return
        if (auction.highest_offer and bid_amount < auction.highest_offer.amount):
            return
        if bidder.id is auction.user.id:
            raise PermissionDenied
        ##after checks create highest bid object in listing model
        new_high_bid = Bid.objects.create(
            auction= auction,
            bidder = bidder,
            amount = bid_amount
        )
        auction.highest_offer = new_high_bid
        auction.save()

class Listing(TimeStampMixin):
    user = models.ForeignKey(
        User, 
        on_delete=models.CASCADE, 
        related_name="seller")
    product = models.ForeignKey(
        Product, 
        on_delete=models.CASCADE, 
        related_name="item")
    start_bid = models.PositiveIntegerField(
        default=0)
    date_end = models.DateTimeField()
    duration = models.ForeignKey(
        Duration, 
        on_delete=models.SET_NULL, 
        null=True,
        blank=True,
    )
    ###static method in Bid model creates this
    highest_offer = models.ForeignKey(
        'Bid',
        related_name = 'highest_bid',
        on_delete = models.SET_NULL,
        null = True,
        blank = True,
    )

    def __str__(self):
        return f"Listing for {self.product} by {self.user}."

Views.py

class BidCreateView(LoginRequiredMixin, CreateView):
    """View to create auction bid"""
    model = Bid
    form_class = BidForm
    template_name = "auction/auction_detail.html"

    ##define url redirected to when form is valid 
    def get_success_url(self):
        return reverse ('detail', kwargs = { 'pk' : self.auction.pk })

    ###get context data to be passed to view
    def get_context_data(self, **kwargs):
        bidder = self.request.user
        ###call parent class to init 
        c = super().get_context_data(**kwargs)
        c["auction"] = self.auction
        ###check if user is bidding on own item
        if bidder.id is self.auction.user.id:
            c["form"] = None
        return c

    ###get_form_kwargs() method to supply user and listing during form creation
    def get_form_kwargs(self):
        ###call parent class
        kwargs = super().get_form_kwargs()
        ###extrack PK from kwargs so we can query for related listing obj
        pk_ = self.kwargs.get('pk')
        auction = Listing.objects.get(pk = pk_)
        kwargs["auction"] = auction
        self.auction = auction
        print(kwargs)
        return kwargs

    def form_invalid(self, form):
        return super().form_invalid(form)

    def form_valid(self, form):
        bid_amount = form.cleaned_data["amount"]

        try:
        ###use transaction.atomic to tie this method with a succesful form submission
            with transaction.atomic():
                Bid.high_bid(
                    self.auction,
                    self.request.user,
                    bid_amount
                )
        except IntegrityError:
            messages.error(self.request, "An unexpected error has occured")

        messages.success(self.request, "Bid submitted successfully!")

        return super().form_valid(form)

Forms.py

class BidForm(forms.ModelForm):
    class Meta:
        model = Bid
        fields = [
            'amount',
        ]

        widgets = {
            'amount' : forms.NumberInput(attrs={'class' : 'form-control'}),
        }

    def __init__(self, *args, **kwargs):
        """override __init__ pop related auction from kwargs"""   
        self.auction = kwargs.pop("auction", None)
        ##call super with related obj 
        super().__init__(*args, **kwargs)
        

    ##clean data/validate, ensuring bid is larger than highest bid and starting bid
    def clean_amount(self):
        amount = self.cleaned_data["amount"]
        ##check if less than starting bid
        if self.auction.start_bid > amount:
            raise ValidationError(_('Bid is less than starting bid'))
        if (self.auction.highest_offer.amount >= amount):
            raise ValidationError(_('Bid is less than current highest bid'))
        return amount

Таблица БД, показывающая дополнительный объект ставки без внешних ключей

Таблица БД

0
vnczrz 10 Окт 2020 в 01:47

1 ответ

Лучший ответ

Удалите строку return super().form_valid(form) из form_valid.

Эта строка кода вызовет form_valid из унаследованного класса, который сохранит объект - это дополнительный объект, создаваемый без внешних ключей.

Вместо этого вы захотите вернуть какой-то HTTP-ответ (например, render_to_response, перенаправление и т. Д.).

0
iri 9 Окт 2020 в 22:54