Add action for setting attributes via endpoint
This commit is contained in:
parent
6f350c60c2
commit
2798dfb712
5 changed files with 92 additions and 8 deletions
|
|
@ -277,7 +277,7 @@ class PickledObjectField(models.Field):
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def value_to_string(self, obj):
|
def value_to_string(self, obj):
|
||||||
value = self._get_val_from_obj(obj)
|
value = self.value_from_object(obj)
|
||||||
return self.get_db_prep_value(value)
|
return self.get_db_prep_value(value)
|
||||||
|
|
||||||
def get_internal_type(self):
|
def get_internal_type(self):
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,14 @@
|
||||||
|
"""
|
||||||
|
Serializers in the Django Rest Framework are similar to Forms in normal django.
|
||||||
|
They're used for transmitting and validating data, both going to clients and
|
||||||
|
coming to the server. However, where forms often contained presentation logic,
|
||||||
|
such as specifying widgets to use for selection, serializers typically leave
|
||||||
|
those decisions in the hands of clients, and are more focused on converting
|
||||||
|
data from the server to JSON (serialization) for a response, and validating
|
||||||
|
and converting JSON data sent from clients to our enpoints into python objects,
|
||||||
|
often django model instances, that we can use (deserialization).
|
||||||
|
"""
|
||||||
|
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
from evennia.objects.models import ObjectDB
|
from evennia.objects.models import ObjectDB
|
||||||
|
|
|
||||||
|
|
@ -110,3 +110,24 @@ class TestEvenniaRESTApi(EvenniaTest):
|
||||||
# check success when sending the required data
|
# check success when sending the required data
|
||||||
response = self.client.post(view_url, data=view.create_data)
|
response = self.client.post(view_url, data=view.create_data)
|
||||||
self.assertEqual(response.status_code, 201, f"Response was {response.data}")
|
self.assertEqual(response.status_code, 201, f"Response was {response.data}")
|
||||||
|
|
||||||
|
def test_set_attribute(self):
|
||||||
|
views = self.get_view_details("set-attribute")
|
||||||
|
for view in views:
|
||||||
|
with self.subTest(msg=f"Testing {view.view_name}"):
|
||||||
|
view_url = reverse(f"api:{view.view_name}", kwargs={"pk": view.obj.pk})
|
||||||
|
# check failures from not sending required fields
|
||||||
|
response = self.client.post(view_url)
|
||||||
|
self.assertEqual(response.status_code, 400, f"Response was: {response.data}")
|
||||||
|
# test adding an attribute
|
||||||
|
self.assertEqual(view.obj.db.some_test_attr, None)
|
||||||
|
attr_name = "some_test_attr"
|
||||||
|
attr_data = {"db_key": attr_name, "db_value": "test_value"}
|
||||||
|
response = self.client.post(view_url, data=attr_data)
|
||||||
|
self.assertEqual(response.status_code, 200, f"Response was: {response.data}")
|
||||||
|
self.assertEquals(view.obj.attributes.get(attr_name), "test_value")
|
||||||
|
# now test removing it
|
||||||
|
attr_data = {"db_key": attr_name}
|
||||||
|
response = self.client.post(view_url, data=attr_data)
|
||||||
|
self.assertEqual(response.status_code, 200, f"Response was: {response.data}")
|
||||||
|
self.assertEquals(view.obj.attributes.get(attr_name), None)
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,20 @@
|
||||||
|
"""
|
||||||
|
The Django Rest Framework provides a way of generating urls for different
|
||||||
|
views that implement standard CRUD operations in a quick way, using 'routers'
|
||||||
|
and 'viewsets'. A viewset implements standard CRUD actions and any custom actions
|
||||||
|
that you want, and then a router will automatically generate URLs based on the
|
||||||
|
actions that it detects for a viewset. For example, below we create a DefaultRouter.
|
||||||
|
We then register ObjectDBViewSet, a viewset for CRUD operations for ObjectDB
|
||||||
|
instances, to the 'objects' base endpoint. That will generate a number of URLs
|
||||||
|
like the following:
|
||||||
|
list objects: action: GET, url: /objects/, view name: object-list
|
||||||
|
create object: action: POST, url: /objects/, view name: object-list
|
||||||
|
retrieve object: action: GET, url: /objects/<:pk>, view name: object-detail
|
||||||
|
update object: action: POST, url: /objects/<:pk>, view name: object-detail
|
||||||
|
delete object: action: DELETE, url: /objects/<:pk>, view name: object-detail
|
||||||
|
set attribute: action: POST, url: /objects/<:pk>/set-attribute, view name: object-set-attribute
|
||||||
|
"""
|
||||||
|
|
||||||
from rest_framework import routers
|
from rest_framework import routers
|
||||||
from evennia.web.api.views import (
|
from evennia.web.api.views import (
|
||||||
ObjectDBViewSet,
|
ObjectDBViewSet,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ can generate a number of views for the common CRUD operations.
|
||||||
"""
|
"""
|
||||||
from rest_framework.viewsets import ModelViewSet
|
from rest_framework.viewsets import ModelViewSet
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
from django_filters.rest_framework import DjangoFilterBackend
|
from django_filters.rest_framework import DjangoFilterBackend
|
||||||
|
|
||||||
|
|
@ -18,17 +20,28 @@ from evennia.web.api.permissions import EvenniaPermission
|
||||||
|
|
||||||
|
|
||||||
class TypeclassViewSetMixin(object):
|
class TypeclassViewSetMixin(object):
|
||||||
|
"""
|
||||||
|
This mixin adds some shared functionality to each viewset of a typeclass. They all use the same
|
||||||
|
permission classes and filter backend. You can override any of these in your own viewsets.
|
||||||
|
"""
|
||||||
|
# permission classes determine who is authorized to call the view
|
||||||
permission_classes = [EvenniaPermission]
|
permission_classes = [EvenniaPermission]
|
||||||
|
# the filter backend allows for retrieval views to have filter arguments passed to it
|
||||||
filter_backends = [DjangoFilterBackend]
|
filter_backends = [DjangoFilterBackend]
|
||||||
|
|
||||||
|
|
||||||
class ObjectDBViewSet(TypeclassViewSetMixin, ModelViewSet):
|
|
||||||
serializer_class = ObjectDBSerializer
|
|
||||||
queryset = ObjectDB.objects.all()
|
|
||||||
filterset_class = ObjectDBFilterSet
|
|
||||||
|
|
||||||
@action(detail=True, methods=["put", "post"])
|
@action(detail=True, methods=["put", "post"])
|
||||||
def add_attribute(self, request, pk=None):
|
def set_attribute(self, request, pk=None):
|
||||||
|
"""
|
||||||
|
This is an example of a custom action added to a viewset. Based on the name of the
|
||||||
|
method, it will create a default url_name (used for reversing) and url_path.
|
||||||
|
The 'pk' argument is automatically passed to this action because it has a url path
|
||||||
|
of the format <object type>/:pk/set-attribute. The get_object method is automatically
|
||||||
|
set in the expected viewset classes that will inherit this, using the pk that's
|
||||||
|
passed along to retrieve the object.
|
||||||
|
|
||||||
|
This action will set an attribute if the db_value is defined, or remove it
|
||||||
|
if no db_value is provided.
|
||||||
|
"""
|
||||||
attr = AttributeSerializer(data=request.data)
|
attr = AttributeSerializer(data=request.data)
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
if attr.is_valid(raise_exception=True):
|
if attr.is_valid(raise_exception=True):
|
||||||
|
|
@ -44,27 +57,49 @@ class ObjectDBViewSet(TypeclassViewSetMixin, ModelViewSet):
|
||||||
handler.add(key=key, value=value, category=category)
|
handler.add(key=key, value=value, category=category)
|
||||||
else:
|
else:
|
||||||
handler.remove(key=key, category=category)
|
handler.remove(key=key, category=category)
|
||||||
|
return Response(AttributeSerializer(obj.db_attributes.all(), many=True).data, status=status.HTTP_200_OK)
|
||||||
|
return Response(attr.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
class ObjectDBViewSet(TypeclassViewSetMixin, ModelViewSet):
|
||||||
|
"""
|
||||||
|
An example of a basic viewset for all ObjectDB instances. It declares the
|
||||||
|
serializer to use for both retrieving and changing/creating/deleting
|
||||||
|
instances. Serializers are similar to django forms, used for the
|
||||||
|
transmitting of data (typically json).
|
||||||
|
"""
|
||||||
|
serializer_class = ObjectDBSerializer
|
||||||
|
queryset = ObjectDB.objects.all()
|
||||||
|
filterset_class = ObjectDBFilterSet
|
||||||
|
|
||||||
|
|
||||||
class CharacterViewSet(ObjectDBViewSet):
|
class CharacterViewSet(ObjectDBViewSet):
|
||||||
|
"""
|
||||||
|
This overrides the queryset to only retrieve Character objects
|
||||||
|
based on your DefaultCharacter typeclass path.
|
||||||
|
"""
|
||||||
queryset = DefaultCharacter.objects.typeclass_search(DefaultCharacter.path, include_children=True)
|
queryset = DefaultCharacter.objects.typeclass_search(DefaultCharacter.path, include_children=True)
|
||||||
|
|
||||||
|
|
||||||
class RoomViewSet(ObjectDBViewSet):
|
class RoomViewSet(ObjectDBViewSet):
|
||||||
|
"""Viewset for Room objects"""
|
||||||
queryset = DefaultRoom.objects.typeclass_search(DefaultRoom.path, include_children=True)
|
queryset = DefaultRoom.objects.typeclass_search(DefaultRoom.path, include_children=True)
|
||||||
|
|
||||||
|
|
||||||
class ExitViewSet(ObjectDBViewSet):
|
class ExitViewSet(ObjectDBViewSet):
|
||||||
|
"""Viewset for Exit objects"""
|
||||||
queryset = DefaultExit.objects.typeclass_search(DefaultExit.path, include_children=True)
|
queryset = DefaultExit.objects.typeclass_search(DefaultExit.path, include_children=True)
|
||||||
|
|
||||||
|
|
||||||
class AccountDBViewSet(TypeclassViewSetMixin, ModelViewSet):
|
class AccountDBViewSet(TypeclassViewSetMixin, ModelViewSet):
|
||||||
|
"""Viewset for Account objects"""
|
||||||
serializer_class = AccountDBSerializer
|
serializer_class = AccountDBSerializer
|
||||||
queryset = AccountDB.objects.all()
|
queryset = AccountDB.objects.all()
|
||||||
filterset_class = AccountDBFilterSet
|
filterset_class = AccountDBFilterSet
|
||||||
|
|
||||||
|
|
||||||
class ScriptDBViewSet(TypeclassViewSetMixin, ModelViewSet):
|
class ScriptDBViewSet(TypeclassViewSetMixin, ModelViewSet):
|
||||||
|
"""Viewset for Script objects"""
|
||||||
serializer_class = ScriptDBSerializer
|
serializer_class = ScriptDBSerializer
|
||||||
queryset = ScriptDB.objects.all()
|
queryset = ScriptDB.objects.all()
|
||||||
filterset_class = ScriptDBFilterSet
|
filterset_class = ScriptDBFilterSet
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue