Model with two e-mail fields uniques

56 views
Skip to first unread message

victor menezes

unread,
Apr 7, 2015, 7:34:02 PM4/7/15
to django...@googlegroups.com
Hi there,

What would be the best way to make a model with two e-mail fields uniques? I was thinking about writing some validation in the save() method but would like to know whether Django has some built-in way to deal with it.

class Person(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    email1 = models.EmailField(null=True, blank=True)
    email2 = models.EmailField(null=True, blank=True)
    def __unicode__(self):
        return self.last_name + ', ' + self.first_name
    def save(self):
        if (len(self.email1) == 0 and len(self.email2) == 0):
            raise ValueError('At least one e-mail is needed to proceed.')
        super(Member, self).save()


John = Person()
John.email1 = 'someEmail'
John.save()

Kane = Person()
Kane.email1 = 'anotherEmail
Kane.email2 = 'someEmail' #same email used by John on email1 field
Kane.save() #should raise error because email2 is already used by John on email1 field

Thanks


Victor Menezes
"Geek by nature, Linux by choice"

Javier Guerra Giraldez

unread,
Apr 7, 2015, 7:55:26 PM4/7/15
to django...@googlegroups.com
On Tue, Apr 7, 2015 at 2:20 PM, victor menezes <menezes...@gmail.com> wrote:
> What would be the best way to make a model with two e-mail fields uniques? I
> was thinking about writing some validation in the save() method but would
> like to know whether Django has some built-in way to deal with it.


it's a very bad idea to have plurality by adding a number of similar
fields. instead, you should have a related table

class Person(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)

class Email(models.Model):
person = models.ForeignKey(Person)
email = models.EmailField(unique=True)


--
Javier

victor menezes

unread,
Apr 7, 2015, 10:48:22 PM4/7/15
to django...@googlegroups.com
Thanks for the help, I end up doing it was easy to implement the email as TabularInline inside the Person form in the Admin but how would you make mandatory to have at least one e-mail?
Should I validate inside the save() method of Person class?

victor menezes

unread,
Apr 8, 2015, 3:02:50 AM4/8/15
to django...@googlegroups.com
Would it be a bad approach to use a ManyToMany with properties null/blank False?

class Email(models.Model):
    email = models.EmailField(unique=True)
    def __unicode__(self):
        return self.email

class Person(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    emails = models.ManyToManyField(Email, null=False, blank=False)    
    def __unicode__(self):
        return self.last_name + ', ' + self.first_name

Bruno A.

unread,
Apr 8, 2015, 3:38:28 PM4/8/15
to django...@googlegroups.com
+1 for Javier's answer, use a simple Foreign key on the e-mail field, not a ManyToMany on the Person (M2M allows an email to belong to multiple users). The Foreign key ensures an e-mail belongs to only 1 user, and that 2 users cannot have the same e-mail, but lets a user have multiple.

To force all users to have at least one e-mail, something like this would do the job:

class Person(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)

    def save(self, *args, **kwargs):
        if self.emails.count() == 0:
            raise ValueError("Need at least one email.")
        return super(Person, self).save(*args, **kwargs)


class Email(models.Model):
    person = models.ForeignKey(Person, related_name='emails')
    email = models.EmailField(unique=True)

Luis Zárate

unread,
Apr 8, 2015, 5:19:00 PM4/8/15
to django...@googlegroups.com

I don't know if work with many to many is the best approach, because if you know that only have two emails for user and you check his email a lot then the cost of join in time and machine resources increase innecessarily  So knowledge of you requirements determine your db scheme.

Using the first solution I thing in something like this.
in models.py


class Person(models.Model):
    first_name = models.CharField(max_length=100)
    last_name = models.CharField(max_length=100)
    email1 = models.EmailField()
    email2 = models.EmailField(null=True, blank=True)



in your forms forms.py

from django.forms import ModelForm
from myapp.models import Person

class PersonForm(ModelForm):

    def clean(self):
        cleaned_data = super(PersonForm, self).clean()
        if cleaned_data['email1'] == cleaned_data['email2']:
            raise forms.ValidationError("Emails are equals ")

        ....


    class Meta:
        model = Person
        fields = ['first_name', 'last_name', 'email1', 'email2']

   
And if you want to use in admin site

class PersonAdmin(admin.ModelAdmin)
    form = PersonForm


--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users...@googlegroups.com.
To post to this group, send email to django...@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/fe0288b9-9382-4163-b2cd-9d998f2e72f3%40googlegroups.com.

For more options, visit https://groups.google.com/d/optout.



--
"La utopía sirve para caminar" Fernando Birri


Javier Guerra Giraldez

unread,
Apr 8, 2015, 8:04:19 PM4/8/15
to django...@googlegroups.com
On Wed, Apr 8, 2015 at 12:18 PM, Luis Zárate <luis...@gmail.com> wrote:
>
> if you know that only have two emails for user and you check his email a lot then the cost of join in time and machine resources increase innecessarily


"normalize until it hurts, denormalize until it runs"

here the OP is way, way, behind of the very first normalizations. any
consideration of an hypothetical "cost of join" would be much later,
if any.


--
Javier

victor menezes

unread,
Apr 8, 2015, 9:22:02 PM4/8/15
to django...@googlegroups.com
I liked the idea at first but after couple tests realized that the it will alway raise the error on save().

The problem is that I can't add any Email without Person, this looks great but, I also can't add a Person without an Email and so it will never allow me to add anything.

Maybe I should keep it as ForeignKey but ensure that the Email is filled just in the form validation, not on database/class level? Following a test that i did:

>>> p = Person()

>>> p.first_name = 'aaa'
>>> p.last_name = 'bbb'
<Person: bbb, aaa>
>>> p.emails.all()
[]
>>> p.emails.count()
0
>>> p.emails.create(email='a...@aaa.aaa')
Traceback (most recent call last):
  File "<console>", line 1, in <module>
...
(value, self.field.rel.to._meta.object_name)
ValueError: Cannot assign "<Person: bbb, aaa>": "Member" instance isn't saved in the database.
>>> p.save()
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  ...
    raise ValueError('Need at least one e-mail.')
ValueError: Need at least one e-mail.

Bruno A.

unread,
Apr 8, 2015, 10:35:33 PM4/8/15
to django...@googlegroups.com
Right, I missed that detail :/
Agree the ModelForm seem a better place to add this validation.


On Wednesday, 8 April 2015 22:22:02 UTC+1, victor menezes wrote:
I liked the idea at first but after couple tests realized that the it will alway raise the error on save().

The problem is that I can't add any Email without Person, this looks great but, I also can't add a Person without an Email and so it will never allow me to add anything.

Maybe I should keep it as ForeignKey but ensure that the Email is filled just in the form validation, not on database/class level? Following a test that i did:

>>> p = Person()

>>> p.first_name = 'aaa'
>>> p.last_name = 'bbb'
<Person: bbb, aaa>
>>> p.emails.all()
[]
>>> p.emails.count()
0

>>> p.emails.create(email='aaa@aaa.aaa')

Reply all
Reply to author
Forward
0 new messages