Ahtisham
Ahtisham

Reputation: 10116

Handling duplicate email address in django allauth

What I am trying to do ?

I am trying to avoid duplicate email address by following these steps:

  1. Before user login to my site from a social account I check to see if the email address already exists.
  2. If no then login the user otherwise check the below steps.

    • Check to see if the provider of registered user match the user trying to login.

    • If no then don't allow user to login otherwise login the user.

What is the problem ?

I get the following error:

Error:AttributeError at /accounts/twitter/login/callback/ 'QuerySet' object has no attribute 'profile'

My Code:

views.py:

@receiver(pre_social_login)
def handleDuplicateEmail(sender, request, sociallogin, **kwargs):
    if sociallogin.account.provider == 'facebook' or sociallogin.account.provider == 'twitter': 
        email_address = sociallogin.account.extra_data['email'] # get email address from fb or twitter social account.
    else:
        email_address = sociallogin.account.extra_data['email-address']  # Get email from linkedin social account.
    users = User.objects.all().filter(email=email_address) # This line is problematic
    if users:  
        if not (users.profile.provider == sociallogin.account.provider):    # Different user is trying to login with already existing user's email address.         
            response = 'Your social account email address is already registered to some account. Please choose a different one.'
            raise ImmediateHttpResponse(render(request, 'index.html', {'type': True, 'response': response}))    # redirect to index template with error message.

models.py:

class Profile(models.Model):
     user = models.OneToOneField(User, related_name='profile', on_delete=models.CASCADE)        
     provider = models.CharField(max_length=256, blank=True, null=True) # for storing name of provider used to login with.

Edit:

Since Facebook, Twitter and Linkedin give users a choice to login with their phone number or email address and if they choose phone number then in that cause users won't have an email address associated with them to handle this situation I have updated my code like so:

if sociallogin.account.provider == 'facebook' or sociallogin.account.provider == 'twitter': 
    try:
        email_address = sociallogin.account.extra_data['email']
    except:
        email_address = None # If social account was created on phone number for facebook & twitter
else:
    try:
        email_address = sociallogin.account.extra_data['email-address'] 
    except:
        email_address = None # If social account was created on phone number or linkedin
if email_address:        
    users = User.objects.all().filter(email=email_address)
    if users.exists():
        ...
else:
    response = 'You have not provided an email address in your social account. Please register as local user or use a different social account.'
    raise ImmediateHttpResponse(render(request, 'index.html', {'type': True, 'response': response}))    

Upvotes: 2

Views: 3483

Answers (1)

dirkgroten
dirkgroten

Reputation: 20692

users = User.objects.all().filter(email=email_address) returns a QuerySet so you can't just call .profile on it. In theory this query could return multiple User objects. But it could also contain 0 objects (which is more likely).

So you need to handle these cases:

if users.exists():
    user = users.first()  # assuming there will always only be one user
    if not user.profile.provider == sociallogin.account.provider:
        etc...

or

if users.exists():
    for user in users:
         if not user.profile.provider == sociallogin.account.provider:
             etc...
             break

Upvotes: 1

Related Questions