Single email field on Django User model

Asked

Viewed 606 times

4

I have a system that has a user register and I’m using the lib django.contrib.auth.models to make this registration. However I I need this model’s email field to be unique.

from django.db import models
from django.contrib.auth.models import User

class CadastroUsuario(models.Model):
    user = models.ForeignKey(User)
    documento = models.CharField(max_length=250)

    def __unicode__(self):
        return self.user.email

Does anyone know the best way to do this? I’m starting out in Django and I’m having trouble finding a solution.

I’m working with Django 1.4.7 in this project.

1 answer

2


You are unlucky, from version 1.5 it is possible use your own template to represent a user, but in 1.4 or below there is (as far as I know) a "clean" way to modify the model User. Unless you’re willing to make a upgrade (and if so, use a newer version, the 1.5 I know was discontinued for security reasons), an alternative strategy will be required.

  • You could change the table directly in the bank by marking the column as UNIQUE; to my knowledge, this will not break the existing models, but you will have an exception if you try to register two users with the same email by the ORM (and the treatment of the same will be less smooth, because your model still "thinks" that the field is not unique).

    • Advantage: no matter where the data comes from - inside or outside your system - the database will not let duplicate data in.
    • Disadvantage (?): I don’t know which undesirable effects the bank may have one structure and the ORM have another (though essentially compatible). You will need to pay attention to unexpected errors, and test the system very well in every situation involving the model User.
  • You can use the sign pre_save to detect when a user is about to be saved, and to do there the unicity check of the email field. I have no experience with signals, but the documentation suggests that it is simply a matter of defining a function as follows:

    from django.db.models.signals import pre_save
    from django.dispatch import receiver
    from django.contrib.auth.models import User
    
    @receiver(pre_save, sender=User)
    def my_handler(sender, **kwargs):
        ... # Aqui sender é o usuário sendo salvo
    
    • Disadvantage: if you create multiple objects in a single operation (eg.: bulk_create) this signal will not be sent, and its function will not be called.
  • If you use the admin, you can try customizing it in a way that responds well to this additional restriction. The topic is extensive, and I don’t have enough experience to cover all the possibilities here (customize the form, give a friendly message if the field value is invalid, etc).

  • In their views, of course, you can give special treatment to that field. There are at least two ways (each of them within a transaction, of course):

    • Make a query to check if any e-mail is repeated, then enter/update the actual element(s):

      if not User.object.filter(email__in=lista_de_emails_a_inserir):
          ... # Insere o(s) novo(s) usuário(s)
      
    • Make the desired changes, and in the end check if any of them caused a duplicate email; if provoked, do rollback in everything:

      ... # Insere o(s) novo(s) usuário(s)
      if User.objects.values('email').annotate(contagem=Count('email')).order_by().filter(contagem__gt=1):
          transaction.rollback()
      

      Source, explaining why this order_by emptiness is needed.

    Note that some solutions may treat the competition better than others, or perform better/worse.

  • You can create a UserProfile for each user, and in this model repeat the field email placing it single. The UserProfile is unique, so there’s no way two users have the same profile.

    • Warning: I can’t remember whether or not it’s possible to "force" everyone User to have a UserProfile - if any User break up without one UserProfile, this solution will not guarantee the uniqueness of the email.
  • Finally, you can try to do "Monkey patch" on the model User, modifying your field email to make it unique. This is the most "extreme" solution, and I don’t know how to anticipate what kind of problems you might encounter.

Maybe there are other ways, but that’s what popped into my head. Note that some of these techniques can be used together, complementing each other (e.g.: change the field in the database to ensure data integrity, and also change the admin/views to ensure smooth handling of errors).

Browser other questions tagged

You are not signed in. Login or sign up in order to post.