11import re
22import uuid
3+ from functools import reduce
4+ from operator import and_ , or_
35
46from django .conf import settings
57from django .core .exceptions import ImproperlyConfigured , ValidationError
68from django .db import connections , models
79from 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
911from django .db .models .fields import BooleanField , CharField
1012from django .db .models .functions import Cast , Lower
1113from 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