Skip to content

Commit f8efea7

Browse files
committed
reverse relation from Folder to Realm-Models
1 parent f843d1f commit f8efea7

File tree

10 files changed

+125
-83
lines changed

10 files changed

+125
-83
lines changed

finder/admin/folder.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -335,10 +335,12 @@ def undo_discarded_inodes(self, request):
335335
def erase_trash_folder(self, request):
336336
if request.method != 'DELETE':
337337
return HttpResponseBadRequest(f"Method {request.method} not allowed. Only DELETE requests are allowed.")
338-
trash_folder = self.get_trash_folder(request)
339-
for inode in trash_folder.listdir():
340-
DiscardedInode.objects.get(inode=inode.id).delete()
341-
inode.delete()
338+
trash_folder_entries = self.get_trash_folder(request).listdir()
339+
DiscardedInode.objects.filter(inode__in=list(trash_folder_entries.values_list('id', flat=True))).delete()
340+
for entry in trash_folder_entries:
341+
# bulk delete does not work here because file must be erased from disk
342+
dummy_obj = FolderModel.objects.get_proxy_object(entry)
343+
dummy_obj.delete()
342344
fallback_folder = self.get_fallback_folder(request)
343345
return JsonResponse({
344346
'success_url': reverse(
@@ -359,7 +361,6 @@ def add_folder(self, request, folder_id):
359361
new_folder = FolderModel.objects.create(
360362
name=body['name'],
361363
parent=parent_folder,
362-
realm=self.get_realm(request),
363364
owner=request.user,
364365
)
365366
return JsonResponse({'new_folder': self.serialize_inode(new_folder)})

finder/admin/inode.py

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
from django.urls import path, reverse
1111

1212
from finder.lookups import annotate_unified_queryset, lookup_by_label, sort_by_attribute
13-
from finder.models.folder import FolderModel, PinnedFolder, RealmModel
13+
from finder.models.folder import FolderModel, PinnedFolder
14+
from finder.models.realm import RealmModel
1415

1516

1617
class InodeAdmin(admin.ModelAdmin):
@@ -45,7 +46,12 @@ def toggle_pin(self, request, folder_id):
4546
current_folder = self.get_object(request, folder_id)
4647
if not (pinned_id := body.get('pinned_id')):
4748
return HttpResponseBadRequest("No pinned_id provided.")
48-
pinned_folder, created = PinnedFolder.objects.get_or_create(owner=request.user, folder_id=pinned_id)
49+
realm = self.get_realm(request)
50+
pinned_folder, created = PinnedFolder.objects.get_or_create(
51+
realm=realm,
52+
owner=request.user,
53+
folder_id=pinned_id,
54+
)
4955
if created:
5056
request.session['finder_last_folder_id'] = None
5157
else:
@@ -86,18 +92,19 @@ def get_realm(self, request):
8692
return realm
8793

8894
def get_root_folder(self, request):
89-
return FolderModel.objects.get_root_folder(self.get_realm(request))
95+
realm = self.get_realm(request)
96+
return FolderModel.objects.get_root_folder(realm)
9097

9198
def get_trash_folder(self, request):
92-
return FolderModel.objects.get_trash_folder(self.get_realm(request), request.user)
99+
realm = self.get_realm(request)
100+
return FolderModel.objects.get_trash_folder(realm, request.user)
93101

94102
def get_fallback_folder(self, request):
95-
realm = self.get_realm(request)
96103
try:
97104
last_folder_id = request.session['finder_last_folder_id']
98-
return FolderModel.objects.get(id=last_folder_id, realm=realm)
105+
return FolderModel.objects.get(id=last_folder_id)
99106
except (FolderModel.DoesNotExist, KeyError):
100-
return FolderModel.objects.get_root_folder(realm)
107+
return self.get_root_folder(request)
101108

102109
def get_inodes(self, request, **lookup):
103110
"""
@@ -106,7 +113,7 @@ def get_inodes(self, request, **lookup):
106113
lookup = dict(lookup_by_label(request), **lookup)
107114
unified_queryset = FolderModel.objects.filter_unified(**lookup)
108115
unified_queryset = sort_by_attribute(request, unified_queryset)
109-
annotate_unified_queryset(unified_queryset, self.admin_site.name)
116+
self.annotate_unified_queryset(unified_queryset)
110117
return unified_queryset
111118

112119
def get_breadcrumbs(self, obj):
@@ -122,10 +129,11 @@ def get_breadcrumbs(self, obj):
122129
return breadcrumbs
123130

124131
def get_favorite_folders(self, request, current_folder):
125-
realm = self.get_realm(request)
132+
site = get_current_site(request)
126133
folders = PinnedFolder.objects.filter(
134+
realm__site=site,
135+
realm__slug=self.admin_site.name,
127136
owner=request.user,
128-
folder__realm__id=realm.id,
129137
).values(
130138
'folder__id',
131139
'folder__name',
@@ -211,3 +219,13 @@ def get_menu_extension_settings(self, request):
211219
"""
212220
return {}
213221

222+
def annotate_unified_queryset(self, queryset):
223+
annotate_unified_queryset(queryset)
224+
for entry in queryset:
225+
entry.update(
226+
change_url=reverse(
227+
'admin:finder_inodemodel_change',
228+
args=(entry['id'],),
229+
current_app=self.admin_site.name,
230+
)
231+
)

finder/browser/urls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
app_name = 'finder-api'
88
urlpatterns = [
99
path(
10-
'structure/<slug:realm>',
10+
'structure/<slug:slug>',
1111
BrowserView.as_view(action='structure'),
1212
),
1313
path(

finder/browser/views.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99

1010
from finder.lookups import annotate_unified_queryset, lookup_by_label, sort_by_attribute
1111
from finder.models.file import FileModel
12-
from finder.models.folder import FolderModel, RealmModel
12+
from finder.models.folder import FolderModel
1313
from finder.models.label import Label
14+
from finder.models.realm import RealmModel
1415

1516

1617
class FormRenderer(DjangoTemplates):
@@ -53,12 +54,15 @@ def _get_children(cls, open_folders, parent):
5354
})
5455
return children
5556

56-
def structure(self, request, realm):
57+
def _get_realm(self, request, slug):
5758
site = get_current_site(request)
5859
try:
59-
realm = RealmModel.objects.get(site=site, slug=realm)
60+
return RealmModel.objects.get(site=site, slug=slug)
6061
except RealmModel.DoesNotExist:
61-
raise ObjectDoesNotExist(f"Realm {realm} not found for {site.domain}.")
62+
raise ObjectDoesNotExist(f"Realm named {slug} not found for {site.domain}.")
63+
64+
def structure(self, request, slug):
65+
realm = self._get_realm(request, slug)
6266
root_folder = FolderModel.objects.get_root_folder(realm)
6367
root_folder_id = str(root_folder.id)
6468
request.session.setdefault('finder.open_folders', [])
@@ -135,7 +139,7 @@ def list(self, request, folder_id):
135139
lookup = lookup_by_label(request)
136140
unified_queryset = FileModel.objects.filter_unified(parent_id=folder_id, is_folder=False, **lookup)
137141
unified_queryset = sort_by_attribute(request, unified_queryset)
138-
annotate_unified_queryset(unified_queryset, folder.realm.slug)
142+
annotate_unified_queryset(unified_queryset)
139143
return {'files': list(unified_queryset)}
140144

141145
def search(self, request, folder_id):
@@ -159,7 +163,7 @@ def search(self, request, folder_id):
159163
'name_lower__icontains': search_query,
160164
}
161165
unified_queryset = FileModel.objects.filter_unified(is_folder=False, **lookup)
162-
annotate_unified_queryset(unified_queryset, starting_folder.realm.slug)
166+
annotate_unified_queryset(unified_queryset)
163167
return {'files': list(unified_queryset)}
164168

165169
def upload(self, request, folder_id):

finder/contrib/archive/admin.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ def unarchive_file(self, request, file_id):
100100
return HttpResponseBadRequest(f"Method {request.method} not allowed. Only POST requests are allowed.")
101101
if not (zip_file_obj := self.get_object(request, file_id)):
102102
return HttpResponseNotFound(f"File {file_id} not found.")
103-
realm = self.get_realm(request)
104103
archive_name = pathlib.Path(zip_file_obj.name)
105104
if archive_name.suffix in ['.zip', '.tar', '.tar.gz', '.gz']:
106105
archive_name = archive_name.stem
@@ -109,15 +108,13 @@ def unarchive_file(self, request, file_id):
109108
if FolderModel.objects.filter(
110109
name=archive_name,
111110
parent=zip_file_obj.folder,
112-
realm=realm,
113111
).exists():
114112
msg = gettext("Can not extract archive. A folder named “{name}” already exists.")
115113
return HttpResponseBadRequest(msg.format(name=archive_name), status=409)
116114
try:
117115
folder_obj = FolderModel.objects.create(
118116
name=archive_name,
119117
parent=zip_file_obj.folder,
120-
realm=realm,
121118
owner=request.user,
122119
)
123120
zip_file_path = default_storage.path(zip_file_obj.file_path)
@@ -129,7 +126,6 @@ def unarchive_file(self, request, file_id):
129126
FolderModel.objects.create(
130127
name=parts[-1],
131128
parent=folder_obj.retrieve(parts[:-1]),
132-
realm=realm,
133129
owner=request.user,
134130
)
135131
continue

finder/lookups.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from finder.models.label import Label
55

66

7-
def annotate_unified_queryset(queryset, realm):
7+
def annotate_unified_queryset(queryset):
88
"""
99
Annotates the given queryset with additional fields for the frontend.
1010
This step must be applied after filtering and sorting.
@@ -13,11 +13,6 @@ def annotate_unified_queryset(queryset, realm):
1313
for entry in queryset:
1414
dummy_obj = FolderModel.objects.get_proxy_object(entry)
1515
entry.update(
16-
change_url=reverse(
17-
'admin:finder_inodemodel_change',
18-
args=(entry['id'],),
19-
current_app=realm,
20-
),
2116
download_url=dummy_obj.get_download_url(),
2217
thumbnail_url=dummy_obj.get_thumbnail_url(),
2318
sample_url=getattr(dummy_obj, 'get_sample_url', lambda: None)(),

finder/migrations/0001_initial.py

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Generated by Django 5.2 on 2024-11-04 11:02
1+
# Generated by Django 5.2 on 2024-11-10 10:34
22

33
import django.db.models.deletion
44
import finder.models.file
@@ -186,37 +186,35 @@ class Migration(migrations.Migration):
186186
name='labels',
187187
field=models.ManyToManyField(blank=True, related_name='+', to='finder.label', verbose_name='Labels'),
188188
),
189-
migrations.CreateModel(
190-
name='PinnedFolder',
191-
fields=[
192-
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
193-
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created at')),
194-
('folder', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='pinned_folders', to='finder.foldermodel')),
195-
('owner', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
196-
],
197-
),
198189
migrations.CreateModel(
199190
name='RealmModel',
200191
fields=[
201192
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
202193
('slug', models.SlugField(editable=False, max_length=200, null=True, verbose_name='Slug')),
194+
('root_folder', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='realm', to='finder.foldermodel')),
203195
('site', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='sites.site', verbose_name='Site')),
196+
('trash_folders', models.ManyToManyField(editable=False, related_name='+', to='finder.foldermodel')),
204197
],
205198
options={
206199
'ordering': ['site', 'slug'],
207200
},
208201
),
209-
migrations.AddField(
202+
migrations.CreateModel(
203+
name='PinnedFolder',
204+
fields=[
205+
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
206+
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Created at')),
207+
('folder', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='pinned_folders', to='finder.foldermodel')),
208+
('owner', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='+', to=settings.AUTH_USER_MODEL)),
209+
('realm', models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='finder.realmmodel')),
210+
],
211+
),
212+
migrations.AddConstraint(
210213
model_name='foldermodel',
211-
name='realm',
212-
field=models.ForeignKey(editable=False, on_delete=django.db.models.deletion.CASCADE, to='finder.realmmodel', verbose_name='Realm'),
214+
constraint=models.UniqueConstraint(fields=('parent', 'name'), name='unique_realm'),
213215
),
214216
migrations.AddConstraint(
215217
model_name='realmmodel',
216218
constraint=models.UniqueConstraint(fields=('site', 'slug'), name='unique_site'),
217219
),
218-
migrations.AddConstraint(
219-
model_name='foldermodel',
220-
constraint=models.UniqueConstraint(fields=('parent', 'name'), name='unique_realm'),
221-
),
222220
]

finder/models/folder.py

Lines changed: 25 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
from functools import lru_cache
2+
13
from django.conf import settings
2-
from django.contrib.sites.models import Site
34
from django.contrib.staticfiles.storage import staticfiles_storage
45
from django.core.exceptions import ValidationError
56
from django.db import models
@@ -12,51 +13,30 @@
1213
ModelManager = models.Manager
1314

1415
from .inode import InodeManagerMixin, InodeModel
15-
16-
17-
class RealmModel(models.Model):
18-
site = models.ForeignKey(
19-
Site,
20-
on_delete=models.CASCADE,
21-
verbose_name=_("Site"),
22-
)
23-
24-
slug = models.SlugField(
25-
_("Slug"),
26-
max_length=200,
27-
null=True,
28-
editable=False,
29-
)
30-
31-
class Meta:
32-
ordering = ['site', 'slug']
33-
constraints = [models.UniqueConstraint(fields=['site', 'slug'], name='unique_site')]
34-
35-
def __str__(self):
36-
return f"{self.slug} @ {self.site.name}"
16+
from.realm import RealmModel
3717

3818

3919
class FolderModelManager(InodeManagerMixin, ModelManager):
4020
def get_root_folder(self, realm):
41-
root_folder, _ = self.get_or_create(parent=None, realm=realm, name='__root__')
42-
return root_folder
21+
try:
22+
return realm.root_folder
23+
except FolderModel.DoesNotExist:
24+
realm.root_folder = self.create(parent=None, name='__root__')
25+
return realm.root_folder
4326

4427
def get_trash_folder(self, realm, owner):
45-
trash_folder, _ = self.get_or_create(parent=None, realm=realm, owner=owner, name='__trash__')
28+
try:
29+
trash_folder = realm.trash_folders.get(owner=owner)
30+
except FolderModel.DoesNotExist:
31+
trash_folder = self.create(parent=None, owner=owner, name='__trash__')
32+
realm.trash_folders.add(trash_folder)
4633
return trash_folder
4734

4835

4936
class FolderModel(InodeModel):
5037
is_folder = True
5138
folderitem_component = None
5239

53-
realm = models.ForeignKey(
54-
RealmModel,
55-
verbose_name=_("Realm"),
56-
editable=False,
57-
on_delete=models.CASCADE,
58-
)
59-
6040
class Meta:
6141
verbose_name = _("Folder")
6242
verbose_name_plural = _("Folders")
@@ -77,6 +57,12 @@ def cast(self):
7757
raise NotImplementedError
7858
return self
7959

60+
@lru_cache
61+
def get_realm(self):
62+
if isinstance(self.ancestors, models.QuerySet):
63+
return self.ancestors.last().realm
64+
return list(self.ancestors)[-1].realm
65+
8066
@property
8167
def folder(self):
8268
return self
@@ -239,6 +225,12 @@ def retrieve(self, path):
239225

240226

241227
class PinnedFolder(models.Model):
228+
realm = models.ForeignKey(
229+
RealmModel,
230+
related_name='+',
231+
on_delete=models.CASCADE,
232+
editable=False,
233+
)
242234
owner = models.ForeignKey(
243235
settings.AUTH_USER_MODEL,
244236
related_name='+',

finder/models/inode.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ def get_queryset(model):
115115

116116
unified_fields = {
117117
field.name: field for field in FolderModel._meta.get_fields()
118-
if field.concrete and not field.many_to_many and field.name is not 'realm'
118+
if field.concrete and not field.many_to_many and field.name != 'realm'
119119
}
120120
for model in FileModel.get_models():
121121
for field in model._meta.get_fields():
@@ -250,6 +250,9 @@ def serializable_value(self, field_name):
250250

251251

252252
class DiscardedInode(models.Model):
253+
"""
254+
Store information about inodes that have been moved to the trash folder so that they can be restored again.
255+
"""
253256
inode = models.UUIDField(
254257
primary_key=True,
255258
)

0 commit comments

Comments
 (0)