Skip to content

Instantly share code, notes, and snippets.

@marioidival
Created March 5, 2015 03:08
Show Gist options
  • Select an option

  • Save marioidival/60a2a06730947cc32b00 to your computer and use it in GitHub Desktop.

Select an option

Save marioidival/60a2a06730947cc32b00 to your computer and use it in GitHub Desktop.
REST API Pyramid, view_defaults and view_config nested
from __future__ import unicode_literals
from pyramid.config import Configurator
from pyramid.response import Response
from pyramid.view import view_config, view_defaults
from wsgiref.simple_server import make_server
PETS = [
{
"id": 1,
"name": "Xhyko",
"type": "cat"
},
{
"id": 2,
"name": "Mieu",
"type": "cat"
},
{
"id": 3,
"name": "Aruk",
"type": "dog"
}
]
@view_defaults(route_name="pets", renderer="json")
class PetViews(object):
def __init__(self, request):
self.request = request
@view_config(route_name="pets.detail", request_method="GET")
@view_config(request_method="GET")
def get_pets(self):
pet_id = int(self.request.matchdict.get("pet_id", 0))
if pet_id:
pet = [pet for pet in PETS if pet["id"] == pet_id]
return pet
return PETS
@view_config(request_method="POST")
def post_pets(self):
params = dict(self.request.params)
params["id"] = int(params["id"])
PETS.append(params)
return PETS[-1]
@view_config(route_name="pets.update", request_method="PUT")
@view_config(request_method="PUT")
def put_pets(self):
pet_id = int(self.request.matchdict.get("pet_id", 0))
if pet_id:
pet = [pet for pet in PETS if pet["id"] == pet_id]
if pet:
params = self.request.params.copy()
pet = pet[0].update(dict(self.request.params))
return pet
@view_config(route_name="pets.delete", request_method="DELETE")
@view_config(request_method="DELETE")
def delete_pets(self):
pet_id = int(self.request.matchdict.get("pet_id", 0))
pet = filter(lambda pet: pet['id'] == pet_id, PETS)
if pet:
PETS.remove(pet[0])
return True
if __name__ == '__main__':
config = Configurator()
config.add_route("pets", '/pets/')
config.add_route("pets.detail", '/pets/{pet_id}/') # Works up to this view, others views raise 404
config.add_route("pets.update", '/pets/{pet_id}/')
config.add_route("pets.delete", '/pets/{pet_id}/')
config.scan()
app = config.make_wsgi_app()
server = make_server('0.0.0.0', 8080, app)
server.serve_forever()
@marioidival
Copy link
Copy Markdown
Author

GET /pets/ OK
GET /pets/some_id/ OK
POST /pets/ OK

PUT /pets/some_id/ ERROR 404
DELETE /pets/some_id/ ERROR 404

@diefans
Copy link
Copy Markdown

diefans commented Mar 5, 2015

You only need two routes:

config.add_route("pets_collection", "/pets")
config.add_route("pets", "/pets/{pet_id}")

@view_config(route_name="pets_collection", method="GET")
def get_all_pets(request):
    pass

@view_config(route_name="pets_collection", method="POST")
def create_pet(request):
    pass

# the following views allways relate to a request with pet_id, otherwise they wouldn't match
# you have to take care that only valid `pet_id`'s arrive
# see http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/urldispatch.html#route-pattern-syntax
# e.g. use "/pets/{pet_id:\d+}"
@view_config(route_name="pets", method="GET")
def get_single_pet(request):
    assert 'pet_id' in request.matchdict

@view_config(route_name="pets", method="PUT")
def replace_single_pet(request):
    assert 'pet_id' in request.matchdict

@view_config(route_name="pets", method="DELETE")
def delete_single_pet(request):
    assert 'pet_id' in request.matchdict

@view_config(route_name="pets", method="PATCH")
def update_single_pet(request):
    assert 'pet_id' in request.matchdict

@marioidival
Copy link
Copy Markdown
Author

Thank you, It's work with @view_* nested :)

from __future__ import unicode_literals

from pyramid.config import Configurator
from pyramid.response import Response
from pyramid.view import view_config, view_defaults

from wsgiref.simple_server import make_server

