mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
[fix] Correct search_typeclass bugs. Resolve #2694.
This commit is contained in:
parent
c7a2b8b37b
commit
e5b698fab8
2 changed files with 95 additions and 31 deletions
|
|
@ -5,12 +5,13 @@ all Attributes and TypedObjects).
|
|||
|
||||
"""
|
||||
import shlex
|
||||
from django.db.models import F, Q, Count, ExpressionWrapper, FloatField
|
||||
|
||||
from django.db.models import Count, ExpressionWrapper, F, FloatField, Q
|
||||
from django.db.models.functions import Cast
|
||||
from evennia.utils import idmapper
|
||||
from evennia.utils.utils import make_iter, variable_from_module
|
||||
from evennia.typeclasses.attributes import Attribute
|
||||
from evennia.typeclasses.tags import Tag
|
||||
from evennia.utils import idmapper
|
||||
from evennia.utils.utils import class_from_module, make_iter, variable_from_module
|
||||
|
||||
__all__ = ("TypedObjectManager",)
|
||||
_GA = object.__getattribute__
|
||||
|
|
@ -537,9 +538,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
|
|||
|
||||
def typeclass_search(self, typeclass, include_children=False, include_parents=False):
|
||||
"""
|
||||
Searches through all objects returning those which has a
|
||||
certain typeclass. If location is set, limit search to objects
|
||||
in that location.
|
||||
Searches through all objects returning those which has a certain typeclass.
|
||||
|
||||
Args:
|
||||
typeclass (str or class): A typeclass class or a python path to a typeclass.
|
||||
|
|
@ -554,34 +553,23 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager):
|
|||
objects (list): The objects found with the given typeclasses.
|
||||
|
||||
"""
|
||||
|
||||
if callable(typeclass):
|
||||
cls = typeclass.__class__
|
||||
typeclass = "%s.%s" % (cls.__module__, cls.__name__)
|
||||
elif not isinstance(typeclass, str) and hasattr(typeclass, "path"):
|
||||
typeclass = typeclass.path
|
||||
|
||||
# query objects of exact typeclass
|
||||
query = Q(db_typeclass_path__exact=typeclass)
|
||||
if not callable(typeclass):
|
||||
typeclass = class_from_module(typeclass)
|
||||
|
||||
if include_children:
|
||||
# build requests for child typeclass objects
|
||||
clsmodule, clsname = typeclass.rsplit(".", 1)
|
||||
cls = variable_from_module(clsmodule, clsname)
|
||||
subclasses = cls.__subclasses__()
|
||||
if subclasses:
|
||||
for child in (child for child in subclasses if hasattr(child, "path")):
|
||||
query = query | Q(db_typeclass_path__exact=child.path)
|
||||
elif include_parents:
|
||||
# build requests for parent typeclass objects
|
||||
clsmodule, clsname = typeclass.rsplit(".", 1)
|
||||
cls = variable_from_module(clsmodule, clsname)
|
||||
parents = cls.__mro__
|
||||
query = typeclass.objects.all_family()
|
||||
else:
|
||||
query = typeclass.objects.all()
|
||||
|
||||
if include_parents:
|
||||
parents = typeclass.__mro__
|
||||
if parents:
|
||||
parent_queries = []
|
||||
for parent in (parent for parent in parents if hasattr(parent, "path")):
|
||||
query = query | Q(db_typeclass_path__exact=parent.path)
|
||||
# actually query the database
|
||||
return super().filter(query)
|
||||
parent_queries.append(super().filter(db_typeclass_path__exact=parent.path))
|
||||
query = query.union(*parent_queries)
|
||||
|
||||
return query
|
||||
|
||||
|
||||
class TypeclassManager(TypedObjectManager):
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ Unit tests for typeclass base system
|
|||
"""
|
||||
|
||||
from django.test import override_settings
|
||||
from evennia.typeclasses import attributes
|
||||
from evennia.objects.objects import DefaultObject
|
||||
from evennia.utils.test_resources import BaseEvenniaTest, EvenniaTestCase
|
||||
from mock import patch
|
||||
from parameterized import parameterized
|
||||
|
|
@ -213,6 +213,82 @@ class TestTypedObjectManager(BaseEvenniaTest):
|
|||
self.assertEqual(tagobj.db_data, "data4")
|
||||
|
||||
|
||||
# setting up testing typeclass with child- and parent class
|
||||
class TestSearchManagerTypeclassParent(DefaultObject):
|
||||
pass
|
||||
|
||||
|
||||
class TestSearchManagerTypeclass(TestSearchManagerTypeclassParent):
|
||||
pass
|
||||
|
||||
|
||||
class TestSearchManagerTypeclassChild(TestSearchManagerTypeclass):
|
||||
pass
|
||||
|
||||
|
||||
class TestSearchTypeclassFamily(EvenniaTestCase):
|
||||
"""
|
||||
Test the manager method for searching for inheriting typeclasses.
|
||||
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.obj_parent, _ = TestSearchManagerTypeclassParent.create(key="obj_parent")
|
||||
self.obj1, _ = TestSearchManagerTypeclass.create(key="obj1")
|
||||
self.obj2, _ = TestSearchManagerTypeclass.create(key="obj2")
|
||||
self.obj_child, _ = TestSearchManagerTypeclassChild.create(key="obj_child")
|
||||
|
||||
def test_typeclass_search__inputs(self):
|
||||
"""Test basic functionality"""
|
||||
|
||||
res1 = self.obj1.__class__.objects.typeclass_search(self.obj1.__class__)
|
||||
res2 = self.obj1.__class__.objects.typeclass_search(
|
||||
"evennia.typeclasses.tests.TestSearchManagerTypeclass"
|
||||
)
|
||||
self.assertEqual(list(res1), [self.obj1, self.obj2])
|
||||
self.assertEqual(list(res2), [self.obj1, self.obj2])
|
||||
|
||||
def test_typeclass_search__children_and_parents(self):
|
||||
"""Test getting parents/child classes"""
|
||||
|
||||
# just the objects of this typeclass
|
||||
res1 = self.obj1.__class__.objects.typeclass_search(self.obj1.__class__)
|
||||
res2 = self.obj2.__class__.objects.typeclass_search(self.obj2.__class__)
|
||||
|
||||
# these objects + children
|
||||
res3 = self.obj1.__class__.objects.typeclass_search(
|
||||
self.obj1.__class__, include_children=True
|
||||
)
|
||||
# these objects + parents
|
||||
res4 = self.obj1.__class__.objects.typeclass_search(
|
||||
self.obj1.__class__, include_parents=True
|
||||
)
|
||||
# these objects + parents + children
|
||||
res5 = self.obj1.__class__.objects.typeclass_search(
|
||||
self.obj1.__class__, include_children=True, include_parents=True
|
||||
)
|
||||
|
||||
self.assertEqual(set(res1), {self.obj1, self.obj2})
|
||||
self.assertEqual(set(res2), {self.obj1, self.obj2})
|
||||
self.assertEqual(set(res3), {self.obj1, self.obj2, self.obj_child})
|
||||
self.assertEqual(set(res4), {self.obj1, self.obj2, self.obj_parent})
|
||||
self.assertEqual(set(res5), {self.obj1, self.obj2, self.obj_child, self.obj_parent})
|
||||
|
||||
def test_typeclass_search__nested(self):
|
||||
"""Test several levels deep searches"""
|
||||
# check all children of the parent
|
||||
res1 = self.obj1.__class__.objects.typeclass_search(
|
||||
self.obj_parent.__class__, include_children=True
|
||||
)
|
||||
# check all parents of the child
|
||||
res2 = self.obj1.__class__.objects.typeclass_search(
|
||||
self.obj_child.__class__, include_parents=True
|
||||
)
|
||||
|
||||
self.assertEqual(set(res1), {self.obj_parent, self.obj1, self.obj2, self.obj_child})
|
||||
self.assertEqual(set(res2), {self.obj_parent, self.obj1, self.obj2, self.obj_child})
|
||||
|
||||
|
||||
class TestTags(BaseEvenniaTest):
|
||||
def test_has_tag_key_only(self):
|
||||
self.obj1.tags.add("tagC")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue