django-solomon 0.4.3__py3-none-any.whl → 0.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
django_solomon/admin.py CHANGED
@@ -1,7 +1,9 @@
1
1
  from django.contrib import admin
2
+ from django.contrib.auth.admin import UserAdmin
2
3
  from django.utils.translation import gettext_lazy as _
3
4
 
4
- from django_solomon.models import BlacklistedEmail, MagicLink
5
+ from django_solomon.forms import CustomUserCreationForm, CustomUserChangeForm
6
+ from django_solomon.models import BlacklistedEmail, MagicLink, CustomUser
5
7
 
6
8
 
7
9
  @admin.register(BlacklistedEmail)
@@ -43,3 +45,36 @@ class MagicLinkAdmin(admin.ModelAdmin):
43
45
  def is_valid(self, obj: MagicLink) -> bool:
44
46
  """Display if the magic link is valid."""
45
47
  return obj.is_valid
48
+
49
+
50
+ @admin.register(CustomUser)
51
+ class CustomUserAdmin(UserAdmin):
52
+ add_form = CustomUserCreationForm
53
+ form = CustomUserChangeForm
54
+ model = CustomUser
55
+ list_display = (
56
+ "email",
57
+ "is_superuser",
58
+ "is_staff",
59
+ "is_active",
60
+ )
61
+ list_filter = (
62
+ "is_superuser",
63
+ "is_staff",
64
+ "is_active",
65
+ )
66
+ fieldsets = (
67
+ (None, {"fields": ("email", "password")}),
68
+ ("Permissions", {"fields": ("is_staff", "is_active", "groups", "user_permissions")}),
69
+ )
70
+ add_fieldsets = (
71
+ (
72
+ None,
73
+ {
74
+ "classes": ("wide",),
75
+ "fields": ("email", "password1", "password2", "is_staff", "is_active", "groups", "user_permissions"),
76
+ },
77
+ ),
78
+ )
79
+ search_fields = ("email",)
80
+ ordering = ("email",)
django_solomon/apps.py CHANGED
@@ -6,25 +6,5 @@ class DjangoSolomonConfig(AppConfig):
6
6
  name = "django_solomon"
7
7
 
8
8
  def ready(self):
9
- # Copyright (c) 2023 Carlton Gibson
10
- # This migration is taken from the fantastic project `django-unique-user-email` created by Carlton Gibson.
11
- # https://github.com/carltongibson/django-unique-user-email/
12
-
13
- from django.contrib.auth.models import User
14
- from django.db import models
15
-
16
- # Updating the field itself triggers the auto-detector
17
- # field = User._meta.get_field("email")
18
- # field._unique = True
19
- #
20
- # But setting a constraint does not...
21
- User.Meta.constraints = [
22
- models.UniqueConstraint(
23
- fields=["email"],
24
- name="unique_user_email",
25
- # deferrable=models.Deferrable.DEFERRED,
26
- ),
27
- ]
28
- User._meta.constraints = User.Meta.constraints
29
- # ... as long as original_attrs is not updated.
30
- # User._meta.original_attrs["constraints"] = User.Meta.constraints
9
+ # Import checks when app is ready
10
+ import django_solomon.checks # noqa
@@ -2,10 +2,11 @@ from typing import Any
2
2
 
3
3
  from django.conf import settings
4
4
  from django.contrib.auth.backends import ModelBackend
5
+ from django.contrib.auth.base_user import AbstractBaseUser
5
6
  from django.http import HttpRequest
6
7
  from django.utils.translation import gettext_lazy as _
7
8
 
8
- from django_solomon.models import MagicLink, UserType
9
+ from django_solomon.models import MagicLink
9
10
  from django_solomon.utilities import get_client_ip
10
11
 
11
12
 
@@ -15,7 +16,7 @@ class MagicLinkBackend(ModelBackend):
15
16
  request: HttpRequest | None = None,
16
17
  token: str | None = None,
17
18
  **kwargs: Any, # noqa: ARG002
