将现有的auth.User数据迁移到新的Django 1.5自定义用户模型?

我不想摧毁我网站上的所有用户。 但我想利用Django 1.5的定制可插拔用户模型。 这是我的新用户模型:

class SiteUser(AbstractUser): site = models.ForeignKey(Site, null=True) 

一切都与我的新模型在一个新的安装(我有其他的代码,以及这样做的一个很好的理由 – 所有这些在这里是无关的)。 但是,如果我把这个放到我的live站点和syncdb&migrate中,我会失去所有的用户,或者至less他们会在一个不同的孤立的表中,而不是为我的新模型创build的新表。

我对南方很熟悉,但基于这篇文章和我的一些试验,似乎它的数据迁移目前不适合这种特定的迁移。 所以我正在寻找一些方法,让南方为此工作或为了一些非南方的迁移(原始SQL,dumpdata / loaddata或其他),我可以在我的每个服务器上运行(Postgres 9.2)来迁移用户一旦新的表已经创build,而旧的auth.User表仍然在数据库中。

南方不仅能够为你做这个迁移,但你需要聪明,分阶段做。 以下是一步一步的指南:(本指南假设您是AbstractUser子类,而不是AbstractBaseUser子类)

  1. 在进行切换之前,请确保在包含自定义用户模型的应用程序中启用了南方支持(为了指导,我们将其称为accounts和模型User )。 在这一点上,你还不应该有一个自定义的用户模型。

     $ ./manage.py schemamigration accounts --initial Creating migrations directory at 'accounts/migrations'... Creating __init__.py in 'accounts/migrations'... Created 0001_initial.py. $ ./manage.py migrate accounts [--fake if you've already syncdb'd this app] Running migrations for accounts: - Migrating forwards to 0001_initial. > accounts:0001_initial - Loading initial data for accounts. 
  2. 在帐户应用程序中创build一个新的空白用户迁移。

     $ ./manage.py schemamigration accounts --empty switch_to_custom_user Created 0002_switch_to_custom_user.py. 
  3. accounts应用程序中创build自定义User模型,但要确保它被定义为:

     class SiteUser(AbstractUser): pass 
  4. 用下面的代码填充空白迁移。

     # encoding: utf-8 from south.db import db from south.v2 import SchemaMigration class Migration(SchemaMigration): def forwards(self, orm): # Fill in the destination name with the table name of your model db.rename_table('auth_user', 'accounts_user') db.rename_table('auth_user_groups', 'accounts_user_groups') db.rename_table('auth_user_user_permissions', 'accounts_user_user_permissions') def backwards(self, orm): db.rename_table('accounts_user', 'auth_user') db.rename_table('accounts_user_groups', 'auth_user_groups') db.rename_table('accounts_user_user_permissions', 'auth_user_user_permissions') models = { ....... } # Leave this alone 
  5. 运行迁移

     $ ./manage.py migrate accounts - Migrating forwards to 0002_switch_to_custom_user. > accounts:0002_switch_to_custom_user - Loading initial data for accounts. 
  6. 现在就对你的用户模型做任何改变。

     # settings.py AUTH_USER_MODEL = 'accounts.User' # accounts/models.py class SiteUser(AbstractUser): site = models.ForeignKey(Site, null=True) 
  7. 创build并运行此更改的迁移

     $ ./manage.py schemamigration accounts --auto + Added field site on accounts.User Created 0003_auto__add_field_user_site.py. $ ./manage.py migrate accounts - Migrating forwards to 0003_auto__add_field_user_site. > accounts:0003_auto__add_field_user_site - Loading initial data for accounts. 

老实说,如果你已经对你的设置有了很好的了解,并且已经在使用南方了,那么应该像在你的账户模块中添加下面的迁移一样简单。

 # encoding: utf-8 from south.db import db from south.v2 import SchemaMigration from django.db import models class Migration(SchemaMigration): def forwards(self, orm): # Fill in the destination name with the table name of your model db.rename_table('auth_user', 'accounts_user') db.rename_table('auth_user_groups', 'accounts_user_groups') db.rename_table('auth_user_permissions', 'accounts_user_permissions') # == YOUR CUSTOM COLUMNS == db.add_column('accounts_user', 'site_id', models.ForeignKey(orm['sites.Site'], null=True, blank=False))) def backwards(self, orm): db.rename_table('accounts_user', 'auth_user') db.rename_table('accounts_user_groups', 'auth_user_groups') db.rename_table('accounts_user_user_permissions', 'auth_user_user_permissions') # == YOUR CUSTOM COLUMNS == db.remove_column('accounts_user', 'site_id') models = { ....... } # Leave this alone 

编辑2/5/13:为auth_user_group表添加重命名。 由于数据库约束,FK将自动更新为指向正确的表,但是M2M字段的表名是从2个结束表的名称生成的,并且将需要以这种方式进行手动更新。

编辑2:感谢@Tuttle&@ pix0r的更正。

我这样做的令人难以置信的懒惰的方式:

  1. 创build一个新的模型(用户),扩展AbstractUser。 在新模型中,在Meta中,覆盖db_table并设置为“auth_user”。

  2. 使用South创build初始迁移。

  3. 迁移,但伪造迁移,在运行迁移时使用--fake

  4. 添加新的字段,创build迁移,正常运行。

这是超越懒惰,但工程。 您现在有一个1.5兼容的用户模型,它只使用旧的用户表。 你也有一个适当的移民历史。

