Django迁移生成OneToOne关联的用户及关联对象种子数据时触发ValueError
Django迁移生成OneToOne关联的用户及关联对象种子数据时触发ValueError
嘿,我一眼就看出问题出在哪了——迁移环境中模型类的实例不匹配!你在迁移里混用了两种获取User模型的方式:get_user_model()拿到的是你当前代码里的User类,而apps.get_model()拿到的是迁移历史快照里的UserProfile/BankAccount类,这俩在Django的迁移系统里是完全不同的类实例,所以即使看起来都是User对象,Django也不认,就抛出了那个ValueError。
下面是具体的修复步骤,我帮你把代码也改好了:
1. 迁移中统一用apps.get_model()获取所有模型
在迁移里,绝对不能直接用get_user_model()或者导入当前的模型类,必须通过apps.get_model()获取对应历史版本的模型。把你迁移里的User = get_user_model()替换成通用的获取方式:
from django.conf import settings User = apps.get_model(settings.AUTH_USER_MODEL)
如果用的是Django默认的auth用户模型,也可以直接写:
User = apps.get_model("auth", "User")
2. 修复用户密码的设置方式
你现在用User.objects.create(..., password=password)是直接把明文密码存进数据库,用户根本没法登录!必须用set_password()方法来哈希密码,这一步很重要:
把创建用户的代码从:
user = User.objects.create(username=username, email=email, password=password)
改成:
# 先创建用户,不设置密码 user = User.objects.create(username=username, email=email) # 用set_password哈希密码后保存 user.set_password(password) user.save()
3. 确保依赖的Currencies数据已存在
你的迁移依赖0002_seed_initial_data,必须保证这个迁移里已经创建了code="USD"的Currencies条目,不然Currencies.objects.get(code="USD")会直接报错。如果之前没创建,也可以在当前迁移里做个容错处理。
修复后的完整迁移代码
from django.db import migrations import random from django.conf import settings seed_data = [ {'first_name': 'John', 'middle_name': 'Martin', 'last_name': 'Doe', 'gender': 'MALE', 'username':'john.d', 'password':'jb2025example.cz', 'email':'john.b@example.cz'}, {'first_name': 'Alice', 'middle_name': 'B.', 'last_name': 'Smith', 'gender': 'FEMALE', 'username': 'alice.b', 'password':'alice.b@example.cz', 'email':'ab2025example.cz'}, {'first_name': 'Bob', 'middle_name': 'C.', 'last_name': 'Johnson', 'gender': 'MALE', 'username': 'bob.c', 'password':'bob.c@example.cz', 'email':'bc2025example.cz'}, {'first_name': 'Carol', 'middle_name': 'D.', 'last_name': 'Williams', 'gender': 'FEMALE', 'username': 'carol.d', 'password':'carol.d@example.cz', 'email':'cd2025example.cz'}, {'first_name': 'David', 'middle_name': 'E.', 'last_name': 'Brown', 'gender': 'MALE', 'username': 'david.e', 'password':'david.e@example.cz', 'email':'de2025example.cz'}, {'first_name': 'Eve', 'middle_name': 'F.', 'last_name': 'Jones', 'gender': 'FEMALE', 'username': 'eve.f', 'password':'eve.f@example.cz', 'email':'ef2025example.cz'}, ] class Migration(migrations.Migration): dependencies = [ ('main', '0002_seed_initial_data'), ] def seed_initial_users_and_profiles(apps, schema_editor): # 所有模型都通过apps.get_model获取 UserProfile = apps.get_model("main", "UserProfile") BankAccount = apps.get_model("main", "BankAccount") Currencies = apps.get_model("main", "Currencies") User = apps.get_model(settings.AUTH_USER_MODEL) # 通用方式获取User模型 # 确保USD货币已存在(做容错处理) try: currency = Currencies.objects.get(code="USD") except Currencies.DoesNotExist: # 临时创建,建议优先在0002迁移中预创建该条目 currency = Currencies.objects.create(country="United States", code="USD", sign="$") for data in seed_data: username = data.pop('username') email = data.pop('email') password = data.pop('password') balance = random.randint(900, 100_000_000) # 正确创建用户并设置哈希密码 user = User.objects.create(username=username, email=email) user.set_password(password) user.save() # 现在创建关联的UserProfile和BankAccount就不会报错了 UserProfile.objects.create(user=user, **data) # BankAccount的save方法会自动生成account_id,无需手动处理 BankAccount.objects.create(currency_code=currency, owner=user, balance=balance) operations = [ migrations.RunPython(seed_initial_users_and_profiles), ]
额外注意点
- 迁移系统是基于历史快照工作的,所以任何在迁移中用到的模型,都必须通过
apps.get_model()获取,绝对不能直接导入当前的模型类(包括User)。 - 如果你以后自定义了User模型,
apps.get_model(settings.AUTH_USER_MODEL)这种方式依然有效,比硬编码"auth", "User"更通用。 - 运行迁移前,最好先检查依赖的0002迁移是否真的创建了所需的Currencies数据,避免出现
DoesNotExist异常。
内容来源于stack exchange
相关产品推荐
相关产品推荐

