Search code examples
pythondjangoformsmodels

Modify models field via view doesn't work


In my Django application, I want to subtract 1 "free_places" field in the "Event" model using the "EventDetailView" view where the form is located. Each time the form is OK (when the user subscribes to the event), the "free_places" field should decrease by 1.

I do not know why my code does not work.

My view:

class EventDetailView(DetailView, ModelFormMixin):
    model = models.Event
    form_class = forms.RegisterForm
    context_object_name = 'event'

    def get_success_url(self):
        return reverse('events:list')

    def get_initial(self):
        return {'event': self.kwargs['pk']}

    def post(self, request, *args, **kwargs):
        form = self.get_form()
        if form.is_valid():
            self.object = self.get_object()
            self.object.free_places - 1
            self.object.save()
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

Models:

class Event(models.Model):
    title = models.CharField(max_length=500)
    date = models.DateField()
    text = models.TextField()
    image = FilerImageField(null=True, blank=True)
    flag = models.ForeignKey(Flag)
    free_places = models.IntegerField()

    class Meta:
        ordering = ['-date']

    def __str__(self):
        return self.title

    @property
    def slug(self):
        return slugify(self.title)

    def get_absolute_url(self):
        return reverse('events:detail', args=[self.slug, self.id])

    def get_model_id(self):
        return self.id


class Register(models.Model):
    event = models.ForeignKey(Event)
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    company = models.CharField(max_length=30, blank=True)
    street = models.CharField(max_length=50, blank=True)
    post_code = models.CharField(max_length=30, blank=True)
    city = models.CharField(max_length=30, blank=True)
    email = models.EmailField()
    phone_number = models.IntegerField()

    def __str__(self):
        return self.first_name

    def get_event_name(self):
        return self.event

Solution

  • You need to assign the result of self.object.free_places - 1. At the moment you are not doing anything with it.

    Change the line to either:

    self.object.free_places -= 1
    

    or

    self.object.free_places = self.object.free_places - 1
    

    The code is vulnerable to race conditions if multiple users submit the form at the same time. You can fix that by using F() objects.

    from django.db.models import F
    self.object.free_places = F('free_places') - 1
    self.object.save()