Перенос существующих данных auth.User на новую пользовательскую модель Django 1.5?
Я бы предпочел не уничтожать всех пользователей на моем сайте. Но я хочу воспользоваться пользовательской моделью подключаемого модуля Django 1.5. Вот моя новая модель пользователя:
class SiteUser(AbstractUser):
site = models.ForeignKey(Site, null=True)
Все работает с моей новой моделью в новой установке (у меня есть другой код, а также веская причина для этого - все это здесь не имеет значения). Но если я размещу это на своем живом сайте и произвожу синхронизацию и миграцию, я потеряю всех своих пользователей, или, по крайней мере, они будут в другой бесхозной таблице, чем новая таблица, созданная для моей новой модели.
Я знаком с Саутом, но, основываясь на этом посте и некоторых испытаниях с моей стороны, кажется, что его миграции данных в настоящее время не подходят для этой конкретной миграции. Поэтому я ищу какой-либо способ заставить South работать для этого или для какой-либо миграции не-South (необработанный SQL, dumpdata/loaddata или иным образом), которую я могу запустить на каждом из моих серверов (Postgres 9.2) для миграции пользователей после создания новой таблицы, когда старая таблица auth.User все еще находится в базе данных.
5 ответов
Юг более чем способен выполнить эту миграцию для вас, но вам нужно быть умным и делать это поэтапно. Вот пошаговое руководство: (Это руководство предполагало, что вы подкласс 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
РЕДАКТИРОВАНИЕ 2/5/13: добавлено переименование для таблицы auth_user_group. FK будут автоматически обновляться, чтобы указывать на правильную таблицу из-за ограничений в БД, но имена таблиц полей M2M генерируются из имен 2-х конечных таблиц и требуют ручного обновления таким образом.
РЕДАКТИРОВАТЬ 2: Спасибо @Tuttle & @pix0r за исправления.
Мой невероятно ленивый способ сделать это:
Создайте новую модель (User), расширяющую AbstractUser. В новой модели, в ее Meta, переопределите db_table и установите "auth_user".
Создайте начальную миграцию с помощью South.
Мигрируйте, но имитируйте миграцию, используя
--fake
при запуске мигрировать.Добавьте новые поля, создайте миграцию, запустите ее как обычно.
Это не лениво, но работает. Теперь у вас есть модель пользователя, соответствующая 1.5, которая просто использует старую таблицу пользователей. У вас также есть правильная история миграции.
Вы можете исправить это позже с помощью миграции вручную, чтобы переименовать таблицу.
Я устал от борьбы с Югом, так что на самом деле я сделал это по-другому, и это хорошо сработало для моей конкретной ситуации:
Сначала я заставил его работать с./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()
С помощью 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.py имя файла loaddata
4) установить AUTH_USER_MODEL
* Примечание: одна из важнейших частей этого типа миграции, независимо от того, какую технику вы используете (Юг, сериализация / модификация / десериализация или иное), заключается в том, что как только вы установите AUTH_USER_MODEL для своей пользовательской модели в текущих настройках, django отключает вас от auth.User, даже если таблица все еще существует.*
Я думаю, что вы правильно определили, что миграционные рамки, такие как Юг, - верный путь. Предполагая, что вы используете Юг, вы сможете использовать функцию переноса данных для переноса старых пользователей на новую модель.
В частности, я бы добавил forwards
способ скопировать все строки в вашей пользовательской таблице в новую таблицу. Что-то вроде:
def forwards(self, orm):
for user in orm.User.objects.all():
new_user = SiteUser(<initialize your properties here>)
new_user.save()
Вы также можете использовать bulk_create
способ ускорить процесс.
Мы решили переключиться на пользовательскую модель в нашем проекте 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)