Модель Django: поле электронной почты уникально, если не null/blank

Скажем, у вас простая модель:

Class Contact(models.Model):
    email = models.EmailField(max_length=70,blank=True)
    first = models.CharField(max_length=25,blank=True)
    last = models.CharField(max_length=25,blank=True)

То, что я хотел бы сделать, это установить, что электронное письмо является уникальным, но при этом я обязательно сделаю так, чтобы исключить пустые адреса электронной почты - и я не хочу этого.

Я думал о чем-то подобном, но мне интересно, есть ли лучший способ справиться с этим.

from django.core.validators import email_re
from django.core.exceptions import ValidationError

def save(self, *args, **kwargs):
    # ... other things not important here
    self.email = self.email.lower().strip() # Hopefully reduces junk to ""
    if self.email != "": # If it not blank
        if not email_re.match(self.email) # If it not an email address
            raise ValidationError(u'%s is not an email address, dummy!' % self.email)
        if Contact.objects.filter(email = self.email) # If it already exists
            raise ValidationError(u'%s already exists in database, jerk' % self.email) 
    super(Contact, self).save(*args, **kwargs)

Есть ли лучший способ сделать это?

Ответ 1

К сожалению, это не так просто, как просто установка null = True, unique = True, blank = True. Всякий раз, когда вы пытаетесь импортировать с помощью csv или какого-либо другого источника на основе текста, некоторая часть Django с целью уникальности рассматривает "" как нечто, что не должно дублироваться.

Обход, это перезаписать метод сохранения следующим образом:

def save(self, *args, **kwargs):
    # ... other things not important here
    self.email = self.email.lower().strip() # Hopefully reduces junk to ""
    if self.email != "": # If it not blank
        if not email_re.match(self.email) # If it not an email address
            raise ValidationError(u'%s is not an email address, dummy!' % self.email)
    if self.email == "":
        self.email = None
    super(Contact, self).save(*args, **kwargs)

Затем использование уникальных, нулевых и пустых будет работать по назначению.

Class Contact(models.Model):
    email = models.EmailField(max_length=70,blank=True, null= True, unique= True)

Ответ 2

Я попытался использовать сохранение, но это все еще не сработало, потому что ошибки уже подняты в чистом методе, поэтому я переписал это для моей модели, это выглядит примерно так:

Class MyModel(models.Model):
    email = models.EmailField(max_length=70,blank=True)
    first = models.CharField(max_length=25,blank=True)
    last = models.CharField(max_length=25,blank=True)
    phase_id = models.CharField('The Phase', max_length=255, null=True, blank=True, unique=True)

    ...

    def clean(self):
        """
        Clean up blank fields to null
        """
        if self.phase_id == "":
            self.phase_id = None

Это отлично работает для меня, и ответ с использованием сохранения может работать в некоторых случаях, этот должен работать, сбросив "на" Нет" до того, как остальная часть проверки будет проведена в базовом классе. Приветствия:)

Ответ 3

Просто сделайте следующее:

class Contact(models.Model):
    email = models.EmailField(max_length=70, null=True, blank=True, unique=True)

Ответ 4

Разрешить CharField иметь значение null и по умолчанию использовать значение None. Если у вас нет нескольких пустых полей ( "" ), ошибка целостности не будет повышена.

#models.py
Class Contact(models.Model):
    email = models.EmailField(max_length=70, blank=True, null=True, unique=True, default=None)
# protect the db from saving any blank fields (from admin or your app form)
def save(self, *args, **kwargs):
    if self.email is not None and self.email.strip() == "":
        self.email = None
    models.Model.save(self, *args, **kwargs)