18
- ) -> UserType | None:
19
+ ) -> AbstractBaseUser | None:
19
20
  """
20
21
  Authenticates a user based on the provided magic link token. This method validates
21
22
  the token, ensures the token is not expired, and checks if the user associated
@@ -0,0 +1,42 @@
1
+ # django_solomon/checks.py
2
+ from django.apps import apps
3
+ from django.conf import settings
4
+ from django.core.checks import Error, register
5
+
6
+
7
+ @register()
8
+ def check_user_model_email(app_configs, **kwargs): # noqa: ARG001
9
+ """
10
+ Check that the user model has a unique email field.
11
+ """
12
+ errors = []
13
+
14
+ # Get the user model
15
+ user_model = apps.get_model(settings.AUTH_USER_MODEL)
16
+
17
+ # Check if email field exists
18
+ try:
19
+ email_field = user_model._meta.get_field("email")
20
+ except Exception:
21
+ errors.append(
22
+ Error(
23
+ "User model does not have an email field.",
24
+ hint="Make sure your user model has an email field.",
25
+ obj=settings.AUTH_USER_MODEL,
26
+ id="django_solomon.E001",
27
+ )
28
+ )
29
+ return errors
30
+
31
+ # Check if email field is unique
32
+ if not email_field.unique:
33
+ errors.append(
34
+ Error(
35
+ "User model email field must be unique.",
36
+ hint="Set unique=True on the email field of your user model.",
37
+ obj=settings.AUTH_USER_MODEL,
38
+ id="django_solomon.E002",
39
+ )
40
+ )
41
+
42
+ return errors
django_solomon/forms.py CHANGED
@@ -1,6 +1,9 @@
1
1
  from django import forms
2
+ from django.contrib.auth.forms import UserCreationForm, UserChangeForm
2
3
  from django.utils.translation import gettext_lazy as _
3
4
 
5
+ from django_solomon.models import CustomUser
6
+
4
7
 
5
8
  class MagicLinkForm(forms.Form):
6
9
  """Form for requesting a magic link."""
@@ -10,3 +13,15 @@ class MagicLinkForm(forms.Form):
10
13
  max_length=254,
11
14
  widget=forms.EmailInput(attrs={"autocomplete": "email"}),
12
15
  )
16
+
17
+
18
+ class CustomUserCreationForm(UserCreationForm):
19
+ class Meta:
20
+ model = CustomUser
21
+ fields = ("email",)
22
+
23
+
24
+ class CustomUserChangeForm(UserChangeForm):
25
+ class Meta:
26
+ model = CustomUser
27
+ fields = ("email",)
@@ -1,6 +1,8 @@
1
- # Generated by Django 5.2 on 2025-04-18 13:46
1
+ # Generated by Django 5.2 on 2025-04-21 11:50
2
2
 
3
+ import django.contrib.auth.validators
3
4
  import django.db.models.deletion
5
+ import django.utils.timezone
4
6
  from django.conf import settings
5
7
  from django.db import migrations, models
6
8
 
@@ -10,7 +12,7 @@ class Migration(migrations.Migration):
10
12
  initial = True
11
13
 
12
14
  dependencies = [
13
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
15
+ ('auth', '0012_alter_user_first_name_max_length'),
14
16
  ]
15
17
 
16
18
  operations = [
@@ -28,6 +30,29 @@ class Migration(migrations.Migration):
28
30
  'ordering': ['-created_at'],
29
31
  },
30
32
  ),
33
+ migrations.CreateModel(
34
+ name='CustomUser',
35
+ fields=[
36
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
37
+ ('password', models.CharField(max_length=128, verbose_name='password')),
38
+ ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
39
+ ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
40
+ ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
41
+ ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
42
+ ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
43
+ ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
44
+ ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
45
+ ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
46
+ ('email', models.EmailField(max_length=254, unique=True, verbose_name='email address')),
47
+ ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
48
+ ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
49
+ ],
50
+ options={
51
+ 'verbose_name': 'user',
52
+ 'verbose_name_plural': 'users',
53
+ 'abstract': False,
54
+ },
55
+ ),
31
56
  migrations.CreateModel(
32
57
  name='MagicLink',
33
58
  fields=[
@@ -36,6 +61,7 @@ class Migration(migrations.Migration):
36
61
  ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created at')),
37
62
  ('expires_at', models.DateTimeField(verbose_name='Expires at')),
38
63
  ('used', models.BooleanField(default=False, verbose_name='Used')),
64
+ ('ip_address', models.GenericIPAddressField(blank=True, null=True, verbose_name='IP Address')),
39
65
  ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='magic_links', to=settings.AUTH_USER_MODEL, verbose_name='User')),
40
66
  ],
41
67
  options={
django_solomon/models.py CHANGED
@@ -1,16 +1,63 @@
1
+ import uuid
1
2
  from datetime import timedelta
2
- from typing import Any, TypeVar
3
+ from typing import Any
3
4
 
4
5
  from django.conf import settings
5
- from django.contrib.auth import get_user_model
6
- from django.contrib.auth.base_user import AbstractBaseUser
6
+ from django.contrib.auth.base_user import AbstractBaseUser, BaseUserManager
7
+ from django.contrib.auth.models import AbstractUser
7
8
  from django.contrib.auth.tokens import default_token_generator
8
9
  from django.db import models
9
10
  from django.utils import timezone
10
11
  from django.utils.translation import gettext_lazy as _
11
12
 
12
- User = get_user_model()
13
- UserType = TypeVar("UserType", bound=AbstractBaseUser)
13
+
14
+ class CustomUserManager(BaseUserManager):
15
+ """
16
+ Custom user model manager where email is the unique identifiers
17
+ for authentication instead of usernames.
18
+ """
19
+
20
+ def create_user(self, email, password, **extra_fields):
21
+ """
22
+ Create and save a user with the given email and password.
23
+ """
24
+ if not email:
25
+ raise ValueError(_("The Email must be set"))
26
+ email = self.normalize_email(email)
27
+ user = self.model(email=email, **extra_fields)
28
+ user.set_password(password)
29
+ user.save()
30
+ return user
31
+
32
+ def create_superuser(self, email, password, **extra_fields):
33
+ """
34
+ Create and save a SuperUser with the given email and password.
35
+ """
36
+ extra_fields.setdefault("is_staff", True)
37
+ extra_fields.setdefault("is_superuser", True)
38
+ extra_fields.setdefault("is_active", True)
39
+
40
+ if extra_fields.get("is_staff") is not True:
41
+ raise ValueError(_("Superuser must have is_staff=True."))
42
+ if extra_fields.get("is_superuser") is not True:
43
+ raise ValueError(_("Superuser must have is_superuser=True."))
44
+ return self.create_user(email, password, **extra_fields)
45
+
46
+
47
+ class CustomUser(AbstractUser):
48
+ email = models.EmailField(_("email address"), unique=True)
49
+
50
+ USERNAME_FIELD = "email"
51
+ REQUIRED_FIELDS = ["username"]
52
+
53
+ objects = CustomUserManager()
54
+
55
+ def __str__(self):
56
+ return self.email
57
+
58
+ def save(self, *args, **kwargs):
59
+ self.username = self.email
60
+ super().save(*args, **kwargs)
14
61
 
15
62
 
16
63
  class BlacklistedEmail(models.Model):
@@ -42,7 +89,7 @@ class MagicLinkManager(models.Manager["MagicLink"]):
42
89
  criteria like token, usage status, and expiration date.
43
90
  """
