Introduce workspace tenancy boundaries
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
# Generated by Pi coding agent
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
def forwards(apps, schema_editor):
|
||||
User = apps.get_model(*settings.AUTH_USER_MODEL.split('.'))
|
||||
Workspace = apps.get_model('core', 'Workspace')
|
||||
WorkspaceMembership = apps.get_model('core', 'WorkspaceMembership')
|
||||
Tag = apps.get_model('core', 'Tag')
|
||||
Team = apps.get_model('core', 'Team')
|
||||
Item = apps.get_model('core', 'Item')
|
||||
Collection = apps.get_model('core', 'Collection')
|
||||
Kanban = apps.get_model('core', 'Kanban')
|
||||
ApiKey = apps.get_model('core', 'ApiKey')
|
||||
|
||||
personal = {}
|
||||
for user in User.objects.all():
|
||||
full_name = f'{getattr(user, "first_name", "")} {getattr(user, "last_name", "")}'.strip()
|
||||
name = full_name or getattr(user, 'username', '') or f'User {user.pk}'
|
||||
workspace, _ = Workspace.objects.get_or_create(slug=f'user-{user.pk}', defaults={'name': name, 'owner_id': user.pk})
|
||||
WorkspaceMembership.objects.get_or_create(workspace=workspace, user=user, defaults={'role': 'owner'})
|
||||
personal[user.pk] = workspace
|
||||
|
||||
def workspace_for_user(user_id):
|
||||
if user_id not in personal:
|
||||
user = User.objects.get(pk=user_id)
|
||||
workspace, _ = Workspace.objects.get_or_create(slug=f'user-{user.pk}', defaults={'name': getattr(user, 'username', '') or f'User {user.pk}', 'owner_id': user.pk})
|
||||
WorkspaceMembership.objects.get_or_create(workspace=workspace, user=user, defaults={'role': 'owner'})
|
||||
personal[user_id] = workspace
|
||||
return personal[user_id]
|
||||
|
||||
for team in Team.objects.select_related('owner'):
|
||||
team.workspace = workspace_for_user(team.owner_id)
|
||||
team.save(update_fields=['workspace'])
|
||||
|
||||
for item in Item.objects.select_related('owner', 'team'):
|
||||
item.workspace = item.team.workspace if item.team_id and item.team.workspace_id else workspace_for_user(item.owner_id)
|
||||
item.save(update_fields=['workspace'])
|
||||
|
||||
for collection in Collection.objects.select_related('owner', 'team'):
|
||||
collection.workspace = collection.team.workspace if collection.team_id and collection.team.workspace_id else workspace_for_user(collection.owner_id)
|
||||
collection.save(update_fields=['workspace'])
|
||||
|
||||
for kanban in Kanban.objects.select_related('owner'):
|
||||
kanban.workspace = workspace_for_user(kanban.owner_id)
|
||||
kanban.save(update_fields=['workspace'])
|
||||
|
||||
for api_key in ApiKey.objects.select_related('owner'):
|
||||
api_key.workspace = workspace_for_user(api_key.owner_id)
|
||||
api_key.save(update_fields=['workspace'])
|
||||
|
||||
fallback_workspace = next(iter(personal.values()), None)
|
||||
for tag in list(Tag.objects.all()):
|
||||
workspace_ids = set(tag.items.exclude(workspace__isnull=True).values_list('workspace_id', flat=True))
|
||||
workspace_ids.update(tag.tag_collections.exclude(workspace__isnull=True).values_list('workspace_id', flat=True))
|
||||
workspace_ids.update(tag.kanbans.exclude(workspace__isnull=True).values_list('workspace_id', flat=True))
|
||||
workspace_ids.update(tag.api_keys.exclude(workspace__isnull=True).values_list('workspace_id', flat=True))
|
||||
workspace_ids.update(Team.objects.filter(slug=tag.name).exclude(workspace__isnull=True).values_list('workspace_id', flat=True))
|
||||
if not workspace_ids and fallback_workspace:
|
||||
workspace_ids.add(fallback_workspace.pk)
|
||||
if not workspace_ids:
|
||||
tag.delete()
|
||||
continue
|
||||
|
||||
first = True
|
||||
for workspace_id in sorted(workspace_ids):
|
||||
if first:
|
||||
scoped_tag = tag
|
||||
scoped_tag.workspace_id = workspace_id
|
||||
scoped_tag.save(update_fields=['workspace'])
|
||||
first = False
|
||||
else:
|
||||
scoped_tag, _ = Tag.objects.get_or_create(workspace_id=workspace_id, name=tag.name)
|
||||
|
||||
for item in list(tag.items.filter(workspace_id=workspace_id)):
|
||||
item.tags.remove(tag)
|
||||
item.tags.add(scoped_tag)
|
||||
for collection in list(tag.tag_collections.filter(workspace_id=workspace_id)):
|
||||
collection.filter_tags.remove(tag)
|
||||
collection.filter_tags.add(scoped_tag)
|
||||
for api_key in list(tag.api_keys.filter(workspace_id=workspace_id)):
|
||||
api_key.tags.remove(tag)
|
||||
api_key.tags.add(scoped_tag)
|
||||
Kanban.objects.filter(workspace_id=workspace_id, tag=tag).update(tag=scoped_tag)
|
||||
|
||||
for team in Team.objects.exclude(workspace__isnull=True):
|
||||
Tag.objects.get_or_create(workspace=team.workspace, name=team.slug)
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('core', '0021_collection_type_filters'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Workspace',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=120)),
|
||||
('slug', models.SlugField(allow_unicode=True, max_length=80, unique=True)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('owner', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='owned_workspaces', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={'ordering': ['name']},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='WorkspaceMembership',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('role', models.CharField(choices=[('owner', 'Owner'), ('admin', 'Admin'), ('member', 'Member')], default='member', max_length=10)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='workspace_memberships', to=settings.AUTH_USER_MODEL)),
|
||||
('workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='memberships', to='core.workspace')),
|
||||
],
|
||||
options={'unique_together': {('workspace', 'user')}},
|
||||
),
|
||||
migrations.AddField('tag', 'workspace', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='tags', to='core.workspace')),
|
||||
migrations.AlterField('tag', 'name', models.SlugField(allow_unicode=True, max_length=80)),
|
||||
migrations.AddField('team', 'workspace', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='teams', to='core.workspace')),
|
||||
migrations.AlterField('team', 'slug', models.SlugField(allow_unicode=True, help_text='Team-Tag ohne #, z.B. my2dos', max_length=80)),
|
||||
migrations.AddField('item', 'workspace', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='items', to='core.workspace')),
|
||||
migrations.AddField('collection', 'workspace', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='collections', to='core.workspace')),
|
||||
migrations.AddField('kanban', 'workspace', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='kanbans', to='core.workspace')),
|
||||
migrations.AddField('apikey', 'workspace', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='api_keys', to='core.workspace')),
|
||||
migrations.RunPython(forwards, migrations.RunPython.noop),
|
||||
migrations.AlterField('tag', 'workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tags', to='core.workspace')),
|
||||
migrations.AlterField('team', 'workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='teams', to='core.workspace')),
|
||||
migrations.AlterField('item', 'workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='items', to='core.workspace')),
|
||||
migrations.AlterField('collection', 'workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='collections', to='core.workspace')),
|
||||
migrations.AlterField('kanban', 'workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='kanbans', to='core.workspace')),
|
||||
migrations.AlterField('apikey', 'workspace', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='api_keys', to='core.workspace')),
|
||||
migrations.AddConstraint('tag', models.UniqueConstraint(fields=('workspace', 'name'), name='unique_workspace_tag_name')),
|
||||
migrations.AddConstraint('team', models.UniqueConstraint(fields=('workspace', 'slug'), name='unique_workspace_team_slug')),
|
||||
]
|
||||
Reference in New Issue
Block a user