Homogenize manager search methods to return querysets. Resolve #2384.

This commit is contained in:
Griatch 2022-01-09 17:13:24 +01:00
parent 01af303457
commit 9c0b44e13a
7 changed files with 65 additions and 39 deletions

View file

@ -32,7 +32,7 @@ class ObjectDBManager(TypedObjectManager):
Querysets or database objects).
dbref (converter)
get_id (alias: dbref_search)
dbref_search
get_dbref_range
object_totals
typeclass_search
@ -162,8 +162,8 @@ class ObjectDBManager(TypedObjectManager):
typeclasses (list, optional): Python pats to restrict matches with.
Returns:
matches (query): Objects fullfilling both the `attribute_name` and
`attribute_value` criterions.
Queryset: Iterable with 0, 1 or more matches fullfilling both the `attribute_name` and
`attribute_value` criterions.
Notes:
This uses the Attribute's PickledField to transparently search the database by matching
@ -222,6 +222,9 @@ class ObjectDBManager(TypedObjectManager):
candidates (list, optional): List of objects to limit search to.
typeclasses (list, optional): List of typeclass-path strings to restrict matches with
Returns:
Queryset: Iterable with 0, 1 or more matches.
"""
if isinstance(property_name, str):
if not property_name.startswith("db_"):
@ -234,11 +237,10 @@ class ObjectDBManager(TypedObjectManager):
)
type_restriction = typeclasses and Q(db_typeclass_path__in=make_iter(typeclasses)) or Q()
try:
return list(
self.filter(cand_restriction & type_restriction & Q(**querykwargs)).order_by("id")
)
return self.filter(
cand_restriction & type_restriction & Q(**querykwargs)).order_by("id")
except exceptions.FieldError:
return []
return self.none()
except ValueError:
from evennia.utils import logger
@ -246,7 +248,7 @@ class ObjectDBManager(TypedObjectManager):
"The property '%s' does not support search criteria of the type %s."
% (property_name, type(property_value))
)
return []
return self.none()
def get_contents(self, location, excludeobj=None):
"""
@ -258,7 +260,8 @@ class ObjectDBManager(TypedObjectManager):
to exclude from the match.
Returns:
contents (query): Matching contents, without excludeobj, if given.
Queryset: Iterable with 0, 1 or more matches.
"""
exclude_restriction = (
Q(pk__in=[_GA(obj, "id") for obj in make_iter(excludeobj)]) if excludeobj else Q()
@ -276,17 +279,18 @@ class ObjectDBManager(TypedObjectManager):
typeclasses (list): Only match objects with typeclasses having thess path strings.
Returns:
matches (query): A list of matches of length 0, 1 or more.
Queryset: An iterable with 0, 1 or more matches.
"""
if not isinstance(ostring, str):
if hasattr(ostring, "key"):
ostring = ostring.key
else:
return []
return self.none()
if is_iter(candidates) and not len(candidates):
# if candidates is an empty iterable there can be no matches
# Exit early.
return []
return self.none()
# build query objects
candidates_id = [_GA(obj, "id") for obj in make_iter(candidates) if obj]
@ -327,10 +331,12 @@ class ObjectDBManager(TypedObjectManager):
# fuzzy matching
key_strings = search_candidates.values_list("db_key", flat=True).order_by("id")
match_ids = []
index_matches = string_partial_matching(key_strings, ostring, ret_index=True)
if index_matches:
# a match by key
return [obj for ind, obj in enumerate(search_candidates) if ind in index_matches]
match_ids = [obj.id for ind, obj in enumerate(search_candidates)
if ind in index_matches]
else:
# match by alias rather than by key
search_candidates = search_candidates.filter(
@ -346,8 +352,10 @@ class ObjectDBManager(TypedObjectManager):
index_matches = string_partial_matching(alias_strings, ostring, ret_index=True)
if index_matches:
# it's possible to have multiple matches to the same Object, we must weed those out
return list({alias_candidates[ind] for ind in index_matches})
return []
match_ids = [alias_candidates[ind].id for ind in index_matches]
# TODO - not ideal to have to do a second lookup here, but we want to return a queryset
# rather than a list ... maybe the above queries can be improved.
return self.filter(id__in=match_ids)
# main search methods and helper functions
@ -422,7 +430,7 @@ class ObjectDBManager(TypedObjectManager):
)
if not searchdata and searchdata != 0:
return []
return self.none()
if typeclass:
# typeclass may also be a list
@ -450,10 +458,11 @@ class ObjectDBManager(TypedObjectManager):
# Easiest case - dbref matching (always exact)
dbref_match = self.dbref_search(dbref)
if dbref_match:
if not candidates or dbref_match in candidates:
return [dbref_match]
dmatch = dbref_match[0]
if not candidates or dmatch in candidates:
return dbref_match
else:
return []
return self.none()
# Search through all possibilities.
match_number = None
@ -478,15 +487,16 @@ class ObjectDBManager(TypedObjectManager):
# this indicates trying to get a single match with a match-number
# targeting some higher-number match (like 2-box when there is only
# one box in the room). This leads to a no-match.
matches = []
matches = self.none()
elif len(matches) > 1 and match_number is not None:
# multiple matches, but a number was given to separate them
if 0 <= match_number < len(matches):
# limit to one match
matches = [matches[match_number]]
# limit to one match (we still want a queryset back)
# TODO: Can we do this some other way and avoid a second lookup?
matches = self.filter(id=matches[match_number].id)
else:
# a number was given outside of range. This means a no-match.
matches = []
matches = self.none()
# return a list (possibly empty)
return matches