44
91
 
45
- def create_for_user(self, user: UserType, ip_address: str = None) -> "MagicLink":
92
+ def create_for_user(self, user: AbstractBaseUser, ip_address: str = None) -> "MagicLink":
46
93
  """
47
94
  Creates a new magic link for a given user. If the setting SOLOMON_ONLY_ONE_LINK_ALLOWED
48
95
  is enabled, marks existing links for the user as used before creating a new one.
@@ -110,7 +157,9 @@ class MagicLink(models.Model):
110
157
  based on a predefined duration from the settings.
111
158
  """
112
159
  if not self.pk or not self.token:
113
- self.token = default_token_generator.make_token(self.user)
160
+ base_token = default_token_generator.make_token(self.user)
161
+ unique_component = uuid.uuid4().hex[:8] # 8 characters from UUID
162
+ self.token = f"{base_token}-{unique_component}"
114
163
 
115
164
  if not self.expires_at:
116
165
  expiration_time = getattr(settings, "SOLOMON_LINK_EXPIRATION", 300) # seconds
@@ -4,14 +4,13 @@ import socket
4
4
 
5
5
  from django.conf import settings
6
6
  from django.contrib.auth import get_user_model
7
+ from django.contrib.auth.base_user import AbstractBaseUser
7
8
  from django.contrib.auth.hashers import make_password
