Abdul Giwa
Abdul Giwa

Reputation: 71

Avoiding duplicate row entry inside django model

I have a CarRent model that is a ForeignKey to the Car model. On the Car detailView page i have a button for drivers to place a Rent which will result in adding a row to the CarRent Table. The rent isn’t active yet until the Car owner approves the rent request. The problem now is that a driver could click the rent button multiple times thereby resulting in many duplicate rows in the CarRent model and also multiple redundant car rent request to the car owner. I am looking for a way to hide the “Place A Rent” form if the current user (driver) has already requested for a particular car and rent_approval is not True.

Thanks. Any help will be greatly appreciated.

models.py

class Car(models.Model):
    
    car_owner = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='car_owner', on_delete=models.CASCADE)
    car_model = models.CharField(max_length=20, blank=True, null=True)
    rented = models.BooleanField(default=False)



class CarRent(models.Model):

    car = models.ForeignKey(Car, related_name='rented_car', on_delete=models.CASCADE)
    driver = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='driver_renting', on_delete=models.CASCADE)
    rent_approval = models.BooleanField(null=True)
    active_rent = models.BooleanField(default=False, null=True)

views.py

class CarView(LoginRequiredMixin, UserPassesTestMixin, FormMixin, DetailView):
    model = Car
    form_class = RentForm

    def get_success_url(self):
        return reverse('app:car-detail', kwargs={'pk': self.object.pk})

    def post(self, request, *args, **kwargs):
        form = self.get_form()
        self.object = self.get_object()

        if form.is_valid():
            return self.form_valid(form)
        else:
            return self.form_invalid(form)

    def form_valid(self, form, *args, **kwargs):
        form.instance.car_id = self.kwargs['pk']
        form.instance.driver = self.request.user
        form.save()
        messages.success(self.request, f'Rent request successful! the car owner will be notified immediately.')
        return super().form_valid(form)

    def test_func(self):
        return self.request.user.is_driver and self.request.user.is_active

car_detail.html

...
<h3>Place a Rent</h3>
                <form action="" method="post" novalidate class="form-inline">
                    {% csrf_token %}
                
                    {% include 'app/bs4_form.html' with form=form %}
                
                <button class="book-now-btn" type="submit">Rent</button>
                </form>


...

Upvotes: 0

Views: 113

Answers (3)

Bivek Chalise
Bivek Chalise

Reputation: 86

So that is a valid problem. For that you may have to provide the validation by overriding the save function for CarRent.

from django.core.exceptions import ValidationError
   
   def save(self, *args, **kwargs):
        has_reservation = self.__class__.objects.filter(car=self.car, driver=self.driver)
        #reservation_valid =  some logic to find if reservation is allowed
        if not reservation_valid:
            raise ValidationError("Driver reservation is not valid", code="drive_not_valid")
        return super().save(*args, **kwargs)

Then in the view

try:
   #try renting
except Exception as e:
   if hasattr(e, "code"):
      if e.code == "drive_not_valid":
         #place your invalid view logic here 

Upvotes: 0

Bivek Chalise
Bivek Chalise

Reputation: 86

For a proper solution you can define a inner Meta class for CarRent and define a unique_together attirbute for fields driver and car to avoid duplicate entry.

class CarRent(models.Model):

    car = models.ForeignKey(Car, related_name='rented_car', on_delete=models.CASCADE)
    driver = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='driver_renting', on_delete=models.CASCADE)
    rent_approval = models.BooleanField(null=True)
    active_rent = models.BooleanField(default=False, null=True)
    
    class Meta:
        unique_together = ("driver", "car")

Upvotes: 0

lucutzu33
lucutzu33

Reputation: 3700

In your CarView class:

def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context['rentAlreadyExists'] = CarRent.objects.filter(driver=self.request.user, car=self.object, active_rent=False).exists()
    return context

Then in your template you can do:

{% if not rentAlreadyExists %}
... your form ...
{% endif %}

Upvotes: 1

Related Questions