稍后可以使用手动迁移来修复此问题,以重命名表。

我想你已经正确地认识到像South这样的迁移框架是正确的。 假设您使用的是南,您应该能够使用数据迁移function将旧用户移植到您的新模型。

具体来说,我会添加一个forwards方法,将用户表中的所有行复制到新表中。 有些东西是:

 def forwards(self, orm): for user in orm.User.objects.all(): new_user = SiteUser(<initialize your properties here>) new_user.save() 

你也可以使用bulk_create方法来加快速度。

我已经厌倦了与南方的挣扎,所以我最终以不同的方式做了这件事情,并且对于我的特殊情况,

首先,我使用./manage.py dumpdata工作,修正了转储,然后是./manage.py loaddata,它工作。 然后,我意识到我可以做一个基本相同的事情,一个单一的,自包含的脚本,只加载必要的Django的设置,并直接进行序列化/反序列化。

自包含的python脚本

 ## userconverter.py ## import json from django.conf import settings settings.configure( DATABASES={ # copy DATABASES configuration from your settings file here, or import it directly from your settings file (but not from django.conf.settings) or use dj_database_url }, SITE_ID = 1, # because my custom user implicates contrib.sites (which is why it's in INSTALLED_APPS too) INSTALLED_APPS = ['django.contrib.sites', 'django.contrib.auth', 'myapp']) # some things you have to import after you configure the settings from django.core import serializers from django.contrib.auth.models import User # this isn't optimized for huge amounts of data -- use streaming techniques rather than loads/dumps if that is your case old_users = json.loads(serializers.serialize('json', User.objects.all())) for user in old_users: user['pk'] = None user['model'] = "myapp.siteuser" user['fields']["site"] = settings['SITE_ID'] for new_user in serializers.deserialize('json', json.dumps(old_users)): new_user.save() 

随着dumpdata / loaddata

我做了以下几点:

1)./manage.py dumpdata auth.User

2)将auth.user数据转换为新用户的脚本。 (或者只是手动search和replace你最喜欢的文本编辑器或grep)我看起来像这样:

 def convert_user_dump(filename, site_id): file = open(filename, 'r') contents = file.read() file.close() user_list = json.loads(contents) for user in user_list: user['pk'] = None # it will auto-increment user['model'] = "myapp.siteuser" user['fields']["site"] = side_id contents = json.dumps(user_list) file = open(filename, 'w') file.write(contents) file.close() 

3)./manage.py loaddata文件名

4)设置AUTH_USER_MODEL

*注意:无论您使用哪种技术(南,序列化/修改/反序列化或其他),执行此类迁移的一个关键部分是只要在当前设置中将AUTH_USER_MODEL设置为您的自定义模型,则django即使表仍然存在,您也可以从auth.User中删除。*

我们决定在我们的Django 1.6 / Django-CMS 3项目中切换到一个自定义用户模型,可能有点晚,因为我们的数据库中有我们不想丢失的数据(一些CMS页面等)。

在将AUTH_USER_MODEL切换到我们的自定义模型后,我们遇到了很多我们没有预料到的问题,因为很多其他表都有旧的auth_user表的外键,而这些外键没有被删除。 因此,尽pipe事情似乎在表面上起作用,但下面有很多事情发生:发布页面,向页面添加图像,添加用户等等,因为他们试图在仍然具有auth_user的外键的表中创build条目,而没有实际上将匹配的logging插入到auth_user

我们发现了一个快速和肮脏的方式来重build所有的表和关系,并复制我们的旧数据(用户除外):

  • mysqldump做一个完整的数据库备份
  • 做另一个没有CREATE TABLE语句的备份,排除一些在重build之后不存在的表,或者将由syncdb --migrate在新数据库上进行syncdb --migrate
    • south_migrationhistory
    • auth_user
    • auth_user_groups
    • auth_user_user_permissions
    • auth_permission
    • django_content_types
    • django_site
    • 属于您从项目中移除的应用程序的任何其他表(您可能只能通过试验find)
  • 删除数据库
  • 重新创build数据库(例如manage.py syncdb --migrate
  • 创build一个空数据库的转储(使其更快地绕过这个循环)
  • 尝试加载上面创build的数据转储
  • 如果由于重复的主键或缺less的表而无法加载,则:
    • 用文本编辑器编辑转储
    • 删除locking,转储和解锁该表的语句
    • 重新加载空的数据库转储
    • 尝试再次加载数据转储
    • 重复,直到数据转储加载没有错误

我们运行的命令(对于MySQL)是:

 mysqldump <database> > ~/full-backup.sql mysqldump <database> \ --no-create-info \ --ignore-table=<database>.south_migrationhistory \ --ignore-table=<database>.auth_user \ --ignore-table=<database>.auth_user_groups \ --ignore-table=<database>.auth_user_user_permissions \ --ignore-table=<database>.auth_permission \ --ignore-table=<database>.django_content_types \ --ignore-table=<database>.django_site \ > ~/data-backup.sql ./manage.py sqlclear ./manage.py syncdb --migrate mysqldump <database> > ~/empty-database.sql ./manage.py dbshell < ~/data-backup.sql (edit ~/data-backup.sql to remove data dumped from a table that no longer exists) ./manage.py dbshell < ~/empty-database.sql ./manage.py dbshell < ~/data-backup.sql (repeat until clean)