├── .gitignore ├── .vscode └── settings.json ├── README.MD ├── README.rst ├── app.py ├── business_code ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── test │ └── abc.py ├── tests.py ├── urls.py └── views.py ├── db.sqlite3 ├── demo_bottle.py ├── demo_django.py ├── django_demo ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py ├── docs ├── 404.html ├── api-shop.png ├── assets │ ├── css │ │ └── styles.7805920a.css │ ├── img │ │ └── back-to-top.8b37f773.svg │ └── js │ │ ├── 616.c8fc0ab0.js │ │ ├── 640.757f6868.js │ │ ├── 640.757f6868.js.LICENSE.txt │ │ ├── 961.fd42e312.js │ │ ├── app.fa19070d.js │ │ ├── runtime~app.6da47921.js │ │ ├── v-08e0d16d.fd6e86c9.js │ │ ├── v-093e9b42.b5ee9f61.js │ │ ├── v-1020f180.fd82e958.js │ │ ├── v-3706649a.61f597b6.js │ │ ├── v-45b8d955.dc25e0d5.js │ │ ├── v-4822d271.377adebc.js │ │ ├── v-712e14fc.943786c4.js │ │ ├── v-8daa1a0e.d74e3c65.js │ │ ├── v-d72a4774.5d1e65ea.js │ │ └── v-f785a3ee.7f618e88.js ├── components │ ├── Api.html │ ├── ApiShop.html │ ├── SingleApiShop.html │ ├── data_format.html │ ├── func.html │ └── index.html ├── index.html ├── introduction │ └── index.html ├── logo.png ├── responseDocs.png └── start │ └── index.html ├── documents ├── docs │ ├── .vuepress │ │ ├── config.js │ │ └── public │ │ │ ├── api-shop.png │ │ │ ├── logo.png │ │ │ └── responseDocs.png │ ├── README.md │ ├── components │ │ ├── Api.md │ │ ├── ApiShop.md │ │ ├── README.md │ │ ├── SingleApiShop.md │ │ ├── data_format.md │ │ └── func.md │ ├── introduction │ │ └── README.md │ └── start │ │ └── README.md ├── package.json ├── push.bat ├── push.ps1 └── yarn.lock ├── flask_demo ├── __init__.py ├── models.py ├── test.db └── views.py ├── requirements.txt ├── src ├── MANIFEST.in ├── README.MD ├── api_shop.egg-info │ ├── PKG-INFO │ ├── SOURCES.txt │ ├── dependency_links.txt │ └── top_level.txt ├── api_shop │ ├── __init__.py │ ├── api_shop.egg-info │ │ ├── PKG-INFO │ │ ├── SOURCES.txt │ │ ├── dependency_links.txt │ │ └── top_level.txt │ ├── api_shop.py │ ├── autofill.py │ ├── data_format.py │ ├── func │ │ ├── __init__.py │ │ └── model.py │ ├── i18n.py │ ├── static │ │ └── document.html │ └── url_parse.py ├── build │ └── lib │ │ └── api_shop │ │ ├── __init__.py │ │ ├── api_shop.py │ │ ├── data_format.py │ │ ├── i18n.py │ │ └── static │ │ └── document.html └── setup.py └── static └── demo.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea/* 3 | .vscode/ 4 | dist/ 5 | build/ 6 | *.pyc 7 | __pycache__/ 8 | node_modules/ 9 | src_template/ 10 | .cache 11 | .temp -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "restructuredtext.confPath": "", 3 | "translateSpeaker.voiceFilename": false, 4 | "translateSpeaker.voiceEditing": false, 5 | "editor.detectIndentation": false, 6 | "editor.tabSize": 4, 7 | "editor.formatOnPaste": true, 8 | "editor.formatOnSave": true 9 | } -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # api-shop ![PyPI](https://img.shields.io/pypi/v/api-shop?logo=api-shop) ![PyPI - Downloads](https://img.shields.io/pypi/dm/api-shop) 2 | 3 | ## [Online Documents](https://pcloth.gitee.io/api-shop/) 4 | 5 | ## [在线文档](https://pcloth.gitee.io/api-shop/) 6 | 7 | > ### api-shop:一个易用的、快速的 restful-api 接口工具包,兼容:`django` / `flask` / `bottle`。 8 | > 9 | > ### `一切只为少加班。` 10 | 11 | ## **demo 图片** 12 | 13 | ![demo](/static/demo.png) 14 | 15 | ## **核心功能:** 16 | 17 | 1. 配置化 api 生成。 18 | 2. 自动校验 request 提交的数据,并转换成指定格式,支持:int,float,list,dict,set,tuple,bool、自定义格式转换器和正则格式校验 19 | 3. 自动生成 api 文档,并提供一个 web 页面可供查询和 mock 数据演示。 20 | 4. 兼容 `django` , `flask` , `bottle` (如果不指定框架,默认按这个顺序识别框架) 21 | 5. 自动生成接口`骨架文件`功能 beta(请谨慎开启)。 22 | 6. 多国语言支持,也支持自定义语言包。 23 | 7. 文档热重载。 24 | 8. 默认值支持方法函数。 25 | 9. 支持 url 中包含参数,例如 `/api/user/`,并且在配置 methods 参数的时候设置它的规则。 26 | 10. 支持多 url 绑定一个接口 27 | 11. 支持指定参数的可选项,例如:[1,4,7],收到这个列表之外的参数就会触发 bad_request 28 | 12. 可以在代码中直接调用 Api 业务代码:`instance.api_run` 29 | 13. 支持在 Api 类中定义 response_docs 来制作返回值文档,并支持模型字段引入;以及模型部分字段引入类:ApiResponseModelFields 30 | 14. 添加了全局单例类 SingleApiShop,用来替代 ApiShop,优点是支持用户封装后覆盖单例 31 | 32 | ## [Online Documents](https://pcloth.gitee.io/api-shop/) 33 | 34 | ## [在线文档](https://pcloth.gitee.io/api-shop/) 35 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | 2 | api-shop 3 | .. image:: https://img.shields.io/pypi/v/api-shop?logo=api-shop 4 | :target: https://img.shields.io/pypi/v/api-shop?logo=api-shop 5 | :alt: PyPI 6 | 7 | .. image:: https://img.shields.io/pypi/dm/api-shop 8 | :target: https://img.shields.io/pypi/dm/api-shop 9 | :alt: PyPI - Downloads 10 | 11 | ============================================================================================================================================================================================================================================================================================ 12 | 13 | `Online Documents `_ 14 | ----------------------------------------------------------- 15 | 16 | `在线文档 `_ 17 | --------------------------------------------------- 18 | 19 | .. 20 | 21 | api-shop:一个易用的、快速的 restful-api 接口工具包,兼容:\ ``django`` / ``flask`` / ``bottle``\ 。 22 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 23 | 24 | ``一切只为少加班。`` 25 | ^^^^^^^^^^^^^^^^^^^^^^^^ 26 | 27 | 28 | **demo 图片** 29 | ----------------- 30 | 31 | 32 | .. image:: /static/demo.png 33 | :target: /static/demo.png 34 | :alt: demo 35 | 36 | 37 | **核心功能:** 38 | ------------------ 39 | 40 | 41 | #. 配置化 api 生成。 42 | #. 自动校验 request 提交的数据,并转换成指定格式,支持:int,float,list,dict,set,tuple,bool、自定义格式转换器和正则格式校验 43 | #. 自动生成 api 文档,并提供一个 web 页面可供查询和 mock 数据演示。 44 | #. 兼容 ``django`` , ``flask`` , ``bottle`` (如果不指定框架,默认按这个顺序识别框架) 45 | #. 自动生成接口\ ``骨架文件``\ 功能 beta(请谨慎开启)。 46 | #. 多国语言支持,也支持自定义语言包。 47 | #. 文档热重载。 48 | #. 默认值支持方法函数。 49 | #. 支持 url 中包含参数,例如 ``/api/user/``\ ,并且在配置 methods 参数的时候设置它的规则。 50 | #. 支持多 url 绑定一个接口 51 | #. 支持指定参数的可选项,例如:[1,4,7],收到这个列表之外的参数就会触发 bad_request 52 | #. 可以在代码中直接调用 Api 业务代码:\ ``instance.api_run`` 53 | #. 支持在 Api 类中定义 response_docs 来制作返回值文档,并支持模型字段引入;以及模型部分字段引入类:ApiResponseModelFields 54 | #. 添加了全局单例类 SingleApiShop,用来替代 ApiShop,优点是支持用户封装后覆盖单例 55 | 56 | `Online Documents `_ 57 | ----------------------------------------------------------- 58 | 59 | `在线文档 `_ 60 | --------------------------------------------------- 61 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, render_template_string 2 | 3 | from flask_demo.views import simple_page 4 | from flask_script import Shell, Manager 5 | 6 | from werkzeug.routing import BaseConverter 7 | from flask_demo import init_db 8 | 9 | 10 | class RegexConverter(BaseConverter): 11 | def __init__(self, map, *args): 12 | self.map = map 13 | self.regex = args[0] 14 | 15 | 16 | app = Flask(__name__) 17 | app.url_map.converters['regex'] = RegexConverter 18 | app.register_blueprint(simple_page) 19 | init_db() 20 | # manager = Manager(app) 21 | 22 | if __name__ == '__main__': 23 | # manager.add_command('shell', Shell(make_context=make_shell_context)) 24 | app.run(host="0.0.0.0",port=9100, debug=True) 25 | -------------------------------------------------------------------------------- /business_code/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcloth/api-shop/6cae15e057172b33ec7d9b3317d033e7ceae71e1/business_code/__init__.py -------------------------------------------------------------------------------- /business_code/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | 3 | # Register your models here. 4 | -------------------------------------------------------------------------------- /business_code/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ApiConfig(AppConfig): 5 | name = 'api' 6 | -------------------------------------------------------------------------------- /business_code/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcloth/api-shop/6cae15e057172b33ec7d9b3317d033e7ceae71e1/business_code/migrations/__init__.py -------------------------------------------------------------------------------- /business_code/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | 5 | class AccountCmdsAlias(models.Model): 6 | class Meta: 7 | db_table = 'account_cmds_alias' 8 | 9 | sender = models.CharField(max_length=64, verbose_name="发送者") 10 | commands = models.TextField(verbose_name="指令内容json") 11 | create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间') -------------------------------------------------------------------------------- /business_code/test/abc.py: -------------------------------------------------------------------------------- 1 | # api-shop automatically inserts code 2 | from src.api_shop import Api, ApiResponseModelFields 3 | from src.api_shop import get_api_result_json 4 | from flask_demo.models import User 5 | class tt(Api): 6 | pass 7 | 8 | class api_login(Api): 9 | """微信账户登录""" 10 | response_docs = { 11 | 'get':{ 12 | 'test':{User.name,User.email}, 13 | 'results':{'test:ss:dddd',ApiResponseModelFields(User,['id',User.name,'email'])}, 14 | } 15 | } 16 | 17 | def post(self, request, data): 18 | """ todo: 19 | api-shop automatically inserts code 20 | data.username # 用户名 21 | data.ddd # 日期 22 | """ 23 | from flask_demo.views import af 24 | ret,code = af.api_run(request, 'weixin/test/1', 'POST') 25 | print(ret,code,'>>>>') 26 | return ret,code 27 | 28 | def get(self, request, data): 29 | """ todo: 30 | api-shop automatically inserts code 31 | data.username # 用户名 32 | data.ddd # 日期 33 | """ 34 | get_api_result_json(tt, 'GET', request) 35 | # from flask_demo.views import af 36 | # ret,code = af.api_run(request, 'weixin/name/2', 'POST') 37 | # ret,code = self.api_run(request, 'weixin/name/2', 'POST') 38 | # print(ret,code,'>>>>') 39 | # return ret,code 40 | -------------------------------------------------------------------------------- /business_code/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /business_code/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path,re_path 2 | from django.http import JsonResponse 3 | from . import views 4 | 5 | from src.api_shop import ApiShop, Api, SingleApiShop 6 | from src.api_shop import data_format 7 | 8 | 9 | conf = [ 10 | { 11 | 'url': 'login', 12 | 'class': 'business_code.views.api_login', 13 | 'name': '账户登录', 14 | 'methods': { 15 | 'POST': [ 16 | {'name': 'username', 'type': data_format.regex( 17 | r'^[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+){0,4}@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+){0,4}$',name='邮箱'), 'required': True, 'min': 3, 'max': 24, 'description': '用户名'}, 18 | {'name':'password', 'type': str, 'required': True, 'description': '密码'}, 19 | ] 20 | } 21 | }, 22 | { 23 | 'url': ['test/alkdjflasjdf/aljdfalf','test2','three/user'], 24 | 'class': 'business_code.views.test', 25 | 'name': '测试数据', 26 | 'methods': { 27 | 'POST': [ 28 | {'name':'add', 'type': str, 'required': True, 'min': 3, 'max': 24, 'description': '地址'}, 29 | {'name':'bb', 'type': int, 'required': True, 'min': 0, 'max': 100, 'description': '百分比','default':95}, 30 | {'name':'list', 'type': list, 'description': '列表'}, 31 | ], 32 | 'DELETE':[ 33 | {'name':'id', 'type': int, 'required': True, 'min': 1,'description': '编号'}, 34 | ] 35 | } 36 | }, 37 | 38 | ] 39 | class CommonApi(SingleApiShop): 40 | def before_running(self, **kwargs): 41 | print('运行前钩子',kwargs) 42 | if kwargs.get('key')=='addroute/mock': 43 | return JsonResponse({'msg':'您没有权限'}, status=400) 44 | def after_running(self, **kwargs): 45 | print('运行后钩子',kwargs) 46 | 47 | af = CommonApi(conf, { 48 | 'framework': 'django', 49 | 'lang': 'zh' 50 | }) 51 | 52 | app_name = 'api' 53 | 54 | 55 | urlpatterns = [ 56 | path('api_data', af.get_api_data, name='api_data'), 57 | path('document/', af.render_documents, name='document'), 58 | re_path(r'([\s\S]*)', af.api_entry, name='index') 59 | ] 60 | 61 | # 引入 62 | apishop_instance = SingleApiShop.get_single_apishop() 63 | 64 | 65 | @apishop_instance.add_api( 66 | name='装饰器模式接口', 67 | url=['addroute/mock/','addroute/test'], 68 | methods={ 69 | 'GET':[ 70 | {'name':'username','required':True, 'type': str, 'min': 3, 'max': 24, 'description': '用户名'}, 71 | ] 72 | } 73 | ) 74 | class ApiMockTest(Api): 75 | def get(self, request, data): 76 | response = apishop_instance.api_run( 77 | request=request, 78 | url='login', 79 | method='POST', 80 | parameter={'username':'test@aa.com','password':99}, 81 | json=True 82 | ) 83 | print('get',response) -------------------------------------------------------------------------------- /business_code/views.py: -------------------------------------------------------------------------------- 1 | # 业务模块,flask和django写法一样 2 | from src.api_shop import Api, ApiShop, single_apishop 3 | from src.api_shop.func import model 4 | 5 | 6 | class api_login(Api): 7 | # af = ApiShop() 8 | # print(af,'af') 9 | def post(self,request,data=None): 10 | '''api登陆接口,方便微信用户绑定账户''' 11 | print(data) 12 | return {'status': 'success'} 13 | 14 | 15 | class test(Api): 16 | def get(self, request, data=None): 17 | print(data) 18 | a={'a':2 ,'b':6} 19 | # return 20 | # # return 'err',400 21 | # return {'msg':'你删除了id={}的账号'.format(data.id)} 22 | return {'msg':'GET信息','data':data} 23 | 24 | def post(self, request, data=None): 25 | print('test', data) 26 | return {'msg':'POST信息','data':data} 27 | -------------------------------------------------------------------------------- /db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcloth/api-shop/6cae15e057172b33ec7d9b3317d033e7ceae71e1/db.sqlite3 -------------------------------------------------------------------------------- /demo_bottle.py: -------------------------------------------------------------------------------- 1 | from bottle import route, run, template, request,HTTPResponse 2 | from src.api_shop import ApiShop 3 | 4 | 5 | conf = [ 6 | { 7 | 'url': 'weixin/login', 8 | 'class': 'business_code.test.abc.api_login', 9 | 'name': '微信账户登录', 10 | 'methods': { 11 | 'POST': [ 12 | {'name': 'username', 'type': str, 'required': True, 13 | 'min': 3, 'max': 24, 'description': '用户名'}, 14 | {'name': 'ddd', 'type': str, 'description': '日期'}, 15 | ] 16 | } 17 | }, 18 | { 19 | 'url': 'weixin//', 20 | 'class': 'business_code.views.test', 21 | 'name': '账户登录', 22 | 'methods': { 23 | 24 | 'POST': [{'name': 'id', 'type': bool, 'required': True, 25 | 'description': '用户id'}, 26 | {'name': 'name', 'type': str, 'min':4,'required': True, 27 | 'description': '用户name'}, 28 | ], 29 | } 30 | }, 31 | 32 | 33 | ] 34 | 35 | 36 | af = ApiShop(conf, 37 | { 38 | 'lang': 'zh', 39 | 'name_classification': ['微信', '账户'], 40 | 'url_classification': ['weixin', 'login'], 41 | 'auto_create_folder': True, # 自动创建文件夹 42 | 'auto_create_file': True, # 自动创建文件 43 | 'auto_create_class': True, # 自动创建类 44 | 'auto_create_method': True, # 自动创建方法 45 | 'framework':'bottle' 46 | }) 47 | 48 | from src.api_shop import get_api_result_json 49 | from business_code.test.abc import api_login 50 | # get_api_result_json(api_class, method, data=None, request=None, not200=True) 51 | print('----->', get_api_result_json(api_login, 'POST')) 52 | 53 | @route('/api/',['GET','PUT','PATCH','DELETE','POST']) 54 | def api_index(url): 55 | print('*'*20,url) 56 | if url == 'document/': 57 | return af.render_documents(request, url) 58 | if url == 'api_data': 59 | return af.get_api_data(request, url) 60 | return af.api_entry(request, url) 61 | 62 | run(host='localhost', port=8080,reloader=True,debug=True) -------------------------------------------------------------------------------- /demo_django.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == '__main__': 6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_demo.settings') 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /django_demo/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcloth/api-shop/6cae15e057172b33ec7d9b3317d033e7ceae71e1/django_demo/__init__.py -------------------------------------------------------------------------------- /django_demo/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for django_demo project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.1. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.1/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '9u53xtwdz1tjxj1xxv4c0*d)btq0n0q8=w2l36xkl*6$ucx6!9' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = [] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | ] 41 | 42 | MIDDLEWARE = [ 43 | 'django.middleware.security.SecurityMiddleware', 44 | 'django.contrib.sessions.middleware.SessionMiddleware', 45 | 'django.middleware.common.CommonMiddleware', 46 | # 'django.middleware.csrf.CsrfViewMiddleware', 47 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 48 | 'django.contrib.messages.middleware.MessageMiddleware', 49 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 50 | ] 51 | 52 | ROOT_URLCONF = 'django_demo.urls' 53 | 54 | TEMPLATES = [ 55 | { 56 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 57 | 'DIRS': [], 58 | 'APP_DIRS': True, 59 | 'OPTIONS': { 60 | 'context_processors': [ 61 | 'django.template.context_processors.debug', 62 | 'django.template.context_processors.request', 63 | 'django.contrib.auth.context_processors.auth', 64 | 'django.contrib.messages.context_processors.messages', 65 | ], 66 | }, 67 | }, 68 | ] 69 | 70 | WSGI_APPLICATION = 'django_demo.wsgi.application' 71 | 72 | 73 | # Database 74 | # https://docs.djangoproject.com/en/2.1/ref/settings/#databases 75 | 76 | DATABASES = { 77 | 'default': { 78 | 'ENGINE': 'django.db.backends.sqlite3', 79 | 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), 80 | } 81 | } 82 | 83 | 84 | # Password validation 85 | # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators 86 | 87 | AUTH_PASSWORD_VALIDATORS = [ 88 | { 89 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 90 | }, 91 | { 92 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 93 | }, 94 | { 95 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 96 | }, 97 | { 98 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 99 | }, 100 | ] 101 | 102 | 103 | # Internationalization 104 | # https://docs.djangoproject.com/en/2.1/topics/i18n/ 105 | 106 | LANGUAGE_CODE = 'en-us' 107 | 108 | TIME_ZONE = 'UTC' 109 | 110 | USE_I18N = True 111 | 112 | USE_L10N = True 113 | 114 | USE_TZ = True 115 | 116 | 117 | # Static files (CSS, JavaScript, Images) 118 | # https://docs.djangoproject.com/en/2.1/howto/static-files/ 119 | 120 | STATIC_URL = '/static/' 121 | -------------------------------------------------------------------------------- /django_demo/urls.py: -------------------------------------------------------------------------------- 1 | """django_demo URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path, include 18 | 19 | urlpatterns = [ 20 | path('admin/', admin.site.urls), 21 | path('api/', include('business_code.urls')), 22 | ] 23 | -------------------------------------------------------------------------------- /django_demo/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for django_demo project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_demo.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /docs/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Api-Shop 11 | 12 | 13 | 14 | 15 |

404

There's nothing here.
Take me home
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/api-shop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcloth/api-shop/6cae15e057172b33ec7d9b3317d033e7ceae71e1/docs/api-shop.png -------------------------------------------------------------------------------- /docs/assets/img/back-to-top.8b37f773.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/assets/js/616.c8fc0ab0.js: -------------------------------------------------------------------------------- 1 | (self.webpackChunkdocuments=self.webpackChunkdocuments||[]).push([[616],{3616:(e,t,n)=>{"use strict";n.r(t),n.d(t,{default:()=>r});var o=n(6252),u=n(3577);const l={class:"theme-container"},a={class:"theme-default-content"},s=(0,o.Wm)("h1",null,"404",-1);var c=n(7621),m=n(1070);const h=(0,o.aZ)({name:"404",setup(){var e,t,n;const o=(0,c.I)(),u=(0,m.X6)(),l=null!=(e=u.value.notFound)?e:["Not Found"];return{getMsg:()=>l[Math.floor(Math.random()*l.length)],homeLink:null!=(t=u.value.home)?t:o.value,homeText:null!=(n=u.value.backToHome)?n:"Back to home"}}});h.render=function(e,t,n,c,m,h){const r=(0,o.up)("RouterLink");return(0,o.wg)(),(0,o.j4)("div",l,[(0,o.Wm)("div",a,[s,(0,o.Wm)("blockquote",null,(0,u.zw)(e.getMsg()),1),(0,o.Wm)(r,{to:e.homeLink},{default:(0,o.w5)((()=>[(0,o.Uk)((0,u.zw)(e.homeText),1)])),_:1},8,["to"])])])};const r=h}}]); -------------------------------------------------------------------------------- /docs/assets/js/640.757f6868.js.LICENSE.txt: -------------------------------------------------------------------------------- 1 | /* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress 2 | * @license MIT */ 3 | 4 | /*! 5 | * vue-router v4.0.10 6 | * (c) 2021 Eduardo San Martin Morote 7 | * @license MIT 8 | */ 9 | 10 | /*! medium-zoom 1.0.6 | MIT License | https://github.com/francoischalifour/medium-zoom */ 11 | -------------------------------------------------------------------------------- /docs/assets/js/app.fa19070d.js: -------------------------------------------------------------------------------- 1 | (self.webpackChunkdocuments=self.webpackChunkdocuments||[]).push([[143],{3131:(e,t,n)=>{"use strict";n.d(t,{g:()=>i});var o=n(952),a=n(6971),d=n(1598);const i=[o.Z,a.Z,d.Z]},9947:(e,t,n)=>{"use strict";n.d(t,{p:()=>o});const o=[n(3051).Z]},4611:(e,t,n)=>{"use strict";n.d(t,{l:()=>i});var o=n(8866),a=n(1263),d=n(6243);const i=[o.Z,a.Z,d.Z]},4150:(e,t,n)=>{"use strict";n.d(t,{Z:()=>a});var o=n(6252);const a={404:(0,o.RC)((()=>n.e(616).then(n.bind(n,3616)))),Layout:(0,o.RC)((()=>n.e(961).then(n.bind(n,2961))))}},6056:(e,t,n)=>{"use strict";n.d(t,{b:()=>a});var o=n(6252);const a={"v-8daa1a0e":(0,o.RC)((()=>n.e(509).then(n.bind(n,587)))),"v-093e9b42":(0,o.RC)((()=>n.e(494).then(n.bind(n,1611)))),"v-f785a3ee":(0,o.RC)((()=>n.e(18).then(n.bind(n,8144)))),"v-08e0d16d":(0,o.RC)((()=>n.e(300).then(n.bind(n,9976)))),"v-45b8d955":(0,o.RC)((()=>n.e(343).then(n.bind(n,2869)))),"v-1020f180":(0,o.RC)((()=>n.e(120).then(n.bind(n,2658)))),"v-4822d271":(0,o.RC)((()=>n.e(96).then(n.bind(n,5040)))),"v-712e14fc":(0,o.RC)((()=>n.e(681).then(n.bind(n,7101)))),"v-d72a4774":(0,o.RC)((()=>n.e(937).then(n.bind(n,7527)))),"v-3706649a":(0,o.RC)((()=>n.e(88).then(n.bind(n,1124))))}},9706:(e,t,n)=>{"use strict";n.d(t,{T:()=>o});const o={"v-8daa1a0e":()=>n.e(509).then(n.bind(n,6464)).then((({data:e})=>e)),"v-093e9b42":()=>n.e(494).then(n.bind(n,4815)).then((({data:e})=>e)),"v-f785a3ee":()=>n.e(18).then(n.bind(n,1143)).then((({data:e})=>e)),"v-08e0d16d":()=>n.e(300).then(n.bind(n,9574)).then((({data:e})=>e)),"v-45b8d955":()=>n.e(343).then(n.bind(n,9581)).then((({data:e})=>e)),"v-1020f180":()=>n.e(120).then(n.bind(n,1705)).then((({data:e})=>e)),"v-4822d271":()=>n.e(96).then(n.bind(n,9241)).then((({data:e})=>e)),"v-712e14fc":()=>n.e(681).then(n.bind(n,8044)).then((({data:e})=>e)),"v-d72a4774":()=>n.e(937).then(n.bind(n,4261)).then((({data:e})=>e)),"v-3706649a":()=>n.e(88).then(n.bind(n,1801)).then((({data:e})=>e))}},4634:(e,t,n)=>{"use strict";n.d(t,{g:()=>a});var o=n(4802);const a=[["v-8daa1a0e","/","",["/index.html","/README.md"]],["v-093e9b42","/components/Api.html","Api类",["/components/Api.md"]],["v-f785a3ee","/components/ApiShop.html","ApiShop类",["/components/ApiShop.md"]],["v-08e0d16d","/components/data_format.html","数据类型扩展",["/components/data_format.md"]],["v-45b8d955","/components/func.html","功能扩展",["/components/func.md"]],["v-1020f180","/components/","组件",["/components/index.html","/components/README.md"]],["v-4822d271","/components/SingleApiShop.html","SingleApiShop类",["/components/SingleApiShop.md"]],["v-712e14fc","/introduction/","",["/introduction/index.html","/introduction/README.md"]],["v-d72a4774","/start/","快速上手",["/start/index.html","/start/README.md"]],["v-3706649a","/404.html","",[]]].reduce(((e,[t,n,a,d])=>(e.push({name:t,path:n,component:o.Y,meta:{title:a}},...d.map((e=>({path:e,redirect:n})))),e)),[{name:"404",path:"/:catchAll(.*)",component:o.Y}])},5220:(e,t,n)=>{"use strict";n.d(t,{H:()=>o});const o={base:"/api-shop/",lang:"zh-CN",title:"Api-Shop",description:"一个快速易用的restful-api接口工具包,兼容:django / flask / bottle。",head:[["script",{},'var _hmt = _hmt || [];\n (function() {\n var hm = document.createElement("script");\n hm.src = "https://hm.baidu.com/hm.js?55405a77e01b4d671e84dd45880905a2";\n var s = document.getElementsByTagName("script")[0];\n s.parentNode.insertBefore(hm, s);\n })();']],locales:{}}},2232:(e,t,n)=>{"use strict";n.d(t,{f:()=>o});const o={repo:"pcloth/api-shop",logo:"https://pcloth.gitee.io/api-shop/logo.png",contributors:!1,navbar:[{text:"首页",link:"/"},{text:"介绍",link:"/introduction/"},{text:"快速上手",link:"/start/"},{text:"组件",children:["/components/ApiShop.md","/components/SingleApiShop.md","/components/Api.md","/components/func.md","/components/data_format.md"]}],locales:{"/":{selectLanguageName:"English"}},darkMode:!0,selectLanguageText:"Languages",selectLanguageAriaLabel:"Select language",sidebar:"auto",sidebarDepth:2,editLink:!0,editLinkText:"Edit this page",lastUpdated:!0,lastUpdatedText:"Last Updated",contributorsText:"Contributors",notFound:["There's nothing here.","How did we get here?","That's a Four-Oh-Four.","Looks like we've got some broken links."],backToHome:"Take me home",openInNewWindow:"open in new window"}}},e=>{"use strict";e.O(0,[460,640],(()=>(8463,e(e.s=8463)))),e.O()}]); -------------------------------------------------------------------------------- /docs/assets/js/runtime~app.6da47921.js: -------------------------------------------------------------------------------- 1 | (()=>{"use strict";var e,r,t,o={},a={};function n(e){var r=a[e];if(void 0!==r)return r.exports;var t=a[e]={exports:{}};return o[e].call(t.exports,t,t.exports,n),t.exports}n.m=o,e=[],n.O=(r,t,o,a)=>{if(!t){var s=1/0;for(l=0;l=a)&&Object.keys(n.O).every((e=>n.O[e](t[i])))?t.splice(i--,1):(d=!1,a0&&e[l-1][2]>a;l--)e[l]=e[l-1];e[l]=[t,o,a]},n.d=(e,r)=>{for(var t in r)n.o(r,t)&&!n.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce(((r,t)=>(n.f[t](e,r),r)),[])),n.u=e=>"assets/js/"+({18:"v-f785a3ee",88:"v-3706649a",96:"v-4822d271",120:"v-1020f180",300:"v-08e0d16d",343:"v-45b8d955",494:"v-093e9b42",509:"v-8daa1a0e",681:"v-712e14fc",937:"v-d72a4774"}[e]||e)+"."+{18:"7f618e88",88:"61f597b6",96:"377adebc",120:"fd82e958",300:"fd6e86c9",343:"dc25e0d5",494:"b5ee9f61",509:"d74e3c65",616:"c8fc0ab0",681:"943786c4",937:"5d1e65ea",961:"fd42e312"}[e]+".js",n.miniCssF=e=>"assets/css/styles.7805920a.css",n.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),r={},t="documents:",n.l=(e,o,a,s)=>{if(r[e])r[e].push(o);else{var d,i;if(void 0!==a)for(var l=document.getElementsByTagName("script"),c=0;c{d.onerror=d.onload=null,clearTimeout(v);var a=r[e];if(delete r[e],d.parentNode&&d.parentNode.removeChild(d),a&&a.forEach((e=>e(o))),t)return t(o)},v=setTimeout(f.bind(null,void 0,{type:"timeout",target:d}),12e4);d.onerror=f.bind(null,d.onerror),d.onload=f.bind(null,d.onload),i&&document.head.appendChild(d)}},n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.p="/api-shop/",(()=>{var e={523:0,460:0};n.f.j=(r,t)=>{var o=n.o(e,r)?e[r]:void 0;if(0!==o)if(o)t.push(o[2]);else if(/^(460|523)$/.test(r))e[r]=0;else{var a=new Promise(((t,a)=>o=e[r]=[t,a]));t.push(o[2]=a);var s=n.p+n.u(r),d=new Error;n.l(s,(t=>{if(n.o(e,r)&&(0!==(o=e[r])&&(e[r]=void 0),o)){var a=t&&("load"===t.type?"missing":t.type),s=t&&t.target&&t.target.src;d.message="Loading chunk "+r+" failed.\n("+a+": "+s+")",d.name="ChunkLoadError",d.type=a,d.request=s,o[1](d)}}),"chunk-"+r,r)}},n.O.j=r=>0===e[r];var r=(r,t)=>{var o,a,[s,d,i]=t,l=0;for(o in d)n.o(d,o)&&(n.m[o]=d[o]);if(i)var c=i(n);for(r&&r(t);l{"use strict";a.r(s),a.d(s,{data:()=>t});const t={key:"v-08e0d16d",path:"/components/data_format.html",title:"数据类型扩展",lang:"zh-CN",frontmatter:{title:"数据类型扩展"},excerpt:"",headers:[{level:2,title:"data_format 扩展",slug:"data-format-扩展",children:[{level:3,title:"data_format.datetime",slug:"data-format-datetime",children:[]},{level:3,title:"data_format.regex 正则格式校验",slug:"data-format-regex-正则格式校验",children:[]},{level:3,title:"内置更多的快速正则校验",slug:"内置更多的快速正则校验",children:[]},{level:3,title:"data_format.DataExpansion 自定义数据类型",slug:"data-format-dataexpansion-自定义数据类型",children:[]}]}],filePathRelative:"components/data_format.md",git:{updatedTime:1624630892e3}}},9976:(n,s,a)=>{"use strict";a.r(s),a.d(s,{default:()=>p});const t=(0,a(6252).uE)('

# data_format 扩展

数据格式扩展

  • data_format.datetime 可以用来填写在conf.methods参数列表中的type,将request参数中的字符串"2019-01-01 08:30:00"转换成datetime格式
  • data_format.DataExpansion 自定义扩展基础类

# data_format.datetime

from api_shop import data_format\nconf = [\n    {\n        'url': 'test',\n        'class': 'account.views.api_test',\n        'name': '测试方法',\n        'methods': {\n            'POST': [\n                {'name':'time', 'type': data_format.datetime, 'required': True, 'min': '2018-01-01', 'max': '2019-12-31', 'description': '时间'}\n            ],\n        }\n    },\n]\n
1
2
3
4
5
6
7
8
9
10
11
12
13

# 提交请求的时候,会把传入的time参数转换成datetime格式,并检查是否大于等于2018年1月1日,小于等于2019年12月31日。

# data_format.regex 正则格式校验

将对上传的字符串进行正则校验。 调用方法:

from src.api_shop import data_format\n\nconf = [\n    {\n        'url': 'test',\n        'class': 'business_code.views.api_test',\n        'name': '测试接口',\n        'methods': {\n            'POST': [\n                {'name': 'email', \n                    'type': data_format.regex(\n                        r'^[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+){0,4}@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+){0,4}$',name='邮箱'), \n                    'required': True, \n                    'min': 6, \n                    'max': 24, \n                    'description': '邮箱'\n                },\n                {'name':'idcard', 'type': data_format.idcard, 'required': True, 'description': '身份证'},\n            ]\n        }\n    },\n]\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# 内置更多的快速正则校验

  • data_format.numeric 数字型字符串
  • email 邮箱
  • data_format.chinese 中文
  • data_format.url url格式
  • data_format.cellphone 手机号码
  • data_format.idcard 身份证号码

# data_format.DataExpansion 自定义数据类型

from api_shop.data_format import DataExpansion\nfrom datetime import datetime as dt\n\nclass datetime(object,metaclass=DataExpansion):\n    '''将str转换成datetime格式'''\n    \n    class_name = "<class 'data_format.datetime'>"\n\n    def __new__(self, string):\n        if ':' in string:\n            return dt.strptime(string, '%Y-%m-%d %H:%M:%S')\n        else:\n            return dt.strptime(string, '%Y-%m-%d')\n
1
2
3
4
5
6
7
8
9
10
11
12
13

TIP

继承的时候请按照上面的写法,继承为元类,并直接用__new__方法来返回一个全新的格式

',13),p={render:function(n,s){return t}}}}]); -------------------------------------------------------------------------------- /docs/assets/js/v-093e9b42.b5ee9f61.js: -------------------------------------------------------------------------------- 1 | (self.webpackChunkdocuments=self.webpackChunkdocuments||[]).push([[494],{4815:(s,n,a)=>{"use strict";a.r(n),a.d(n,{data:()=>e});const e={key:"v-093e9b42",path:"/components/Api.html",title:"Api类",lang:"zh-CN",frontmatter:{title:"Api类"},excerpt:"",headers:[{level:2,title:"Api类",slug:"api类",children:[]},{level:2,title:"例子",slug:"例子",children:[]},{level:2,title:"methods类方法",slug:"methods类方法",children:[{level:3,title:"传入的 request 说明",slug:"传入的-request-说明",children:[]},{level:3,title:"传入的 data 说明",slug:"传入的-data-说明",children:[]}]},{level:2,title:"response_docs返回值文档",slug:"response-docs返回值文档",children:[]},{level:2,title:"ApiResponseModelFields",slug:"apiresponsemodelfields",children:[{level:3,title:"response_docs在文档中的表现",slug:"response-docs在文档中的表现",children:[]}]}],filePathRelative:"components/Api.md",git:{updatedTime:1624630892e3}}},1611:(s,n,a)=>{"use strict";a.r(n),a.d(n,{default:()=>p});const e=(0,a(6252).uE)('

# Api类

TIP

api业务代码父类,你写的业务代码都要继承它。

# 例子

from api_shop import Api, ApiResponseModelFields\n\nclass api_login(Api):\n    """用户账号登陆,这里的文字会被加载到文档里"""\n    response_docs = {\n        # 这个response_docs将会在文档中生成返回值说明文档\n        'get':{\n            'results':{PartyData},\n            'test':{\n                ApiResponseModelFields(PartyData,['id',PartyData.name]), # 使用部分字段,django必须使用这个类包裹才能引入部分字段,flask则可以直接使用字段名\n                'photos:Array:照片数据' # 手写字段文档\n                },\n            'user_party_info':{PartyUsers,'photos:Array:照片数据'} # PartyUsers模型的全部字段叠加部分其他字段\n        }\n    }\n\n    def get(self, request, data):\n        '''这是request的get方法'''\n        return \n\n    def post(self, request, data):\n        """ \n        这是request的post方法\n        data.username # 用户名\n        data.ddd # 日期\n        """\n        return {'msg':'登陆成功'}\n\n    def patch(self, request, data):\n        '''这是request的patch方法'''\n        return \n    \n    def delete(self, request, data):\n        '''这是request的delete方法'''\n        return \n    \n    def put(self, request, data):\n        '''这是request的put方法'''\n        return \n
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
31
32
33
34
35
36
37
38
39

返回值

  • 返回值可以为None,前端会收到一个200状态的空响应,例如 return None
  • 也可以指定状态码,比如 return {'msg':'错误信息'}, 400
  • 当然也可以直接返回其他数据 return 1 前端会收到一个body内容为1的响应

# methods类方法

TIP

  1. get 方法,对应的是request的get method
  2. post 方法,对应的是request的post method
  3. patch 方法,对应的是request的patch method
  4. delete 方法,对应的是request的delete method
  5. put 方法,对应的是request的put method

# 传入的 request 说明

  • 正常情况下,传入的request就是当前请求的reqeust

# 传入的 data 说明

  • data对象就是校验转换后的参数dict,数据内容可以通过属性访问,例如:data.name

# response_docs返回值文档

这个response_docs将会在文档中生成返回值说明文档

它的第一层是一个dict字典,key为method名。 第二层开始就是描述返回值的说明,可以直接使用orm的model类对象。 也可以使用ApiResponseModelFields类方法来包裹ORM的Model类,并描述需要的部分字段 甚至你可以使用用冒号分割的一个字符串描述'photos:Array:照片数据' # 手写字段文档

# ApiResponseModelFields

这个类可以捕捉ORM的数据模型里面的字段和描述信息,用来生成返回值文档

ApiResponseModelFields(Model,List)\n
1

TIP

ApiResponseModelFields初始化需要两个参数。 Model为你需要引用的ORM数据模型 List是包含了需要返回字段的列表,列表中可以是字符串key,也可以是Model.key (key为字段名)

# response_docs在文档中的表现

response_docs

',20),p={render:function(s,n){return e}}}}]); -------------------------------------------------------------------------------- /docs/assets/js/v-3706649a.61f597b6.js: -------------------------------------------------------------------------------- 1 | (self.webpackChunkdocuments=self.webpackChunkdocuments||[]).push([[88],{1801:(t,e,n)=>{"use strict";n.r(e),n.d(e,{data:()=>a});const a={key:"v-3706649a",path:"/404.html",title:"",lang:"zh-CN",frontmatter:{layout:"404"},excerpt:"",headers:[],filePathRelative:null,git:{}}},1124:(t,e,n)=>{"use strict";n.r(e),n.d(e,{default:()=>a});const a={render:function(t,e){return null}}}}]); -------------------------------------------------------------------------------- /docs/assets/js/v-45b8d955.dc25e0d5.js: -------------------------------------------------------------------------------- 1 | (self.webpackChunkdocuments=self.webpackChunkdocuments||[]).push([[343],{9581:(n,s,a)=>{"use strict";a.r(s),a.d(s,{data:()=>p});const p={key:"v-45b8d955",path:"/components/func.html",title:"功能扩展",lang:"zh-CN",frontmatter:{title:"功能扩展"},excerpt:"",headers:[{level:2,title:"func.model模具相关",slug:"func-model模具相关",children:[{level:3,title:"将dict数据写到ORM模具",slug:"将dict数据写到orm模具",children:[]},{level:3,title:"将ORM实例数据转换成json",slug:"将orm实例数据转换成json",children:[]},{level:3,title:"将ORM实例列表转换成json列表",slug:"将orm实例列表转换成json列表",children:[]},{level:3,title:"loads和dumps使用例子",slug:"loads和dumps使用例子",children:[]}]}],filePathRelative:"components/func.md",git:{updatedTime:1624630892e3}}},2869:(n,s,a)=>{"use strict";a.r(s),a.d(s,{default:()=>t});const p=(0,a(6252).uE)('

# func.model模具相关

# 将dict数据写到ORM模具

func.model.loads

loads方法接受两个参数:

  1. model_obj ORM模具对象实例
  2. dict_data 需要更新的dict数据
func.model.loads(user,{'nickname':'新昵称','email':'aa@gmail.com'})\n
1

上面这句代码等同于下面这一段

user.nickname = '新昵称'\nuser.email = 'aa@gmail.com'\n
1
2

这个用来给post更新方法,非常的便捷。

# 将ORM实例数据转换成json

func.model.dumps

dumps 方法接收参数如下:

  1. model_obj 数据模具
  2. exclude 不处理列表中的字段

    默认值 exclude = ['_sa_instance_state', 'password_hash', '_state', 'password'] 默认不处理输出密码字段

  3. include 只输出列表中的字段
  4. string 直接转换成string格式
  5. func 默认输出模具类中的属性方法

# 将ORM实例列表转换成json列表

func.model.models_to_list

等同于[func.model.dumps(x) for x in models]

# loads和dumps使用例子

from api_shop import func,Api\nfrom models import Users\nclass users(Api):\n    def get(self, request, data):\n        user = Users.query.filter(Users.id == data.id).first()\n        ret = func.models.dumps(user) # 将ORM的数据转换成json格式,方便前端使用。\n        print(ret)\n        return {'user':ret}\n    def post(self, request, data):\n        user = Users.query.filter(Users.id == data.id).first()\n\n        func.models.loads(user, data) # 将data包含的数据附加到user上\n\n        user.save()\n\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
',13),t={render:function(n,s){return p}}}}]); -------------------------------------------------------------------------------- /docs/assets/js/v-4822d271.377adebc.js: -------------------------------------------------------------------------------- 1 | (self.webpackChunkdocuments=self.webpackChunkdocuments||[]).push([[96],{9241:(n,s,a)=>{"use strict";a.r(s),a.d(s,{data:()=>p});const p={key:"v-4822d271",path:"/components/SingleApiShop.html",title:"SingleApiShop类",lang:"zh-CN",frontmatter:{title:"SingleApiShop类"},excerpt:"",headers:[{level:2,title:"初始化",slug:"初始化",children:[]},{level:2,title:"在其他地方引入",slug:"在其他地方引入",children:[]}],filePathRelative:"components/SingleApiShop.md",git:{updatedTime:null}}},5040:(n,s,a)=>{"use strict";a.r(s),a.d(s,{default:()=>e});const p=(0,a(6252).uE)('

# 初始化

TIP

继承自 ApiShop 类,和它的功能一样,唯一区别就是 SingleApiShop 是一个全局单例类,不但保持单例,多次继承也可以保持最后一次单例

推荐只需要单例的项目中使用SingleApiShop代替ApiShop初始化

from api_shop import SingleApiShop\n\nclass GeteWayApi(SingleApiShop):\n    def before_running(self, **kwargs):\n        print('运行前钩子',kwargs)\n    def after_running(self, **kwargs):\n        print('运行后钩子',kwargs)\n\nap = GeteWayApi(conf,options)\n
1
2
3
4
5
6
7
8
9

# 在其他地方引入

from api_shop import SingleApiShop\n\n# 可以获得的GeteWayApi的实例ap,方便在其他代码中调用api接口\napi_instance = SingleApiShop.get_single_apishop()\n\n
1
2
3
4
5
',5),e={render:function(n,s){return p}}}}]); -------------------------------------------------------------------------------- /docs/assets/js/v-712e14fc.943786c4.js: -------------------------------------------------------------------------------- 1 | (self.webpackChunkdocuments=self.webpackChunkdocuments||[]).push([[681],{8044:(n,s,a)=>{"use strict";a.r(s),a.d(s,{data:()=>t});const t={key:"v-712e14fc",path:"/introduction/",title:"",lang:"zh-CN",frontmatter:{prev:{text:"首页",link:"/"},next:{text:"快速上手",link:"/start/"},lastUpdated:!0},excerpt:"",headers:[{level:2,title:"介绍",slug:"介绍",children:[{level:3,title:"它是如何工作的?",slug:"它是如何工作的",children:[]}]},{level:2,title:"生命周期",slug:"生命周期",children:[{level:3,title:"钩子的使用",slug:"钩子的使用",children:[]},{level:3,title:"钩子例子",slug:"钩子例子",children:[]},{level:3,title:"before_running 运行前",slug:"before-running-运行前",children:[]},{level:3,title:"after_running 运行后",slug:"after-running-运行后",children:[]}]}],filePathRelative:"introduction/README.md",git:{updatedTime:1624611183e3}}},7101:(n,s,a)=>{"use strict";a.r(s),a.d(s,{default:()=>p});const t=(0,a(6252).uE)('

# 介绍

api-shop是一个python库,它用来帮助使用django、flask或者bottle作为web框架的开发者,快速的进行restful-api开发。

# 它是如何工作的?

api-shop并不会直接接管web框架的路由控制器,它需要你在web框架的路由控制器中添加一个正则路由,并指向api-shop实例入口

比如,在Flask中,我们需要你这样引入

from api_shop from ApiShop\n\naf = ApiShop(\n    conf=[],\n    options={\n        'lang': 'zh',\n        'framework': 'flask',\n    })\n\n\n@app.route('/api/<regex("([\\s\\S]*)"):url>', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH'])\ndef hello_world(url):\n    if url == 'document/':\n        return af.render_documents(request, url)\n    if url == 'api_data':\n        return af.get_api_data(request, url)\n\n    return af.api_entry(request, url)\n
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

TIP

其中,document/这个路由是可以修改的,api_data这个路由确是需要固定的,它是为了默认的文档工具能够拿到接口数据。除非你自己重写文档页面。

# 生命周期

TIP

让你更加的专注到业务上,其他的事情交给api-shop

An image

# 钩子的使用

TIP

我们提供了before_running和after_running两个钩子,你只需要继承ApiShop并复写它们就可以使用 钩子函数可以返回一个response来替代原本将要返回的对象。

# 钩子例子

class CommonApi(ApiShop):\n    def before_running(self, **kwargs):\n        print('运行前钩子',kwargs)\n        if kwargs.get('key')=='addroute/mock':\n            return JsonResponse({'msg':'您没有权限'}, status=400)\n    def after_running(self, **kwargs):\n        print('运行后钩子',kwargs)\n\naf = CommonApi(conf, {\n    'framework': 'django',\n    'lang': 'zh'\n})\n
1
2
3
4
5
6
7
8
9
10
11
12

# before_running 运行前

参数名类型说明
request请求实例用户提交的请求实例
data请求参数对象(该对象继承于dict)ApiShop把请求参数解析后的数据对象,支持data.name的方法访问参数
model继承于Api类的业务对象接口将要调用的Api类
keystr用户当前引用到的url参数(在conf配置中的url参数)

# after_running 运行后

参数名类型说明
request请求实例用户提交的请求实例
responseresponse对象Api业务代码执行后,即将返回给用户的response对象
model继承于Api类的业务对象接口调用的Api类
keystr用户当前引用到的url参数(在conf配置中的url参数)
',18),p={render:function(n,s){return t}}}}]); -------------------------------------------------------------------------------- /docs/assets/js/v-8daa1a0e.d74e3c65.js: -------------------------------------------------------------------------------- 1 | (self.webpackChunkdocuments=self.webpackChunkdocuments||[]).push([[509],{6464:(i,e,l)=>{"use strict";l.r(e),l.d(e,{data:()=>t});const t={key:"v-8daa1a0e",path:"/",title:"",lang:"zh-CN",frontmatter:{home:!0,heroImage:"/logo.png",description:"这是一个Api控制模组,让你快速开始业务代码。",actions:[{text:"了解Api-shop →",link:"/introduction/",type:"primary"}],features:[{title:"简洁",details:"用一个json数据或者@add_api装饰器配置接口"},{title:"自动",details:"自动校验参数、自动转换参数格式、自动生成文档和mock工具"},{title:"省时",details:"简化业务代码,外部和内部均可复用。一切只为少加班。"}],footer:"MIT Licensed | Copyright © 2019-2021 Pcloth"},excerpt:"",headers:[{level:2,title:"核心功能:",slug:"核心功能",children:[]}],filePathRelative:"README.md",git:{updatedTime:1624611183e3}}},587:(i,e,l)=>{"use strict";l.r(e),l.d(e,{default:()=>o});const t=(0,l(6252).uE)('

PyPI PyPI - Downloads

# 核心功能:

  1. 配置化 api 生成。
  2. 自动校验 request 提交的数据,并转换成指定格式,支持:int,float,list,dict,set,tuple,bool、自定义格式转换器和正则格式校验
  3. 自动生成 api 文档,并提供一个 web 页面可供查询和 mock 数据演示。
  4. 兼容 django , flask , bottle (如果不指定框架,默认按这个顺序识别框架)
  5. 自动生成接口骨架文件功能 beta(请谨慎开启)。
  6. 多国语言支持,也支持自定义语言包。
  7. 文档热重载。
  8. 默认值支持方法函数。
  9. 支持 url 中包含参数,例如 /api/user/<id>,并且在配置 methods 参数的时候设置它的规则。
  10. 支持多 url 绑定一个接口
  11. 支持指定参数的可选项,例如:[1,4,7],收到这个列表之外的参数就会触发 bad_request
  12. 可以在代码中直接调用 Api 业务代码:instance.api_run
  13. 支持在 Api 类中定义 response_docs 来制作返回值文档,并支持模型字段引入;以及模型部分字段引入类:ApiResponseModelFields
  14. 添加了全局单例类 SingleApiShop,用来替代 ApiShop,优点是支持用户封装后覆盖单例
',3),o={render:function(i,e){return t}}}}]); -------------------------------------------------------------------------------- /docs/assets/js/v-f785a3ee.7f618e88.js: -------------------------------------------------------------------------------- 1 | (self.webpackChunkdocuments=self.webpackChunkdocuments||[]).push([[18],{1143:(n,s,a)=>{"use strict";a.r(s),a.d(s,{data:()=>t});const t={key:"v-f785a3ee",path:"/components/ApiShop.html",title:"ApiShop类",lang:"zh-CN",frontmatter:{title:"ApiShop类"},excerpt:"",headers:[{level:2,title:"初始化",slug:"初始化",children:[{level:3,title:"ApiShop 接口配置(conf)",slug:"apishop-接口配置-conf",children:[]},{level:3,title:"ApiShop 设置参数(options)",slug:"apishop-设置参数-options",children:[]}]},{level:2,title:"业务代码入口",slug:"业务代码入口",children:[]},{level:2,title:"调用其他接口",slug:"调用其他接口",children:[]},{level:2,title:"执行前钩子",slug:"执行前钩子",children:[]},{level:2,title:"执行后钩子",slug:"执行后钩子",children:[{level:3,title:"钩子例子",slug:"钩子例子",children:[]}]},{level:2,title:"路由装饰器",slug:"路由装饰器",children:[]}],filePathRelative:"components/ApiShop.md",git:{updatedTime:1646982377e3}}},8144:(n,s,a)=>{"use strict";a.r(s),a.d(s,{default:()=>p});const t=(0,a(6252).uE)('

# 初始化

TIP

api-shop 核心类,实例化后生成接口对象,你只需要访问实例的方法,就可以调用相应的业务代码。

ap = ApiShop(conf,options)\n
1

# ApiShop 接口配置(conf)

类型说明例子
urlstr/list接口访问的 url,支持 list 包裹多个 url,也支持<参数名>模式,url 中添加<参数名>的话,方法参数列表中必须要包含该参数,并指定required=Truelogin,user/<id>
classstr/Class 对象为字符串时,是Api接口继承对象的路径,为对象时就是该Api接口继承对象account.views.api_login
methodsdict这个对象包含了每个接口可以接受的方法和参数key 可以为 GET/POST/PUT/PATCH/PUT/DELETE 等 http 支持的方法

# methods 配置说明

在配置参数中或者@add_api 类装饰器添加

单个 methods 的 value 为一个 list 对象,其中描述了该方法支持的参数

类型说明例子
namestr参数名字username
typeobject参数类型,目前支持 int,float,list,dict,set,tuple,bool,也支持自定义类型和其他可以被转换的类型详情可以查看 data_format 扩展
requiredbool是否必要参数,如果为 True,用户请求中没有该参数将会被拦截缺省为False
minint参数的最小值(int)或者最小长度(str/list/set/tuple),用户提交小于或者短于这个值的参数,将会被拦截5
maxint参数的最大值(int)或者最大长度(str/list/set/tuple),用户提交大于或者超过长度的参数,将会被拦截5
default同类型参数方法比如 type=int 的一个参数,default=1 的时候,用户如果不提交参数,将会自动填充 1,如果 default 为一个方法或者类,将会自动填充运行/实例化结果
optionslist这是参数的可选项,用户提交的参数必须在可选项中,否则将会被拦截options=['a','c'] 表示用户只能提交该参数的值为 a 或者 c
descriptionstr参数的描述,用来给前端开发人员的参考说明

# ApiShop 设置参数(options)

参数名称类型默认值说明
base_urlstr/api/基础 url,用以组合给前端的 api url
bad_requestboolTrue参数 bad_request 如果是真,发生错误返回一个坏请求给前端,否则都返回 200 的 response,里面附带 status=error 和 msg 附带错误信息
bad_request_error_statusstr'error'默认的 bad_request 状态信息,bad_request=False 生效
documentstrdocument.html文档模板绝对路径
langstrenApiShop 的语言,默认为 en 英文,可选项为 zh 中文
debugboolTrue是否开启调试信息
auto_create_folderboolFalse自动创建文件夹(实验方法)
auto_create_fileboolFalse自动创建文件(实验方法)
auto_create_classboolFalse自动创建类(实验方法)
auto_create_methodboolFalse自动创建方法(实验方法)

# 业务代码入口

实例方法 api_entry

实例化 ApiShop 类之后,需要在 web 框架的路由中添加一个正则路由,好让 api 开头的路由,全部被 ApiShop 接管。

ap = ApiShop(conf,options)\n\napp_name='api'\n\nurlpatterns = [\n    path('api_data', ap.get_api_data, name='api_data'), # api文档需要的接口\n    path('document/', ap.render_documents, name='document'), #api文档渲染的路由\n    re_path(r'([\\s\\S]*)', ap.api_entry, name='index') # 接管api/下面其他的全部路由到api_entry入口方法\n]\n
1
2
3
4
5
6
7
8
9

# 调用其他接口

实例方法 api_run

从 1.12.0 版本开始,ApiShop 核心类提供了一个 api_run 的方法,用来在代码中运行另外一个 api 代码,方便复用。 这个方法将取代原本的 get_api_result_json 和 get_api_result_response

'''\n    request   直接传入当前request,\n    url       就是你想要访问的接口url\n    method    如果不传入,就是 = request.method\n    parameter 请求参数,如果不传入,就没有参数传入到api中\n    json      默认True返回json数据,False就会返回response\n'''\nresponse_json,code = ap.api_run(request, url, method=None, parameter={'a':1}, json=True)\n
1
2
3
4
5
6
7
8

# 执行前钩子

before_running

# 执行后钩子

after_running

# 钩子例子

class CommonApi(ApiShop):\n    def before_running(self, **kwargs):\n        print('运行前钩子',kwargs)\n        if kwargs.get('key')=='addroute/mock':\n            return JsonResponse({'msg':'您没有权限'}, status=400)\n    def after_running(self, **kwargs):\n        print('运行后钩子',kwargs)\n\naf = CommonApi(conf, {\n    'framework': 'django',\n    'lang': 'zh'\n})\n
1
2
3
4
5
6
7
8
9
10
11
12

# 路由装饰器

TIP

在 Api 继承对象前使用 @add_api 类装饰器添加路由和参数,这样就无须使用 conf 集中配置文件

@af.add_api(\n    name='装饰器模式接口',\n    url=['addroute/mock/<username>','addroute/test'],\n    methods={\n        'GET':[\n            {'name':'username','required':True, 'type': str, 'min': 3, 'max': 24, 'description': '用户名'},\n        ]\n    }\n)\nclass ApiMockTest(Api):\n    def get(self, request, data):\n        print('get',data)\n
1
2
3
4
5
6
7
8
9
10
11
12
',25),p={render:function(n,s){return t}}}}]); -------------------------------------------------------------------------------- /docs/components/SingleApiShop.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | SingleApiShop类 | Api-Shop 11 | 12 | 13 | 14 | 15 |

# 初始化

TIP

继承自 ApiShop 类,和它的功能一样,唯一区别就是 SingleApiShop 是一个全局单例类,不但保持单例,多次继承也可以保持最后一次单例

推荐只需要单例的项目中使用SingleApiShop代替ApiShop初始化

from api_shop import SingleApiShop
16 | 
17 | class GeteWayApi(SingleApiShop):
18 |     def before_running(self, **kwargs):
19 |         print('运行前钩子',kwargs)
20 |     def after_running(self, **kwargs):
21 |         print('运行后钩子',kwargs)
22 | 
23 | ap = GeteWayApi(conf,options)
24 | 
1
2
3
4
5
6
7
8
9

# 在其他地方引入

from api_shop import SingleApiShop
25 | 
26 | # 可以获得的GeteWayApi的实例ap,方便在其他代码中调用api接口
27 | api_instance = SingleApiShop.get_single_apishop()
28 | 
29 | 
1
2
3
4
5
30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /docs/components/func.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 功能扩展 | Api-Shop 11 | 12 | 13 | 14 | 15 |

# func.model模具相关

# 将dict数据写到ORM模具

func.model.loads

loads方法接受两个参数:

  1. model_obj ORM模具对象实例
  2. dict_data 需要更新的dict数据
func.model.loads(user,{'nickname':'新昵称','email':'aa@gmail.com'})
16 | 
1

上面这句代码等同于下面这一段

user.nickname = '新昵称'
17 | user.email = 'aa@gmail.com'
18 | 
1
2

这个用来给post更新方法,非常的便捷。

# 将ORM实例数据转换成json

func.model.dumps

dumps 方法接收参数如下:

  1. model_obj 数据模具
  2. exclude 不处理列表中的字段

    默认值 exclude = ['_sa_instance_state', 'password_hash', '_state', 'password'] 默认不处理输出密码字段

  3. include 只输出列表中的字段
  4. string 直接转换成string格式
  5. func 默认输出模具类中的属性方法

# 将ORM实例列表转换成json列表

func.model.models_to_list

等同于[func.model.dumps(x) for x in models]

# loads和dumps使用例子

from api_shop import func,Api
19 | from models import Users
20 | class users(Api):
21 |     def get(self, request, data):
22 |         user = Users.query.filter(Users.id == data.id).first()
23 |         ret = func.models.dumps(user) # 将ORM的数据转换成json格式,方便前端使用。
24 |         print(ret)
25 |         return {'user':ret}
26 |     def post(self, request, data):
27 |         user = Users.query.filter(Users.id == data.id).first()
28 | 
29 |         func.models.loads(user, data) # 将data包含的数据附加到user上
30 | 
31 |         user.save()
32 | 
33 | 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Api-Shop 11 | 12 | 13 | 14 | 15 |
Api-Shop

Api-Shop

一个快速易用的restful-api接口工具包,兼容:django / flask / bottle。

了解Api-shop →

简洁

用一个json数据或者@add_api装饰器配置接口

自动

自动校验参数、自动转换参数格式、自动生成文档和mock工具

省时

简化业务代码,外部和内部均可复用。一切只为少加班。

PyPI PyPI - Downloads

# 核心功能:

  1. 配置化 api 生成。
  2. 自动校验 request 提交的数据,并转换成指定格式,支持:int,float,list,dict,set,tuple,bool、自定义格式转换器和正则格式校验
  3. 自动生成 api 文档,并提供一个 web 页面可供查询和 mock 数据演示。
  4. 兼容 django , flask , bottle (如果不指定框架,默认按这个顺序识别框架)
  5. 自动生成接口骨架文件功能 beta(请谨慎开启)。
  6. 多国语言支持,也支持自定义语言包。
  7. 文档热重载。
  8. 默认值支持方法函数。
  9. 支持 url 中包含参数,例如 /api/user/<id>,并且在配置 methods 参数的时候设置它的规则。
  10. 支持多 url 绑定一个接口
  11. 支持指定参数的可选项,例如:[1,4,7],收到这个列表之外的参数就会触发 bad_request
  12. 可以在代码中直接调用 Api 业务代码:instance.api_run
  13. 支持在 Api 类中定义 response_docs 来制作返回值文档,并支持模型字段引入;以及模型部分字段引入类:ApiResponseModelFields
  14. 添加了全局单例类 SingleApiShop,用来替代 ApiShop,优点是支持用户封装后覆盖单例
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcloth/api-shop/6cae15e057172b33ec7d9b3317d033e7ceae71e1/docs/logo.png -------------------------------------------------------------------------------- /docs/responseDocs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcloth/api-shop/6cae15e057172b33ec7d9b3317d033e7ceae71e1/docs/responseDocs.png -------------------------------------------------------------------------------- /documents/docs/.vuepress/config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | base: '/api-shop/', 3 | // dest: '../docs', 4 | head: [ 5 | // 添加百度统计 6 | [ 7 | "script", 8 | {}, 9 | `var _hmt = _hmt || []; 10 | (function() { 11 | var hm = document.createElement("script"); 12 | hm.src = "https://hm.baidu.com/hm.js?55405a77e01b4d671e84dd45880905a2"; 13 | var s = document.getElementsByTagName("script")[0]; 14 | s.parentNode.insertBefore(hm, s); 15 | })();` 16 | ] 17 | ], 18 | lang: 'zh-CN', 19 | title: 'Api-Shop', 20 | description: '一个快速易用的restful-api接口工具包,兼容:django / flask / bottle。', 21 | themeConfig: { 22 | repo: 'pcloth/api-shop', 23 | logo: 'https://pcloth.gitee.io/api-shop/logo.png', 24 | contributors: false, 25 | navbar: [ 26 | { text: '首页', link: '/' }, 27 | { text: '介绍', link: '/introduction/' }, 28 | { text: '快速上手', link: '/start/' }, 29 | { 30 | text: '组件', children: [ 31 | '/components/ApiShop.md', 32 | '/components/SingleApiShop.md', 33 | '/components/Api.md', 34 | '/components/func.md', 35 | '/components/data_format.md', 36 | ] 37 | }, 38 | ] 39 | }, 40 | } -------------------------------------------------------------------------------- /documents/docs/.vuepress/public/api-shop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcloth/api-shop/6cae15e057172b33ec7d9b3317d033e7ceae71e1/documents/docs/.vuepress/public/api-shop.png -------------------------------------------------------------------------------- /documents/docs/.vuepress/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcloth/api-shop/6cae15e057172b33ec7d9b3317d033e7ceae71e1/documents/docs/.vuepress/public/logo.png -------------------------------------------------------------------------------- /documents/docs/.vuepress/public/responseDocs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcloth/api-shop/6cae15e057172b33ec7d9b3317d033e7ceae71e1/documents/docs/.vuepress/public/responseDocs.png -------------------------------------------------------------------------------- /documents/docs/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | home: true 3 | heroImage: /logo.png 4 | description: "这是一个Api控制模组,让你快速开始业务代码。" 5 | actions: 6 | - text: 了解Api-shop → 7 | link: /introduction/ 8 | type: primary 9 | features: 10 | - title: 简洁 11 | details: 用一个json数据或者@add_api装饰器配置接口 12 | - title: 自动 13 | details: 自动校验参数、自动转换参数格式、自动生成文档和mock工具 14 | - title: 省时 15 | details: 简化业务代码,外部和内部均可复用。一切只为少加班。 16 | footer: MIT Licensed | Copyright © 2019-2021 Pcloth 17 | --- 18 | 19 | ![PyPI](https://img.shields.io/pypi/v/api-shop?logo=api-shop) ![PyPI - Downloads](https://img.shields.io/pypi/dm/api-shop) 20 | 21 | ## **核心功能:** 22 | 23 | 1. 配置化 api 生成。 24 | 2. 自动校验 request 提交的数据,并转换成指定格式,支持:int,float,list,dict,set,tuple,bool、自定义格式转换器和正则格式校验 25 | 3. 自动生成 api 文档,并提供一个 web 页面可供查询和 mock 数据演示。 26 | 4. 兼容 `django` , `flask` , `bottle` (如果不指定框架,默认按这个顺序识别框架) 27 | 5. 自动生成接口`骨架文件`功能 beta(请谨慎开启)。 28 | 6. 多国语言支持,也支持自定义语言包。 29 | 7. 文档热重载。 30 | 8. 默认值支持方法函数。 31 | 9. 支持 url 中包含参数,例如 `/api/user/`,并且在配置 methods 参数的时候设置它的规则。 32 | 10. 支持多 url 绑定一个接口 33 | 11. 支持指定参数的可选项,例如:[1,4,7],收到这个列表之外的参数就会触发 bad_request 34 | 12. 可以在代码中直接调用 Api 业务代码:`instance.api_run` 35 | 13. 支持在 Api 类中定义 response_docs 来制作返回值文档,并支持模型字段引入;以及模型部分字段引入类:ApiResponseModelFields 36 | 14. 添加了全局单例类 SingleApiShop,用来替代 ApiShop,优点是支持用户封装后覆盖单例 37 | -------------------------------------------------------------------------------- /documents/docs/components/Api.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Api类 3 | --- 4 | 5 | ## Api类 6 | ::: tip 7 | api业务代码父类,你写的业务代码都要继承它。 8 | ::: 9 | 10 | ## 例子 11 | ``` python 12 | from api_shop import Api, ApiResponseModelFields 13 | 14 | class api_login(Api): 15 | """用户账号登陆,这里的文字会被加载到文档里""" 16 | response_docs = { 17 | # 这个response_docs将会在文档中生成返回值说明文档 18 | 'get':{ 19 | 'results':{PartyData}, 20 | 'test':{ 21 | ApiResponseModelFields(PartyData,['id',PartyData.name]), # 使用部分字段,django必须使用这个类包裹才能引入部分字段,flask则可以直接使用字段名 22 | 'photos:Array:照片数据' # 手写字段文档 23 | }, 24 | 'user_party_info':{PartyUsers,'photos:Array:照片数据'} # PartyUsers模型的全部字段叠加部分其他字段 25 | } 26 | } 27 | 28 | def get(self, request, data): 29 | '''这是request的get方法''' 30 | return 31 | 32 | def post(self, request, data): 33 | """ 34 | 这是request的post方法 35 | data.username # 用户名 36 | data.ddd # 日期 37 | """ 38 | return {'msg':'登陆成功'} 39 | 40 | def patch(self, request, data): 41 | '''这是request的patch方法''' 42 | return 43 | 44 | def delete(self, request, data): 45 | '''这是request的delete方法''' 46 | return 47 | 48 | def put(self, request, data): 49 | '''这是request的put方法''' 50 | return 51 | ``` 52 | 53 | ::: warning 返回值 54 | - 返回值可以为None,前端会收到一个200状态的空响应,例如 return None 55 | - 也可以指定状态码,比如 return {'msg':'错误信息'}, 400 56 | - 当然也可以直接返回其他数据 return 1 前端会收到一个body内容为1的响应 57 | ::: 58 | 59 | ## methods类方法 60 | ::: tip 61 | 1. get 方法,对应的是request的`get` method 62 | 2. post 方法,对应的是request的`post` method 63 | 3. patch 方法,对应的是request的`patch` method 64 | 4. delete 方法,对应的是request的`delete` method 65 | 5. put 方法,对应的是request的`put` method 66 | ::: 67 | 68 | ### 传入的 request 说明 69 | > - 正常情况下,传入的request就是当前请求的reqeust 70 | 71 | ### 传入的 data 说明 72 | > - data对象就是校验转换后的参数dict,数据内容可以通过属性访问,例如:data.name 73 | 74 | 75 | ## response_docs返回值文档 76 | 这个response_docs将会在文档中生成返回值说明文档 77 | > 它的第一层是一个dict字典,key为method名。 78 | > 第二层开始就是描述返回值的说明,可以直接使用orm的model类对象。 79 | > 也可以使用ApiResponseModelFields类方法来包裹ORM的Model类,并描述需要的部分字段 80 | > 甚至你可以使用用冒号分割的一个字符串描述`'photos:Array:照片数据'` # 手写字段文档 81 | 82 | ## ApiResponseModelFields 83 | 这个类可以捕捉ORM的数据模型里面的字段和描述信息,用来生成返回值文档 84 | ```py 85 | ApiResponseModelFields(Model,List) 86 | ``` 87 | ::: tip 88 | ApiResponseModelFields初始化需要两个参数。 89 | Model为你需要引用的ORM数据模型 90 | List是包含了需要返回字段的列表,列表中可以是字符串key,也可以是Model.key (key为字段名) 91 | ::: 92 | 93 | ### response_docs在文档中的表现 94 | ![response_docs](/responseDocs.png) 95 | -------------------------------------------------------------------------------- /documents/docs/components/ApiShop.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: ApiShop类 3 | --- 4 | 5 | ## 初始化 6 | 7 | ::: tip 8 | api-shop 核心类,实例化后生成接口对象,你只需要访问实例的方法,就可以调用相应的业务代码。 9 | ::: 10 | 11 | ```python 12 | ap = ApiShop(conf,options) 13 | ``` 14 | 15 | ### ApiShop 接口配置(conf) 16 | 17 | | 键 | 类型 | 说明 | 例子 | 18 | | ------- | -------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | 19 | | url | str/list | 接口访问的 url,支持 list 包裹多个 url,也支持<参数名>模式,url 中添加<参数名>的话,方法参数列表中必须要包含该参数,并指定`required=True` | `login`,`user/` | 20 | | class | str/Class 对象 | 为字符串时,是`Api接口继承对象`的路径,为对象时就是该`Api接口继承对象` | account.views.api_login | 21 | | methods | dict | 这个对象包含了每个接口可以接受的方法和参数 | key 可以为 GET/POST/PUT/PATCH/PUT/DELETE 等 http 支持的方法 | 22 | 23 | #### methods 配置说明 24 | 25 | ::: tip 在配置参数中或者@add_api 类装饰器添加 26 | 单个 methods 的 value 为一个 list 对象,其中描述了该方法支持的参数 27 | ::: 28 | | 键 | 类型 | 说明 | 例子 | 29 | | ----------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | 30 | | name | str | 参数名字 | username | 31 | | type | object | 参数类型,目前支持 int,float,list,dict,set,tuple,bool,也支持自定义类型和其他可以被转换的类型 | 详情可以查看 data_format 扩展 | 32 | | required | bool | 是否必要参数,如果为 True,用户请求中没有该参数将会被拦截 | 缺省为`False` | 33 | | min | int | 参数的最小值(int)或者最小长度(str/list/set/tuple),用户提交小于或者短于这个值的参数,将会被拦截 | 5 | 34 | | max | int | 参数的最大值(int)或者最大长度(str/list/set/tuple),用户提交大于或者超过长度的参数,将会被拦截 | 5 | 35 | | default | `同类型参数`、`方法`、`类` | 比如 type=int 的一个参数,default=1 的时候,用户如果不提交参数,将会自动填充 1,如果 default 为一个方法或者类,将会自动填充`运行`/`实例化`结果 | | 36 | | options | list | 这是参数的可选项,用户提交的参数必须在可选项中,否则将会被拦截 | options=['a','c'] 表示用户只能提交该参数的值为 a 或者 c | 37 | | description | str | 参数的描述,用来给前端开发人员的参考说明 | | 38 | 39 | ### ApiShop 设置参数(options) 40 | 41 | | 参数名称 | 类型 | 默认值 | 说明 | 42 | | ------------------------ | ---- | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | 43 | | base_url | str | /api/ | 基础 url,用以组合给前端的 api url | 44 | | bad_request | bool | True | 参数 bad_request 如果是真,发生错误返回一个坏请求给前端,否则都返回 200 的 response,里面附带 status=error 和 msg 附带错误信息 | 45 | | bad_request_error_status | str | 'error' | 默认的 bad_request 状态信息,bad_request=False 生效 | 46 | | document | str | document.html | 文档模板绝对路径 | 47 | | lang | str | en | ApiShop 的语言,默认为 en 英文,可选项为 zh 中文 | 48 | | debug | bool | True | 是否开启调试信息 | 49 | | use_gateway | bool | False | 是否启用网关模式,网关模式需要自行继承 SingleApiShop 类并处理执行钩子 before_running 和 after_running,参数错误等会调用 before_running,所有返回信息会调用 after_running | 50 | | docs_mock | bool | False | 文档中的接口测试是否自动填写随机值 | 51 | | auto_create_folder | bool | False | 自动创建文件夹(实验方法) | 52 | | auto_create_file | bool | False | 自动创建文件(实验方法) | 53 | | auto_create_class | bool | False | 自动创建类(实验方法) | 54 | | auto_create_method | bool | False | 自动创建方法(实验方法) | 55 | 56 | ## 业务代码入口 57 | 58 | ::: tip 实例方法 api_entry 59 | 实例化 ApiShop 类之后,需要在 web 框架的路由中添加一个正则路由,好让 api 开头的路由,全部被 ApiShop 接管。 60 | ::: 61 | 62 | ```python 63 | ap = ApiShop(conf,options) 64 | 65 | app_name='api' 66 | 67 | urlpatterns = [ 68 | path('api_data', ap.get_api_data, name='api_data'), # api文档需要的接口 69 | path('document/', ap.render_documents, name='document'), #api文档渲染的路由 70 | re_path(r'([\s\S]*)', ap.api_entry, name='index') # 接管api/下面其他的全部路由到api_entry入口方法 71 | ] 72 | ``` 73 | 74 | ## 调用其他接口 75 | 76 | ::: tip 实例方法 api_run 77 | 从 1.12.0 版本开始,ApiShop 核心类提供了一个 api_run 的方法,用来在代码中运行另外一个 api 代码,方便复用。 78 | 这个方法将取代原本的 get_api_result_json 和 get_api_result_response 79 | ::: 80 | 81 | ```python 82 | ''' 83 | request 直接传入当前request, 84 | url 就是你想要访问的接口url 85 | method 如果不传入,就是 = request.method 86 | parameter 请求参数,如果不传入,就没有参数传入到api中 87 | json 默认True返回json数据,False就会返回response 88 | ''' 89 | response_json,code = ap.api_run(request, url, method=None, parameter={'a':1}, json=True) 90 | ``` 91 | 92 | ## 执行前钩子 93 | 94 | before_running 95 | 96 | ## 执行后钩子 97 | 98 | after_running 99 | 100 | ### 钩子例子 101 | 102 | ```python 103 | class CommonApi(ApiShop): 104 | def before_running(self, **kwargs): 105 | print('运行前钩子',kwargs) 106 | if kwargs.get('key')=='addroute/mock': 107 | return JsonResponse({'msg':'您没有权限'}, status=400) 108 | def after_running(self, **kwargs): 109 | print('运行后钩子',kwargs) 110 | 111 | af = CommonApi(conf, { 112 | 'framework': 'django', 113 | 'lang': 'zh' 114 | }) 115 | ``` 116 | 117 | ## 路由装饰器 118 | 119 | ::: tip 120 | 在 Api 继承对象前使用 @add_api 类装饰器添加路由和参数,这样就无须使用 conf 集中配置文件 121 | ::: 122 | 123 | ```py 124 | @af.add_api( 125 | name='装饰器模式接口', 126 | url=['addroute/mock/','addroute/test'], 127 | methods={ 128 | 'GET':[ 129 | {'name':'username','required':True, 'type': str, 'min': 3, 'max': 24, 'description': '用户名'}, 130 | ] 131 | } 132 | ) 133 | class ApiMockTest(Api): 134 | def get(self, request, data): 135 | print('get',data) 136 | ``` 137 | -------------------------------------------------------------------------------- /documents/docs/components/README.md: -------------------------------------------------------------------------------- 1 | # 组件 2 | 3 | | 模块名字 | 功能说明 | 模块介绍 | 4 | | :------------------------ | :-----------------------------------------: | :----------------------------------------------------------------------------- | 5 | | ApiShop | api初始化类 | 用以加载conf和options | 6 | | api_run | ApiShop实例方法 | 用来在业务代码中运行别的接口(取代get_api_result_json和get_api_result_response) | 7 | | Api | 业务基础类 | 用来继承后写实际的业务代码 | 8 | | ApiResponseModelFields | 制作包含部分model字段的response返回对象文档 | 9 | | get_api_result_json | 直接调用业务类(后面版本将删除它) | 返回 json,status_code | 10 | | get_api_result_response | 直接调用业务类(后面版本将删除它) | 返回response | 11 | | data_format | 内置自定义数据格式 | data_format.datetime 可以将一个字符串转换成datetime格式 | 12 | | data_format.DataExpansion | 自定义数据基础类 | 用来写自定义数据格式 | 13 | 14 | 15 | 16 | ## ApiShop 类组件 17 | ::: tip 18 | api-shop核心类,实例化后生成接口对象,你只需要访问实例的方法,就可以调用相应的业务代码。 19 | ::: 20 | 21 | 22 | ```python 23 | ap = ApiShop(conf,options) 24 | ``` 25 | ### 代码中直接运行另一个api接口 26 | ::: tip 27 | 从1.12.0版本开始,ApiShop核心类提供了一个api_run的方法,用来在代码中运行另外一个api代码,方便复用。 28 | 这个方法将取代原本的get_api_result_json和get_api_result_response 29 | ::: 30 | ```python 31 | ''' 32 | request 直接传入当前request, 33 | url 就是你想要访问的接口url 34 | method 如果不传入,就是 = request.method 35 | parameter 请求参数,如果不传入,就没有参数传入到api中 36 | json 默认True返回json数据,False就会返回response 37 | ''' 38 | response_json,code = ap.api_run(request, url, method=None, parameter={'a':1}, json=True) 39 | print(response_json,code) 40 | 41 | # 或者直接在Api实例中使用 # Or use it directly in the Api instance 42 | class test_api(Api): 43 | def post(self, request, data): 44 | response_json,code = self..api_run(request, other_url, method=None, parameter={'a':1}, json=True) 45 | print(response_json,code) 46 | ``` 47 | ### ApiShop conf 说明 48 | #### ApiShop conf 例子 49 | ``` python 50 | conf = [ 51 | { 52 | 'url': 'login', 53 | 'class': 'account.views.api_login', 54 | 'name': '账户登录', 55 | 'methods': { 56 | 'POST': [ 57 | {'name':'username', 'type': str, 'required': True, 'min': 3, 'max': 24, 'description': '用户名'}, 58 | {'name':'password', 'type': str, 'required': True, 'min': 3, 'max': 24, 'description': '密码'}, 59 | ], 60 | 'GET':[] 61 | } 62 | }, 63 | ] 64 | 65 | ``` 66 | 67 | #### ApiShop conf 详细 68 | | 键 | 值类型 | 说明 | 69 | | :------ | :-------- | -----------------------------------------------------------------------------------------------: | 70 | | url | str,list | 接口的url地址,只需要填写相对地址,如果有多条url,可以配置成`list`。支持url参数:`/api/url/` | 71 | | class | str,class | 接口实际调用的业务类(继承至Api),可以是对象,也可以是引用地址 | 72 | | name | str | 接口的名字 | 73 | | methods | dict | 接口所能接收的methods:有GET POST DELETE PUT PATCH | 74 | 75 | ### ApiShop conf methods 说明 76 | #### ApiShop conf methods 详细 77 | | 键 | 值类型 | 说明 | 78 | | :---------- | :----------- | -----------------------------------------------------------------------------------------------------------------------------------------------------------------: | 79 | | name | str | 参数名,接收后在data.name | 80 | | type | class | str,int,float,bool,list,dict,tuple等等,也支持data_format.datetime时间格式,你也可以自定义一个类型转换器 | 81 | | required | bool | 是否是必要值 | 82 | | default | str,function | 当没有接收到时的默认值,注意,它也会被type所指定的类型转换器转换。当它是一个function时,如果没有收到请求参数,将会自动运行这个方法获取值,同时将不再进行类型转换。 | 83 | | min | int,str | 最小值/最小长度,为字符串时,会被type指定的类型转换器转换。 | 84 | | max | int,str | 最大值/最大长度,为字符串时,会被type指定的类型转换器转换。 | 85 | | description | str | 功能描述,给前端人员看文档的内容 | 86 | | options | list | 参数必须在这个列表中的值,例如:[1,4,7],收到这个列表之外的参数就会触发bad_request | 87 | 88 | 89 | ### ApiShop options 说明 90 | #### ApiShop options 例子 91 | ```python 92 | options = { 93 | 'base_url':'/api/', 94 | 'bad_request': True, 95 | 'document': BASE_DIR + '/api_shop/static/document.html', 96 | 'lang':'en', 97 | 'lang_pack':{} 98 | } 99 | ``` 100 | #### ApiShop options 详细 101 | | 键 | 值类型 | 默认值 | 说明 | 102 | | :----------------------- | :----------- | :------ | ---------------------------------------------------------------------------------------------------------------------: | 103 | | base_url | str | /api/ | 接口url前缀 | 104 | | bad_request | bool | True | 如果请求不合法,是否以坏请求方式返回;否则就是全部是200返回 | 105 | | bad_request_error_status | str,int,bool | 'error' | 如果bad_request参数设置为False,那么这个参数就会启用,会在坏请求里附带一个status='error'的信息,你可以自定义这个信息。 | 106 | | document | str(path) | 略 | 文档页面的html模板所在的路径,默认会有一个简易模板 | 107 | | lang | str | en | 多国语言支持,目前内置en, zh | 108 | | lang_pack | dict | 无 | 扩展语言包,如果你想让api-shop支持更多语言 | 109 | | name_classification | list | 无 | 用于默认的文档模板对接口名称进行过滤,便于查找 | 110 | | url_classification | list | 无 | 用于默认的文档模板对接口url进行过滤,便于查找。例子:'url_classification':['weixin','login'] | 111 | | auto_create_folder | bool | False | 自动创建文件夹,debug参数也必须为True才可以生效。 | 112 | | auto_create_file | bool | False | 自动创建文件,debug参数也必须为True才可以生效。 | 113 | | auto_create_class | bool | False | 自动创建类,debug参数也必须为True才可以生效。 | 114 | | auto_create_method | bool | False | 自动创建方法,debug参数也必须为True才可以生效。 | 115 | | framework | str | 无 | 手动指定框架,目前支持django、flask、bottle,如果不指定,将按顺序识别框架,如果同时安装了多个框架,请手动指定。 | 116 | | debug | bool | True | 加载api业务代码的时候,遇到错误抛出。 | 117 | 118 | ### ApiShop options lang_pack 说明 119 | #### ApiShop options lang_pack 例子 120 | ```python 121 | 'lang_pack':{ 122 | 'zh': { 123 | 'no attributes found': '没有找到属性:', 124 | 'not found in conf': '在conf参数中没找到方法: ', 125 | 'no such interface': '没有这个接口', 126 | 'is required': '是必要的', 127 | 'parameter': '参数', 128 | 'can not be empty': '不能为空', 129 | 'must be type': '必须是类型', 130 | 'minimum length': '最小长度', 131 | 'minimum value': '最小值', 132 | 'maximum length': '最大长度', 133 | 'maximum value': '最大值', 134 | 'The wrong configuration, methons must be loaded inside the list container.': '错误的配置,methons必须装的list容器内。', 135 | 'no such interface method': '这个接口没有这个method', 136 | 'Framework version is not compatible.': 'api-shop不支持当前框架版本。', 137 | 'Not support': '不支持', 138 | 'supported framework as follows:': '支持的框架如下:', 139 | 'Did not find the framework':'没找指定的框架,请安装', 140 | } 141 | } 142 | ``` 143 | 144 | 145 | 146 | 147 | ## 方法组件 148 | ### get_api_result_json 方法(将删除) 149 | ::: tip 150 | - 直接调用业务代码类,获取返回数据和状态码 151 | - 请注意:由于绕开了参数监测,所有参数都必须填写在data中,没有的用None填写 152 | ::: 153 | #### flask 例子 154 | ``` python 155 | from api_shop import get_api_result_json 156 | 157 | from views import api_login # 继承了Api类的业务代码 158 | 159 | @simple_page.route('/test') 160 | def hello_world(url): 161 | 162 | data = get_api_result_json( 163 | api_login, 'POST', {'name':'testuser','password':'12345'} 164 | ) 165 | print(data) # 这样在程序内部也能快速的调用业务代码。 166 | 167 | ``` 168 | ::: tip get_api_result_json 参数说明 169 | - get_api_result_json(api_class, method, data=None, request=None, not200=True) 170 | - 直接调用api代码,并拿到返回json 171 | - api_class 是业务api类的对象(不是实例) 172 | - method 是请求方法,str格式 173 | - data 是附加数据,dict格式 174 | - request=None 是当前request,如果method和request.method不相同,请自己封装一个适合业务代码用的request,如果业务代码不用reqeust,请不要传入。 175 | - not200=True 是允许status_code不等于200的结果,为False的时候,遇到200以外程序中断并抛错 176 | - return json,status_code 177 | ::: 178 | 179 | 180 | ### get_api_result_response 方法(将删除) 181 | ::: tip 182 | - 直接调用业务代码类,获取返回响应包response 183 | - 请注意:由于绕开了参数监测,所有参数都必须填写在data中,没有的用None填写 184 | ::: 185 | #### flask 例子 186 | ``` python 187 | from api_shop import get_api_result_response 188 | 189 | from views import api_login # 继承了Api类的业务代码 190 | 191 | @simple_page.route('/test') 192 | def hello_world(url): 193 | 194 | response = get_api_result_response( 195 | api_login, 'POST', {'name':'testuser','password':'12345'} 196 | ) 197 | print(response) # 这样在程序内部也能快速的调用业务代码。 198 | 199 | ``` 200 | ::: tip 201 | 和get_api_result_json方法是一样的,不过get_api_result_response的返回值是一个response 202 | ::: 203 | 204 | -------------------------------------------------------------------------------- /documents/docs/components/SingleApiShop.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: SingleApiShop类 3 | --- 4 | 5 | ## 初始化 6 | 7 | ::: tip 8 | 继承自 ApiShop 类,和它的功能一样,唯一区别就是 SingleApiShop 是一个全局单例类,不但保持单例,多次继承也可以保持最后一次单例 9 | 10 | `推荐只需要单例的项目中使用SingleApiShop代替ApiShop初始化` 11 | ::: 12 | 13 | ```python 14 | from api_shop import SingleApiShop 15 | 16 | class GeteWayApi(SingleApiShop): 17 | def before_running(self, **kwargs): 18 | print('运行前钩子',kwargs) 19 | def after_running(self, **kwargs): 20 | print('运行后钩子',kwargs) 21 | 22 | ap = GeteWayApi(conf,options) 23 | ``` 24 | 25 | ## 在其他地方引入 26 | 27 | ``` 28 | from api_shop import SingleApiShop 29 | 30 | # 可以获得的GeteWayApi的实例ap,方便在其他代码中调用api接口 31 | api_instance = SingleApiShop.get_single_apishop() 32 | 33 | ``` 34 | -------------------------------------------------------------------------------- /documents/docs/components/data_format.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 数据类型扩展 3 | --- 4 | ## data_format 扩展 5 | ::: tip 数据格式扩展 6 | - data_format.datetime 可以用来填写在conf.methods参数列表中的type,将request参数中的字符串`"2019-01-01 08:30:00"`转换成datetime格式 7 | - data_format.DataExpansion 自定义扩展基础类 8 | ::: 9 | 10 | ### data_format.datetime 11 | ``` python 12 | from api_shop import data_format 13 | conf = [ 14 | { 15 | 'url': 'test', 16 | 'class': 'account.views.api_test', 17 | 'name': '测试方法', 18 | 'methods': { 19 | 'POST': [ 20 | {'name':'time', 'type': data_format.datetime, 'required': True, 'min': '2018-01-01', 'max': '2019-12-31', 'description': '时间'} 21 | ], 22 | } 23 | }, 24 | ] 25 | ``` 26 | #### 提交请求的时候,会把传入的time参数转换成datetime格式,并检查是否大于等于2018年1月1日,小于等于2019年12月31日。 27 | 28 | ### data_format.regex 正则格式校验 29 | 将对上传的字符串进行正则校验。 30 | 调用方法: 31 | ```python 32 | from src.api_shop import data_format 33 | 34 | conf = [ 35 | { 36 | 'url': 'test', 37 | 'class': 'business_code.views.api_test', 38 | 'name': '测试接口', 39 | 'methods': { 40 | 'POST': [ 41 | {'name': 'email', 42 | 'type': data_format.regex( 43 | r'^[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+){0,4}@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+){0,4}$',name='邮箱'), 44 | 'required': True, 45 | 'min': 6, 46 | 'max': 24, 47 | 'description': '邮箱' 48 | }, 49 | {'name':'idcard', 'type': data_format.idcard, 'required': True, 'description': '身份证'}, 50 | ] 51 | } 52 | }, 53 | ] 54 | ``` 55 | 56 | ### 内置更多的快速正则校验 57 | - data_format.numeric 数字型字符串 58 | - email 邮箱 59 | - data_format.chinese 中文 60 | - data_format.url url格式 61 | - data_format.cellphone 手机号码 62 | - data_format.idcard 身份证号码 63 | 64 | 65 | 66 | 67 | ### data_format.DataExpansion 自定义数据类型 68 | ``` python 69 | from api_shop.data_format import DataExpansion 70 | from datetime import datetime as dt 71 | 72 | class datetime(object,metaclass=DataExpansion): 73 | '''将str转换成datetime格式''' 74 | 75 | class_name = "" 76 | 77 | def __new__(self, string): 78 | if ':' in string: 79 | return dt.strptime(string, '%Y-%m-%d %H:%M:%S') 80 | else: 81 | return dt.strptime(string, '%Y-%m-%d') 82 | ``` 83 | ::: tip 84 | 继承的时候请按照上面的写法,继承为元类,并直接用__new__方法来返回一个全新的格式 85 | ::: 86 | -------------------------------------------------------------------------------- /documents/docs/components/func.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: 功能扩展 3 | --- 4 | ## func.model模具相关 5 | 6 | ### 将dict数据写到ORM模具 7 | ::: tip func.model.loads 8 | loads方法接受两个参数: 9 | 1. model_obj ORM模具对象实例 10 | 2. dict_data 需要更新的dict数据 11 | ::: 12 | ```python 13 | func.model.loads(user,{'nickname':'新昵称','email':'aa@gmail.com'}) 14 | ``` 15 | 上面这句代码等同于下面这一段 16 | ```python 17 | user.nickname = '新昵称' 18 | user.email = 'aa@gmail.com' 19 | ``` 20 | 这个用来给post更新方法,非常的便捷。 21 | 22 | ### 将ORM实例数据转换成json 23 | ::: tip func.model.dumps 24 | dumps 方法接收参数如下: 25 | 1. model_obj 数据模具 26 | 2. exclude 不处理列表中的字段 27 | > 默认值 exclude = ['_sa_instance_state', 'password_hash', '_state', 'password'] 28 | > 默认不处理输出密码字段 29 | 3. include 只输出列表中的字段 30 | 4. string 直接转换成string格式 31 | 5. func 默认输出模具类中的属性方法 32 | ::: 33 | 34 | ### 将ORM实例列表转换成json列表 35 | ::: tip func.model.models_to_list 36 | 等同于[func.model.dumps(x) for x in models] 37 | ::: 38 | 39 | 40 | ### loads和dumps使用例子 41 | ```python 42 | from api_shop import func,Api 43 | from models import Users 44 | class users(Api): 45 | def get(self, request, data): 46 | user = Users.query.filter(Users.id == data.id).first() 47 | ret = func.models.dumps(user) # 将ORM的数据转换成json格式,方便前端使用。 48 | print(ret) 49 | return {'user':ret} 50 | def post(self, request, data): 51 | user = Users.query.filter(Users.id == data.id).first() 52 | 53 | func.models.loads(user, data) # 将data包含的数据附加到user上 54 | 55 | user.save() 56 | 57 | ``` 58 | 59 | -------------------------------------------------------------------------------- /documents/docs/introduction/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | prev: 3 | text: 首页 4 | link: / 5 | next: 6 | text: 快速上手 7 | link: /start/ 8 | lastUpdated: true 9 | --- 10 | 11 | ## 介绍 12 | 13 | api-shop 是一个 python 库,它用来帮助使用 django、flask 或者 bottle 作为 web 框架的开发者,快速的进行 restful-api 开发。 14 | 15 | ### 它是如何工作的? 16 | 17 | api-shop 并不会直接接管 web 框架的路由控制器,它需要你在 web 框架的路由控制器中添加一个正则路由,并指向 api-shop 实例入口 18 | 19 | 比如,在`Flask`中,我们需要你这样引入 20 | 21 | ```python 22 | from api_shop from ApiShop 23 | 24 | af = ApiShop( 25 | conf=[], 26 | options={ 27 | 'lang': 'zh', 28 | 'framework': 'flask', 29 | }) 30 | 31 | 32 | @app.route('/api/', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH']) 33 | def hello_world(url): 34 | if url == 'document/': 35 | return af.render_documents(request, url) 36 | if url == 'api_data': 37 | return af.get_api_data(request, url) 38 | 39 | return af.api_entry(request, url) 40 | ``` 41 | 42 | ::: tip 43 | 其中,`document/`这个路由是可以修改的,`api_data`这个路由确是需要固定的,它是为了默认的文档工具能够拿到接口数据。除非你自己重写文档页面。 44 | ::: 45 | 46 | ## 生命周期 47 | 48 | ::: tip 49 | 让你更加的专注到业务上,其他的事情交给 api-shop 50 | ::: 51 | ![An image](/api-shop.png) 52 | 53 | ### 钩子的使用 54 | 55 | ::: tip 56 | 我们提供了 before_running 和 after_running 两个钩子,你只需要继承 ApiShop 并复写它们就可以使用 57 | 钩子函数可以返回一个 response 来替代原本将要返回的对象。 58 | ::: 59 | 60 | ### 钩子例子 61 | 62 | ```python 63 | class CommonApi(ApiShop): 64 | def before_running(self, **kwargs): 65 | print('运行前钩子',kwargs) 66 | if kwargs.get('key')=='addroute/mock': 67 | return JsonResponse({'msg':'您没有权限'}, status=400) 68 | def after_running(self, **kwargs): 69 | print('运行后钩子',kwargs) 70 | 71 | af = CommonApi(conf, { 72 | 'framework': 'django', 73 | 'lang': 'zh' 74 | }) 75 | ``` 76 | 77 | ### before_running 运行前 78 | 79 | | 参数名 | 类型 | 说明 | 80 | | ------- | ------------------------------- | ----------------------------------------------------------------- | 81 | | request | 请求实例 | 用户提交的请求实例 | 82 | | data | 请求参数对象(该对象继承于 dict) | ApiShop 把请求参数解析后的数据对象,支持 data.name 的方法访问参数 | 83 | | model | 继承于 Api 类的业务对象 | 接口将要调用的 Api 类 | 84 | | key | str | 用户当前引用到的 url 参数(在 conf 配置中的 url 参数) | 85 | 86 | ### after_running 运行后 87 | 88 | | 参数名 | 类型 | 说明 | 89 | | -------- | ----------------------- | ------------------------------------------------------ | 90 | | request | 请求实例 | 用户提交的请求实例 | 91 | | response | response 对象 | Api 业务代码执行后,即将返回给用户的 response 对象 | 92 | | model | 继承于 Api 类的业务对象 | 接口调用的 Api 类 | 93 | | key | str | 用户当前引用到的 url 参数(在 conf 配置中的 url 参数) | 94 | 95 | ## api 网关封装 96 | 97 | ::: tip 98 | 1.14 版本之后提供了一个`SingleApiShop`的全局单例类,用户继承它之后,覆盖`before_running`和`after_running`用来统一处理接口前后内容。 99 | ::: 100 | -------------------------------------------------------------------------------- /documents/docs/start/README.md: -------------------------------------------------------------------------------- 1 | # 快速上手 2 | ## 安装 3 | ```sh 4 | sudo pip install api-shop 5 | ``` 6 | ## 引入 7 | ```python 8 | from api_shop from ApiShop,Api,data_format 9 | ``` 10 | ## ApiShop 初始化 11 | ``` python 12 | from api_shop import ApiShop 13 | conf = [ 14 | { 15 | 'url': 'user', 16 | 'class': 'account.api_class.UserApi', 17 | 'name': '账户接口', 18 | 'methods': { 19 | 'POST': [ 20 | {'name':'username', 'type': str, 'required': True, 'min': 3, 'max': 24, 'description': '用户名'}, 21 | {'name':'password', 'type': str, 'required': True, 'min': 3, 'max': 24, 'description': '密码'}, 22 | ], 23 | 'GET':[] 24 | } 25 | }, 26 | ] 27 | 28 | options = { 29 | 'lang': 'zh', 30 | } 31 | 32 | af = ApiShop(conf,options) 33 | 34 | ``` 35 | 36 | ### 例子 37 | 38 | #### django 39 | ``` python 40 | ap = ApiShop(conf,options) 41 | 42 | app_name='api' 43 | 44 | urlpatterns = [ 45 | path('api_data', ap.get_api_data, name='api_data'), # api文档需要的接口 46 | path('document/', ap.render_documents, name='document'), #api文档渲染的路由 47 | re_path(r'([\s\S]*)', ap.api_entry, name='index') # 接管api/下面其他的全部路由到api_entry入口方法 48 | ] 49 | ``` 50 | 51 | #### flask 52 | ``` python 53 | from flask import Flask,request,render_template_string 54 | 55 | from werkzeug.routing import BaseConverter 56 | 57 | from api_shop import ApiShop,Api 58 | 59 | class RegexConverter(BaseConverter): 60 | def __init__(self, map, *args): 61 | self.map = map 62 | self.regex = args[0] 63 | 64 | app = Flask(__name__) 65 | # 如果使用蓝图,添加正则处理器必须是在注册蓝图之前使用。 66 | app.url_map.converters['regex'] = RegexConverter 67 | 68 | conf = [ 69 | { 70 | 'url': 'login', 71 | 'class': 'api.views.api_login', 72 | 'name': '账户登录', 73 | 'methods': { 74 | 'POST': [ 75 | {'name':'username', 'type': str, 'required': True, 'min': 3, 'max': 24, 'description': '用户名'}, 76 | {'name':'password', 'type': str, 'required': True, 'min': 3, 'max': 24, 'description': '密码'}, 77 | ] 78 | } 79 | }, 80 | { 81 | 'url': 'test', 82 | 'class': 'api.views.test', 83 | 'name': '测试数据', 84 | 'methods': { 85 | 'GET':[{'name':'bb', 'type': int, 'required': True, 'min': 0, 'max': 100, 'description': '百分比','default':95},], 86 | 'POST': [ 87 | {'name':'add', 'type': str, 'required': True, 'min': 3, 'max': 24, 'description': '地址'}, 88 | {'name':'bb', 'type': int, 'required': True, 'min': 0, 'max': 100, 'description': '百分比','default':95}, 89 | {'name':'list', 'type': list, 'description': '列表'}, 90 | ], 91 | 'DELETE':[ 92 | {'name':'id', 'type': int, 'required': True, 'min': 1,'description': '编号'}, 93 | ] 94 | } 95 | }, 96 | 97 | ] 98 | 99 | 100 | af = ApiShop(conf) 101 | 102 | 103 | 104 | @app.route('/api/',methods=['GET', 'POST','PUT','DELETE','PATCH']) 105 | def hello_world(url): 106 | print(url) 107 | if url=='document/': 108 | return af.render_documents(request,url) 109 | if url=='api_data': 110 | return af.get_api_data(request,url) 111 | 112 | return af.api_entry(request,url) 113 | 114 | if __name__ == '__main__': 115 | app.run(host="0.0.0.0",debug=True) 116 | ``` 117 | 118 | 119 | #### bottle 120 | ```python 121 | from bottle import route, run, template, request,HTTPResponse 122 | from src.api_shop import ApiShop 123 | 124 | conf = [ 125 | { 126 | 'url': 'weixin/login', 127 | 'class': 'business_code.test.abc.api_login', 128 | 'name': '微信账户登录', 129 | 'methods': { 130 | 'POST': [ 131 | {'name': 'username', 'type': str, 'required': True, 132 | 'min': 3, 'max': 24, 'description': '用户名'}, 133 | {'name': 'ddd', 'type': str, 'description': '日期'}, 134 | ] 135 | } 136 | }, 137 | { 138 | 'url': 'weixin//', 139 | 'class': 'business_code.views.test', 140 | 'name': '账户登录', 141 | 'methods': { 142 | 143 | 'POST': [{'name': 'id', 'type': bool, 'required': True, 144 | 'description': '用户id'}, 145 | {'name': 'name', 'type': str, 'min':4,'required': True, 146 | 'description': '用户name'}, 147 | ], 148 | } 149 | }, 150 | ] 151 | 152 | af = ApiShop(conf, 153 | { 154 | 'lang': 'zh', 155 | 'name_classification': ['微信', '账户'], 156 | 'url_classification': ['weixin', 'login'], 157 | 'framework':'bottle' 158 | }) 159 | 160 | 161 | @route('/api/',['GET','PUT','PATCH','DELETE','POST']) 162 | def api_index(url): 163 | print('*'*20,url) 164 | if url == 'document/': 165 | return af.render_documents(request, url) 166 | if url == 'api_data': 167 | return af.get_api_data(request, url) 168 | return af.api_entry(request, url) 169 | 170 | run(host='localhost', port=8080,reloader=True,debug=True) 171 | ``` 172 | 173 | 174 | ## Api接口业务 175 | ::: tip Api类 176 | 我们提供了一个Api类,用来封装你的业务代码 177 | ::: 178 | 179 | ```python 180 | from api_shop import Api 181 | 182 | class UserApi(Api): 183 | '''这里是接口描述文档''' 184 | 185 | def get(self, request, data): 186 | '''查询用户''' 187 | return {'user_name': '测试用户'} 188 | 189 | def post(self, request, data): 190 | '''接受新增用户或者修改用户''' 191 | return {'status':'success'} 192 | 193 | def patch(self, request, data): 194 | '''这里是patch方法''' 195 | 196 | def delete(self, request, data): 197 | '''这里是删除方法''' 198 | return {'msg':'这是一个错误请求'}, 401 199 | 200 | def put(self, request, data): 201 | '''这里是put方法''' 202 | ``` 203 | 204 | ### methods方法 205 | 我们提供了一个快速的构建methods方法接口,如上面的代码所示,类方法get、post、patch、delete、put各自代表了请求的method方法 206 | 1. 类方法中直接返回一个dict对象,就可以让用户收到一个json的response。 207 | 2. 如果需要返回非200的response,则需要在return中标识出来 208 | -------------------------------------------------------------------------------- /documents/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "documents", 3 | "version": "1.0.0", 4 | "main": "index.js", 5 | "author": "pcloth", 6 | "license": "MIT", 7 | "scripts": { 8 | "docs:dev": "vuepress dev docs", 9 | "docs:build": "vuepress build docs" 10 | }, 11 | "devDependencies": { 12 | "vuepress": "^2.0.0-beta.19" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /documents/push.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF&PUSHD %~DP0 2 | rem 请将qingruan.tech项目和qingruan.admin项目放在同一目录 3 | %1 %2 4 | mshta vbscript:createobject("shell.application").shellexecute("%~s0","goto :runas","","runas",1)(window.close)&goto :eof 5 | :runas 6 | Powershell.exe -executionpolicy remotesigned -File %~dp0\push.ps1 7 | prase -------------------------------------------------------------------------------- /documents/push.ps1: -------------------------------------------------------------------------------- 1 | npm run docs:build 2 | sleep(1) 3 | del ..\docs -recurse 4 | sleep(1) 5 | echo d | xcopy .\docs\.vuepress\dist ..\docs /E 6 | -------------------------------------------------------------------------------- /flask_demo/__init__.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine 2 | from sqlalchemy.orm import scoped_session, sessionmaker 3 | from sqlalchemy.ext.declarative import declarative_base 4 | 5 | engine = create_engine('sqlite:////webroot/flask_demo/test.db', convert_unicode=True) 6 | db_session = scoped_session(sessionmaker(autocommit=False, 7 | autoflush=False, 8 | bind=engine)) 9 | Base = declarative_base() 10 | Base.query = db_session.query_property() 11 | 12 | def init_db(): 13 | # 在这里导入定义模型所需要的所有模块,这样它们就会正确的注册在 14 | # 元数据上。否则你就必须在调用 init_db() 之前导入它们。 15 | from flask_demo.models import User 16 | Base.metadata.create_all(bind=engine) -------------------------------------------------------------------------------- /flask_demo/models.py: -------------------------------------------------------------------------------- 1 | from . import Base 2 | from sqlalchemy import Column, Integer, String 3 | 4 | class User(Base): 5 | __tablename__ = 'users' 6 | id = Column(Integer, primary_key=True, comment='ID') 7 | name = Column(String(50), unique=True, comment='姓名') 8 | email = Column(String(120), unique=True) 9 | 10 | def __repr__(self): 11 | return '' % (self.name) -------------------------------------------------------------------------------- /flask_demo/test.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcloth/api-shop/6cae15e057172b33ec7d9b3317d033e7ceae71e1/flask_demo/test.db -------------------------------------------------------------------------------- /flask_demo/views.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | from flask import Blueprint, request 4 | 5 | from src.api_shop import ApiShop, Api, data_format,ApiDataClass 6 | 7 | 8 | simple_page = Blueprint('simple_page', __name__, 9 | template_folder='templates') 10 | 11 | conf = [ 12 | { 13 | 'url': 'weixin/login', 14 | 'class': 'business_code.test.abc.api_login', 15 | 'name': '微信账户登录', 16 | 'methods': { 17 | 'GET': [ 18 | {'name': 'username', 'type': str, 'required': True, 'description': '用户名',}, 19 | {'name': 'ddd', 'type': str, 'description': '日期',}, 20 | ] 21 | } 22 | }, 23 | { 24 | 'url': ['weixin','weixin//'], 25 | 'class': 'business_code.views.test', 26 | 'name': '账户登录', 27 | 'methods': { 28 | 'POST': [ 29 | {'name': 'id', 'type': str, 'description': '用户id'}, 30 | {'name': 'name', 'type': str, 'min': 4, 'required': True, 'description': '用户name'}, 31 | {'name': 'a', 'type': str, 'description': '参数A'}, 32 | ], 33 | } 34 | }, 35 | 36 | 37 | ] 38 | 39 | 40 | af = ApiShop(conf, 41 | { 42 | # 'lang': 'zh', 43 | 'name_classification': ['微信', '账户'], 44 | 'url_classification': ['weixin', 'login'], 45 | # 'auto_create_folder': True, # 自动创建文件夹 46 | # 'auto_create_file': True, # 自动创建文件 47 | # 'auto_create_class': True, # 自动创建类 48 | 'auto_create_method': True, # 自动创建方法 49 | 'framework': 'flask', 50 | # 'debug':False, 51 | }) 52 | 53 | from business_code.test.abc import api_login 54 | 55 | 56 | @simple_page.route('/api/', methods=['GET', 'POST', 'PUT', 'DELETE', 'PATCH']) 57 | def hello_world(url): 58 | if url == 'document/': 59 | return af.render_documents(request, url) 60 | if url == 'api_data': 61 | return af.get_api_data(request, url) 62 | 63 | return af.api_entry(request, url) 64 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django==2.2.17 2 | Flask==2.0.1 3 | Flask-Script==2.0.6 4 | SQLAlchemy==1.4.17 5 | Flask-SQLAlchemy-2.5.1 -------------------------------------------------------------------------------- /src/MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.MD 2 | recursive-include api_shop/ * 3 | recursive-include api_shop/static * -------------------------------------------------------------------------------- /src/README.MD: -------------------------------------------------------------------------------- 1 | # api-shop ![PyPI](https://img.shields.io/pypi/v/api-shop?logo=api-shop) ![PyPI - Downloads](https://img.shields.io/pypi/dm/api-shop) 2 | 3 | ## [Online Documents](https://pcloth.gitee.io/api-shop/) 4 | ## [在线文档](https://pcloth.gitee.io/api-shop/) 5 | 6 | > ### api-shop:一个易用的、快速的restful-api接口工具包,兼容:`django` / `flask` / `bottle`。 7 | > ### `一切只为少加班。` 8 | 9 | ## **demo 图片** 10 | 11 | ![demo](/static/demo.png) 12 | 13 | 14 | ## **核心功能:** 15 | 1. 配置化api生成。 16 | 2. 自动校验request提交的数据,并转换成指定格式,支持:int,float,list,dict,set,tuple,bool 17 | 3. 自动生成api文档,并提供一个web页面可供查询和mock数据演示。 18 | 4. 兼容 `django` , `flask` , `bottle` (如果不指定框架,默认按这个顺序识别框架) 19 | 5. 自动生成接口`骨架文件`功能(请谨慎开启)。 20 | 6. 自定义格式转换器,data_format.datetime格式转换类;'2019-01-18 23:25:25' to datetime 21 | 7. 支持正则格式校验。 22 | 8. 多国语言支持,也支持自定义语言包。 23 | 9. 文档热重载。 24 | 10. 默认值支持方法函数。 25 | 11. 支持url中包含参数,例如 `/api/user/`,并且在配置methods参数的时候设置它的规则。 26 | 12. 支持多url绑定一个接口 27 | 13. 支持指定参数的可选项,例如:[1,4,7],收到这个列表之外的参数就会触发bad_request 28 | 14. 可以在代码中直接调用Api业务代码:`api_run`(将移除`get_api_result_json`和`get_api_result_response`) 29 | 15. 支持在Api类中定义response_docs来制作返回值文档,并支持模型字段引入;以及模型部分字段引入类:ApiResponseModelFields 30 | 31 | 32 | 33 | ## [Online Documents](https://pcloth.gitee.io/api-shop/) 34 | ## [在线文档](https://pcloth.gitee.io/api-shop/) 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/api_shop.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: api-shop 3 | Version: 1.14.1 4 | Summary: RESTful api shop for django or flask or bottle 5 | Home-page: https://github.com/pcloth/api-shop 6 | Author: pcloth 7 | Author-email: pcloth@gmail.com 8 | Maintainer: pcloth 9 | Maintainer-email: pcloth@gmail.com 10 | License: BSD License 11 | Description: # api-shop ![PyPI](https://img.shields.io/pypi/v/api-shop?logo=api-shop) ![PyPI - Downloads](https://img.shields.io/pypi/dm/api-shop) 12 | 13 | ## [Online Documents](https://pcloth.gitee.io/api-shop/) 14 | ## [在线文档](https://pcloth.gitee.io/api-shop/) 15 | 16 | > ### api-shop:一个易用的、快速的restful-api接口工具包,兼容:`django` / `flask` / `bottle`。 17 | > ### `一切只为少加班。` 18 | 19 | ## **demo 图片** 20 | 21 | ![demo](/static/demo.png) 22 | 23 | 24 | ## **核心功能:** 25 | 1. 配置化api生成。 26 | 2. 自动校验request提交的数据,并转换成指定格式,支持:int,float,list,dict,set,tuple,bool 27 | 3. 自动生成api文档,并提供一个web页面可供查询和mock数据演示。 28 | 4. 兼容 `django` , `flask` , `bottle` (如果不指定框架,默认按这个顺序识别框架) 29 | 5. 自动生成接口`骨架文件`功能(请谨慎开启)。 30 | 6. 自定义格式转换器,data_format.datetime格式转换类;'2019-01-18 23:25:25' to datetime 31 | 7. 支持正则格式校验。 32 | 8. 多国语言支持,也支持自定义语言包。 33 | 9. 文档热重载。 34 | 10. 默认值支持方法函数。 35 | 11. 支持url中包含参数,例如 `/api/user/`,并且在配置methods参数的时候设置它的规则。 36 | 12. 支持多url绑定一个接口 37 | 13. 支持指定参数的可选项,例如:[1,4,7],收到这个列表之外的参数就会触发bad_request 38 | 14. 可以在代码中直接调用Api业务代码:`api_run`(将移除`get_api_result_json`和`get_api_result_response`) 39 | 15. 支持在Api类中定义response_docs来制作返回值文档,并支持模型字段引入;以及模型部分字段引入类:ApiResponseModelFields 40 | 41 | 42 | 43 | ## [Online Documents](https://pcloth.gitee.io/api-shop/) 44 | ## [在线文档](https://pcloth.gitee.io/api-shop/) 45 | 46 | 47 | 48 | Keywords: api-shop,Flask-RESTful,Django REST framework,RESTful 49 | Platform: all 50 | Classifier: Development Status :: 4 - Beta 51 | Classifier: Operating System :: OS Independent 52 | Classifier: Intended Audience :: Developers 53 | Classifier: License :: OSI Approved :: BSD License 54 | Classifier: Programming Language :: Python 55 | Classifier: Programming Language :: Python :: Implementation 56 | Classifier: Programming Language :: Python :: 3.5 57 | Classifier: Programming Language :: Python :: 3.6 58 | Classifier: Programming Language :: Python :: 3.7 59 | Classifier: Programming Language :: Python :: 3.8 60 | Classifier: Topic :: Software Development :: Libraries 61 | Description-Content-Type: text/markdown 62 | -------------------------------------------------------------------------------- /src/api_shop.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | MANIFEST.in 2 | README.MD 3 | setup.py 4 | api_shop/__init__.py 5 | api_shop/api_shop.py 6 | api_shop/autofill.py 7 | api_shop/data_format.py 8 | api_shop/i18n.py 9 | api_shop/url_parse.py 10 | api_shop.egg-info/PKG-INFO 11 | api_shop.egg-info/SOURCES.txt 12 | api_shop.egg-info/dependency_links.txt 13 | api_shop.egg-info/top_level.txt 14 | api_shop/func/__init__.py 15 | api_shop/func/model.py 16 | api_shop/static/document.html -------------------------------------------------------------------------------- /src/api_shop.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/api_shop.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | api_shop 2 | -------------------------------------------------------------------------------- /src/api_shop/__init__.py: -------------------------------------------------------------------------------- 1 | from .api_shop import * 2 | 3 | name = 'api_shop' 4 | __version__ = "1.14.1" 5 | -------------------------------------------------------------------------------- /src/api_shop/api_shop.egg-info/PKG-INFO: -------------------------------------------------------------------------------- 1 | Metadata-Version: 1.1 2 | Name: api-shop 3 | Version: 1.6.1 4 | Summary: RESTful api shop for django or flask 5 | Home-page: https://github.com/pcloth/api-shop 6 | Author: pcloth 7 | Author-email: pcloth@gmail.com 8 | License: BSD License 9 | Description-Content-Type: UNKNOWN 10 | Description: api-shop 11 | 12 | English Documents 13 | 14 | https://github.com/pcloth/api-shop/blob/master/README.EN.MD 15 | 16 | ====================================== 17 | 18 | 什么是api-shop:提供易用的、轻量化的restful-api接口工具包,基于django或者flask框架。 19 | 20 | **demo 图片** 21 | ------------- 22 | 23 | .. figure:: /static/demo.png 24 | :alt: demo 25 | 26 | demo 27 | **核心功能:** 28 | -------------- 29 | 30 | 1. 配置化api生成。 31 | 2. 自动校验request提交的数据,并转换成制定格式。 32 | 3. 自动生成api文档,并提供一个web页面可供查询和mock数据演示。 33 | 4. 兼容django 和 flask 34 | 5. 容器格式转换,data\_format.datetime格式转换类;'2019-01-18 23:25:25' 35 | to datetime 36 | 6. 自定义格式转换器 37 | 7. 多国语言支持。 38 | 8. 文档支持热重载。 39 | 40 | **更新记录:** 41 | -------------- 42 | > 2019-01-29 43 | > 44 | > ver 1.6.1 45 | - 文档支持热重载 46 | - 文档添加版本支持 47 | - 优化错误提示 48 | 49 | -------------- 50 | 51 | 2019-01-23 52 | 53 | ver 1.6.0 54 | 55 | - 添加多国语言支持,可以在options里指定语言或者扩展语言包。 56 | - 文档改进 57 | 58 | **用法:** 59 | ---------- 60 | 61 | 1. 安装: 62 | 63 | .. code:: sh 64 | 65 | sudo pip install api-shop 66 | 67 | 2. 引入: 68 | 69 | .. code:: python 70 | 71 | from api_shop from ApiShop,Api,data_format 72 | 73 | +------------+---------------+------------------------------+ 74 | | 模块名字 | 功能说明 | 模块介绍 | 75 | +============+===============+==============================+ 76 | | ApiShop | api初始化类 | 用以加载conf和options | 77 | +------------+---------------+------------------------------+ 78 | | Api | 业务继承类 | 用来继承后写实际的业务代码 | 79 | +------------+---------------+------------------------------+ 80 | 81 | 3. 初始化 \`\`\` python conf = [ { 'url': 'login', 'class': 82 | 'account.views.api\_login', 'name': '账户登录', 'methods': { 'POST': 83 | [ {'name':'username', 'type': str, 'required': True, 'min': 3, 'max': 84 | 24, 'description': '用户名'}, {'name':'password', 'type': str, 85 | 'required': True, 'min': 3, 'max': 24, 'description': '密码'}, ], 86 | 'GET':` <#section>`__ } }, ] 87 | 88 | \`\`\` > conf 配置说明 > 键 \| 值类型 \| 说明 :----------- \| 89 | :----------- \| -----------: url \| str \| 90 | 接口的url地址,只需要填写相对地址 class \| str,class \| 91 | 接口实际调用的业务类(继承至Api),可以是对象,也可以是引用地址 name \| 92 | str \| 接口的名字 methods \| dict \| 接口所能接收的methods:有GET POST 93 | DELETE PUT PATCH 94 | 95 | methods 配置说明 96 | 97 | +---------------+-----------+-------------------------------------------------------------------------------------------------------------+ 98 | | 键 | 值类型 | 说明 | 99 | +===============+===========+=============================================================================================================+ 100 | | name | str | 参数名,接收后在data.name | 101 | +---------------+-----------+-------------------------------------------------------------------------------------------------------------+ 102 | | type | class | str,int,float,bool,list,dict,tuple等等,也支持data\_format.datetime时间格式,你也可以自定义一个类型转换器 | 103 | +---------------+-----------+-------------------------------------------------------------------------------------------------------------+ 104 | | required | bool | 是否是必要值 | 105 | +---------------+-----------+-------------------------------------------------------------------------------------------------------------+ 106 | | default | str | 当没有接收到时的默认值,注意,它也会被type所指定的类型转换器转换。 | 107 | +---------------+-----------+-------------------------------------------------------------------------------------------------------------+ 108 | | min | int,str | 最小值/最小长度,为字符串时,会被type指定的类型转换器转换。 | 109 | +---------------+-----------+-------------------------------------------------------------------------------------------------------------+ 110 | | max | int,str | 最大值/最大长度,为字符串时,会被type指定的类型转换器转换。 | 111 | +---------------+-----------+-------------------------------------------------------------------------------------------------------------+ 112 | | description | str | 功能描述,给前端人员看文档的内容 | 113 | +---------------+-----------+-------------------------------------------------------------------------------------------------------------+ 114 | 115 | 4. 配置 116 | 117 | .. code:: python 118 | 119 | options = { 120 | 'base_url':'/api/', 121 | 'bad_request': True, 122 | 'document': BASE_DIR + '/api_shop/static/document.html', 123 | 'lang':'en', 124 | 'lang_pack':{} 125 | } 126 | 127 | options 配置说明 128 | 129 | +----------------+-------------+----------+---------------------------------------------------------------+ 130 | | 键 | 值类型 | 默认值 | 说明 | 131 | +================+=============+==========+===============================================================+ 132 | | base\_url | str | /api/ | 接口url前缀 | 133 | +----------------+-------------+----------+---------------------------------------------------------------+ 134 | | bad\_request | bool | True | 如果请求不合法,是否以坏请求方式返回;否则就是全部是200返回 | 135 | +----------------+-------------+----------+---------------------------------------------------------------+ 136 | | document | str(path) | 略 | 文档页面的html模板所在的路径,默认会有一个简易模板 | 137 | +----------------+-------------+----------+---------------------------------------------------------------+ 138 | | lang | str | en | 多国语言支持,目前内置en, zh | 139 | +----------------+-------------+----------+---------------------------------------------------------------+ 140 | | lang\_pack | dict | 无 | 扩展语言包,如果你想让api-shop支持更多语言 | 141 | +----------------+-------------+----------+---------------------------------------------------------------+ 142 | 143 | lang\_pack 语言包 144 | 145 | value 就是目标语言 146 | 147 | .. code:: python 148 | 149 | 'lang_pack':{ 150 | 'en': { 151 | 'django version error': 'Django version is not compatible', 152 | 'not flask or django': 'Currently only compatible with django and flask', 153 | 'no attributes found': 'No attributes found: ', 154 | 'not found in conf': 'Not found in conf: ', 155 | 'document template not found': 'Document template not found', 156 | 'no such interface': 'No such interface', 157 | 'is required': 'is required', 158 | 'parameter': 'Parameter', 159 | 'can not be empty': 'can not be empty', 160 | 'must be type': 'must be type', 161 | 'minimum length': 'minimum length', 162 | 'minimum value': 'minimum value', 163 | 'maximum length': 'maximum length', 164 | 'maximum value': 'maximum value', 165 | 'The wrong configuration, methons must be loaded inside the list container.': 'The wrong configuration, methons must be loaded inside the list container.', 166 | 'no such interface method': 'No such interface method', 167 | } 168 | } 169 | 170 | 1. 自定义格式转换器 171 | 172 | .. code:: python 173 | 174 | # 使用自定义格式转换器的时候,min和max也会自动加载这个转换器转换了进行比较 175 | from datetime import datetime as dt 176 | class datetime(): 177 | '''将str转换成datetime格式''' 178 | def __new__(self, string): 179 | if ':' in string: 180 | return dt.strptime(string, '%Y-%m-%d %H:%M:%S') 181 | else: 182 | return dt.strptime(string, '%Y-%m-%d') 183 | 184 | 例子 185 | ---- 186 | 187 | 1. `Django例子 `__ 188 | \`\`\`python ## urls.py from api\_shop import ApiShop 189 | 190 | 接口配置数据 191 | ------------ 192 | 193 | conf = [ { 'url': 'login', 'class': 'account.views.api\_login', 194 | #需要引入的api类,继承于上面说的Api接口类 'name': '账户登录', 'methods': 195 | { 'POST': [ {'name':'username', 'type': str, 'required': True, 'min': 3, 196 | 'max': 24, 'description': '用户名'}, {'name':'password', 'type': str, 197 | 'required': True, 'min': 3, 'max': 24, 'description': '密码'}, ] ## 198 | 这里可以插入更多的methods,比如GET,DELETE,POST,PATCH } }, ## 199 | 这里可以插入更多的api接口 200 | 201 | ] 202 | 203 | api-shop参数设置: 204 | ------------------ 205 | 206 | options = { 'base\_url':'/api/',# 基础url,用以组合给前端的api url 207 | 可默认 # 'document':BASE\_DIR+'/api\_shop/static/document.html', # 208 | 文档路由渲染的模板 可默认 'bad\_request':True, # 209 | 参数bad\_request如果是真,发生错误返回一个坏请求给前端,否则都返回200的response,里面附带status=error和msg附带错误信息 210 | 可默认 } 211 | 212 | ap = ApiShop(conf,options) 213 | 214 | app\_name='api' 215 | 216 | urlpatterns = [ path('api\_data', ap.get\_api\_data, name='api\_data'), 217 | # api文档需要的接口 path('document/', ap.render\_documents, 218 | name='document'), #api文档渲染的路由 re\_path(r'([]\*)', ap.api\_entry, 219 | name='index') # 接管api/下面其他的全部路由到api\_entry入口方法 ] 220 | 221 | \`\`\` 222 | 223 | .. code:: python 224 | 225 | ## account/views.py 226 | from api_shop from Api 227 | 228 | class api_login(Api): 229 | def post(self,request,data=None): 230 | '''api登陆接口,方便微信用户绑定账户''' 231 | username = data.username 232 | password = data.password 233 | user = authenticate(username=username, password=password) 234 | if user: 235 | login(request, user) 236 | token = TokenBackend.make_token(user).decode('utf-8') 237 | return JsonResponse({'status': 'success', 'msg': '执行成功', 'token': token}) 238 | 239 | return JsonResponse({'status': 'error', 'msg': '用户登录失败'}) 240 | 241 | 2. `flask例子 `__ 242 | \`\`\`python from flask import Flask,request,render\_template\_string 243 | 244 | from werkzeug.routing import BaseConverter 245 | 246 | from api\_shop import ApiShop,Api 247 | 248 | class RegexConverter(BaseConverter): def **init**\ (self, map, \*args): 249 | self.map = map self.regex = args[0] 250 | 251 | app = Flask(\ **name**) # 252 | 如果使用蓝图,添加正则处理器必须是在注册蓝图之前使用。 253 | app.url\_map.converters['regex'] = RegexConverter 254 | 255 | conf = [ { 'url': 'login', 'class': 'api.views.api\_login', 'name': 256 | '账户登录', 'methods': { 'POST': [ {'name':'username', 'type': str, 257 | 'required': True, 'min': 3, 'max': 24, 'description': '用户名'}, 258 | {'name':'password', 'type': str, 'required': True, 'min': 3, 'max': 24, 259 | 'description': '密码'}, ] } }, { 'url': 'test', 'class': 260 | 'api.views.test', 'name': '测试数据', 'methods': { 'GET':[{'name':'bb', 261 | 'type': int, 'required': True, 'min': 0, 'max': 100, 'description': 262 | '百分比','default':95},], 'POST': [ {'name':'add', 'type': str, 263 | 'required': True, 'min': 3, 'max': 24, 'description': '地址'}, 264 | {'name':'bb', 'type': int, 'required': True, 'min': 0, 'max': 100, 265 | 'description': '百分比','default':95}, {'name':'list', 'type': list, 266 | 'description': '列表'}, ], 'DELETE':[ {'name':'id', 'type': int, 267 | 'required': True, 'min': 1,'description': '编号'}, ] } }, 268 | 269 | ] 270 | 271 | af = ApiShop(conf) 272 | 273 | @app.route('/api/',methods=['GET', 'POST','PUT','DELETE','PATCH']) def 274 | hello\_world(url): print(url) if url=='document/': return 275 | af.render\_documents(request,url) if url=='api\_data': return 276 | af.get\_api\_data(request,url) 277 | 278 | :: 279 | 280 | return af.api_entry(request,url) 281 | 282 | if **name** == '**main**\ ': app.run(host="0.0.0.0",debug=True) \`\`\` 283 | 284 | Keywords: api-shop,Flask-RESTful,Django REST framework,RESTful 285 | Platform: all 286 | Classifier: Development Status :: 4 - Beta 287 | Classifier: Operating System :: OS Independent 288 | Classifier: Intended Audience :: Developers 289 | Classifier: License :: OSI Approved :: BSD License 290 | Classifier: Programming Language :: Python 291 | Classifier: Programming Language :: Python :: Implementation 292 | Classifier: Programming Language :: Python :: 2.7 293 | Classifier: Programming Language :: Python :: 3 294 | Classifier: Programming Language :: Python :: 3.4 295 | Classifier: Programming Language :: Python :: 3.5 296 | Classifier: Programming Language :: Python :: 3.6 297 | Classifier: Topic :: Software Development :: Libraries 298 | -------------------------------------------------------------------------------- /src/api_shop/api_shop.egg-info/SOURCES.txt: -------------------------------------------------------------------------------- 1 | MANIFEST.in 2 | setup.py 3 | api_shop/api_shop.egg-info/PKG-INFO 4 | api_shop/api_shop.egg-info/SOURCES.txt 5 | api_shop/api_shop.egg-info/dependency_links.txt 6 | api_shop/api_shop.egg-info/top_level.txt 7 | api_shop/static/document.html -------------------------------------------------------------------------------- /src/api_shop/api_shop.egg-info/dependency_links.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/api_shop/api_shop.egg-info/top_level.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/api_shop/autofill.py: -------------------------------------------------------------------------------- 1 | import os, re 2 | 3 | def __find_path(name): 4 | # 深度切分: 5 | deep = name.split('.') 6 | if len(deep)>=3: 7 | path = '/'.join(deep[:-1]) # 检查路径 8 | folder = '/'.join(deep[:-2]) # 第三个开始是文件夹 9 | filename = deep[-2] # 倒数第二个是文件名 10 | classname = deep[-1] # 最后一个是类 11 | elif len(deep)==2: 12 | path = deep[0] 13 | folder = None 14 | filename = deep[0] # 倒数第二个是文件名 15 | classname = deep[1] # 最后一个是类 16 | else: 17 | # 太短了,不创建文件 18 | return None, None, None, None 19 | return path, folder, filename, classname 20 | 21 | def auto_fill(thisconf, options): 22 | name = thisconf['class'] 23 | 24 | if type(name)!=str: 25 | # 直接传入的对象 26 | return False 27 | methods = thisconf['methods'] 28 | 29 | folder_flag = options.get('auto_create_folder') 30 | file_flag = options.get('auto_create_file') 31 | class_flag = options.get('auto_create_class') 32 | method_flag = options.get('auto_create_method') 33 | 34 | path, folder, filename, classname = __find_path(name) 35 | if not path: 36 | return False 37 | 38 | if folder_flag and (not os.path.exists(folder)): 39 | # 文件夹不存在, 40 | os.makedirs(folder) 41 | 42 | if file_flag and (not os.path.exists(path+'.py')): 43 | # 文件不存在 44 | create_file(path) 45 | 46 | try: 47 | exec('from {} import {}'.format('.'.join(path), classname)) 48 | except: 49 | # 类不存在 50 | if class_flag: 51 | create_class(path, classname, thisconf) 52 | else: 53 | return False 54 | 55 | return False 56 | 57 | # 将参数填充进文档注释 58 | def fill_method_args(method_): 59 | string = '' 60 | for d in method_: 61 | string += '\n data.{} # {}'.format(d.get('name'),d.get('description','')) 62 | return string 63 | 64 | # 检查并填充方法和参数备注 65 | def check_fill_methods(model, thisconf): 66 | name = thisconf['class'] 67 | if type(name)!=str: 68 | # 直接传入的对象 69 | return False 70 | methods = thisconf['methods'] 71 | addstring = '' 72 | 73 | for m in methods.keys(): 74 | key = m.lower() 75 | if not hasattr(model, key): 76 | # 没有指定方法 77 | addstring += ''' 78 | def {}(self, request, data): 79 | """ todo: 80 | api-shop automatically inserts code{} 81 | """ 82 | pass'''.format(key,fill_method_args(methods[m])) 83 | if not addstring: 84 | return False 85 | 86 | path, folder, filename, classname = __find_path(name) 87 | if not path: 88 | return False 89 | 90 | file = open(path + '.py', 'r', encoding='utf-8') 91 | content = file.read() 92 | file.close() 93 | pos = content.find("class {}(".format(classname)) 94 | if pos < 0: 95 | # 没找到类 96 | return False 97 | 98 | q = re.compile(r'(\n\S)', re.DOTALL) 99 | regex_ret = q.search(content[pos:]) 100 | # 如果是换行符后的非空字符,就在前面插入;否则就在文档最后插入 101 | pos = regex_ret.span()[0] + pos if regex_ret else len(content) - 1 102 | content = content[:pos] + addstring + content[pos:] 103 | file = open(path + '.py', 'w', encoding='utf-8') 104 | file.write(content) 105 | file.close() 106 | 107 | # 创建文件 108 | def create_file(path): 109 | file = open(path + '.py', 'a',encoding='utf-8') 110 | file.write(''' 111 | # -*- coding: utf-8 -*- 112 | #!/usr/bin/env python3 113 | # api-shop automatically inserts code 114 | 115 | from api_shop import Api 116 | 117 | ''') 118 | file.close() 119 | 120 | 121 | # 创建类 122 | def create_class(path, classname,thisconf): 123 | name = thisconf['name'] 124 | methods = thisconf['methods'] 125 | exe_path = '.'.join(thisconf['class'].split('.')[:-1]) 126 | try: 127 | exec('from {} import Api'.format(exe_path)) 128 | except: 129 | # print('Api未引入') 130 | file = open(path + '.py', 'r', encoding='utf-8') 131 | content = file.read() 132 | file.close() 133 | file = open(path + '.py', 'w', encoding='utf-8') 134 | file.write('# api-shop automatically inserts code\nfrom api_shop import Api\n\n' + content) 135 | file.close() 136 | file = open(path + '.py', 'a', encoding='utf-8') 137 | file.write(''' 138 | class {}(Api): 139 | """{}""" 140 | pass 141 | ''' .format(classname, name)) 142 | file.close() 143 | -------------------------------------------------------------------------------- /src/api_shop/data_format.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from datetime import datetime as dt 4 | from .api_shop import _ 5 | import re 6 | class DataExpansion(type): 7 | def __repr__(self): 8 | return self.class_name or "" 9 | 10 | class baseFormat(object, metaclass=DataExpansion): 11 | '''基础格式,方便动态继承''' 12 | 13 | class datetime(object, metaclass=DataExpansion): 14 | '''将str转换成datetime格式''' 15 | class_name = f"" 16 | def __new__(self, value): 17 | if ':' in value: 18 | return dt.strptime(value, '%Y-%m-%d %H:%M:%S') 19 | else: 20 | return dt.strptime(value, '%Y-%m-%d') 21 | @staticmethod 22 | def now(): 23 | return dt.now() 24 | 25 | def __regex__(self, value): 26 | '''用正则校验参数''' 27 | if self.func == 'search': 28 | ret = re.search(self.regex, value, self.flag) 29 | elif self.func == 'match': 30 | ret = re.match(self.regex, value, self.flag) 31 | else: 32 | ValueError(_('regular not have this func.')) 33 | if ret: 34 | return value 35 | raise ValueError(_('regular test failed.')) 36 | 37 | class regex(type, metaclass=DataExpansion): 38 | '''正则校验参数 39 | regex(reg, func = 'match') 40 | 参数: 41 | reg = 正则语法 42 | name = RegularCheck 你要求的参数格式说明 43 | func = 默认使用match方法校验 ,你可以改成match或者search 44 | flag = 正则参数 45 | ''' 46 | class_name = f"" 47 | def __new__(self, reg, name = 'RegularCheck', func = 'match', flag = 0): 48 | Hello = type('Hello', (baseFormat,), dict(__new__=__regex__)) 49 | Hello.regex = reg 50 | Hello.func = func 51 | Hello.flag = flag 52 | Hello.class_name = f"" 53 | return Hello 54 | 55 | 56 | numeric = regex(r'^\d+$', name='numeric') 57 | email = regex(r'^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$', name='email') 58 | chinese = regex(r'^[\u0391-\uFFE5]+$', name='chinese') 59 | url = regex(r'^[a-zA-Z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\s*)?$', name='url') 60 | cellphone = regex(r'^1\d{10}$', name='cellphone') 61 | idcard = regex(r'^([1-9]\d{5}[12]\d{3}(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])\d{3}[0-9xX])$', name='idcard') 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/api_shop/func/__init__.py: -------------------------------------------------------------------------------- 1 | from . import model -------------------------------------------------------------------------------- /src/api_shop/func/model.py: -------------------------------------------------------------------------------- 1 | import json, decimal 2 | from datetime import datetime, date 3 | 4 | def loads(model_obj, data_dict): 5 | '''将dict数据按key写入到模具的属性''' 6 | for key, value in data_dict.items(): 7 | if hasattr(model_obj, key) and getattr(model_obj, key) == value: 8 | # 数据相同不处理 9 | continue 10 | # 更新数据 11 | setattr(model_obj, key, value) 12 | 13 | def __to_dict__(model_obj, exclude=None, include=None, tojson=False, func=False): 14 | '''将当前数据表转换成dict格式 15 | # 不将列表中字段转出来 16 | exclude = ['_sa_instance_state', 'password_hash', '_state', 'password'] 17 | # 只将列表中的字段转出来,如果有这个参数时,例外参数失效, 18 | # 并且默认例外参数不也失效(除非指定了password才会被转出) 19 | include = [''] 20 | ''' 21 | default_exclude = ['_sa_instance_state', 'password_hash', '_state', 'password'] 22 | 23 | if exclude: 24 | default_exclude += exclude 25 | if not include: 26 | include = [] 27 | out_dict = {} 28 | dkeys = [] 29 | obj_dict_keys = model_obj.__dict__.keys() 30 | for key in dir(model_obj): 31 | if include: 32 | if key in include: 33 | dkeys.append(key) 34 | elif key in default_exclude: 35 | continue 36 | elif key in obj_dict_keys: 37 | dkeys.append(key) 38 | elif func and type(getattr(model_obj.__class__, key)) == property: 39 | # 如果是@property属性方法,也提取数据 40 | dkeys.append(key) 41 | for key in dkeys: 42 | value = getattr(model_obj, key) 43 | if tojson: 44 | # 转换到json格式 45 | if isinstance(value, datetime): 46 | value = value.strftime('%Y-%m-%d %H:%M:%S') 47 | elif isinstance(value, date): 48 | value = value.strftime('%Y-%m-%d') 49 | elif isinstance(value, decimal.Decimal): 50 | value = float(value) 51 | out_dict.update({key: value}) 52 | return out_dict 53 | 54 | def dumps(model_obj, exclude=None, include=None, string=False, func=True): 55 | ''' 56 | @model_obj 数据模具 57 | 58 | @exclude 不处理列表中的字段 59 | 60 | 默认值 exclude = ['_sa_instance_state', 'password_hash', '_state', 'password'] 61 | 默认不处理输出密码字段 62 | 63 | @include 只输出列表中的字段 64 | 65 | @string 直接转换成string格式 66 | 67 | @func 默认输出模具类中的属性方法 68 | ''' 69 | data = __to_dict__(model_obj, exclude=exclude, include=include,tojson=True, func=func) 70 | if string: 71 | return json.dumps(data) 72 | return data 73 | 74 | def models_to_list(models, exclude=None, include=None, string=False, func=True): 75 | ret = [] 76 | for model_obj in models: 77 | this = dumps(model_obj, exclude=exclude, include=include, string=string, func=func) 78 | ret.append(this) 79 | return ret -------------------------------------------------------------------------------- /src/api_shop/i18n.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | class i18n_init(): 4 | '''多国语言模块 5 | ''' 6 | lang = { 7 | 'en': { 8 | 'no attributes found': 'No attributes found: ', 9 | 'not found in conf': 'Not found in conf: ', 10 | 'document template not found': 'Document template not found', 11 | 'no such interface': 'No such interface', 12 | 'is required': 'is required', 13 | 'parameter': 'Parameter', 14 | 'can not be empty': 'can not be empty', 15 | 'must be type': 'must be type', 16 | 'minimum length': 'minimum length', 17 | 'minimum value': 'minimum value', 18 | 'maximum length': 'maximum length', 19 | 'maximum value': 'maximum value', 20 | 'The wrong configuration, methons must be loaded inside the list container.': 'The wrong configuration, methons must be loaded inside the list container.', 21 | 'no such interface method': 'No such interface method', 22 | 'Framework version is not compatible.': 'Framework version is not compatible.', 23 | 'Not support': 'Not support', 24 | 'supported framework as follows:': 'supported framework as follows:', 25 | 'Did not find the framework': 'Did not find the framework. Please install ', 26 | 'must be in the list of options': 'must be in the list of options', 27 | 'value is not string type number.':'value is not string type number.', 28 | }, 29 | 'zh': { 30 | 'no attributes found': '没有找到属性:', 31 | 'not found in conf': '在conf参数中没找到方法: ', 32 | 'no such interface': '没有这个接口', 33 | 'is required': '是必要的', 34 | 'parameter': '参数', 35 | 'can not be empty': '不能为空', 36 | 'must be type': '必须是类型', 37 | 'minimum length': '最小长度', 38 | 'minimum value': '最小值', 39 | 'maximum length': '最大长度', 40 | 'maximum value': '最大值', 41 | 'The wrong configuration, methons must be loaded inside the list container.': '错误的配置,methons必须装的list容器内。', 42 | 'no such interface method': '这个接口没有这个method', 43 | 'Framework version is not compatible.': 'api-shop不支持当前框架版本。', 44 | 'Not support': '不支持', 45 | 'supported framework as follows:': '支持的框架如下:', 46 | 'Did not find the framework': '没找指定的框架,请安装', 47 | 'must be in the list of options': '必须在可选项列表中', 48 | 'request.method and method are not equal': 'request.method和method不相等', 49 | 'api-shop return result is not success.': 'api-shop返回结果不成功。', 50 | 'value is not string type number.': '值不是字符串数字。', 51 | 'numeric': '字符串数字', 52 | 'email': '邮箱', 53 | 'chinese': '中文', 54 | 'cellphone': '手机号', 55 | 'idcard':'身份证', 56 | "Django's independent fields must use the ApiResponseModelFields class":'django的独立字段必须使用ApiResponseModelFields类', 57 | "Please use the ApiShop.api_run instance method instead of this method, this method will be removed in later versions!!":"请使用ApiShop.api_run实例方法替代本方法,后期版本将移除本方法!!", 58 | } 59 | } 60 | 61 | def __init__(self, lang_name): 62 | self.lang_name = lang_name 63 | 64 | def _(self, text): 65 | cur = self.lang.get(self.lang_name) 66 | if not cur: 67 | return text 68 | return cur.get(text) or text 69 | -------------------------------------------------------------------------------- /src/api_shop/url_parse.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | _rule_re = re.compile(r''' 4 | (?P[^<]*) # static rule data 5 | < 6 | (?: 7 | (?P[a-zA-Z_][a-zA-Z0-9_]*) # converter name 8 | (?:\((?P.*?)\))? # converter arguments 9 | \: # variable delimiter 10 | )? 11 | (?P[a-zA-Z_][a-zA-Z0-9_]*) # variable name 12 | > 13 | ''' , re.VERBOSE) 14 | 15 | def parse_rule(rule): 16 | pos = 0 17 | end = len(rule) 18 | do_match = _rule_re.match 19 | used_names = set() 20 | while pos < end: 21 | m = do_match(rule, pos) 22 | if m is None: 23 | break 24 | data = m.groupdict() 25 | if data['static']: 26 | yield None, None, data['static'] 27 | variable = data['variable'] 28 | converter = data['converter'] or 'default' 29 | if variable in used_names: 30 | raise ValueError('variable name %r used twice.' % variable) 31 | used_names.add(variable) 32 | yield converter, data['args'] or None, variable 33 | pos = m.end() 34 | if pos < end: 35 | remaining = rule[pos:] 36 | if '>' in remaining or '<' in remaining: 37 | raise ValueError('malformed url rule: %r' % rule) 38 | yield None, None, remaining 39 | 40 | -------------------------------------------------------------------------------- /src/build/lib/api_shop/__init__.py: -------------------------------------------------------------------------------- 1 | from .api_shop import * 2 | 3 | name = 'api_shop' 4 | __version__ = "1.14.1" 5 | -------------------------------------------------------------------------------- /src/build/lib/api_shop/data_format.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from datetime import datetime as dt 4 | from .api_shop import _ 5 | import re 6 | class DataExpansion(type): 7 | def __repr__(self): 8 | return self.class_name or "" 9 | 10 | class baseFormat(object, metaclass=DataExpansion): 11 | '''基础格式,方便动态继承''' 12 | 13 | class datetime(object, metaclass=DataExpansion): 14 | '''将str转换成datetime格式''' 15 | class_name = f"" 16 | def __new__(self, value): 17 | if ':' in value: 18 | return dt.strptime(value, '%Y-%m-%d %H:%M:%S') 19 | else: 20 | return dt.strptime(value, '%Y-%m-%d') 21 | @staticmethod 22 | def now(): 23 | return dt.now() 24 | 25 | def __regex__(self, value): 26 | '''用正则校验参数''' 27 | if self.func == 'search': 28 | ret = re.search(self.regex, value, self.flag) 29 | elif self.func == 'match': 30 | ret = re.match(self.regex, value, self.flag) 31 | else: 32 | ValueError(_('regular not have this func.')) 33 | if ret: 34 | return value 35 | raise ValueError(_('regular test failed.')) 36 | 37 | class regex(type, metaclass=DataExpansion): 38 | '''正则校验参数 39 | regex(reg, func = 'match') 40 | 参数: 41 | reg = 正则语法 42 | name = RegularCheck 你要求的参数格式说明 43 | func = 默认使用match方法校验 ,你可以改成match或者search 44 | flag = 正则参数 45 | ''' 46 | class_name = f"" 47 | def __new__(self, reg, name = 'RegularCheck', func = 'match', flag = 0): 48 | Hello = type('Hello', (baseFormat,), dict(__new__=__regex__)) 49 | Hello.regex = reg 50 | Hello.func = func 51 | Hello.flag = flag 52 | Hello.class_name = f"" 53 | return Hello 54 | 55 | 56 | numeric = regex(r'^\d+$', name='numeric') 57 | email = regex(r'^[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+$', name='email') 58 | chinese = regex(r'^[\u0391-\uFFE5]+$', name='chinese') 59 | url = regex(r'^[a-zA-Z]+://(\w+(-\w+)*)(\.(\w+(-\w+)*))*(\?\s*)?$', name='url') 60 | cellphone = regex(r'^1\d{10}$', name='cellphone') 61 | idcard = regex(r'^([1-9]\d{5}[12]\d{3}(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])\d{3}[0-9xX])$', name='idcard') 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /src/build/lib/api_shop/i18n.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | class i18n_init(): 4 | '''多国语言模块 5 | ''' 6 | lang = { 7 | 'en': { 8 | 'no attributes found': 'No attributes found: ', 9 | 'not found in conf': 'Not found in conf: ', 10 | 'document template not found': 'Document template not found', 11 | 'no such interface': 'No such interface', 12 | 'is required': 'is required', 13 | 'parameter': 'Parameter', 14 | 'can not be empty': 'can not be empty', 15 | 'must be type': 'must be type', 16 | 'minimum length': 'minimum length', 17 | 'minimum value': 'minimum value', 18 | 'maximum length': 'maximum length', 19 | 'maximum value': 'maximum value', 20 | 'The wrong configuration, methons must be loaded inside the list container.': 'The wrong configuration, methons must be loaded inside the list container.', 21 | 'no such interface method': 'No such interface method', 22 | 'Framework version is not compatible.': 'Framework version is not compatible.', 23 | 'Not support': 'Not support', 24 | 'supported framework as follows:': 'supported framework as follows:', 25 | 'Did not find the framework': 'Did not find the framework. Please install ', 26 | 'must be in the list of options': 'must be in the list of options', 27 | 'value is not string type number.':'value is not string type number.', 28 | }, 29 | 'zh': { 30 | 'no attributes found': '没有找到属性:', 31 | 'not found in conf': '在conf参数中没找到方法: ', 32 | 'no such interface': '没有这个接口', 33 | 'is required': '是必要的', 34 | 'parameter': '参数', 35 | 'can not be empty': '不能为空', 36 | 'must be type': '必须是类型', 37 | 'minimum length': '最小长度', 38 | 'minimum value': '最小值', 39 | 'maximum length': '最大长度', 40 | 'maximum value': '最大值', 41 | 'The wrong configuration, methons must be loaded inside the list container.': '错误的配置,methons必须装的list容器内。', 42 | 'no such interface method': '这个接口没有这个method', 43 | 'Framework version is not compatible.': 'api-shop不支持当前框架版本。', 44 | 'Not support': '不支持', 45 | 'supported framework as follows:': '支持的框架如下:', 46 | 'Did not find the framework': '没找指定的框架,请安装', 47 | 'must be in the list of options': '必须在可选项列表中', 48 | 'request.method and method are not equal': 'request.method和method不相等', 49 | 'api-shop return result is not success.': 'api-shop返回结果不成功。', 50 | 'value is not string type number.': '值不是字符串数字。', 51 | 'numeric': '字符串数字', 52 | 'email': '邮箱', 53 | 'chinese': '中文', 54 | 'cellphone': '手机号', 55 | 'idcard':'身份证', 56 | "Django's independent fields must use the ApiResponseModelFields class":'django的独立字段必须使用ApiResponseModelFields类', 57 | "Please use the ApiShop.api_run instance method instead of this method, this method will be removed in later versions!!":"请使用ApiShop.api_run实例方法替代本方法,后期版本将移除本方法!!", 58 | } 59 | } 60 | 61 | def __init__(self, lang_name): 62 | self.lang_name = lang_name 63 | 64 | def _(self, text): 65 | cur = self.lang.get(self.lang_name) 66 | if not cur: 67 | return text 68 | return cur.get(text) or text 69 | -------------------------------------------------------------------------------- /src/setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding=utf-8 3 | 4 | from setuptools import setup, find_packages 5 | import re, ast, pathlib 6 | 7 | 8 | 9 | _version_re = re.compile(r'__version__\s+=\s+(.*)') 10 | 11 | with open('api_shop/__init__.py', 'rb') as f: 12 | version = str(ast.literal_eval(_version_re.search( 13 | f.read().decode('utf-8') 14 | ).group(1))) 15 | 16 | 17 | setup( 18 | name='api-shop', 19 | version=version, 20 | description=( 21 | 'RESTful api shop for django or flask or bottle' 22 | ), 23 | long_description=pathlib.Path('README.MD').read_text(encoding='utf-8'), 24 | long_description_content_type='text/markdown', 25 | author='pcloth', 26 | author_email='pcloth@gmail.com', 27 | maintainer='pcloth', 28 | maintainer_email='pcloth@gmail.com', 29 | license='BSD License', 30 | packages=find_packages(), 31 | include_package_data=True, 32 | exclude_package_date={'':['.gitignore']}, 33 | keywords=['api-shop', 'Flask-RESTful', 'Django REST framework', 'RESTful'], 34 | platforms=["all"], 35 | url='https://github.com/pcloth/api-shop', 36 | classifiers=[ 37 | 'Development Status :: 4 - Beta', 38 | 'Operating System :: OS Independent', 39 | 'Intended Audience :: Developers', 40 | 'License :: OSI Approved :: BSD License', 41 | 'Programming Language :: Python', 42 | 'Programming Language :: Python :: Implementation', 43 | 'Programming Language :: Python :: 3.5', 44 | 'Programming Language :: Python :: 3.6', 45 | 'Programming Language :: Python :: 3.7', 46 | 'Programming Language :: Python :: 3.8', 47 | 'Topic :: Software Development :: Libraries' 48 | ], 49 | ) 50 | -------------------------------------------------------------------------------- /static/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pcloth/api-shop/6cae15e057172b33ec7d9b3317d033e7ceae71e1/static/demo.png --------------------------------------------------------------------------------