web-dev-qa-db-ja.com

既存のauth.Userデータを新しいDjango 1.5カスタムユーザーモデルに移行しますか?

私は自分のサイトのすべてのユーザーを破壊したくない。しかし、私はDjango 1.5のカスタムプラグ可能なユーザーモデルを利用したいと思います。これが私の新しいユーザーモデルです:

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

すべてが新しいインストールで私の新しいモデルで動作します(これを行う正当な理由とともに、他のコードがあります-これらはすべてここでは関係ありません)。しかし、これをライブサイトに配置し、syncdb&migrateを実行すると、すべてのユーザーが失われるか、少なくとも、新しいモデル用に作成された新しいテーブルとは異なる孤立したテーブルになります。

私はSouthに精通していますが、 この投稿 と私の側のいくつかの試行に基づいて、そのデータ移行は現在この特定の移行に適合していないようです。そのため、これに対してSouthを機能させる方法、または各サーバー(Postgres 9.2)で実行してユーザーを移行できるSouth以外の移行(raw SQL、dumpdata/loaddataなど)を行う方法を探しています。古いauth.Userテーブルがまだデータベースにある間に、新しいテーブルが作成された後。

34
Ben Roberts

サウスはあなたのためにこの移行を行うことができる以上のものですが、あなたは賢く、段階的にそれを行う必要があります。ステップバイステップガイドは次のとおりです:(このガイドでは、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. アカウントアプリで新しい空白のユーザー移行を作成します。

    $ ./manage.py schemamigration accounts --empty switch_to_custom_user
    Created 0002_switch_to_custom_user.py.
    
  3. Userアプリでカスタムaccountsモデルを作成しますが、次のように定義されていることを確認してください。

    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. この変更の移行を作成して実行します

    $ ./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は、dbの制約により正しいテーブルを指すように自動更新されますが、M2Mフィールドのテーブル名は、2つのエンドテーブルの名前から生成されるため、この方法で手動で更新する必要があります。

編集2:訂正してくれた@Tuttleと@ pix0rに感謝します。

49
Thomas

これを行う私の信じられないほど怠惰な方法:

  1. AbstractUserを拡張して、新しいモデル(User)を作成します。新しいモデル内で、そのメタで、 db_table をオーバーライドし、「auth_user」に設定します。

  2. Southを使用して初期移行を作成します。

  3. 移行しますが、移行の実行時に--fakeを使用して、移行を偽造します。

  4. 新しいフィールドを追加し、移行を作成して、通常どおり実行します。

これは怠惰を超えていますが、機能します。これで、1.5準拠のユーザーモデルができました。これは、古いユーザーテーブルのみを使用します。また、適切な移行履歴があります。

後で手動で移行してテーブルの名前を変更することで、これを修正できます。

16
Jack Shedd

私は南との闘いにうんざりしていたので、実際にはこれを別の方法で行うことになり、特定の状況でうまくいきました。

最初に、。/ manage.py dumpdataで動作させてダンプを修正し、次に./manage.pyloaddataで動作させました。次に、必要な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データを新しいユーザーに変換するスクリプト。 (または、お気に入りのテキストエディタまたは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.pyloaddataファイル名

4)AUTH_USER_MODELを設定します

*補足:使用する手法(南、シリアル化/変更/逆シリアル化など)に関係なく、このタイプの移行を行う上で重要な部分の1つは、現在の設定でAUTH_USER_MODELをカスタムモデルに設定するとすぐに= Djangoテーブルがまだ存在している場合でも、auth.Userから切り離されます。*

4
Ben Roberts

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 メソッドを使用して処理を高速化することもできます。

4

Django 1.6/Django-CMS 3プロジェクトで、カスタムユーザーモデルに切り替えることにしました。データベースに失いたくないデータがあったため、おそらく少し遅れました(一部のCMSページなど)。

AUTH_USER_MODELをカスタムモデルに切り替えた後、他の多くのテーブルに、削除されなかった古いauth_userテーブルへの外部キーがあったため、予期していなかった多くの問題が発生しました。そのため、表面的には機能しているように見えますが、ページの公開、ページへの画像の追加、ユーザーの追加など、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
    • プロジェクトから削除したアプリに属する​​他のテーブル(これは実験によってのみわかる場合があります)
  • データベースを削除します
  • データベースを再作成します(例:manage.py syncdb --migrate
  • 空のデータベースのダンプを作成します(このループを再度実行するのを高速化するため)
  • 上で作成したデータダンプを読み込もうとします
  • 主キーが重複しているか、テーブルがないためにロードに失敗した場合は、次のようになります。
    • テキストエディタでダンプを編集する
    • そのテーブルをロック、ダンプ、およびロック解除するステートメントを削除します
    • 空のデータベースダンプをリロードします
    • データダンプを再度ロードしてみてください
    • データダンプがエラーなしでロードされるまで繰り返します

(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)
2
qris