Как проверить ограничение уникальности по внешнему ключу (django)
У меня есть следующая (упрощенная) структура данных:
Site
-> Zone
-> Room
-> name
Я хочу, чтобы имя каждой комнаты было уникальным для каждого Сайта.
Я знаю, что если бы я просто хотел уникальности для каждой Зоны, я мог бы сделать:
class Room(models.Model):
zone = models.ForeignKey(Zone)
name = models.CharField(max_length=255)
class Meta:
unique_together = ('name', 'zone')
Но я не могу делать то, что действительно хочу, а именно:
class Room(models.Model):
zone = models.ForeignKey(Zone)
name = models.CharField(max_length=255)
class Meta:
unique_together = ('name', 'zone__site')
Я попытался добавить метод 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)
но я должен неправильно понимать точку/реализацию validate_unique, потому что он не вызывается, когда я сохраняю объект Room.
Каким будет правильный способ реализовать эту проверку?
Ответы
Ответ 1
Методы не вызывают самостоятельно при сохранении модели.
Один из способов сделать это - создать собственный метод сохранения, который вызывает метод 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)
Ответ 2
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',]})
Мне нужно было сделать подобную программу. Это сработало.
Ответ 3
Документация Django Validation описывает шаги, связанные с проверкой, включая этот фрагмент
Обратите внимание, что full_clean() не будет вызываться автоматически, когда вы вызываете метод сохранения модели()
Если экземпляр модели создается в результате использования ModelForm
, тогда проверка будет выполняться, когда форма будет проверена.
Есть несколько вариантов того, как вы обрабатываете проверку.
- Перед сохранением вызовите экземпляр модели
full_clean()
вручную.
-
Переопределите метод save()
модели для выполнения проверки при каждом сохранении. Вы можете выбрать, какая валидация должна произойти здесь, хотите ли вы полной проверки или только проверки уникальности.
class Room(models.Model):
def save(self, *args, **kwargs):
self.full_clean()
super(Room, self).save(*args, **kwargs)
-
Используйте обработчик сигнала 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')