将现有的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
子类)
-
在进行切换之前,请确保在包含自定义用户模型的应用程序中启用了南方支持(为了指导,我们将其称为
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.
-
在帐户应用程序中创build一个新的空白用户迁移。
$ ./manage.py schemamigration accounts --empty switch_to_custom_user Created 0002_switch_to_custom_user.py.
-
在
accounts
应用程序中创build自定义User
模型,但要确保它被定义为:class SiteUser(AbstractUser): pass
-
用下面的代码填充空白迁移。
# 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
-
运行迁移
$ ./manage.py migrate accounts - Migrating forwards to 0002_switch_to_custom_user. > accounts:0002_switch_to_custom_user - Loading initial data for accounts.
-
现在就对你的用户模型做任何改变。
# settings.py AUTH_USER_MODEL = 'accounts.User' # accounts/models.py class SiteUser(AbstractUser): site = models.ForeignKey(Site, null=True)
-
创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的更正。
我这样做的令人难以置信的懒惰的方式:
-
创build一个新的模型(用户),扩展AbstractUser。 在新模型中,在Meta中,覆盖db_table并设置为“auth_user”。
-
使用South创build初始迁移。
-
迁移,但伪造迁移,在运行迁移时使用
--fake
。 -
添加新的字段,创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)