diff --git a/evennia/typeclasses/managers.py b/evennia/typeclasses/managers.py index abaf389a1d..dbec3ae547 100644 --- a/evennia/typeclasses/managers.py +++ b/evennia/typeclasses/managers.py @@ -5,7 +5,7 @@ all Attributes and TypedObjects). """ import shlex -from django.db.models import Q +from django.db.models import Q, Count from evennia.utils import idmapper from evennia.utils.utils import make_iter, variable_from_module from evennia.typeclasses.attributes import Attribute @@ -236,7 +236,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager): """ return self.get_tag(key=key, category=category, obj=obj, tagtype="alias") - def get_by_tag(self, key=None, category=None, tagtype=None): + def get_by_tag(self, key=None, category=None, tagtype=None, **kwargs): """ Return objects having tags with a given key or category or combination of the two. Also accepts multiple tags/category/tagtype @@ -250,7 +250,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager): tagtype (str, optional): 'type' of Tag, by default this is either `None` (a normal Tag), `alias` or `permission`. This always apply to all queried tags. - + Returns: objects (list): Objects with matching tag. @@ -261,6 +261,10 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager): """ if not (key or category): return [] + + global _Tag + if not _Tag: + from evennia.typeclasses.models import Tag as _Tag keys = make_iter(key) if key else [] categories = make_iter(category) if category else [] @@ -271,7 +275,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager): query = ( self.filter(db_tags__db_tagtype__iexact=tagtype, db_tags__db_model__iexact=dbmodel) .distinct() - .order_by("id") + #.order_by("id") ) if n_keys > 0: @@ -286,16 +290,30 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager): "get_by_tag needs a single category or a list of categories " "the same length as the list of tags." ) + + clauses = Q() for ikey, key in enumerate(keys): - query = query.filter( - db_tags__db_key__iexact=key, db_tags__db_category__iexact=categories[ikey] - ) + # Keep each key and category together, grouped by AND + clause = Q(db_key__iexact=key, db_category__iexact=categories[ikey]) + clauses |= clause + + query = query.filter(db_tags__in=_Tag.objects.filter(clauses)).annotate(num_tags=Count('db_tags', distinct=True)).filter(num_tags__gte=len(keys)) + + print(keys, query.query) + else: # only one or more categories given - for category in categories: - query = query.filter(db_tags__db_category__iexact=category) + clauses = Q() + uniq_categories = sorted(set(categories)) + for category in uniq_categories: + clause = Q(db_category__iexact=category,) + + # Join all discrete clauses with an OR + clauses |= clause + + query = query.filter(db_tags__in=_Tag.objects.filter(clauses)).annotate(num_tags=Count('db_tags__db_category', distinct=True)).filter(num_tags__gte=len(uniq_categories)) - return query + return query.order_by('-num_tags') def get_by_permission(self, key=None, category=None): """