如何跨外键验证唯一性约束(django)

sap*_*api 17 python django django-models

我有以下(简化)数据结构:

Site
-> Zone
   -> Room
      -> name
Run Code Online (Sandbox Code Playgroud)

我希望每个房间的名称对于每个网站都是唯一的.

我知道,如果我只想要每个区域的唯一性,我可以这样做:

class Room(models.Model):
    zone = models.ForeignKey(Zone)
    name = models.CharField(max_length=255) 

    class Meta:
        unique_together = ('name', 'zone')
Run Code Online (Sandbox Code Playgroud)

但我不能做我真正想要的,这是:

class Room(models.Model):
    zone = models.ForeignKey(Zone)
    name = models.CharField(max_length=255) 

    class Meta:
        unique_together = ('name', 'zone__site')
Run Code Online (Sandbox Code Playgroud)

我尝试添加validate_unique方法,如此问题所示:

class Room(models.Model):
    zone = models.ForeignKey(Zone)
    name = models.CharField(max_length=255) 

    def validate_unique(self, exclude=None):
        qs = Room.objects.filter(name=self.name)
        if qs.filter(zone__site=self.zone__site).exists():
            raise ValidationError('Name must be unique per site')

        models.Model.validate_unique(self, exclude=exclude)
Run Code Online (Sandbox Code Playgroud)

但我必须误解validate_unique的要点/实现,因为当我保存Room对象时它没有被调用.

实施此检查的正确方法是什么?

Sep*_*man 10

保存模型时,不会自行调用方法.一种方法是使用自定义保存方法,在保存模型时调用validate_unique方法:

class Room(models.Model):
    zone = models.ForeignKey(Zone)
    name = models.CharField(max_length=255) 

    def validate_unique(self, exclude=None):
        qs = Room.objects.filter(name=self.name)
        if qs.filter(zone__site=self.zone__site).exists():
            raise ValidationError('Name must be unique per site')


    def save(self, *args, **kwargs):

        self.validate_unique()

        super(Room, self).save(*args, **kwargs)
Run Code Online (Sandbox Code Playgroud)

  • @dragostis:我没有在```Model.save()``中看到``validate_unique()``调用(https://github.com/django/django/blob/master/django/db /models/base.py#L525)方法.所以,不,它在保存模型时不会自动执行:) (4认同)

Aus*_*ips 6

Django验证对象文档解释了验证所涉及的步骤,包括此代码段

请注意,当您调用模型的 save() 方法时,不会自动调用 full_clean()

如果模型实例是使用 a 创建的ModelForm,那么在验证表单时将进行验证。

处理验证的方式有一些选择。

  1. full_clean()在保存之前手动调用模型实例。
  2. 覆盖save()模型的方法以在每次保存时执行验证。您可以选择此处应进行多少验证,无论您想要完整验证还是仅进行唯一性检查。

    class Room(models.Model):
        def save(self, *args, **kwargs):
            self.full_clean()
            super(Room, self).save(*args, **kwargs)
    
    Run Code Online (Sandbox Code Playgroud)
  3. 使用 Django pre_save信号处理程序,它将在保存前自动执行验证。这提供了一种非常简单的方法来在现有模型上添加验证,而无需任何额外的模型代码。

    # In your models.py
    from django.db.models.signals import pre_save
    
    def validate_model_signal_handler(sender, **kwargs):
        """
        Signal handler to validate a model before it is saved to database.
        """
        # Ignore raw saves.
        if not kwargs.get('raw', False):
            kwargs['instance'].full_clean()
    
    
    pre_save.connect(validate_model_signal_handler,
      sender=Room,
      dispatch_uid='validate_model_room')
    
    Run Code Online (Sandbox Code Playgroud)


aki*_*524 6

class Room(models.Model):
    zone = models.ForeignKey(Zone)
    name = models.CharField(max_length=255)

    def validate_unique(self, *args, **kwargs):
        super(Room, self).validate_unique(*args, **kwargs)
        qs = Room.objects.filter(name=self.name)
        if qs.filter(zone__site=self.zone__site).exists():
            raise ValidationError({'name':['Name must be unique per site',]})
Run Code Online (Sandbox Code Playgroud)

我需要做类似的程序。有效。