From 75812dcdd5f2ab0b1aad169ac9ecf575e8f7f143 Mon Sep 17 00:00:00 2001 From: TehomCD Date: Fri, 13 Mar 2020 21:52:03 -0400 Subject: [PATCH] Fix attribute serializer, add name filter --- evennia/web/api/filters.py | 34 +++++++++++++++++++++++++++++++++- evennia/web/api/serializers.py | 26 +++++++++++++++++++++++--- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/evennia/web/api/filters.py b/evennia/web/api/filters.py index 0c93aa3650..93622c6b85 100644 --- a/evennia/web/api/filters.py +++ b/evennia/web/api/filters.py @@ -6,6 +6,7 @@ documentation specifically regarding DRF integration. https://django-filter.readthedocs.io/en/latest/guide/rest_framework.html """ +from typing import Union from django.db.models import Q from django_filters.rest_framework.filterset import FilterSet from django_filters.filters import CharFilter, EMPTY_VALUES @@ -15,6 +16,19 @@ from evennia.accounts.models import AccountDB from evennia.scripts.models import ScriptDB +def get_tag_query(tag_type: Union[str, None], key: str) -> Q: + """ + + Args: + tag_type(str or None): The type of tag (None, 'alias', etc) + key (str): The name of the tag + + Returns: + A Q object that for searching by this tag type and name + """ + return Q(db_tags__db_tagtype=tag_type) & Q(db_tags__db_key__iexact=key) + + class TagTypeFilter(CharFilter): """ This class lets you create different filters for tags of a specified db_tagtype. @@ -26,7 +40,7 @@ class TagTypeFilter(CharFilter): if value in EMPTY_VALUES: return qs # if they enter a value, we filter objects by having a tag of this type with the given name - return qs.filter(Q(db_tags__db_tagtype=self.tag_type) & Q(db_tags__db_key=value)) + return qs.filter(get_tag_query(self.tag_type, value)).distinct() class AliasFilter(TagTypeFilter): @@ -46,6 +60,22 @@ class BaseTypeclassFilterSet(FilterSet): """A parent class with filters for aliases and permissions""" alias = AliasFilter(lookup_expr="iexact") permission = PermissionFilter(lookup_expr="iexact") + name = CharFilter(lookup_expr="iexact", method="filter_name", field_name="db_key") + + def filter_name(self, queryset, name, value): + """ + + Args: + queryset: The queryset being filtered + name: The name of the field + value: The value passed in from GET params + + Returns: + The filtered queryset + """ + query = Q(**{f"{name}__iexact": value}) + query |= get_tag_query("alias", value) + return queryset.filter(query).distinct() class ObjectDBFilterSet(BaseTypeclassFilterSet): @@ -58,6 +88,8 @@ class ObjectDBFilterSet(BaseTypeclassFilterSet): class AccountDBFilterSet(BaseTypeclassFilterSet): """This adds filters for Account objects""" + name = CharFilter(lookup_expr="iexact", method="filter_name", field_name="username") + class Meta: model = AccountDB fields = SHARED_FIELDS + ["username", "db_is_connected", "db_is_bot"] diff --git a/evennia/web/api/serializers.py b/evennia/web/api/serializers.py index 117ca0a816..a1d265f3b7 100644 --- a/evennia/web/api/serializers.py +++ b/evennia/web/api/serializers.py @@ -19,9 +19,26 @@ from evennia.typeclasses.tags import Tag class AttributeSerializer(serializers.ModelSerializer): + value_display = serializers.SerializerMethodField(source="value") + db_value = serializers.CharField(write_only=True, required=False) + class Meta: model = Attribute - fields = ["db_key", "db_value", "db_category", "db_attrtype"] + fields = ["db_key", "db_category", "db_attrtype", "value_display", "db_value"] + + @staticmethod + def get_value_display(obj: Attribute) -> str: + """ + Gets the string display of an Attribute's value for serialization + Args: + obj: Attribute being serialized + + Returns: + The Attribute's value in string format + """ + if obj.db_strvalue: + return obj.db_strvalue + return str(obj.value) class TagSerializer(serializers.ModelSerializer): @@ -41,9 +58,9 @@ class TypeclassSerializerMixin(object): might note that the methods and fields are defined here, but they're included explicitly in each child class. What gives? It's a DRF error: serializer method fields which are inherited do not resolve correctly in child classes, and as of this current version (3.11) you must have them in the child classes explicitly - to avoid field errors. + to avoid field errors. Similarly, the child classes must contain the attribute serializer explicitly to + not have them render PK-related fields. """ - db_attributes = AttributeSerializer(many=True) shared_fields = ["id", "db_key", "db_attributes", "db_typeclass_path", "aliases", "tags", "permissions"] @@ -82,6 +99,7 @@ class TypeclassSerializerMixin(object): class ObjectDBSerializer(TypeclassSerializerMixin, serializers.ModelSerializer): + db_attributes = AttributeSerializer(many=True, read_only=True) contents = serializers.SerializerMethodField() exits = serializers.SerializerMethodField() tags = serializers.SerializerMethodField() @@ -120,6 +138,7 @@ class ObjectDBSerializer(TypeclassSerializerMixin, serializers.ModelSerializer): class AccountSerializer(TypeclassSerializerMixin, serializers.ModelSerializer): """This uses the DefaultAccount object to have access to the sessions property""" + db_attributes = AttributeSerializer(many=True, read_only=True) db_key = serializers.CharField(required=False) session_ids = serializers.SerializerMethodField() tags = serializers.SerializerMethodField() @@ -144,6 +163,7 @@ class AccountSerializer(TypeclassSerializerMixin, serializers.ModelSerializer): class ScriptDBSerializer(TypeclassSerializerMixin, serializers.ModelSerializer): + db_attributes = AttributeSerializer(many=True, read_only=True) tags = serializers.SerializerMethodField() aliases = serializers.SerializerMethodField() permissions = serializers.SerializerMethodField()