Xây dựng nhanh chóng API với Django Tastypie [Phần 1]

Thông thường, khi nhắc tới Restful Python thì mọi người thường nghĩ tới Flask API, hoặc Django Rest Framework. Xin thứ lỗi mình sẽ gọi tắt Django Rest Framework là Django Rest và Django Tastypie là Tastypie . Điểm mạnh của Flask Rest và Django Rest chình là flexible và decoupled. Tuy nhiên, nếu bạn không quá khắt khe về tính flexible, bạn chỉ cần viết nhanh API thì Tastypie cũng là sự lựa chọn tuyệt vời. Nói như thế không hẳn Tastypie không flexible mà nó sẽ không bằng 2 ông anh kia thôi.

Now, mình sẽ giới thiệu sơ lược nhất về xây dựng API sử dụng Tastypie!

Installation

Dependencies

  • Python 2.7+ or Python 3.4+
  • Django 1.8+
  • Install Tastypie: $pip install django-tastypie
  • Mình có cài đặt thêm virtualenv và sử dụng nó

Dưới đây là các gói:

(venv) % pip freeze                                                                                     
Django==1.10
django-tastypie==0.14.0
python-dateutil==2.7.2
python-mimeparse==1.6.0
pytz==2018.3
six==1.11.0

Configuration

Đầu tiên bạn tạo ra một Django project và một app bằng Django CLI:

(venv) % django-admin startproject simple
(venv) % cd simple
(venv) % django-admin startapp entry

Trong settings của django tại /simple/simple, add tastypieentry vào INSTALLED_APPS

# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'tastypie',
    'entry'
]

To do

Sau khi config xong mình sẽ migration data:

(venv) % cd simple                                                                                  
(venv) % ls                                                                                  
entry  manage.py  simple
No changes detected
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions, tastypie
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying sessions.0001_initial... OK
  Applying tastypie.0001_initial... OK
(venv) %     

Mình sẽ tạo ra một số user bằng Django shell. User này là một models mặc định của Django.

(venv) % python manage.py shell 
Python 3.6.1 (default, Nov  9 2017, 10:16:55) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.contrib.auth.models import User
>>> User.objects.create(username="user1", password="12345678", email="[email protected]")
<User: user1>
>>> User.objects.create(username="user2", password="12345678", email="[email protected]")
<User: user2>
>>> User.objects.create(username="user3", password="12345678", email="[email protected]")
<User: user3>

Sau đó, trong app Entry, mình tạo 1 file lấy tên là resource.py. Trong resource.py:

from tastypie.resources import ModelResource, ALL
from django.contrib.auth.models import User


class UserResource(ModelResource):
    class Meta:
        queryset = User.objects.all()
        resource_name = 'user'
        allowed_methods = ('get', 'post', 'put', 'delete', 'patch')
        filtering = {"id": ALL}

    def dehydrate(self, bundle):
        return super(UserResource, self).dehydrate(bundle=bundle)

API trả về mình sẽ hển thị tất cẩ info của các user mình vừa tạo bên trên.

Cuối cùng trong urls.py, add thêm resouce vào là xong:

from django.contrib import admin
from django.conf.urls import include, url
from tastypie.api import Api

from entry.resource import UserResource
v1_api = Api(api_name='v1')
v1_api.register(UserResource())

urlpatterns = [
    url(r'admin/', admin.site.urls),
    url(r'api/', include(v1_api.urls))
]

  • v1_api = Api(api_name='v1'): định nghĩa 1 version cho API
  • v1_api.register(UserResource()): register 1 resource
  • url(r'api/', include(v1_api.urls)): define url

Kết quả url API sẽ theo cấu trúc kiểu như này: /api/v1/<resource_name>/....

Ok, Run server nào:

(venv) % python manage.py runserver                                                                     
Performing system checks...

System check identified no issues (0 silenced).
March 27, 2018 - 03:29:53
Django version 1.10, using settings 'simple.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

