54 lines
2.5 KiB
Python
54 lines
2.5 KiB
Python
"""Normalize fresh installations to the physical storage root.
|
|
|
|
Older databases with existing /root content MUST be converted explicitly using
|
|
`manage.py flatten_drive_root --apply`, after a backup. This migration does not
|
|
move, delete or rewrite any user file.
|
|
"""
|
|
from pathlib import Path
|
|
|
|
from django.conf import settings
|
|
from django.db import migrations, transaction
|
|
from django.db.models import Q
|
|
|
|
|
|
def normalize_unused_root(apps, schema_editor):
|
|
Space = apps.get_model('drive', 'DriveSpace')
|
|
Item = apps.get_model('drive', 'DriveItem')
|
|
Upload = apps.get_model('drive', 'UploadSession')
|
|
root = Space.objects.filter(parent__isnull=True, private_owner__isnull=True).order_by('pk').first()
|
|
if root is None or root.relative_path != 'root':
|
|
return
|
|
|
|
# The previous migrations create a logical "root" even for an empty DB.
|
|
# Existing installations must not be migrated behind the user's back:
|
|
# physical bytes must be moved consistently with DB paths.
|
|
old_dir = Path(settings.DRIVE_STORAGE_ROOT).resolve() / 'root'
|
|
if (old_dir.exists() and (not old_dir.is_dir() or any(old_dir.iterdir()))):
|
|
print('GoodDrive: root/ has data; run flatten_drive_root --apply after backup.')
|
|
return
|
|
if (Item.objects.filter(storage_location__startswith='root/').exists()
|
|
or Upload.objects.exists()):
|
|
print('GoodDrive: existing files/uploads; run flatten_drive_root --apply after backup.')
|
|
return
|
|
|
|
old = list(Space.objects.filter(Q(pk=root.pk) | Q(relative_path__startswith='root/')))
|
|
new = {s.pk: ('' if s.pk == root.pk else s.relative_path[5:]) for s in old}
|
|
if any(not path or path.startswith('/') for pk, path in new.items() if pk != root.pk):
|
|
print('GoodDrive: invalid old space paths; use flatten_drive_root after inspection.')
|
|
return
|
|
other_paths = set(Space.objects.exclude(pk__in=new).values_list('relative_path', flat=True))
|
|
if any(path in other_paths for path in new.values()):
|
|
print('GoodDrive: space paths conflict; use flatten_drive_root after inspection.')
|
|
return
|
|
|
|
with transaction.atomic():
|
|
for space in old:
|
|
space.relative_path = new[space.pk]
|
|
Space.objects.bulk_update(old, ['relative_path'])
|
|
print('GoodDrive: new database root uses DRIVE_STORAGE_ROOT directly (no /root/).')
|
|
|
|
|
|
class Migration(migrations.Migration):
|
|
dependencies = [('drive', '0009_managed_spaces')]
|
|
operations = [migrations.RunPython(normalize_unused_root, migrations.RunPython.noop)]
|