Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to implement appendable list of embedded objects

Tags:

eve

I'm attempting to use Eve to provide an RESTful API for a simple list of items.

I'd like to use 1) one HTTP request to create a list (possibly with initial items), 2) one HTTP request to add an item(s) (a common operation), 3) one HTTP request to get the list (including all child items). In other words:

1) POST /lists with body

{
  "title": "My List",
  "items": [{
      "name": "Alice"
    },
    {
      "name": "Bob"
    }]
}

2) POST /lists/555555555555555555555555/items with body

{
   "name": "Carol"
}

3) GET /lists/555555555555555555555555

{
  "_id": "555555555555555555555555",
  "title": "My List",
  "items": [{
      "_id": "aaaaaaaaaaaaaaaaaaaaaaaa",
      "name": "Alice"
    },
    {
      "_id": "bbbbbbbbbbbbbbbbbbbbbbbb",
      "name": "Bob"
    },
    {
      "_id": "cccccccccccccccccccccccc",
      "name": "Carol"
    }]
}

I haven't figured out how to do this with Eve. I can do (1) using an embedded list of dicts, but then I can't do (2)—I'd have to POST an item and then PATCH the list (?). I can do (2) using sub-resources, but then I can't do (1) ("value '{'name': 'Alice'}' cannot be converted to a ObjectId"). Or am I missing something?

If all three can't be done, could at least both (2) and (3)?

like image 634
jrc Avatar asked Oct 18 '22 18:10

jrc


1 Answers

I figured out how to implement (2) and (3), using database event hooks to inject the embedded child documents into the parent list before it's returned to the client (and also delete the children when the parent is deleted). This works and supports the expected REST usage on individual list items. It results in two DB queries, however.

I suspect (1) could also be implemented using an event hook, but this will suffice for now.

Any further improvements/suggestions are welcome. It would be nice if there were an easier way to accomplish this (keywords: One-to-Many Relationships with Embedded Documents).

settings.py:

RESOURCE_METHODS = ['GET', 'POST', 'DELETE']
ITEM_METHODS = ['GET', 'PUT', 'PATCH', 'DELETE']

lists = {
    'schema': {
        'title': {
            'type': 'string'
        }
    }
}

items = {
    'url': 'lists/<regex("[a-f0-9]{24}"):list_id>/items',
    'schema': {
        'name': {'type': 'string',
                 'required': True
                 },
        'list_id': {
            'type': 'objectid',
            'required': True,
            'data_relation': {
                'resource': 'lists',
                'field': '_id'
            }
        }
    }
}

DOMAIN = {
    'lists': lists,
    'items': items
}

main.py:

from bson.objectid import ObjectId

def before_returning_lists(response):
    list_id = response['_id']
    response['items'] = list(db.items.find({'list_id': ObjectId(list_id)}))

def after_deleting_lists(item):
    list_id = item['_id']
    db.items.delete_many({'list_id': ObjectId(list_id)})

app.on_fetched_item_lists += after_fetching_lists
app.on_deleted_item_lists += after_deleting_lists

Usage

curl -X POST http://127.0.0.1:5000/lists -d title="My List"

# (2)
curl -X POST http://127.0.0.1:5000/lists/5895fdb5a663e2dcad9e7647/items -d 'name=Alice'
curl -X POST http://127.0.0.1:5000/lists/5895fdb5a663e2dcad9e7647/items -d 'name=Bob'
curl -X POST http://127.0.0.1:5000/lists/5895fdb5a663e2dcad9e7647/items -d 'name=Carol'

# (3)
curl -X GET http://127.0.0.1:5000/lists/5895fdb5a663e2dcad9e7647
like image 115
jrc Avatar answered Oct 21 '22 03:10

jrc