我不想销毁我网站上的所有用户。但我想利用Django 1.5的自定义可插拔用户模型。这是我的新用户模型:
class SiteUser(AbstractUser):
site = models.ForeignKey(Site, null=True)
在新安装中我的新模型一切正常(我还有其他代码,还有一个很好的理由 - 所有这些都与此无关)。但是,如果我把它放在我的实时网站和syncdb&迁移,我将丢失所有用户,或者至少他们将处于与为我的新模型创建的新表不同的孤立表中。
我对南方很熟悉,但基于this post和我的一些试验,似乎它的数据迁移目前还不适合这种特定的迁移。所以我正在寻找一些方法来让South为此工作或者为我的每个服务器(Postgres 9.2)运行的一些非南迁移(原始SQL,dumpdata / loaddata或其他)迁移用户一旦创建了新表,而旧的auth.User表仍在数据库中。
答案 0 :(得分:49)
South无法为您执行此迁移,但您需要聪明并分阶段执行此操作。以下是分步指南:(本指南以子类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.
在帐户应用中创建新的空白用户迁移。
$ ./manage.py schemamigration accounts --empty switch_to_custom_user
Created 0002_switch_to_custom_user.py.
在User
应用中创建自定义accounts
模型,但请确保其定义为:
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)
为此次更改创建并运行迁移
$ ./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
EDIT 2/5/13:为auth_user_group表添加了重命名。由于db约束,FK将自动更新以指向正确的表,但是M2M字段的表名是从2个结束表的名称生成的,并且需要以这种方式手动更新。
编辑2:感谢@Tuttle& @ pix0r进行更正。
答案 1 :(得分:16)
我非常懒惰地这样做:
创建一个新模型(User),扩展AbstractUser。在新模型中,在它的Meta中,覆盖db_table并设置为'auth_user'。
使用South创建初始迁移。
迁移,但在运行迁移时使用--fake
伪造迁移。
添加新字段,创建迁移,正常运行。
这超出了懒惰,但有效。您现在拥有符合1.5的用户模型,它只使用旧的用户表。您还有适当的迁移历史记录。
您可以稍后通过手动迁移解决此问题,以重命名该表。
答案 2 :(得分:4)
我认为您已经正确地确定了像South这样的迁移框架是正确的方法。假设您正在使用South,您应该能够使用Data Migrations功能将旧用户移植到新模型。
具体来说,我会添加一个forwards
方法将用户表中的所有行复制到新表中。有点像:
def forwards(self, orm):
for user in orm.User.objects.all():
new_user = SiteUser(<initialize your properties here>)
new_user.save()
您还可以使用bulk_create
方法加快速度。
答案 3 :(得分:4)
我厌倦了与南方的挣扎,所以我实际上最终以不同的方式做到这一点,并且它很好地适应了我的特殊情况:
首先,我使用./manage.py dumpdata,修复转储,然后运行./manage.py loaddata。然后我意识到我可以用一个单独的,自包含的脚本做同样的事情,它只加载必要的django设置并直接进行序列化/反序列化。
## 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()
我做了以下事情:
1)./manage.py dumpdata auth.User
2)将auth.user数据转换为新用户的脚本。 (或者只是在您喜欢的文本编辑器或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 filename
4)设置AUTH_USER_MODEL
*旁注:执行此类迁移的一个关键部分是,无论您使用哪种技术(南,序列化/修改/反序列化或其他技术),只要您在当前设置AUTH_USER_MODEL到自定义模型设置,即使表仍然存在,django也会将你从auth.User中删除。*
答案 4 :(得分:2)
我们决定在我们的Django 1.6 / Django-CMS 3项目中切换到自定义用户模型,可能有点迟,因为我们的数据库中有数据我们不想丢失(某些CMS页面等)
在我们将AUTH_USER_MODEL切换到我们的自定义模型之后,我们遇到了许多我们没有预料到的问题,因为很多其他表都有旧的auth_user
表的外键,但没有删除。因此,尽管事情似乎在表面上起作用,但很多事情都在下面发生:发布页面,向页面添加图像,添加用户等等,因为他们试图在仍然具有{{1的外键的表的表中创建条目没有实际将匹配记录插入auth_user
。
我们找到了一种快速而又脏的方法来重建所有表和关系,并复制我们的旧数据(用户除外):
auth_user
mysqldump
语句的备份,并排除重建后不存在的几个表,或者在新数据库上由CREATE TABLE
填充:
syncdb --migrate
south_migrationhistory
auth_user
auth_user_groups
auth_user_user_permissions
auth_permission
django_content_types
django_site
)我们运行的命令(对于MySQL)是:
manage.py syncdb --migrate