Skip to content

Commit b9405c7

Browse files
committed
add and use query to filter inodes by mime-type
1 parent 28bee5c commit b9405c7

File tree

6 files changed

+48
-21
lines changed

6 files changed

+48
-21
lines changed

demoapp/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ class DemoAppModel(models.Model):
88
verbose_name="Demo File",
99
null=True,
1010
blank=True,
11+
accept_mime_types=['image/*'],
1112
)

finder/browser/views.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,8 +153,7 @@ def list(self, request, folder_id):
153153
offset = int(request.GET.get('offset', 0))
154154
recursive = 'recursive' in request.GET
155155
lookup = lookup_by_label(request)
156-
if mime_types := request.GET.getlist('mimetypes'):
157-
lookup['mime_types'] = mime_types
156+
lookup['mime_types'] = request.GET.getlist('mimetypes')
158157
if recursive:
159158
descendants = FolderModel.objects.get(id=folder_id).descendants
160159
if isinstance(descendants, QuerySet):

finder/forms/fields.py

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,14 @@
66

77
class FinderFileField(UUIDField):
88
widget = FinderFileSelect
9-
mime_types = None
109
realm = 'admin'
1110

12-
def __init__(self, mime_types=None, *args, **kwargs):
13-
if isinstance(mime_types, (list, tuple)):
14-
self.mime_types = mime_types
11+
def __init__(self, *args, **kwargs):
12+
self.accept_mime_types = kwargs.pop('accept_mime_types', None)
1513
super().__init__(*args, **kwargs)
1614

1715
def widget_attrs(self, widget):
18-
widget.mime_types = self.mime_types
16+
widget.accept_mime_types = self.accept_mime_types
1917
return super().widget_attrs(widget)
2018

2119

finder/forms/widgets.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212

1313
class FinderFileSelect(TextInput):
1414
template_name = 'finder/widgets/finder_file_select.html'
15-
mime_types = None
15+
accept_mime_types = None
1616
realm = 'admin'
1717

1818
class Media:
@@ -38,11 +38,11 @@ def get_context(self, name, value, attrs):
3838
context = super().get_context(name, value, attrs)
3939
context.update(
4040
base_url=reverse('finder-api:base-url'),
41-
realm='admin',
41+
realm=self.realm,
4242
style_url=static('finder/css/finder-browser.css'),
4343
)
44-
if isinstance(self.mime_types, (list, tuple)) and self.mime_types:
45-
context['mime_types'] = ','.join(self.mime_types)
44+
if isinstance(self.accept_mime_types, (list, tuple)) and self.accept_mime_types:
45+
context['mime_types'] = ','.join(self.accept_mime_types)
4646
return context
4747

4848
def format_value(self, value):

finder/models/fields.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import uuid
22

3+
from django.core.exceptions import ValidationError
34
from django.db.models.fields import UUIDField
45
from django.utils.translation import gettext_lazy as _
56

@@ -10,10 +11,15 @@
1011
class FinderFileField(UUIDField):
1112
description = _("Reference to a file in the finder app.")
1213

14+
def __init__(self, *args, **kwargs):
15+
self.accept_mime_types = kwargs.pop('accept_mime_types', None)
16+
super().__init__(*args, **kwargs)
17+
1318
def formfield(self, **kwargs):
1419
return super().formfield(
1520
**{
16-
"form_class": FormFileField,
21+
'form_class': FormFileField,
22+
'accept_mime_types': self.accept_mime_types,
1723
**kwargs,
1824
}
1925
)
@@ -25,4 +31,7 @@ def deconstruct(self):
2531
def from_db_value(self, value, expression, connection):
2632
if not isinstance(value, uuid.UUID):
2733
return value
28-
return FileModel.objects.get_inode(id=value, is_folder=False)
34+
try:
35+
return FileModel.objects.get_inode(id=value, is_folder=False, mime_types=self.accept_mime_types)
36+
except FileModel.DoesNotExist:
37+
return

finder/models/inode.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import re
22
import uuid
3+
from functools import reduce
4+
from operator import and_, or_
35

46
from django.conf import settings
57
from django.core.exceptions import ImproperlyConfigured, ValidationError
68
from django.db import connections, models
79
from django.db.models.aggregates import Aggregate
8-
from django.db.models.expressions import F, Value
10+
from django.db.models.expressions import F, Value, Q
911
from django.db.models.fields import BooleanField, CharField
1012
from django.db.models.functions import Cast, Lower
1113
from django.utils.functional import cached_property
@@ -87,6 +89,27 @@ class InodeManagerMixin:
8789
Mixin class to be added to managers for models ineriting from `Inode`.
8890
"""
8991

92+
def get_query(self, model, lookup):
93+
lookup = dict(lookup) # copy to avoid modifying the original
94+
model_field_names = [field.name for field in model._meta.get_fields()]
95+
mime_types = lookup.pop('mime_types', None)
96+
labels = lookup.pop('labels__in', None)
97+
query = reduce(and_, (Q(**{key: value}) for key, value in lookup.items()), Q())
98+
if mime_types and 'mime_type' in model_field_names:
99+
queries = []
100+
for mime_type in mime_types:
101+
main_type, sub_type = mime_type.split('/', 1)
102+
if main_type == '*':
103+
return Q()
104+
if sub_type == '*':
105+
queries.append(Q(mime_type__startswith=f'{main_type}/'))
106+
else:
107+
queries.append(Q(mime_type=mime_type))
108+
query &= reduce(or_, queries, Q())
109+
if labels and 'labels' in model_field_names:
110+
query &= Q(labels__in=labels)
111+
return query
112+
90113
def filter_unified(self, **lookup):
91114
"""
92115
Returns a unified QuerySet of all folders and files with fields from all involved models
@@ -118,12 +141,8 @@ def get_queryset(model):
118141
elif name not in model_field_names:
119142
value = None if field.default is models.NOT_PROVIDED else field.default
120143
expressions[name] = Value(value, output_field=field)
121-
queryset = model.objects.values(*concrete_fields, **expressions).annotate(**annotations)
122-
model_lookup = dict(lookup)
123-
labels = model_lookup.pop('labels__in', None)
124-
if labels and 'labels' in model_field_names:
125-
model_lookup['labels__in'] = labels
126-
return queryset.filter(**model_lookup)
144+
query = self.get_query(model, lookup)
145+
return model.objects.values(*concrete_fields, **expressions).annotate(**annotations).filter(query)
127146

128147
unified_fields = {
129148
field.name: field for field in FolderModel._meta.get_fields()
@@ -148,7 +167,8 @@ def get_inode(self, **lookup):
148167
elif (folder_qs := FolderModel.objects.filter(**lookup)).exists():
149168
return folder_qs.get()
150169
values = folder_qs.values('id', mime_type=Value(None, output_field=models.CharField())).union(*[
151-
model.objects.values('id', 'mime_type').filter(**lookup) for model in FileModel.get_models()
170+
model.objects.values('id', 'mime_type').filter(self.get_query(model, lookup))
171+
for model in FileModel.get_models()
152172
]).get()
153173
return FileModel.objects.get_model_for(values['mime_type']).objects.get(id=values['id'])
154174

0 commit comments

Comments
 (0)