JR90
JR90

Reputation: 537

Django ModelForm Validation using non form data

I'm new to Django (1.9.6) and I'm trying to understand if it is possible to validate a field on a ModelForm that needs to reference information that is contained in a Foreign Key from the referenced model.

How can I validate that the value a user inputs for "num_tickets" on the OrderForm is less than or equal to the "tickets_remaining" field on the Event class which is connected through a foreign key relationship?

I don't want to expose the Event field from the Order class on the OrderForm as the user has already accessed the specific event page, and has already selected to purchase tickets.

Models.py

class Order(models.Model):

    first_name = models.CharField('First Name', max_length=120,null=False, blank=False)
    last_name = models.CharField('Last Name', max_length=120, null=False, blank=False)
    email = models.EmailField('Email', null=False, blank=False)
    event = models.ForeignKey(Event)
    num_tickets = models.PositiveIntegerField('Tickets', null=False, blank=False, validators=[MinValueValidator(0)])
    total_price = models.DecimalField('Total', max_digits=8, decimal_places=2, default=0.0)
    timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)

class Event(models.Model):

    event_name = models.CharField(max_length=200)
    price = models.DecimalField(max_digits=8, decimal_places=2, default=00.00, validators=[MinValueValidator(0)])
    tickets_remaining = models.PositiveIntegerField(default=300)

Forms.py

class OrderForm(forms.ModelForm):

class Meta:
    model = Order
    fields = ['first_name', 'last_name', 'email', 'num_tickets']

def clean_num_tickets(self):
    tickets = self.cleaned_data["num_tickets"]

    # validation Logic. Want to ensure a user cannot purchase more
    # tickets than what an event has for "tickets_remaining"

    return tickets

Upvotes: 0

Views: 893

Answers (1)

Daniel Roseman
Daniel Roseman

Reputation: 599906

You don't show how you're associating the order with the event in the first place. If you haven't done that, then your problem is wider than just validating the tickets available.

I would recommend passing that Event from the view into the form instantiation. You can then use it both to associate the order with that event, and to validate the tickets.

class OrderForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        self.event = kwargs.pop('event', None)
        super(OrderForm, self).__init__(*args, **kwargs)

    def clean_num_tickets(self):
        tickets = self.cleaned_data["num_tickets"]
        if tickets > self.event.tickets_remaining:
            raise ValidationError('Too many tickets')
        return tickets

    def save(self, commit=False):
        order = super(OrderForm, self).save(commit=False)
        order.event = self.event
        if commit:
            order.save()
        return commit

Now pass the event into the form when instantiating it:

form = OrderForm(request.POST, event=event)

Upvotes: 1

Related Questions