Skip to content

Commit

Permalink
Merge 8e6e3d9 into 4f6ccb7
Browse files Browse the repository at this point in the history
  • Loading branch information
jnns committed Dec 17, 2021
2 parents 4f6ccb7 + 8e6e3d9 commit f8baa85
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 2 deletions.
3 changes: 2 additions & 1 deletion ChangeLog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ Note worthy changes
installed. Therefore, enabling or disabling ``sites`` is not something you can
do on the fly.


- Added database-level unique constraint for a single primary email address
per user for Django 2.2 and above (supported on PostgreSQL and SQLite).

Backwards incompatible changes
------------------------------
Expand Down
26 changes: 26 additions & 0 deletions allauth/account/migrations/0003_unique_primary_email.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

import django
from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
("account", "0002_email_max_length"),
]

if django.VERSION >= (2, 2):
operations = [
migrations.AddConstraint(
model_name="emailaddress",
constraint=models.UniqueConstraint(
condition=models.Q(primary=True),
fields=("user", "primary"),
name="only_one_primary_for_user",
),
),
]
else:
operations = []
9 changes: 9 additions & 0 deletions allauth/account/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import datetime

import django
from django.core import signing
from django.db import models, transaction
from django.utils import timezone
Expand Down Expand Up @@ -37,6 +38,14 @@ class Meta:
verbose_name_plural = _("email addresses")
if not app_settings.UNIQUE_EMAIL:
unique_together = [("user", "email")]
if django.VERSION >= (2, 2):
constraints = [
models.UniqueConstraint(
fields=["user", "primary"],
condition=models.Q(primary=True),
name="only_one_primary_for_user",
)
]

def __str__(self):
return self.email
Expand Down
26 changes: 25 additions & 1 deletion allauth/account/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
import json
import uuid
from datetime import timedelta
from unittest import skipIf

import django
from django import forms
from django.conf import settings
from django.contrib import messages
Expand All @@ -14,6 +16,7 @@
from django.core import mail, validators
from django.core.exceptions import ValidationError
from django.db import models
from django.db.utils import IntegrityError
from django.http import HttpResponseRedirect
from django.template import Context, Template
from django.test.client import Client, RequestFactory
Expand Down Expand Up @@ -684,6 +687,27 @@ def test_login_failed_attempts_exceeded_cleared_on_password_reset(self):
resp, settings.LOGIN_REDIRECT_URL, fetch_redirect_response=False
)

@skipIf(
django.VERSION < (2, 2),
"Django only supports conditional indexes since version 2.2",
)
def test_only_one_primary_email_per_user(self):
user = get_user_model().objects.create(
username="john", email="john@example.org", is_active=True
)
user2 = self._create_user(username="john2", email="john2@example.com")

EmailAddress.objects.create(
user=user, email="john@example.org", primary=True, verified=True
)
EmailAddress.objects.create(
user=user2, email="john2@example.com", primary=True, verified=True
)
with self.assertRaisesMessage(IntegrityError, "UNIQUE constraint failed"):
EmailAddress.objects.create(
user=user, email="john@example.com", primary=True, verified=False
)

@override_settings(
ACCOUNT_AUTHENTICATION_METHOD=app_settings.AuthenticationMethod.EMAIL,
ACCOUNT_EMAIL_VERIFICATION=app_settings.EmailVerificationMethod.MANDATORY,
Expand All @@ -700,7 +724,7 @@ def test_login_using_unverified_email_address_is_prohibited(self):
user=user, email="john@example.org", primary=True, verified=True
)
EmailAddress.objects.create(
user=user, email="john@example.com", primary=True, verified=False
user=user, email="john@example.com", primary=False, verified=False
)

resp = self.client.post(
Expand Down

0 comments on commit f8baa85

Please sign in to comment.