Table Of Contents

Previous topic

YARHP: Yet Another REST Helper for Pyramid

Next topic

Design and Conventions

This Page

Getting Started with YARHP

Basic layout

Let’s consider a simple web game tictactoe that has the following resources:

  • /users
  • /users/{id}/scores
  1. Create a pyramid application using pcreate -t starter tictactoe.You will now have basic structure with some files and __init__.py among them. Remove all the lines in main except the first and last two, leaving only config object, scan and return statement.

  2. In your Pyramid project’s __init__.py add the following:

    config.include('yarhp')
    root = config.get_root_resource()
    
    user = root.add('user', 'users', model='yarhp.NoModel')
    user.add('score', 'scores', model='yarhp.NoModel')
    

    Obviously you have to have yarhp pip’d.

  3. Create view files for each resource. By default, yarhp expects view file for each resource under your_project/views module. In our case we create the following files with following content:

    tictactoe/views/users.py

    from yarhp.view import BaseView
    
    
    class UsersView(BaseView):
        def index(self):
            return []
    

    tictactoe/views/user_scores.py

    from yarhp.view import BaseView
    
    
    class UserScoresView(BaseView):
        def index(self, user_id):
            return []
    
        def show(self, user_id, id):
            return 'show score %s for user %s ' % (id, user_id)
    

    Notice the class naming is in plural form (the collection name of the resource) followed by View in CamelCase. This would be different for models in the examples bellow. Models use the Capitalized singular form.

  4. Run pserve development.ini in the root of the project and point your browser to http://localhost:6543/users.

    You should get a json response that looks like {"total": 0, "users": []}. Similarly you can navigate to http://localhost:6543/users/100/scores and should get the same empty result.

Introducing Models

In the previous section the application was bare minimum. Now lets have some persistence.

Yarhp expects that each resource has by default its model under project/model module. (dont forget to create the __init__.py file to make the folder a python module.)

  1. Create models for each resource. Note that in the case of models, names are in singular form, not plural as oppose to the views.

    tictactoe/model/user.py

    import shelve
    
    class User(object):
        db = shelve.open('user')
        def __init__(self, **kw):
            self.__dict__.update(kw)
    
        def save(self):
            self.db[self.id] = self.__dict__
            return self
    

    tictactoe/model/user_score.py

    import shelve
    
    class UserScore(object):
        db = shelve.open('user_score')
        def __init__(self, **kw):
            self.__dict__.update(kw)
    
        def save(self):
            #key is "composite"
            self.db['%s_%s' % (self.user['id'], self.id)] = self.__dict__
            return self
    
  2. In project’s __init__.py file remove model='yarhp.NoModel', since we have models now. Add some fixtures in the main like so:

    #test data
    user1 = User(id='1', name='Bob').save()
    user2 = User(id='2', name='Alice').save()
    
    UserScore(id='1', user=user1.__dict__, score='100').save()
    UserScore(id='2', user=user1.__dict__, score='200').save()
    UserScore(id='3', user=user1.__dict__, score='300').save()
    
    UserScore(id='1', user=user1.__dict__, score='1000').save()
    UserScore(id='2', user=user2.__dict__, score='2000').save()
    
  3. The views now look little different too:

    from yarhp.view import BaseView
    from tictactoe.model.user import User
    
    class UsersView(BaseView):
        def index(self):
            return User.db.values()
    
        def show(self, id):
            return User.db[id]
    

    and

    from yarhp.view import BaseView
    from tictactoe.model.user_score import UserScore
    
    class UserScoresView(BaseView):
        def index(self, user_id):
            return [score for score in UserScore.db.values()
                        if score['user']['id'] == user_id]
    
        def show(self, user_id, id):
            return UserScore.db["%s_%s" % (user_id, id)]
    

Run the server and check in the browser the resources if they work.

The model files can be manually specified in __init__.py file during the declaration of the resource if its not the default location:

user.add('score', 'scores', model='tictactoe.custom_model.MyFunkyScoreModel')

Same goes for the view files as well. view param passed to add method will do the trick. In both cases you can pass either dotted path to the model class or the class object itself, obviously importing it first.

Validators

Basic idea behind validators is to, well, validate. In yarhp you can either use built-in validators or write your own to validate inputs coming from the request as well as output going into to response.

Lets assume we want to validate the creation of the user resource.

from yarhp.wrappers import validator

class UserView(BaseView):
    @validator(name={'type':str, 'required':True},
                age={'type':int, 'required':False},
                gender={'type':str, 'required':False})
    def create(self):
        User(name=self.request.params['name'],
            gender=self.request.params.get('gender'))

        return HTTPCreated()

As you can see, validator decorator does 2 things. It forces required fields to be present in the request and also checks for the types. In the example above, name is required field and it must have a type str, age is an int and required, gender is a str but could be ommited.

Automagic Validation

In case the model is based on SQL Alchemy (or Elixir for that matter), you have an option of deriving your view from yarhp.sqla.SQLAView class, which will do bunch of validations for you and also implement basic behaviours of all actions. You, of course, can define your own action in your view. Validation with SQLAView works by checking your sql schema and validating your input against that. For example if you have the following elixir entity for User model:

class User(Entity):
    using_options(tablename='user', identity='1', inheritance='multi')

    id = Field(BigInteger, primary_key=True, autoincrement=False)
    name = Field(Unicode(128), required=True)
    gender = Field(Unicode(10), required=False)
    age = Field(Integer, required=False)

And the following view:

class UserView(SQLAView):
    ...

    def create(self):
        User(**self.request.params).save()
        return HTTPCreated()

The create method will be validated against your model defined in User automatically. That validation would be equivalent of the validator decorator explained in the pervious section.

Since user resource is a simple root resource, you could just skip the whole view all together and yarhp would implement all actions and validations for you. In case of nested resources, yarhp is still in experimental phase.