1616
1717# pylint: disable=line-too-long
1818
19+ import inspect
1920import os
2021import pkgutil
2122import sys
3839 FloatField ,
3940 GenericIPAddressField ,
4041 IntegerField ,
42+ Manager ,
4143 Max ,
4244 Model ,
4345 Q ,
4446 TextField ,
4547 UUIDField ,
4648)
49+ from django .db .models .query import QuerySet
50+ from django .utils .translation import gettext as _
4751
4852Completer = t .Callable [[Context , Parameter , str ], t .List [CompletionItem ]]
4953Strings = t .Union [t .Sequence [str ], t .KeysView [str ], t .Generator [str , None , None ]]
@@ -107,7 +111,7 @@ def handle(
107111 function that returns a configured parser and completer for a model object
108112 and helps reduce boilerplate.
109113
110- :param model_cls : The Django model class to query .
114+ :param model_or_qry : The Django model class or a queryset to filter against .
111115 :param lookup_field: The name of the model field to use for lookup.
112116 :param help_field: The name of the model field to use for help text or None if
113117 no help text should be provided.
@@ -130,6 +134,7 @@ def handle(
130134 QueryBuilder = t .Callable [["ModelObjectCompleter" , Context , Parameter , str ], Q ]
131135
132136 model_cls : t .Type [Model ]
137+ _queryset : t .Optional [QuerySet ] = None
133138 lookup_field : str
134139 help_field : t .Optional [str ] = None
135140 query : t .Callable [[Context , Parameter , str ], Q ]
@@ -144,6 +149,10 @@ def handle(
144149
145150 _field : Field
146151
152+ @property
153+ def queryset (self ) -> t .Union [QuerySet , Manager [Model ]]:
154+ return self ._queryset or self .model_cls .objects
155+
147156 def to_str (self , obj : t .Any ) -> str :
148157 return str (obj )
149158
@@ -253,7 +262,11 @@ def uuid_query(self, context: Context, parameter: Parameter, incomplete: str) ->
253262 self ._offset += 1
254263
255264 if len (uuid ) > 32 :
256- raise ValueError (f"Too many UUID characters: { incomplete } " )
265+ raise ValueError (
266+ _ ("Too many UUID characters: {incomplete}" ).format (
267+ incomplete = incomplete
268+ )
269+ )
257270 min_uuid = UUID (uuid + "0" * (32 - len (uuid )))
258271 max_uuid = UUID (uuid + "f" * (32 - len (uuid )))
259272 return Q (** {f"{ self .lookup_field } __gte" : min_uuid }) & Q (
@@ -262,15 +275,23 @@ def uuid_query(self, context: Context, parameter: Parameter, incomplete: str) ->
262275
263276 def __init__ (
264277 self ,
265- model_cls : t .Type [Model ],
278+ model_or_qry : t .Union [ t . Type [Model ], QuerySet ],
266279 lookup_field : t .Optional [str ] = None ,
267280 help_field : t .Optional [str ] = help_field ,
268281 query : t .Optional [QueryBuilder ] = None ,
269282 limit : t .Optional [int ] = limit ,
270283 case_insensitive : bool = case_insensitive ,
271284 distinct : bool = distinct ,
272285 ):
273- self .model_cls = model_cls
286+ if inspect .isclass (model_or_qry ) and issubclass (model_or_qry , Model ):
287+ self .model_cls = model_or_qry
288+ elif isinstance (model_or_qry , QuerySet ): # type: ignore
289+ self .model_cls = model_or_qry .model
290+ self ._queryset = model_or_qry
291+ else :
292+ raise ValueError (
293+ _ ("ModelObjectCompleter requires a Django model class or queryset." )
294+ )
274295 self .lookup_field = str (
275296 lookup_field or getattr (self .model_cls ._meta .pk , "name" , "id" )
276297 )
@@ -295,7 +316,9 @@ def __init__(
295316 self .query = self .float_query
296317 else :
297318 raise ValueError (
298- f"Unsupported lookup field class: { self ._field .__class__ .__name__ } "
319+ _ ("Unsupported lookup field class: {cls}" ).format (
320+ cls = self ._field .__class__ .__name__
321+ )
299322 )
300323
301324 def __call__ (
@@ -343,9 +366,7 @@ def __call__(
343366 ],
344367 help = getattr (obj , self .help_field , None ) if self .help_field else "" ,
345368 )
346- for obj in getattr (self .model_cls , "objects" )
347- .filter (completion_qry )
348- .distinct ()[0 : self .limit ]
369+ for obj in self .queryset .filter (completion_qry ).distinct ()[0 : self .limit ]
349370 if (
350371 getattr (obj , self .lookup_field ) is not None
351372 and self .to_str (getattr (obj , self .lookup_field ))
0 commit comments