PETS = [
    {
        "id": 1,
        "name": "Xhyko",
        "type": "cat"
    },
    {
        "id": 2,
        "name": "Mieu",
        "type": "cat"
    },
    {
        "id": 3,
        "name": "Aruk",
        "type": "dog"
    }
]

@view_defaults(route_name="pets_collection", renderer="json")
class PetViews(object):

    def __init__(self, request):
        self.request = request

    @view_config(route_name="pets", request_method="GET")
    @view_config(request_method="GET")
    def get_pets(self):
        pet_id = int(self.request.matchdict.get("pet_id", 0))

        if pet_id:
            pet = [pet for pet in PETS if pet["id"] == pet_id]
            return pet

        return PETS

    @view_config(request_method="POST")
    def post_pets(self):
        params = dict(self.request.params)
        params["id"] = int(params["id"])

        PETS.append(params)
        return PETS[-1]

    @view_config(route_name="pets", request_method="PUT")
    def put_pets(self):
        pet_id = int(self.request.matchdict.get("pet_id", 0))

        if pet_id:
            pet = [pet for pet in PETS if pet["id"] == pet_id]
            if pet:
                params = self.request.params.copy()
                pet = pet[0].update(dict(self.request.params))
                return pet

    @view_config(route_name="pets", request_method="DELETE")
    def delete_pets(self):
        pet_id = int(self.request.matchdict.get("pet_id", 0))
        pet = filter(lambda pet: pet['id'] == pet_id, PETS)
        if pet:
            PETS.remove(pet[0])
            return True


if __name__ == '__main__':
    config = Configurator()

    config.add_route("pets_collection", '/pets/')
    config.add_route("pets", '/pets/{pet_id}/')

    config.scan()
    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()

@blaflamme
Copy link
Copy Markdown

A way I like to think about this is to add the request_method predicate to the route instead of the view. To me, in that circumstance (REST-Like), it makes sense that each routes represent an action, it makes it convenient to refer to names instead of request methods. What's great with pyramid is you can do things in a lot of ways :)

from __future__ import unicode_literals

from itertools import ifilter

from pyramid.config import Configurator
from pyramid.response import Response
from pyramid.view import view_config, view_defaults

from wsgiref.simple_server import make_server

PETS = [
    {
        'id': 1,
        'name': 'Xhyko',
        'type': 'cat'
    },
    {
        'id': 2,
        'name': 'Mieu',
        'type': 'cat'
    },
    {
        'id': 3,
        'name': 'Aruk',
        'type': 'dog'
    }
]

NEXT_ID = 4


@view_defaults(renderer='json')
class PetViews(object):

    def __init__(self, request):
        self.request = request
        self.id = int(self.request.matchdict.get('pet_id', 0))

    def _get_pet(self):
        return next(ifilter(lambda x: x['id']==self.id, PETS), None)

    @view_config(route_name='pets.list')
    def list(self):
        return {'pets': PETS}

    @view_config(route_name='pets.create')
    def create(self):
        pet = dict(self.request.params.copy())
        pet['id'] = self.request.registry.next_id
        self.request.registry.next_id += 1
        PETS.append(pet)
        return {'pet': PETS[-1]}

    @view_config(route_name='pets.read')
    def read(self):
        pet = self._get_pet()
        if not pet:
            return {'success': False}
        return {'pet': pet}

    @view_config(route_name='pets.update')
    def update(self):
        pet = self._get_pet()
        if not pet:
            return {'success': False}
        params = self.request.params.copy()
        pet.update(dict(self.request.params))
        return {'pet': pet}

    @view_config(route_name='pets.delete')
    def delete(self):
        pet = self._get_pet()
        if not pet:
            return {'success': False}
        PETS.remove(pet)
        return {'success': True}


if __name__ == '__main__':
    config = Configurator()
    config.add_route(
        'pets.list',
        '/pets',
        request_method='GET'
        )
    config.add_route(
        'pets.create',
        '/pets',
        request_method='POST'
        )
    config.add_route(
        'pets.read',
        '/pets/{pet_id}',
        request_method='GET'
        )
    config.add_route(
        'pets.update',
        '/pets/{pet_id}',
        request_method='PUT'
        )
    config.add_route(
        'pets.delete',
        '/pets/{pet_id}',
        request_method='DELETE'
        )
    config.scan()
    config.registry.next_id = NEXT_ID
    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()
