在 django 中通过管理站点添加对象时出错

Posted

技术标签:

【中文标题】在 django 中通过管理站点添加对象时出错【英文标题】:Error while adding objects via admin site in django 【发布时间】:2013-03-23 02:51:14 【问题描述】:

我创建了以下模型:

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',))

一般情况下,通过管理站点添加 RentPeriod 实例时,我希望避免在指定日期内重叠时间。当我尝试添加 RentPeriod 实例时,出现以下错误:

'' 需要为字段“rentperiod”设置一个值,然后才能使用这种多对多关系。

我读过click,但我不知道如何让它工作。你能帮忙吗?

注意:复杂度(n^2)在这种情况下无关紧要。

更新 我根据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

和 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

不幸的是,自己的验证器“没有被调用”——我的意思是即使测试 ValidationError 也没有提高。

更新 2 找出是“注册”语句的问题。应该是:

admin.site.register(RentPeriod, RentPeriodAdmin)

【问题讨论】:

【参考方案1】:

我建议模型上的 validate_unique 不是这样做的地方。

要进行此级别的验证,您需要模型有一个 PK,但为了有一个 PK,它需要保存到 DB,但为了保存到 DB,它需要验证。看到问题了吗?

您需要将这种验证移到表单/表单集中。这意味着重载 InlineFormset 的 clean() 方法,我假设您使用该方法输入小时数。参考:Inline Form Validation in Django

【讨论】:

是的,我看到了问题所在。我创建了自定义验证器。我添加验证器和使用 InlineFormset 的方式有区别吗?我也尝试过使用 InlineFormset,但没有调用验证。 OK,发现问题出在 register 语句错误。

以上是关于在 django 中通过管理站点添加对象时出错的主要内容,如果未能解决你的问题,请参考以下文章

在 Access 中通过 VBA 传输表时出错

在 Django 中通过 admin 生成的表单

为啥在 Django 管理员的 save() 覆盖中将站点添加到对象似乎不起作用?

如何在 Django 1.7 中通过外键将 created_by 和 updated_by 用户添加到 model.py? [复制]

Django:从管理站点显示换行符?

如何在:Django 中通过 URL 传递带空格的变量