8
9
 
9
- from django_solomon.models import UserType
10
-
11
10
  logger = logging.getLogger(__name__)
12
11
 
13
12
 
14
- def get_user_by_email(email: str) -> UserType | None:
13
+ def get_user_by_email(email: str) -> AbstractBaseUser | None:
15
14
  """
16
15
  Get a user by email.
17
16
 
@@ -53,7 +52,7 @@ def get_user_by_email(email: str) -> UserType | None:
53
52
  return None
54
53
 
55
54
 
56
- def create_user_from_email(email: str) -> UserType:
55
+ def create_user_from_email(email: str) -> AbstractBaseUser:
57
56
  """
58
57
  Creates a new user with the given email.
59
58
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-solomon
3
- Version: 0.4.3
3
+ Version: 0.5.0
4
4
  Summary: A Django app for passwordless authentication using magic links.
5
5
  Project-URL: Home, https://django-solomon.rtfd.io/
6
6
  Project-URL: Documentation, https://django-solomon.rtfd.io/
@@ -30,7 +30,7 @@ License: MIT License
30
30
  SOFTWARE.
31
31
  License-File: LICENSE
32
32
  Keywords: django
33
- Classifier: Development Status :: 3 - Alpha
33
+ Classifier: Development Status :: 4 - Beta
34
34
  Classifier: Environment :: Web Environment
35
35
  Classifier: Framework :: Django :: 4.2
36
36
  Classifier: Framework :: Django :: 5.0
@@ -65,6 +65,7 @@ A Django app for passwordless authentication using magic links.
65
65
  ## Features
66
66
 
67
67
  - Passwordless authentication using magic links sent via email
68
+ - Email-based authentication with a built-in CustomUser model
68
69
  - Configurable link expiration time
69
70
  - Blacklist functionality to block specific email addresses
70
71
  - Support for auto-creating users when they request a magic link
@@ -72,8 +73,7 @@ A Django app for passwordless authentication using magic links.
72
73
  - Privacy-focused IP anonymization
73
74
  - Customizable templates for emails and pages
74
75
  - Compatible with Django's authentication system
75
- - Enforces unique email addresses for users (Code is taken
76
- from [django-unique-user-email](https://github.com/carltongibson/django-unique-user-email). Thanks!)
76
+ - System checks to ensure proper configuration
77
77
 
78
78
  ## Installation
79
79
 
@@ -93,7 +93,15 @@ INSTALLED_APPS = [
93
93
  ]
94
94
  ```
95
95
 
96
- 2. Add the authentication backend to your settings:
96
+ 2. Configure the user model in your settings:
97
+
98
+ ```python
99
+ AUTH_USER_MODEL = 'django_solomon.CustomUser'
100
+ ```
101
+
102
+ This setting is required for django-solomon to work properly, as it needs a user model with a unique email field for email-based authentication. You can also use your own custom user model as long as it has a unique email field.
103
+
104
+ 3. Add the authentication backend to your settings:
97
105
 
98
106
  ```python
99
107
  AUTHENTICATION_BACKENDS = [
@@ -102,7 +110,7 @@ AUTHENTICATION_BACKENDS = [
102
110
  ]
103
111
  ```
104
112
 
105
- 3. Include the django-solomon URLs in your project's `urls.py`:
113
+ 4. Include the django-solomon URLs in your project's `urls.py`:
106
114
 
107
115
  ```python
108
116
  from django.urls import include, path
@@ -114,7 +122,7 @@ urlpatterns = [
114
122
  ]
115
123
  ```
116
124
 
117
- 4. Set the login URL in your settings to use django-solomon's login view:
125
+ 5. Set the login URL in your settings to use django-solomon's login view:
118
126
 
119
127
  ```python
120
128
  LOGIN_URL = 'django_solomon:login'
@@ -122,7 +130,7 @@ LOGIN_URL = 'django_solomon:login'
122
130
 
123
131
  This ensures that when users need to authenticate, they'll be redirected to the magic link login page.
124
132
 
125
- 5. Configure your email settings to ensure emails can be sent:
133
+ 6. Configure your email settings to ensure emails can be sent:
126
134
 
127
135
  ```python
128
136
  EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
@@ -1,28 +1,27 @@
1
1
  django_solomon/__init__.py,sha256=4GdjeQVyChzdc7pZ1jrpknjcnu9Do_0nSh42UZNICKo,80
2
- django_solomon/admin.py,sha256=hwesCOQgUDDHPh5qq97AObfSyMgIOKsdzoa69naMPx4,1428
3
- django_solomon/apps.py,sha256=hfkXVS1kp1kAWtl5q2CPkxhP5rxnGq09JvQrYTNj2PA,1114
4
- django_solomon/backends.py,sha256=3RN4dsCehg5urp9sfASERFda1VR9nislNvHrxUc7rXM,3171
2
+ django_solomon/admin.py,sha256=H1q4qXN0uccLY3OFt7v9FisnLwANStDIr5JWMP8gByw,2401
3
+ django_solomon/apps.py,sha256=Ln0Nbv7ZQTb25l4p2CXyxtKFqy60Q9AEBCXIapYaDLk,268
4
+ django_solomon/backends.py,sha256=ruv8Lop5js4MQmTQV0cl07Hc11sZ1Kty6QOS5J92ZbI,3228
5
+ django_solomon/checks.py,sha256=yF2IFogiUreaAQcw2YzTUtq1mkd9hpzvdgbh3u0oopY,1198
5
6
  django_solomon/config.py,sha256=JEl3cY0BnfC9ja0ZVeLmfIwUStbUAO6vRhGZrrig5Yw,787
6
- django_solomon/forms.py,sha256=ymDsZ-KWyJfo12vGbZCxIhIvVket93xYR4MgDif4fh0,312
7
- django_solomon/models.py,sha256=G-74LDemMJMVOXN94tSz7E9XwspCNkzmHbhEP3Kl80U,4904
7
+ django_solomon/forms.py,sha256=Y6PsFbeSNLaCB3gfWE4ekvg6sJmtyy9DGn35ZJ5Qejo,667
8
+ django_solomon/models.py,sha256=9npDBTcCvlFbi4wSfV7IEU3WDgoincjMDmwPQqGU8lU,6567
8
9
  django_solomon/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
10
  django_solomon/urls.py,sha256=tT-I-p6dA2rD1IvH3pAcJRkPXYW0oYmYWXZAoiLJzGY,471
10
- django_solomon/utilities.py,sha256=I6LiBwAwZaa8h-iH_6RLP9fkJkK9mEb_24LPzLLuwFA,4559
11
+ django_solomon/utilities.py,sha256=FpWFqkPf4tzjItjuTrVfKRhjZw5449VZ7Y1gjPrcuhQ,4590
11
12
  django_solomon/views.py,sha256=gqsj1SVd2bbQvla3xpZYVHYCUxhu3fr_5I-LY-Tvm2g,5737
12
13
  django_solomon/locale/de/LC_MESSAGES/django.po,sha256=6lo72lsDV1bKBVowiRsQoaVKH5Mbkf34z78sG1LEYq4,747
13
14
  django_solomon/locale/en/LC_MESSAGES/django.po,sha256=6MjGCWQn-1AjOnP_gXtLa0Oad4qfNAn5tXdhnw_wEcg,732
14
15
  django_solomon/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
16
  django_solomon/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
- django_solomon/migrations/0001_initial.py,sha256=i-UhR1KQ4p7WmbDg9xUQfMmDo8yYdIigvHNsefLHJEc,1981
17
- django_solomon/migrations/0002_magiclink_ip_address.py,sha256=hs3MISYV7IHEqF-vELCmFtzhhvK8GEHkZu8RMc4X1YU,432
18
- django_solomon/migrations/0003_add_unique_constraint_auth_user_email.py,sha256=2eZm-oSTtf9zmEy4fJYE6fjMAsn_VUS_zKfW3WQNGoQ,1487
17
+ django_solomon/migrations/0001_initial.py,sha256=RlyI1VV2kDXcGS3waQ1ujieq8gGx66YU75phWhwxTlU,4558
19
18
  django_solomon/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
19
  django_solomon/templates/django_solomon/base/invalid_magic_link.html,sha256=3U4T2n4uvPvktiOOHYVyNmk0aXFfE4DPrURE1DinWKk,554
21
20
  django_solomon/templates/django_solomon/base/login_form.html,sha256=VYGnrno3c36W9f04XdRqJpjsDfOy0sq5D_cLayPz8Q0,546
22
21
  django_solomon/templates/django_solomon/base/magic_link_sent.html,sha256=GIMxnw3E98TXVkVQkMRTHmX5mz0xUsvgXVj25gO2HPQ,461
23
22
  django_solomon/templates/django_solomon/email/magic_link.mjml,sha256=OnfVGm2yFrOmoQ1syo6-Duq_Qraf3Tv0Vy5oidvt55g,1427
24
23
  django_solomon/templates/django_solomon/email/magic_link.txt,sha256=yl01eie3U2HkFEJvB1Qlm8Z6FPuop5yubDXFY5V507Q,502
25
- django_solomon-0.4.3.dist-info/METADATA,sha256=RFZs39oVqVqI_Hc8LlX25fQC7shz7qdvZsMiPAp6MCs,10508
26
- django_solomon-0.4.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
27
- django_solomon-0.4.3.dist-info/licenses/LICENSE,sha256=tbfFOFdH5mHIfmNGo6KGOC3U8f-hTCLfetcr8A89WwI,1071
28
- django_solomon-0.4.3.dist-info/RECORD,,
24
+ django_solomon-0.5.0.dist-info/METADATA,sha256=KmHBIVEGRYWuemsWRm4YLBEA2Wx-Wn2w1DCNoWzWnUQ,10792
25
+ django_solomon-0.5.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
26
+ django_solomon-0.5.0.dist-info/licenses/LICENSE,sha256=tbfFOFdH5mHIfmNGo6KGOC3U8f-hTCLfetcr8A89WwI,1071
27
+ django_solomon-0.5.0.dist-info/RECORD,,
@@ -1,18 +0,0 @@
1
- # Generated by Django 5.2 on 2025-04-20 14:59
2
-
3
- from django.db import migrations, models
4
-
5
-
6
- class Migration(migrations.Migration):
7
-
8
- dependencies = [
9
- ('django_solomon', '0001_initial'),
10
- ]
11
-
12
- operations = [
13
- migrations.AddField(
14
- model_name='magiclink',
15
- name='ip_address',
16
- field=models.GenericIPAddressField(blank=True, null=True, verbose_name='IP Address'),
17
- ),
18
- ]
@@ -1,42 +0,0 @@
1
- from django.db import migrations, models
2
-
3
-
4
- # Copyright (c) 2023 Carlton Gibson
5
- # This migration is taken from the fantastic project `django-unique-user-email` created by Carlton Gibson.
6
- # https://github.com/carltongibson/django-unique-user-email/
7
-
8
-
9
- class CustomAddConstraint(migrations.AddConstraint):
10
- """
11
- Override app_label to target auth.User
12
- """
13
-
14
- def state_forwards(self, app_label, state):
15
- state.add_constraint("auth", self.model_name_lower, self.constraint)
16
-
17
- def database_forwards(self, app_label, schema_editor, from_state, to_state):
18
- model = to_state.apps.get_model("auth", self.model_name)
19
- if self.allow_migrate_model(schema_editor.connection.alias, model):
20
- schema_editor.add_constraint(model, self.constraint)
21
-
22
- def database_backwards(self, app_label, schema_editor, from_state, to_state):
23
- model = to_state.apps.get_model("auth", self.model_name)
24
- if self.allow_migrate_model(schema_editor.connection.alias, model):
25
- schema_editor.remove_constraint(model, self.constraint)
26
-
27
-
28
- class Migration(migrations.Migration):
29
- dependencies = [
30
- ("django_solomon", "0002_magiclink_ip_address"),
31
- ("auth", "0012_alter_user_first_name_max_length"),
32
- ]
33
-
34
- operations = [
35
- CustomAddConstraint(
36
- model_name="user",
37
- constraint=models.UniqueConstraint(
38
- fields=["email"],
39
- name="unique_user_email",
40
- ),
41
- ),
42
- ]