$ curl -X "GET" "http://localhost:8080/pets"
{"pets": [{"type": "cat", "id": 1, "name": "Xhyko"}, {"type": "cat", "id": 2, "name": "Mieu"}, {"type": "dog", "id": 3, "name": "Aruk"}]}

$ curl -X "POST" "http://localhost:8080/pets" \
    -d "type=dog" \
    -d "name=ren"
{"pet": {"type": "dog", "name": "ren", "id": 5}}

$ curl -X "GET" "http://localhost:8080/pets/1"
{"pet": {"type": "cat", "id": 1, "name": "Xhyko"}}

$ curl -X "PUT" "http://localhost:8080/pets/1" \
    -d "type=dog"
{"pet": {"type": "dog", "id": 1, "name": "Xhyko"}}

$ curl -X "DELETE" "http://localhost:8080/pets/1"
{"success": true}

@blaflamme
Copy link
Copy Markdown

If you prefer having two routes and discriminate on the view:

from __future__ import unicode_literals

from itertools import ifilter

from pyramid.config import Configurator
from pyramid.response import Response
from pyramid.view import view_config, view_defaults

from wsgiref.simple_server import make_server

PETS = [
    {
        'id': 1,
        'name': 'Xhyko',
        'type': 'cat'
    },
    {
        'id': 2,
        'name': 'Mieu',
        'type': 'cat'
    },
    {
        'id': 3,
        'name': 'Aruk',
        'type': 'dog'
    }
]

NEXT_ID = 4


@view_defaults(
    route_name='pets.collection',
    renderer='json'
    )
class PetCollectionViews(object):

    def __init__(self, request):
        self.request = request

    @view_config(request_method='GET')
    def collection(self):
        return {'pets': PETS}

    @view_config(request_method='POST')
    def create(self):
        pet = dict(self.request.params.copy())
        pet['id'] = self.request.registry.next_id
        self.request.registry.next_id += 1
        PETS.append(pet)
        return {'pet': PETS[-1]}


@view_defaults(
    route_name='pets.item',
    renderer='json'
    )
class PetItemViews(object):

    def __init__(self, request):
        self.request = request
        self.id = int(self.request.matchdict.get('pet_id', 0))
        self.pet = self._get_pet(self.id)

    def _get_pet(self, id):
        return next(ifilter(lambda x: x['id']==id, PETS), None)

    @view_config(request_method='GET')
    def read(self):
        if not self.pet:
            return {'success': False}
        return {'pet': self.pet}

    @view_config(request_method='PUT')
    def update(self):
        if not self.pet:
            return {'success': False}
        params = self.request.params.copy()
        self.pet.update(dict(self.request.params))
        return {'pet': self.pet}

    @view_config(request_method='DELETE')
    def delete(self):
        if not self.pet:
            return {'success': False}
        PETS.remove(self.pet)
        return {'success': True}


if __name__ == '__main__':
    config = Configurator()
    config.add_route('pets.collection', '/pets')
    config.add_route('pets.item', '/pets/{pet_id}')
    config.scan()
    config.registry.next_id = NEXT_ID
    app = config.make_wsgi_app()
    server = make_server('0.0.0.0', 8080, app)
    server.serve_forever()
$ curl -X "GET" "http://localhost:8080/pets"
{"pets": [{"type": "cat", "id": 1, "name": "Xhyko"}, {"type": "cat", "id": 2, "name": "Mieu"}, {"type": "dog", "id": 3, "name": "Aruk"}]}

$ curl -X "POST" "http://localhost:8080/pets" \
    -d "type=dog" \
    -d "name=ren"
{"pet": {"type": "dog", "name": "ren", "id": 5}}

$ curl -X "GET" "http://localhost:8080/pets/1"
{"pet": {"type": "cat", "id": 1, "name": "Xhyko"}}

$ curl -X "PUT" "http://localhost:8080/pets/1" \
    -d "type=dog"
{"pet": {"type": "dog", "id": 1, "name": "Xhyko"}}

$ curl -X "DELETE" "http://localhost:8080/pets/1"
{"success": true}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment