# Generated by Django 5.2.12 on 2026-09-11 13:06 import django.db.models.deletion from django.conf import settings from django.db import migrations, models def backfill_legacy_memberships(apps, schema_editor): """Keep existing installs usable while making tenant access explicit. Existing superusers retain operator access to every active tenant. Other users get the configured/default tenant, and the historical demo account also gets the demo tenant needed by the public demo token flow. """ Tenant = apps.get_model('core', 'Tenant') Membership = apps.get_model('core', 'TenantMembership') User = apps.get_model(settings.AUTH_USER_MODEL.split('.')[0], settings.AUTH_USER_MODEL.split('.')[1]) default = Tenant.objects.filter(code='default', is_active=True).first() demo = Tenant.objects.filter(code='demo', is_active=True).first() for user in User.objects.all().iterator(): tenants = list(Tenant.objects.filter(is_active=True)) if user.is_superuser else ([default] if default else []) if user.username == 'demo' and demo is not None: tenants.append(demo) for tenant in {t.pk: t for t in tenants if t is not None}.values(): Membership.objects.get_or_create( user_id=user.pk, tenant_id=tenant.pk, defaults={'role': 'owner' if user.is_superuser else 'member', 'is_active': True}, ) class Migration(migrations.Migration): dependencies = [ ('core', '0001_initial'), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] operations = [ migrations.CreateModel( name='TenantMembership', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('role', models.CharField(choices=[('owner', '所有者'), ('admin', '管理员'), ('member', '成员')], default='member', max_length=16)), ('is_active', models.BooleanField(default=True)), ('created_at', models.DateTimeField(auto_now_add=True)), ('updated_at', models.DateTimeField(auto_now=True)), ('tenant', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='memberships', to='core.tenant')), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tenant_memberships', to=settings.AUTH_USER_MODEL)), ], options={ 'db_table': 'core_tenant_membership', 'indexes': [models.Index(fields=['user', 'is_active'], name='core_tenant_user_id_51dd63_idx'), models.Index(fields=['tenant', 'is_active'], name='core_tenant_tenant__f85f17_idx')], 'constraints': [models.UniqueConstraint(fields=('user', 'tenant'), name='core_membership_user_tenant_uniq')], }, ), migrations.RunPython(backfill_legacy_memberships, migrations.RunPython.noop), ] def backfill_legacy_memberships(apps, schema_editor): """Keep existing installs usable while making tenant access explicit. Existing superusers retain operator access to every active tenant. Other users get the configured/default tenant, and the historical demo account also gets the demo tenant needed by the public demo token flow. """ Tenant = apps.get_model('core', 'Tenant') Membership = apps.get_model('core', 'TenantMembership') User = apps.get_model(settings.AUTH_USER_MODEL.split('.')[0], settings.AUTH_USER_MODEL.split('.')[1]) default = Tenant.objects.filter(code='default', is_active=True).first() demo = Tenant.objects.filter(code='demo', is_active=True).first() for user in User.objects.all().iterator(): tenants = list(Tenant.objects.filter(is_active=True)) if user.is_superuser else ([default] if default else []) if user.username == 'demo' and demo is not None: tenants.append(demo) for tenant in {t.pk: t for t in tenants if t is not None}.values(): Membership.objects.get_or_create( user_id=user.pk, tenant_id=tenant.pk, defaults={'role': 'owner' if user.is_superuser else 'member', 'is_active': True}, )