Bạn thử truy cập vào cá địa chỉ sau và thưởng thức thành quả:

  • http://127.0.0.1:8000/api/v1/: API này sẽ hiển thị toàn bộ API version 1:
{
    "user": {
    "list_endpoint": "/api/v1/user/",
    "schema": "/api/v1/user/schema/"
}
  • http://127.0.0.1:8000/api/v1/user/: API lấy ra toàn bộ info của user
{
    "meta": {
    "limit": 20,
    "next": null,
    "offset": 0,
    "previous": null,
    "total_count": 3
},
"objects": [
    {
        "date_joined": "2018-03-27T02:40:37.326805",
        "email": "[email protected]",
        "first_name": "",
        "id": 1,
        "is_active": true,
        "is_staff": false,
        "is_superuser": false,
        "last_login": null,
        "last_name": "",
        "password": "12345678",
        "resource_uri": "/api/v1/user/1/",
        "username": "user1"
    },{
        "date_joined": "2018-03-27T02:40:44.214801",
        "email": "[email protected]",
        "first_name": "",
        "id": 2,
        "is_active": true,
        "is_staff": false,
        "is_superuser": false,
        "last_login": null,
        "last_name": "",
        "password": "12345678",
        "resource_uri": "/api/v1/user/2/",
        "username": "user2"
    },{
        "date_joined": "2018-03-27T02:40:50.830747",
        "email": "[email protected]",
        "first_name": "",
        "id": 3,
        "is_active": true,
        "is_staff": false,
        "is_superuser": false,
        "last_login": null,
        "last_name": "",
        "password": "12345678",
        "resource_uri": "/api/v1/user/3/",
        "username": "user3"
    }]
}
  • http://127.0.0.1:8000/api/v1/user/set/1;3/: Lấy ra info user có id = 1 và id =3
{
    "objects": [
    {
        "date_joined": "2018-03-27T02:40:37.326805",
        "email": "[email protected]",
        "first_name": "",
        "id": 1,
        "is_active": true,
        "is_staff": false,
        "is_superuser": false,
        "last_login": null,
        "last_name": "",
        "password": "12345678",
        "resource_uri": "/api/v1/user/1/",
        "username": "user1"
    },{
        "date_joined": "2018-03-27T02:40:50.830747",
        "email": "[email protected]",
        "first_name": "",
        "id": 3,
        "is_active": true,
        "is_staff": false,
        "is_superuser": false,
        "last_login": null,
        "last_name": "",
        "password": "12345678",
        "resource_uri": "/api/v1/user/3/",
        "username": "user3"
    }]
}
  • http://127.0.0.1:8000/api/v1/user/1/: Lấy info của user với id = 1
{
    "date_joined": "2018-03-27T02:40:37.326805",
    "email": "[email protected]",
    "first_name": "",
    "id": 1,
    "is_active": true,
    "is_staff": false,
    "is_superuser": false,
    "last_login": null,
    "last_name": "",
    "password": "12345678",
    "resource_uri": "/api/v1/user/1/",
    "username": "user1"
}
  • http://127.0.0.1:8000/api/v1/user/schema/: Xem cấu trúc schema của User models
{
    "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": {
        "date_joined": {
            "blank": false,
            "default": "2018-03-27T03:39:04.252851",
            "help_text": "A date & time as a string. Ex: \"2010-11-10T03:07:43\"",
            "nullable": false,
            "primary_key": false,
            "readonly": false,
            "type": "datetime",
            "unique": false,
            "verbose_name": "date joined"
        },
        "email": {
            "blank": true,
            "default": "",
            "help_text": "Unicode string data. Ex: \"Hello World\"",
            "nullable": false,
            "primary_key": false,
            "readonly": false,
            "type": "string",
            "unique": false,
            "verbose_name": "email address"
        },
        "first_name": {
            "blank": true,
            "default": "",
            "help_text": "Unicode string data. Ex: \"Hello World\"",
            "nullable": false,
            "primary_key": false,
            "readonly": false,
            "type": "string",
            "unique": false,
            "verbose_name": "first name"
        },
        "id": {
            "blank": true,
            "default": "",
            "help_text": "Integer data. Ex: 2673",
            "nullable": false,
            "primary_key": true,
            "readonly": false,
            "type": "integer",
            "unique": true,
            "verbose_name": "ID"
        },
        "is_active": {
            "blank": true,
            "default": true,
            "help_text": "Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
            "nullable": false,
            "primary_key": false,
            "readonly": false,
            "type": "boolean",
            "unique": false,
            "verbose_name": "active"
        },
        "is_staff": {
            "blank": true,
            "default": false,
            "help_text": "Designates whether the user can log into this admin site.",
            "nullable": false,
            "primary_key": false,
            "readonly": false,
            "type": "boolean",
            "unique": false,
            "verbose_name": "staff status"
        },
        "is_superuser": {
            "blank": true,
            "default": false,
            "help_text": "Designates that this user has all permissions without explicitly assigning them.",
            "nullable": false,
            "primary_key": false,
            "readonly": false,
            "type": "boolean",
            "unique": false,
            "verbose_name": "superuser status"
        },
        "last_login": {
            "blank": false,
            "default": "No default provided.",
            "help_text": "A date & time as a string. Ex: \"2010-11-10T03:07:43\"",
            "nullable": true,
            "primary_key": false,
            "readonly": false,
            "type": "datetime",
            "unique": false,
            "verbose_name": "last login"
        },
        "last_name": {
            "blank": true,
            "default": "",
            "help_text": "Unicode string data. Ex: \"Hello World\"",
            "nullable": false,
            "primary_key": false,
            "readonly": false,
            "type": "string",
            "unique": false,
            "verbose_name": "last name"
        },
        "password": {
            "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": "password"
        },
        "resource_uri": {
            "blank": false,
            "default": "No default provided.",
            "help_text": "Unicode string data. Ex: \"Hello World\"",
            "nullable": false,
            "primary_key": false,
            "readonly": true,
            "type": "string",
            "unique": false,
            "verbose_name": "resource uri"
        },
        "username": {
            "blank": false,
            "default": "No default provided.",
            "help_text": "Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
            "nullable": false,
            "primary_key": false,
            "readonly": false,
            "type": "string",
            "unique": true,
            "verbose_name": "username"
        }
    },
    "filtering": {
        "id": 1
    }
}

Có vẻ hơi dài rồi. Tạm thời post này, mình chỉ hướng dẫn tới đây thôi. Mình đang sử dụng với model mặc định của DjangoUser. Bạn có thể làm tương tự với 1 models bạn tự tạo. Phần này mình sẽ hướng dẫn trong phần 2.

Phần 2 hứa hẹn sẽ cung cấp thêm cho các bạn về những thứ có thể customer của Tastypie

Thanks you for reading!