This page looks best with JavaScript enabled

Django Restful APIs with Tastypie

 ·  ☕ 3 min read

1. Restful

REST is short for Representational State Transfer, meaning “representation state transfer”. Restful is a development philosophy and a software architecture style for the World Wide Web.

1.1 Features of Restful

  • Abstract resources
    Images, text, songs, and videos are all resource entities; on the network they are abstracted as resources. In Restful, JSON is often used as the carrier for these resources, uniformly exposing data to the outside world.
  • Uniform interface
    Create, read, update, and delete operations on data map to different HTTP methods.
  • GET (SELECT): Retrieve a resource (one or many) from the server.
  • POST (CREATE): Create a new resource on the server.
  • PUT (UPDATE): Update a resource on the server (the client provides the complete resource data).
  • PATCH (UPDATE): Update a resource on the server (the client provides only the resource data that needs to change).
  • DELETE (DELETE): Delete a resource from the server.
  • HEAD: Retrieve metadata about a resource.
  • OPTIONS: Retrieve information about which attributes of a resource the client can change.

1.2 Restful Rules

  • API URL prefix. To keep the API extensible, it is recommended to set the API URL prefix to http://example.com/api/v1/
  • URLs may contain only nouns. For example resource names and parameters, http://example.com/api/v1/user/?id=1
  • Operations are set via HTTP headers. There are five common HTTP methods — GET, POST, PUT, PATCH, DELETE — plus two less common verbs, HEAD and OPTIONS. Usage:
    • GET /zoos/: List all zoos
    • POST /zoos/: Create a zoo
    • GET /zoos/ID/: Get information about a specific zoo
    • PUT /zoos/ID/: Update information about a specific zoo (providing the zoo’s complete information)
    • PATCH /zoos/ID/: Update information about a specific zoo (providing partial information about the zoo)
    • DELETE /zoos/ID/: Delete a zoo
    • GET /zoos/ID/animals: List all animals of a specific zoo
    • DELETE /zoos/ID/animals/ID/: Delete a specific animal of a specific zoo
  • Filtering rules
  • ?limit=10: Specify the number of records to return
  • ?offset=10: Specify the starting position of the returned records.
  • ?page=2&per_page=100: Specify which page, and the number of records per page
  • ?sortby=name&order=asc: Specify which attribute the results are sorted by, and the sort order
  • ?animal_type_id=1: Specify filter conditions

2. Tastypie

2.1 Introduction

Tastypie is a Restful API development framework based on Django. With simple configuration, it can expose Restful-style interfaces to the outside world.

2.2 Installation and Configuration

1
pip instal django-tastypie

settings.py configuration

1
2
3
 INSTALLED_APPS = (
    'tastypie',
)
1
python manage.py syncdb

2.3 Authorization

If you need to restrict permissions on interfaces, Tastypie also provides corresponding support.

First, subclass the Authorization class to implement a MyAuthorization permission management class.
The functions that need to be implemented are:

  • def read_list(self, object_list, bundle)
  • def read_detail(self, objec_list, bundle)
  • def create_detail(self, object_list, bundle)
  • def update_list(self, object_list, bundle)
  • def update_detail(self, object_list, bundle)
  • def delete_list(self, object_list, bundle)
  • def delete_detail(self, object_list, bundle)

Second, specify the permission management instance in the Resource’s Meta

1
2
class Meta(BaseMeta):
      authorization = MyAuthorization()

2.4 Bundles

In the process of customizing a Resource, you will inevitably use bundles. This is an abstract concept that represents the wrapping of a single resource while retrieving or writing it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class BucketObject(object):
    def __init__(self, initial=None):
        self.__dict__['_data'] = {}

        if hasattr(initial, 'items'):
            self.__dict__['_data'] = initial

    def __getattr__(self, name):
        return self._data.get(name, None)

    def __setattr__(self, name, value):
        self.__dict__['_data'][name] = value

    def to_dict(self):
        return self._data

The BucketObject here does not correspond to any Model; it is simply a one-time packaging of a resource — it may be a collection of several models, or data wrapped after an interface call.

2.5 ModelResource

ModelResource provides a Restful API based on an existing Model.

First, create the resource

