自定义Django数据迁移创建allauth EmailAddress记录遇NodeNotFoundError
问题
原本使用Django默认auth系统的项目,通过django.contrib.auth.tokens实现邮箱验证——自定义User模型(继承AbstractUser)的is_active字段初始为False,用户验证邮箱后改为True。
升级到django-allauth后,其他功能正常,但邮箱验证逻辑异常。由于allauth通过EmailAddress模型的verified字段判断邮箱是否已验证,因此编写了自定义数据迁移,将原有用户的is_active状态映射到EmailAddress的verified字段。运行迁移时触发错误:
django.db.migrations.exceptions.NodeNotFoundError: Migration accounts.0003_create_allauth_email_records_for_existing_users dependencies reference nonexistent parent node ('allauth.account', '0002_email_max_length')
自定义迁移文件accounts/migrations/0003_create_allauth_email_records_for_existing_users.py代码:
# Generated by Django 3.2.11 on 2022-12-09 10:56 from django.db import migrations class Migration(migrations.Migration): dependencies = [ ('accounts', '0002_delete_profile'), ('allauth.account', '0002_email_max_length') ] def create_allauth_email_records_for_existing_users(apps, schema_editor): UserModel = apps.get_model("accounts", "User") EmailMoldel = apps.get_model("allauth.account", "EmailAddress") for user in UserModel.objects.all(): email_record = EmailMoldel.objects.filter(email=user.email).first() if email_record == None: if user.is_active: email_verified = True else: email_verified = False new_email = EmailMoldel.objects.create( user = user, email = user.email, verified = email_verified, ) def reverse_func(apps, schema_editor): UserModel = apps.get_model("accounts", "User") EmailMoldel = apps.get_model("allauth.account", "EmailAddress") for email in EmailMoldel.objects.all(): if not email.primary: email.delete() operations = [ migrations.RunPython( create_allauth_email_records_for_existing_users, reverse_code= reverse_func ) ]
自定义User模型accounts/models.py代码:
from django.db import models from django.contrib.auth.models import AbstractUser from django.utils.translation import gettext_lazy as _ class User(AbstractUser): email = models.EmailField(_('email address'), unique=True)
已在INSTALLED_APPS中正确配置allauth相关应用,且不能通过删除迁移或重置数据库解决问题,需修正迁移依赖让Django正确识别allauth.account的迁移节点。
解决方案
1. 确认allauth.account的实际迁移版本
不同版本的django-allauth,其allauth.account的迁移文件编号可能不同。执行命令查看当前环境中allauth.account的可用迁移:
python manage.py showmigrations allauth.account
输出示例:
allauth.account [X] 0001_initial [X] 0003_username_max_length
根据输出确认存在的迁移节点,比如如果没有0002_email_max_length,则使用0001_initial或其他存在的迁移编号。
2. 修改迁移依赖
将迁移文件的dependencies中的('allauth.account', '0002_email_max_length')替换为实际存在的迁移节点。例如:
dependencies = [ ('accounts', '0002_delete_profile'), ('allauth.account', '0001_initial') ]
3. 完善EmailAddress记录创建逻辑
原代码未设置primary字段,而allauth要求每个用户有一个主邮箱,建议补充该字段:
new_email = EmailMoldel.objects.create( user=user, email=user.email, verified=email_verified, primary=True # 添加主邮箱标记 )
4. 修正拼写错误
原代码中EmailMoldel为拼写错误,建议修正为EmailModel,避免后续维护问题。
5. 重新运行迁移
修改完成后执行:
python manage.py migrate accounts
内容的提问来源于stack exchange,提问作者Del_Wiz

