如何在保留数据的前提下修补Django User模型?升级1.6至1.7遇迁移问题
1. Resolving makemigrations Failures After Migrating from South
When moving from South to Django's native migration system (introduced in 1.7), just deleting old migration files isn't enough—you need to fully clean up South's traces and initialize native migrations correctly:
Step 1: Remove South completely
- Take
'south'out of yourINSTALLED_APPSinsettings.py. - Delete all South-generated migration folders (the
migrationsdirectories with South-specific files) from your apps. - Drop the South-specific database table by running this SQL query in your database shell:
DROP TABLE IF EXISTS south_migrationhistory;
- Take
Step 2: Initialize native migrations safely
Since your database already has all tables from Django 1.6 + South, you don't want migrations to try recreating them. Follow these steps:- Double-check that all your models match the current database schema exactly (no unapplied South changes left).
- Generate initial native migration files for your apps:
python manage.py makemigrations - Mark these initial migrations as already applied to avoid database conflicts:
python manage.py migrate --fake-initial
This tells Django the tables already exist, so it won't attempt to create them again.
2. Extending auth.User Without Breaking Migrations (And Preserving Data)
Using User.add_to_class() is a risky move here—Django's migration system can't detect dynamically added fields, which is definitely contributing to your migration problems. Here are two better, maintainable approaches:
Option 1: Use a OneToOne Profile Model (Recommended)
This is the safest way to extend user data without modifying the built-in auth.User model:
- Keep your existing
access.Profilemodel, but replace the ForeignKey with a OneToOneField (since each user should have exactly one profile):from django.contrib.auth.models import User from django.db import models class Profile(models.Model): user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') # Add your custom fields here (the ones you tried to add via add_to_class) # Example: preferred_language = models.CharField(max_length=10, null=True) - Generate a migration for this change:
python manage.py makemigrations access - Apply the migration (this adds the
user_idcolumn to youraccess_profiletable without losing existing data):python manage.py migrate access - If you have existing Profile records linked via ForeignKey, clean up any duplicates first to ensure each User has only one Profile before applying the migration.
Option 2: Switch to a Custom User Model (More Involved)
If you truly need to add fields directly to the user model (not recommended unless necessary), create a custom user model inheriting from AbstractUser:
- Create your custom user model in an app (e.g.,
accounts/models.py):from django.contrib.auth.models import AbstractUser from django.db import models class CustomUser(AbstractUser): # Add your custom fields here profile = models.ForeignKey('access.Profile', null=True, on_delete=models.SET_NULL) # Other custom fields... - Update
settings.pyto use this model:AUTH_USER_MODEL = 'accounts.CustomUser' - Generate initial migrations for the
accountsapp:python manage.py makemigrations accounts - Fake the initial migration first (since your
auth_usertable already exists):python manage.py migrate accounts --fake-initial - Write a custom data migration to copy existing
auth_userdata to the newaccounts_customusertable. - Apply any remaining migrations to add your custom fields.
Note: This approach requires updating all references to auth.User in your codebase to use settings.AUTH_USER_MODEL instead.
内容的提问来源于stack exchange,提问作者Clara Daia

