4.7 KiB
4.7 KiB
Resetting migrations and introducing new User model
This how-to is based on a comment to ticket #25313. The reason migrations had been reset in this project are the following:
- Replacing
auth.User
model; which is not possible mid-project without migrations being reset; - Simplifying test runs;
- Having a possibility of running tests with in-memory SQLite instead of PostgreSQL.
Step zero: backup your production database
Make sure you make backups of your production database.
Step one: an identical User model and initial migrations
users app
Because you probably have a multitude of FKs to the auth.User
model, it's easier to create a completely
separate app that will contain it. This ensures
Re-creating migrations
find . -path "*/migrations/*.py" -not -name "__init__.py" -delete
find . -path "*/migrations/*.pyc" -delete
./manage.py makemigrations
Some data migrations have to be added manually
blender_id_oauth_client
constraint:
# FIXME(anna): because this table is en external dependency,
# there's no other way to enforce this contraint
# Addin OAuthUserInfo._meta.get_field('oauth_user_id')._unique = True
# would generate a migration inside `blender_id_oauth_client`
migrations.RunSQL(
# Generated with sqlmigrate from an automatic migration that was removed right after
# e.g. if blender_id_oauth_client migration number is NNNN
sql=[
# ./manage.py sqlmigrate blender_id_oauth_client NNNN
'ALTER TABLE "blender_id_oauth_client_oauthuserinfo" '
'ADD CONSTRAINT "blender_id_oauth_client__oauth_user_id_b1e52371_uniq" '
'UNIQUE ("oauth_user_id");',
'CREATE INDEX "blender_id_oauth_client__oauth_user_id_b1e52371_like" '
'ON "blender_id_oauth_client_oauthuserinfo" ("oauth_user_id" varchar_pattern_ops);',
],
reverse_sql=[
# ./manage.py sqlmigrate blender_id_oauth_client NNNN --backwards
'DROP INDEX IF EXISTS "blender_id_oauth_client__oauth_user_id_b1e52371_like";',
'ALTER TABLE "blender_id_oauth_client_oauthuserinfo" '
'DROP CONSTRAINT IF EXISTS "blender_id_oauth_client__oauth_user_id_b1e52371_uniq";',
],
)
can_view_content
to thedemo
andsubscriber
groups:
def add_can_view_content_permission_to_groups(apps, schema_editor):
Group = apps.get_model('auth', 'Group')
Permission = apps.get_model('auth', 'Permission')
ContentType = apps.get_model('contenttypes', 'ContentType')
Profile = apps.get_model('profiles', 'Profile')
content_type = ContentType.objects.get_for_model(Profile)
# permissions are created in a `post_migrate` signal and won't be available until the **next** ./manage.py migrate,
# causing __fake__.DoesNotExist: Permission matching query does not exist
permission, _ = Permission.objects.get_or_create(
codename='can_view_content',
content_type=content_type,
name='Can view subscription-only content',
)
# Add this permission to the groups that are currently interpreted as having active subscription status
for group_name in ('demo', 'subscriber'):
# Create these groups, so that tests and newly setup dev environments worked as expected
group, _ = Group.objects.get_or_create(name=group_name)
group.permissions.add(permission)
...
migrations.RunPython(add_can_view_content_permission_to_groups),
...
At this point testsuite should complete without issues and a commit can be made.
Deployment steps
echo 'TRUNCATE TABLE django_migrations;' | psql --dbname=postgresql://studio:PASSWORD@localhost:5432/studio
git pull
./manage.py migrate --fake
rewrite content types
Now rewrite LogEntry to keep the admin history intact:
./manage.py shell
>>>
>>> from django.contrib.admin.models import LogEntry
>>> from django.contrib.contenttypes.models import ContentType
>>>
>>> auth_user = ContentType.objects.get(app_label='auth', model='user')
>>> new_user = ContentType.objects.get(app_label='users', model='user')
>>>
>>> for le in LogEntry.objects.filter(content_type=auth_user):
... le.content_type = new_user
... le.save()
...
>>> from actstream.models import Action # same for Action
>>> for le in Action.objects.filter(actor_content_type=auth_user):
>>> le.actor_content_type = new_user
>>> le.save()
Run this and make sure to answer NO to find more models that might need refer to ContentType
that need updating:
./manage.py remove_stale_contenttypes