diff --git a/evennia/typeclasses/managers.py b/evennia/typeclasses/managers.py index e9ad42ed11..c886a67400 100644 --- a/evennia/typeclasses/managers.py +++ b/evennia/typeclasses/managers.py @@ -573,7 +573,7 @@ class TypedObjectManager(idmapper.manager.SharedMemoryManager): 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 self.filter(query) + return super().filter(query) class TypeclassManager(TypedObjectManager): diff --git a/evennia/web/api/README.md b/evennia/web/api/README.md index 3f1b26ce07..d06838fbf4 100644 --- a/evennia/web/api/README.md +++ b/evennia/web/api/README.md @@ -50,9 +50,21 @@ setting `REST_API_ENABLED = True` in your `settings.py`, endpoints will be accessible by users who make authenticated requests as users with builder permissions. Individual objects will check lockstrings to determine if the user has permission to perform retrieve/update/delete actions upon them. +To start with, you can view a synopsis of endpoints by making a GET request +to the `yourgame/api/` endpoint by using the excellent [requests library][requests]: +```pythonstub +>>> import requests +>>> r = requests.get("https://www.mygame.com/api", auth=("user", "pw")) +>>> r.json() +{'accounts': 'http://www.mygame.com/api/accounts/', + 'objects': 'http://www.mygame.com/api/objects/', +'characters': 'http://www.mygame.comg/api/characters/', +'exits': 'http://www.mygame.com/api/exits/', +'rooms': 'http://www.mygame.com/api/rooms/', +'scripts': 'http://www.mygame.com/api/scripts/'} +``` -To view an object, you might make a request by using the -excellent [requests library][requests]: +To view an object, you might make a request like this: ```pythonstub >>> import requests >>> response = requests.get("https://www.mygame.com/api/objects/57", @@ -61,7 +73,35 @@ excellent [requests library][requests]: {"db_key": "A rusty longsword", "id": 57, "db_location": 213, ...} ``` The above example makes a GET request to the /objects/ endpoint to retrieve the -object with an ID of 57, retrieving basic data for it. Now suppose that you want +object with an ID of 57, retrieving basic data for it. + +For listing a number of objects, you might do this: +```pythonstub +>>> response = requests.get("https://www.mygame.com/api/objects", + auth=("Myusername", "password123")) +>>> response.json() +{ +"count": 125, +"next": "https://www.mygame.com/api/objects/?limit=25&offset=25", +"previous": null, +"results" : [{"db_key": "A rusty longsword", "id": 57, "db_location": 213, ...}]} +``` +In the above example, it now displays the objects inside the "results" array, while it has a "count" value +for the number of total objects, and "next" and "previous" links for the next and previous page, if any. +This is called [pagination][pagination], and the link displays "limit" and "offset" as query parameters that +can be added to the url to control the output. Other query parameters can be defined as [filters][filters] which +allow you to further narrow the results. For example, to only get accounts with developer permissions: +```pythonstub +>>> response = requests.get("https://www.mygame.com/api/accounts/?permission=developer", + auth=("user", "pw")) +>>> response.json() +{ +"count": 1, +"results": [{"username": "bob",...}] +} +``` + +Now suppose that you want to use the API to create an object: ```pythonstub >>> data = {"db_key": "A shiny sword"} @@ -76,12 +116,12 @@ decided you didn't like the name, and wanted to change it for the newly created object: ```pythonstub >>> data = {"db_key": "An even SHINIER sword", "db_location": 50} ->>> response = requests.post("https://www.mygame.com/api/objects/214", +>>> response = requests.put("https://www.mygame.com/api/objects/214", data=data, auth=("Alsoauser", "Badpassword")) >>> response.json() {"db_key": "An even SHINIER sword", "id": 214, "db_location": 50, ...} ``` -By making a POST request to the endpoint that includes the object ID, it becomes +By making a PUT request to the endpoint that includes the object ID, it becomes a request to update the object with the specified data you pass along. In most cases, you won't be making API requests to the backend with python, @@ -92,6 +132,8 @@ the native [Fetch][fetch]. [wiki-api]: https://en.wikipedia.org/wiki/Application_programming_interface [drf]: https://www.django-rest-framework.org/ +[pagination]: https://www.django-rest-framework.org/api-guide/pagination/ +[filters]: https://www.django-rest-framework.org/api-guide/filtering/#filtering [json]: https://en.wikipedia.org/wiki/JSON [crud]: https://en.wikipedia.org/wiki/Create,_read,_update_and_delete [serializers]: https://www.django-rest-framework.org/api-guide/serializers/ diff --git a/evennia/web/api/urls.py b/evennia/web/api/urls.py index 7eea5d01b8..0594e26d16 100644 --- a/evennia/web/api/urls.py +++ b/evennia/web/api/urls.py @@ -28,6 +28,7 @@ from evennia.web.api.views import ( app_name = "api" router = routers.DefaultRouter() +router.trailing_slash = "/?" router.register(r'accounts', AccountDBViewSet, basename="account") router.register(r'objects', ObjectDBViewSet, basename="object") router.register(r'characters', CharacterViewSet, basename="character")