Closed trottomv closed 10 months ago
Hi @trottomv — You'll need to give me more to go on. This works directly for me.
Set up a project:
mktmpenv
pip install Django django-unique-user-email psycopg
django-admin startproject issue5
cd issue5
bbedit .
Adjust the settings:
# in settings.py
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql",
"NAME": "issue5",
}
}
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"unique_user_email",
]
And migrate:
./manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions, unique_user_email
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying sessions.0001_initial... OK
Applying unique_user_email.0001_initial... OK
Hi @carltongibson,
I was in a specific context with some 'bells and whistles,' such as Docker. I found myself in a situation where I needed to apply modifications to an already started project, although still very minimal. However, the auth
migrations had already been applied, this context to clarify: https://docs.djangoproject.com/en/4.2/topics/auth/customizing/#changing-to-a-custom-user-model-mid-project
I attempted to execute the same identical minimal setup on a virtualenv, as you clearly outlined above. Indeed, the first migration for 'django_unique_email' run succesfully, but...
Upon running the subsequent 'makemigrations,' a migration 0013 was added to 'auth' that removes the constraint:
$ python manage.py makemigrations
Migrations for 'auth':
/Users/trotto/experiments/django_unique_email/.venv/lib/python3.11/site-packages/django/contrib/auth/migrations/0013_remove_user_unique_user_email.py
- Remove constraint unique_user_email from model user
Following this, each subsequent 'python manage.py migrate' is executed, an error a bit different that I've reported earlier is encountered. It doesn't exactly the same error I initially reported, I can't reproduce it in this minimal virtualenv context now. However, it seems that the migration 0013 in 'auth' is causing some disruption.
I'm pasting here the entire traceback for completeness:
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions, unique_user_email
Running migrations:
Applying auth.0014_remove_user_unique_user_email...Traceback (most recent call last):
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 87, in _execute
return self.cursor.execute(sql)
^^^^^^^^^^^^^^^^^^^^^^^^
File "~/django_unique_email/.venv/lib/python3.11/site-packages/psycopg/cursor.py", line 737, in execute
raise ex.with_traceback(None)
psycopg.errors.UndefinedObject: constraint "unique_user_email" of relation "auth_user" does not exist
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "~/django_unique_email/issue5/manage.py", line 22, in <module>
main()
File "~/django_unique_email/issue5/manage.py", line 18, in main
execute_from_command_line(sys.argv)
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
utility.execute()
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/core/management/__init__.py", line 436, in execute
self.fetch_command(subcommand).run_from_argv(self.argv)
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/core/management/base.py", line 412, in run_from_argv
self.execute(*args, **cmd_options)
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/core/management/base.py", line 458, in execute
output = self.handle(*args, **options)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/core/management/base.py", line 106, in wrapper
res = handle_func(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/core/management/commands/migrate.py", line 356, in handle
post_migrate_state = executor.migrate(
^^^^^^^^^^^^^^^^^
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/db/migrations/executor.py", line 135, in migrate
state = self._migrate_all_forwards(
^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/db/migrations/executor.py", line 167, in _migrate_all_forwards
state = self.apply_migration(
^^^^^^^^^^^^^^^^^^^^^
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/db/migrations/executor.py", line 252, in apply_migration
state = migration.apply(state, schema_editor)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/db/migrations/migration.py", line 132, in apply
operation.database_forwards(
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/db/migrations/operations/models.py", line 1178, in database_forwards
schema_editor.remove_constraint(model, constraint)
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/db/backends/base/schema.py", line 542, in remove_constraint
self.execute(sql)
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/db/backends/postgresql/schema.py", line 48, in execute
return super().execute(sql, None)
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/db/backends/base/schema.py", line 201, in execute
cursor.execute(sql, params)
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 102, in execute
return super().execute(sql, params)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 67, in execute
return self._execute_with_wrappers(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
return executor(sql, params, many, context)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 84, in _execute
with self.db.wrap_database_errors:
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/db/utils.py", line 91, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "~/django_unique_email/.venv/lib/python3.11/site-packages/django/db/backends/utils.py", line 87, in _execute
return self.cursor.execute(sql)
^^^^^^^^^^^^^^^^^^^^^^^^
File "~/django_unique_email/.venv/lib/python3.11/site-packages/psycopg/cursor.py", line 737, in execute
raise ex.with_traceback(None)
django.db.utils.ProgrammingError: constraint "unique_user_email" of relation "auth_user" does not exist
OK, good, I got you… 👍 — We'll need to massage the auto-detector there 🤔 (I'll need to have a play.)
In the meantime, if you make sure to always pass the [app_label ...]
argument to makemigrations
you won't trigger the issue in auth
.
Hi @trottomv, it looks like it works with the following diff:
diff --git a/src/unique_user_email/apps.py b/src/unique_user_email/apps.py
index d407dfb..fea87f7 100644
--- a/src/unique_user_email/apps.py
+++ b/src/unique_user_email/apps.py
@@ -1,4 +1,5 @@
from django.apps import AppConfig
+from django import db
class UniqueUserEmailConfig(AppConfig):
@@ -23,4 +24,14 @@ class UniqueUserEmailConfig(AppConfig):
]
User._meta.constraints = User.Meta.constraints
# ... as long as original_attrs is not updated.
- # User._meta.original_attrs["constraints"] = User.Meta.constraints
+ # BUT that must be updated once the unique_user_email migration is applied.
+ with db.connection.cursor() as cursor:
+ try:
+ cursor.execute(
+ "SELECT name FROM django_migrations WHERE app = 'unique_user_email'"
+ )
+ except db.OperationalError:
+ pass # No migrations run yet.
+ else:
+ if bool(cursor.fetchall()):
+ User._meta.original_attrs["constraints"] = User.Meta.constraints
I don't know if it'd be the best test but a makemigrations --dry-run
should output No changes detected
if it's all working correctly. (Any better ideas? 🤔)
Would you fancy making a pull request to resolve this issue? 🎁
I would avoid the RAW query and the various with try except else
and suggest utilizing the django orm through MigrationRecorder.
from django.db.migrations.recorder import MigrationRecorder
...
if bool(MigrationRecorder.Migration.objects.filter(app="unique_user_email")):
User._meta.original_attrs["constraints"] = User.Meta.constraints
Yep, happy to look at that. 👍
I have opened the PR that fix that.
After various tests and checks, it seems to me that the control on the presence of migration is a bit redundant and, indeed, could create other problems. So, I limited myself to adding a test and uncommenting the already existing line that acts on original_attrs["constraint"]
without introducing the hypothesized control. Am I missing something?
Because it should be enough that "unique_user_email" is present in the INSTALLED_APPS
and avoid checking the presence of migrations.
@trottomv Great thanks. I replied on the PR... — let's continue there 🎁
Hi, even after following the various instructions for installing the app, I receive the following error when applying migrations.
python version 3.11 django version Django==4.2.7 postgresql version 14