Django clean() method raises 'Bid' object has no attribute 'kwargs'

Viewed 67

I'm trying to restrict a bidder that bids on certain project via a form from bidding neither less than the minimum budget that the project publisher has specifies neither greater than the maximum budget. After checking similar problems i found that overriding the clean method is the most suitable, but i'm facing " 'Bid' object has no attribute 'kwargs' " error

This is the clean method:

    def clean(self):
        if Project.objects.get(pk=self.kwargs['pk']).budget_min >= self.bid_amount or self.bid_amount >= Project.objects.get(pk=self.kwargs['pk']).budget_max:
            raise ValidationError('the bid amount should be between the minimum and maximum project budget')

This is my models.py

# projects/models.py
from django.contrib.auth import get_user_model
from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
from django.core.validators import MaxValueValidator, MinLengthValidator, MinValueValidator
from upload_validator import FileTypeValidator
from .validators import user_directory_path, validate_file_size
from multiselectfield import MultiSelectField


class Project(models.Model):
    title = models.CharField(
        'Choose a title for your project',
        max_length=255,
        validators=[
            MinLengthValidator(
                15, 'Title must be descriptive and greater than 15 characters')
        ]
    )
    body = models.TextField('Tell us more about your project',
                            max_length=3000,
                            validators=[
                                MinLengthValidator(
                                    10, 'Must be descriptive and greater than 10 characters')
                            ]
                            )
    upload_file = models.FileField(
        upload_to=user_directory_path,
        verbose_name="upload file:(Max file size: 2.5 MB)",
        validators=[
            validate_file_size,
            FileTypeValidator(
                allowed_types=[
                    'application/msword',
                    'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
                    'application/pdf',
                    'application/vnd.ms-powerpoint',
                    'application/vnd.openxmlformats-officedocument.presentationml.presentation',
                    'application/vnd.ms-excel',
                    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
                    'image/tiff',
                    'image/jpeg',
                    'image/png'
                ],
                allowed_extensions=['.doc', '.docx',
                                    '.jpg', '.jpeg', '.png', '.pdf', '.zip']
            )
        ],
        null=True, blank=True
    )

    POSSIBLE_SKILLS = (
        ('Bash Scripting', 'Bash Scripting'), ('CSS', 'CSS'), ('Django', 'Django'),
        ('HTML', 'HTML'), ('JavaScript', 'JavaScript'), ('React', 'React'),
        ('Linux', 'Linux'), ('MongoDB', 'MongoDB'), ('NoSQL', 'NoSQL'),
        ('Powershell', 'Powershell'), ('Python', 'Python'),
        ('Shell Script', 'Shell Script')
    )
    skills = MultiSelectField(choices=POSSIBLE_SKILLS, default='')
    PAYMENT_TYPE = (
        ('Fixed', 'Fixed'),
        ('Hourly', 'Hourly'),
    )
    payments = models.CharField(
        'How do you want to pay?',
        max_length=20, choices=PAYMENT_TYPE, default="Fixed")
    budget_min = models.PositiveIntegerField(
        'What is your estimated Minimum budget?',
        default=10, validators=[MinValueValidator(1), MaxValueValidator(100000)])
    budget_max = models.PositiveIntegerField(
        'What is your estimated Maximum budget? :',
        default=100, validators=[MinValueValidator(1), MaxValueValidator(100000)])
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    # author is a FOREIGN KEY field in Project table, that refers to the PRIMARY KEY in CustomUser table
    # the on_delete method used to tell django when deleting the author delete he's projects
    author = models.ForeignKey(
        get_user_model(),
        on_delete=models.CASCADE,
    )

    # Shows up in the admin list
    def __str__(self):
        return self.title

    def get_absolute_url(self):
        """[summary: where to go when a Project object is created. 
]

        Returns:
            [description: this method should appear to return a string that can be used to refer
            to the object over HTTP.]
        """
        return reverse('project_detail', args=[str(self.id)])


class Bid(models.Model):
    bid_amount = models.PositiveIntegerField(
        default=10, validators=[MinValueValidator(1), MaxValueValidator(1000000)]
    )
    delivery_in = models.PositiveIntegerField(
        default=1, validators=[MinValueValidator(1), MaxValueValidator(10000)]
    )
    describe_your_proposal = models.TextField(
        max_length=144,
        validators=[
            MinLengthValidator(
                20, "bid must be descriptive and greater than 20 characters")
        ]
    )
    STATUS_TYPE = (
        ('Approved', 'Approved'),
        ('Closed', 'Closed'),
        ('Pending', 'Pending'),
    )
    status = models.CharField(
        'How do you want to pay?',
        max_length=20, choices=STATUS_TYPE, default="Pending")
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    # project is a FOREIGN KEY field in Bid table, that refers to
    # the PRIMARY KEY in Project table using related name project.bids
    # in templates to access bids of the project
    # the on_delete method used to tell django when deleting the project delete it's bids
    project = models.ForeignKey(
        Project,
        on_delete=models.CASCADE,
        related_name='bids',
    )
    # author is a FOREIGN KEY field in Bid table, that refers to
    # the PRIMARY KEY in CustomUser table
    author = models.ForeignKey(
        get_user_model(),
        on_delete=models.CASCADE,
    )

    #
    def clean(self):
        if Project.objects.get(pk=self.kwargs['pk']).budget_min >= self.bid_amount or self.bid_amount >= Project.objects.get(pk=self.kwargs['pk']).budget_max:
            raise ValidationError('the bid amount should be between the minimum and maximum project budget')

    # Shows up in the admin list
    def __str__(self):
        return self.describe_your_proposal

    def get_absolute_url(self):
        """[summary: where to go when a Bid object is created.
    ]

        Returns:
            [description: this method should appear to return a string that can be used
            to refer to the object over HTTP.]
        """
        return reverse('bid_detail', args=[str(self.id)])

1 Answers

If the project referenced in the clean method is the bid.project object, then you can just call it directly.

def clean(self):
    if not self.project.budget_min <= self.bid_amount <= self.project.budget_max:
        raise ValidationError('The bid amount should be between the minimum and maximum project budget.')

I'd also recommend switching up the check if the bid is in between the min and max values, I think it is easier to read.

Related