reasource.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
from tastypie.resources import ModelResource
from .models import QuickView
class QickViewResource(ModelResource):
    class Meta:
        # 定义查询范围
        queryset = QuickView.objects.all()
        # 定义资源名
        resource_name = 'QucikView'
        # 其他配置
        excludes = ['create_time', 'update_time']

Second, configure the URL routing and register the interface

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from tastypie.api import Api
from ci.api.resources import QickViewResource
# 创建API V1版本,URL形式为/api/v1/
api_v1 = Api(api_name='v1')
# 注册资源
api_v1.register(QickViewResource())
# 新增资源注册放在这里

urlpatterns = patterns('',
                      (r'^api/', include(api_v1.urls)),
                      )

Third, start using it

/api/v1/?format=json

View all resources registered under api v1. list_endpoint indicates the resource endpoint, and the URL provided by schema lets you view the fields and usage rules.

1
2
3
4
5
6
{
  "QucikView": {
    "list_endpoint": "/api/v1/QucikView/",
    "schema": "/api/v1/QucikView/schema/"
  }
}

/api/v1/QucikView/schema/?format=json

Gives the basic operation permissions, default parameters, the fields of the returned object, and field hints, etc.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
    allowed_detail_http_methods: [
        "get",
        "post",
        "put",
        "delete",
        "patch"
    ],
    allowed_list_http_methods: [
        "get",
        "post",
        "put",
        "delete",
        "patch"
    ],
    default_format: "application/json",
    default_limit: 20,
    fields: {
        app_id: {
            blank: false,
            default: "No default provided.",
            help_text: "Unicode string data. Ex: "Hello World"",
            nullable: false,
            primary_key: false,
            readonly: false,
            type: "string",
            unique: false,
            verbose_name: "业务ID"
        }
}

/api/v1/QucikView/?format=json&limit=1

Retrieve the list of QucikView resources. If there is pagination, tastypie also provides a hint link.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
{
  "meta": {
    "limit": 1,
    "next": "/api/v1/QucikView/?offset=1&limit=1&format=json",
    "offset": 0,
    "previous": null,
    "total_count": 8
  },
  "objects": [
    {
      "app_id": "1",
      "create_time": "2017-06-12T11:42:30.071000",
      "data": "{sadknlsakbf}",
      "id": 1,
      "index": 0,
      "is_deleted": false,
      "pipeline_id": "2",
      "resource_uri": "/ci/api/v1/QucikView/1/"
    }
  ]
}

2.6 Resource

If you need to expose a Restful interface based on a third-party interface, several Models, or a non-ORM data source, ModelResource may not fit. tastypie provides the Resource class to abstract this kind of resource.

The key here is understanding the Bundles concept mentioned above: write a BucketObject class and wrap the resource in it.

reasource.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class BuildResource(Resource):
    buildNo = fields.CharField(attribute='buildNo')
    starter = fields.CharField(attribute='starter')
    duration = fields.CharField(attribute='duration')
    endTime = fields.CharField(attribute='endTime')
    csReleaseNote = fields.CharField(attribute='csReleaseNote')
    statusBuild = fields.CharField(attribute='statusBuild')
    totalBuildNum = fields.CharField(attribute='totalBuildNum')

    class Meta:
        resource_name = 'BuildList'
        allowed_methods = ['get']
        object_class = BucketObject
        authorization = Authorization()

    def obj_get_list(self, request=None, **kwargs):
        if not request:
            request = kwargs['bundle'].request
        return self.get_object_list(request)

    def get_object_list(self, request):
        results = []
        key_list = ("buildNo", "starter", "duration", "endTime", "csReleaseNote",
                    "statusBuild", "statusBuild", "totalBuildNum")
        new_obj = BucketObject()
        for key in key_list:
            # 这里调用第三方接口,或者取其他Model数据,填充BucketObject
            setattr(new_obj, key, key+"_vaule")
        results.append(new_obj)
        return results

The URL configuration is the same as for ModelResource. Here only the get function overload is implemented; if you need to handle other operations, you must override the corresponding functions as well. There are nine in total:

  • detail _uri _kwargs
  • get
  • object_list
  • obj _get _list
  • obj _get
  • obj _create
  • obj _update
  • obj _delete_list
  • obj _delete
  • rollback

3. References


微信公众号
WRITTEN BY
微信公众号