djkit 开发记录

原因

在公司的研发团队中,我除了要写代码,还要做一些项目架构和基础设施搭建的工作,每次搭建一个新项目都会有不少重复的工作,一两次还好,多了我真的有点受不了,于是思索着写这么一个东西来减少工作的痛苦。

技术架构

工作中主要使用 Python,API 主要使用 Django、Django RESTframework 来做,一些 Python 依赖和目录结构都是统一的,我就先减少这部分的重复工作吧。

requirements

Django==1.9.10
django-cors-headers==1.1.0
django-nose==1.4.4
django-rest-swagger==0.3.7
djangorestframework==3.4.6
djangorestframework-jwt==1.8.0
mysqlclient==1.3.7
raven==5.26.0

Django 使用 1.9.10 的原因是这个版本和 django-rest-swagger 的 0.3.7 版本配合比较好,比较适合我们团队, 可以很方便的在浏览器下调试,效果如下:

21986351-ADE1-4AAA-9E2F-CC05B27E5EE1.png

目录结构

├── .gitignore                         # python 的 gitignore 文件
├── .venv                              # virtualenv
├── manage.py
├── requirements                       # python 依赖
│   ├── base.txt
│   ├── dev.txt
│   └── prod.txt
└── webapi                             # 项目代码
    ├── __init__.py
    ├── admin.py                       # model 统一添加到 django admin 中
    ├── migrations
    │   └── __init__.py
    ├── models                         # models
    │   └── __init__.py
    ├── settings                       # settings
    │   ├── __init__.py
    │   ├── local_settings.py.tpl
    │   └── settings.py
    ├── urls.py
    └── wsgi.py

requirements 做成一个文件夹是考虑到 开发环境 的依赖和 生产环境 可能会有区别。
models 做成一个文件夹是因为我们想在一个地方统一管理所有 models 而不是分散在各个 app 中。
models/__init__.py 的代码

# coding: utf-8

# 每新增一个 models 文件,就添加一行 import 代码
# from .your_models import *

settings 做成一个文件夹是为了区分 开发环境生产环境 的配置。
秘密都藏在 settings/__init__.py 文件中。

# coding: utf-8

from .settings import *

try:
    from .local_settings import *
except:
    pass

admin.py 功能是把所有 models 添加到 django admin 中,方便开发过程中的调试。
admin.py 代码

# coding: utf-8

import inspect

from django.contrib import admin

import models as app_models

for attr in dir(app_models):
    model = getattr(app_models, attr)
    if not inspect.isclass(model):
        continue

    try:
        admin.site.register(model)
    except:
        pass

settings 的额外配置

import datetime

# django restframework app
INSTALLED_APPS += [
    'rest_framework',
    'rest_framework_jwt',
    'rest_framework_swagger',
    'corsheaders',
]

# Django REST Framework settings
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 20,
    'DEFAULT_PARSER_CLASSES': (
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser',
    ),
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
    ),
}

# jwt auth settings
JWT_AUTH = {
    'JWT_ENCODE_HANDLER':
        'rest_framework_jwt.utils.jwt_encode_handler',

    'JWT_DECODE_HANDLER':
        'rest_framework_jwt.utils.jwt_decode_handler',

    'JWT_PAYLOAD_HANDLER':
        'rest_framework_jwt.utils.jwt_payload_handler',

    'JWT_PAYLOAD_GET_USER_ID_HANDLER':
        'rest_framework_jwt.utils.jwt_get_user_id_from_payload_handler',

    'JWT_RESPONSE_PAYLOAD_HANDLER':
        'rest_framework_jwt.utils.jwt_response_payload_handler',

    'JWT_SECRET_KEY': SECRET_KEY,
    'JWT_ALGORITHM': 'HS512',
    'JWT_VERIFY': True,
    'JWT_VERIFY_EXPIRATION': True,
    'JWT_LEEWAY': 0,
    'JWT_EXPIRATION_DELTA': datetime.timedelta(seconds=60 * 60 * 24),

    'JWT_ALLOW_REFRESH': False,
    'JWT_REFRESH_EXPIRATION_DELTA': datetime.timedelta(days=7),

    'JWT_AUTH_HEADER_PREFIX': 'JWT',
}

# cors header
CORS_ORIGIN_ALLOW_ALL = True

以上的模板可以在 django-starter-template 里找到。

技术思路

我的需求

  1. 最终效果是使用 djkit init your_project 一条命令搞定项目初始化。
  2. 写成一个库,使用 pip 可以安装,方便以后升级。

技术调研

命令行工具的选用,经过了各种筛选,最终使用了 click 这个库来做命令行工具的解析,原因很简单, 个人直觉。
文件的操作我就直接使用了笨办法,subprocess 调用 shell 命令解决。

初始化流程

  1. clone django-starter-template 到 /tmp 目录。
  2. /tmp 目录初始化一个 virtualenv 里面安装 django 1.9.10。
  3. 使用 django-admin.py startproject your_project 初始化项目。
  4. 生成的项目中初始化 virtualenv 安装 requirements 的依赖。
  5. 复制 django-starter-template 模板的代码到生成的项目中。
  6. 删除 /tmp 下生成的临时文件。

实现

目录结构

├── LICENSE
├── README.md
├── bin
│   └── djkit
├── djkit
│   ├── __init__.py
│   └── djkit.py
└── setup.py

核心代码在 djkit/djkit.py 里。
所有代码实现在 djkit 项目,对细节感兴趣的可以去看源代码。

写成 Python 库

只需要一个 setup.py 搞定。

# coding: utf-8

from setuptools import setup

setup(
    name='djkit',
    version='0.3',
    description='django starter kit',
    url='https://github.com/BurnishTechCN/djkit',
    author='runforever',
    author_email='c.chenchao.c@gmail.com',
    license='WTFPL',
    packages=['djkit'],
    install_requires=[
        'virtualenv',
        'click',
    ],
    scripts = ['bin/djkit'],
    zip_safe=False,
)

程序入口 bin/djkit 文件,依然是 Python

#!/usr/bin/env python

from djkit import djkit
djkit.cli()

安装和使用

pip install .
# 或者
pip install git+https://github.com/BurnishTechCN/djkit.git

# 使用
djkit init webapi

开源和 License

没有开源社区,我写不出来这个工具,所以依旧开源回馈社区,如果能解决到别人的问题,那就达到效果了, 不知道从哪听来的 WTFPL,感觉碉堡了,于是就他了。

wtfpl.png