Search code examples
pythondjangoadminmodels

Error while adding objects via admin site in django


I've created the following models:

class BasePrice(models.Model):
        start_hour = models.TimeField(blank=False)
        end_hour = models.TimeField(blank=False)
        monday = models.BooleanField(blank=False)
        tuesday = models.BooleanField(blank=False)
        wednesday = models.BooleanField(blank=False)
        thursday = models.BooleanField(blank=False)
        friday = models.BooleanField(blank=False)
        price = models.IntegerField(blank=False)

        def __unicode__(self):

            days = ""

            if (self.monday == True):
                days = days + " Mon"
            if (self.tuesday == True):
                days = days + " Tue"
            if (self.wednesday == True):
                days = days + " Wed"
            if (self.thursday == True):
                days = days + " Thu"
            if (self.friday == True):
                days = days + " Fri"
            return "Price " + str(self.price) + " at " + str(self.start_hour) + ":" +str(self.end_hour) + " in " + days

    def clean(self):

        if self.start_hour > self.end_hour:
            raise ValidationError('Start hour is older than end hour!')

class RentPeriod(models.Model):
    start_date = models.DateField(blank=False)
    end_date = models.DateField(blank=False)
    desk = models.ForeignKey(Desk)
    base_prices = models.ManyToManyField(BasePrice)

    def __unicode__(self):
        return "Period " + str(self.start_date) + " to " + str(self.end_date) + " for " + self.desk.__unicode__()

    def clean(self):

        if self.start_date > self.end_date:
            raise ValidationError('Start date is older than end date!')

    def validate_unique(self, *args, **kwargs):
        super(RentPeriod, self).validate_unique(*args, **kwargs)
    # overlaping hours
        basePrices = self.base_prices.all()

        for price in basePrices:
            qs = self.__class__._default_manager.filter(
                (Q(monday=True) & Q(monday=price.monday)) |
                (Q(tuesday=True) & Q(tuesday=price.tuesday)) |
                (Q(wednesday=True) & Q(wednesday=price.wednesday)) |
                (Q(thursday=True) & Q(thursday=price.thursday)) |
                (Q(friday=True) & Q(friday=price.friday)),
                start_hour__lte=self.end_hour,
                end_hour__gte=self.start_hour
            )

            if qs.exists():
                raise ValidationError({NON_FIELD_ERRORS: ('overlaping hours range',)})

Generally, I'd like to avoid overlaping time in specified days when adding instances of RentPeriod via admin site. When I try to add RentPeriod instance, I get following error:

'' needs to have a value for field "rentperiod" before this many-to-many relationship can be used.

I've read click, but I don't have idea how to get it to work. Could you help?

Attention: complexity (n^2) has no matter in this case.

UPDATE I've created custom validator according to documentation

forms.py

from biurrko.rents.models import RentPeriod
from django.forms.models import ModelForm

class RentPeriodForm(ModelForm):
    class Meta:
        model = RentPeriod

    def clean(self):
        cleaned_data = self.cleaned_data
        basePrices = cleaned_data['base_prices']
        end_hour = cleaned_data['end_hour']
        start_hour = cleaned_data['start_hour']

        for price in basePrices:
            qs = basePrices.filter(
                (Q(monday=True) & Q(monday=price.monday)) |
                (Q(tuesday=True) & Q(tuesday=price.tuesday)) |
                (Q(wednesday=True) & Q(wednesday=price.wednesday)) |
                (Q(thursday=True) & Q(thursday=price.thursday)) |
                (Q(friday=True) & Q(friday=price.friday)),
                start_hour__lte=end_hour,
                end_hour__gte=start_hour
            )

            if qs.exists():
                raise ValidationError({NON_FIELD_ERRORS: ('overlaping hours range',)}) 

        # only for test
        raise ValidationError({NON_FIELD_ERRORS: ('reached',)}) 
        # return cleaned_data

and admin.py

from django.contrib import admin
from biurrko.rents.models import Desk, Room, RentPeriod, BasePrice
from biurrko.rents.forms import RentPeriodForm

admin.site.register(Desk)
admin.site.register(Room)
admin.site.register(RentPeriod)
admin.site.register(BasePrice)


class RentPeriodAdmin(admin.ModelAdmin):
    form = RentPeriodForm

Unfortunately, own validator "is not called" - I mean even test ValidationError is not raising.

UPDATE 2 Figuret out that it was problem with "register" statement. It should be:

admin.site.register(RentPeriod, RentPeriodAdmin)

Solution

  • I would suggest that validate_unique on the model would NOT be the place to do this.

    To do this level of validation you need the model to have a PK, but in order to have a PK it needs to be saved to DB, but in order to save to DB it needs to validate. See the problem?

    You would need to move this kind of validation into the form/formset. This means overloading the clean() method of the InlineFormset that I assume your using for input of hours. Ref: Inline Form Validation in Django