├── README.md ├── apis ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── base.cpython-36.pyc │ ├── enum.cpython-36.pyc │ ├── setting.cpython-36.pyc │ └── urls.cpython-36.pyc └── api_v1 │ ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── base.cpython-36.pyc │ ├── enum.cpython-36.pyc │ ├── setting.cpython-36.pyc │ └── urls.cpython-36.pyc │ ├── base.py │ ├── enum.py │ ├── setting.py │ ├── urls.py │ └── user │ ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── json_models.cpython-36.pyc │ ├── param_models.cpython-36.pyc │ ├── serializers.cpython-36.pyc │ └── views.cpython-36.pyc │ ├── serializers.py │ └── views.py ├── apps ├── __pycache__ │ └── __init__.cpython-36.pyc └── user │ ├── __init__.py │ ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── admin.cpython-36.pyc │ ├── apps.cpython-36.pyc │ ├── models.cpython-36.pyc │ ├── urls.cpython-36.pyc │ └── views.cpython-36.pyc │ ├── admin.py │ ├── apps.py │ ├── migrations │ ├── 0001_initial.py │ └── __pycache__ │ │ ├── 0001_initial.cpython-36.pyc │ │ └── __init__.cpython-36.pyc │ ├── models.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── attendances.xml ├── attendances ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── settings.cpython-36.pyc │ ├── urls.cpython-36.pyc │ ├── view.cpython-36.pyc │ └── wsgi.cpython-36.pyc ├── settings.py ├── urls.py └── wsgi.py ├── manage.py ├── requirements.txt └── test.py /README.md: -------------------------------------------------------------------------------- 1 | # Python3 开发以及部署 RESTful API项目(Python3 + Django2.0 + Django REST FrameWork + Centos7 + uWsgi + Nginx) 2 | 文档分为两个部分,分别从开发和部署两个方面先介绍项目流程,然后会说明项目中最常遇到的问题以及解决方案。如果项目中有什么处理不正确的地方,也希望大家给予指正、交流。 3 | 4 | 1. 开发环境采用Python3.6.3版本,项目采用Django2.0,Django REST FrameWork3.7.7去搭建。 5 | 6 | 2. 部署的时候,系统版本为Centos7,uWsgi版本使用本文发布时最新的2.0.15,Nginx版本1.13.7 7 | 8 | ## 第一部分 开发流程以及问题说明 9 | 由于项目本身基于 Token 的身份验证,另外有些接口不只是处理一个表里的数据,直接通过ORM不太好映射,所以并没有完全按照Django REST FrameWork进行开发,并且只采用GET和POST方法。 10 | 11 | ### 一、开发步骤 12 | 首先搭建开发环境并且创建一个新的项目。 13 | #### 1. 安装Python3:如果使用的是 Mac OS X ,系统可能已经预装了 Python 。我们可以通过homebrew安装Python3。 14 | ``` shell 15 | $ brew install python3 16 | ``` 17 | 安装了Python3之后,会有pip3,使用pip install XXX 新安装的库会放在这个目录下面python2.7/site-packages使用pip3 install XXX 新安装的库会放在这个目录下面python3.6/site-packages如果使用python3执行程序,那么就不能import python2.7/site-packages中的库 18 | #### 2. 安装Django和Django REST FrameWork:可以下载django最新版本,若想指定版本,请在命令后面加上版本号。 19 | ``` shell {.line-numbers} 20 | $ pip3 install django 21 | $ pip3 install djangorestframework 22 | ``` 23 | ``` shell {.line-numbers} 24 | $ pip3 install django==版本号 25 | $ pip3 install djangorestframework==版本号 26 | ``` 27 | #### 3. 创建一个新的项目。 28 | ``` shell {.line-numbers} 29 | $ cd ~ 30 | $ django-admin.py startproject attendances 31 | $ cd attendances 32 | $ python manage.py startapp user apps/user 33 | ``` 34 | #### 4. 将新user应用和rest_framework应用添加到配置文件attendances/settings.py文件的INSTALLED_APPS。 35 | ``` python {.line-numbers} 36 | INSTALLED_APPS = ( 37 | ... 38 | 'rest_framework', 39 | 'apps.user', 40 | ) 41 | ``` 42 | #### 5. 写接口代码前的一点准备。 43 | 做完以上步骤,我们就把项目开发需要的环境搭建完成了,接下来就可以在我们的项目中添加接口了。 44 | 45 | 另外还有一点要说明的是,为了给我们的api增加版本控制,我们的文件目录和路由是这样处理的,这样我们就可以通过添加新的版本文件并且修改配置,同时运行多个版本的接口: 46 | 47 | **文件目录结构** 48 | 79 | 80 | **路由** 81 | ``` python {.line-numbers} 82 | # 项目路由 83 | urlpatterns = [ 84 | url('admin/', admin.site.urls), 85 | url(r'^api/v1/', include('apis.api_v1.urls', namespace='api-v1')), 86 | ] 87 | ``` 88 | ``` python {.line-numbers} 89 | # apis下面的路由 90 | from apis.api_v1.user import views as api_user 91 | 92 | urlpatterns = [ 93 | # user 94 | url(r'^user/$', api_user.user, name='user'), 95 | ] 96 | 97 | app_name = 'api-v1' 98 | ``` 99 | **调用地址:** https://{Domain}/api/v1/user 100 | 101 | **请求方法:** GET-获取用户信息 POST-修改用户信息 102 | 103 | > 文件说明:apis存放的是api接口文件,app存放的是应用或者可以说是模块,attendances是项目文件存放一些项目配置,static静态文件,attendances.xml是uWsgi的配置文件,requirements.txt是一些项目相关的依赖,test.py是为了测试uWsgi是否配置成功的文件。 104 | #### 6. 创建一个可以使用的模型以及Serializer类、Django视图。 105 | **在apps/user/models.py创建User模型** 106 | ``` python {.line-numbers} 107 | """ 108 | 用户模块 109 | """ 110 | from django.db import models, transaction 111 | 112 | class User(models.Model): 113 | """ 114 | 用户信息表 115 | """ 116 | user_id = models.AutoField(primary_key=True, verbose_name='用户id') 117 | user_guid = models.CharField(max_length=150, verbose_name='用户guid') 118 | user_name = models.CharField( 119 | max_length=100, blank=True, null=True, verbose_name='用户名') 120 | real_name = models.CharField( 121 | max_length=50, blank=True, null=True, verbose_name='真实姓名') 122 | avatar = models.CharField( 123 | max_length=250, blank=True, null=True, verbose_name='头像') 124 | mobile = models.CharField( 125 | max_length=50, blank=True, null=True, verbose_name='手机') 126 | balance = models.DecimalField( 127 | max_digits=8, decimal_places=2, blank=True, null=True, verbose_name='账户余额') 128 | available_balance = models.DecimalField( 129 | max_digits=8, decimal_places=2, blank=True, null=True, verbose_name='可用金额') 130 | frozen_balance = models.DecimalField( 131 | max_digits=8, decimal_places=2, blank=True, null=True, verbose_name='冻结金额') 132 | all_balance = models.DecimalField( 133 | max_digits=8, decimal_places=2, blank=True, null=True, verbose_name='累计金额') 134 | wx_open_id = models.CharField(max_length=150, verbose_name='微信OpenID') 135 | wx_union_id = models.CharField( 136 | max_length=150, blank=True, null=True, verbose_name='微信UnionID') 137 | create_date = models.FloatField(blank=True, null=True, verbose_name='创建时间') 138 | last_login_date = models.FloatField( 139 | blank=True, null=True, verbose_name='最后登录时间') 140 | ip_address = models.CharField( 141 | max_length=50, blank=True, null=True, verbose_name='IP地址') 142 | gender = models.IntegerField(blank=True, null=True, verbose_name='性别') 143 | province = models.CharField( 144 | max_length=50, blank=True, null=True, verbose_name='省份') 145 | city = models.CharField( 146 | max_length=50, blank=True, null=True, verbose_name='城市') 147 | session_key = models.CharField( 148 | max_length=150, blank=True, null=True, verbose_name='会话秘钥') 149 | is_notify = models.IntegerField(blank=True, null=True, verbose_name='是否开启打卡通知') 150 | 151 | @classmethod 152 | def update_user_balance(cls, user_id, amount): 153 | # 手动让select for update和update语句发生在一个完整的事务里面 154 | with transaction.atomic(): 155 | user = ( 156 | cls.objects 157 | .select_for_update() 158 | .get(user_id=user_id) 159 | ) 160 | user.available_balance += amount 161 | user.balance = user.available_balance + user.frozen_balance 162 | if amount > 0: 163 | user.all_balance += amount 164 | user.save() 165 | return user 166 | 167 | class Meta: 168 | managed = False 169 | db_table = 'user' 170 | ordering = ['-create_date'] 171 | verbose_name = '用户' 172 | verbose_name_plural = '用户' 173 | ``` 174 | **在apis/api_v1/user/serializers.py创建Serializer类** 175 | ``` python {.line-numbers} 176 | """ 177 | 用户模块 178 | """ 179 | from rest_framework import serializers 180 | from apps.user.models import User 181 | from apis.api_v1.enum import ErrorCode 182 | from apis.api_v1.base import BaseApi 183 | 184 | 185 | class GetUserSerializer(serializers.Serializer): 186 | """ 187 | 获取用户信息 188 | """ 189 | token = serializers.CharField(max_length=150) 190 | user_id = serializers.IntegerField() 191 | 192 | def get_user(self, validated_data): 193 | result = dict() 194 | base_api = BaseApi() 195 | # 获取用户 196 | try: 197 | user = User.objects.get(user_id=validated_data["user_id"]) 198 | except User.DoesNotExist: 199 | result["error_code"] = ErrorCode.用户不存在.value 200 | result["error"] = "用户不存在" 201 | return result 202 | # 认证 203 | if not base_api.authenticate_user(validated_data["token"], user.user_guid): 204 | result["error_code"] = ErrorCode.认证错误.value 205 | result["error"] = "认证错误" 206 | return result 207 | result["nick_name"] = user.user_name 208 | result["avatar"] = user.avatar 209 | result["error_code"] = ErrorCode.正确.value 210 | result["error"] = "" 211 | return result 212 | 213 | ``` 214 | **在apis/api_v1/user/view.py创建视图** 215 | ``` python {.line-numbers} 216 | """ 217 | 用户模块 218 | """ 219 | import json 220 | from rest_framework.decorators import api_view 221 | from rest_framework.response import Response 222 | from rest_framework import status 223 | from apis.api_v1.user.serializers import * 224 | from apis.api_v1.enum import ErrorCode 225 | 226 | 227 | @api_view(['GET', 'POST']) 228 | def user(request): 229 | """ 230 | GET:获取用户信息 231 | POST:更新用户信息 232 | """ 233 | if request.method == 'GET': 234 | param = dict() 235 | param["token"] = request.GET.get("token", None) 236 | param["user_id"] = request.GET.get("user_id", 0) 237 | serializer = GetUserSerializer(data=param) 238 | if serializer.is_valid(): 239 | result = serializer.get_user(serializer.validated_data) 240 | return Response(result, status=status.HTTP_201_CREATED) 241 | return Response(dict(error_code=ErrorCode.参数错误.value, error=json.dumps(serializer.errors, ensure_ascii=False)), status=status.HTTP_400_BAD_REQUEST) 242 | # elif request.method == 'POST': 243 | # serializer = PostUserSerializer(data=request.data) 244 | # if serializer.is_valid(): 245 | # result = serializer.update_user(serializer.validated_data) 246 | # return Response(result, status=status.HTTP_201_CREATED) 247 | # return Response(dict(error_code=ErrorCode.参数错误.value, error=json.dumps(serializer.errors, ensure_ascii=False)), status=status.HTTP_400_BAD_REQUEST) 248 | ``` 249 | 到目前为止,我们已经完成了一个简单接口的创建,接下来可以对项目进行测试 250 | #### 4. 测试我们的Web API。首先启动一个开发服务器,之后在浏览器里输入地址访问我们的接口。 251 | 启动开发服务器 252 | ``` shell {.line-numbers} 253 | python manage.py runserver 254 | 255 | Validating models... 256 | 257 | 0 errors found 258 | Django version 2.0, using settings 'attendances.settings' 259 | Development server is running at http://127.0.0.1:8000/ 260 | Quit the server with CONTROL-C. 261 | ``` 262 | 在浏览器里输入接口地址,获取到接口数据:http://127.0.0.1:8000/api/v1/user/?token=21cd4161-2a78-451e-8979-5fbd8538935e&user_id=4 263 | ``` python {.line-numbers} 264 | GET /api/v1/user/?token=21cd4161-2a78-451e-8979-5fbd8538935e&user_id=4 265 | ``` 266 | ``` python {.line-numbers} 267 | HTTP 201 Created 268 | Allow: POST, GET, OPTIONS 269 | Content-Type: application/json 270 | Vary: Accept 271 | 272 | { 273 | "nick_name": "Band", 274 | "avatar": "http://wx.qlogo.cn/mmopen/vi_32/aSKcBBPpibyKNicHNTMM0qJVh8Kjgiak2AHWr8MHM4WgMEm7GFhsf8OYrySdbvAMvTsw3mo8ibKicsnfN5pRjl1p8HQ/0", 275 | "error_code": 0, 276 | "error": "" 277 | } 278 | ``` 279 | ### 二、相关问题说明 280 | #### 1. 在配置文件里记得修改成本地时区以及将语言设置成中文。 281 | ``` python {.line-numbers} 282 | LANGUAGE_CODE = 'zh-Hans' 283 | TIME_ZONE = 'Asia/Shanghai' 284 | ``` 285 | #### 2. 在安装python3之后,使用pip安装依赖的时候记得使用pip3,或者改掉python和pip的。 286 | ``` shell {.line-numbers} 287 | $ alias python='/usr/bin/python3' 288 | ``` 289 | 为了不混淆项目,并且大家在项目中遇到的问题可能各不一样,暂时不列出更多的问题,如果有疑问可以留言。 290 | ## 第二部分 部署流程以及问题说明 291 | 部署的时候建议大家安装docker容器,如果你没有太多的时间了解docker,可以先不用安装,直接按下面的流程去部署,部署的方式都是一样的,只是运行的地方不一样而已。这里就不列出docker的安装以及使用了。 292 | ### 一、部署步骤 293 | #### 1. 安装python3 294 | 首先安装依赖包。libxml模块是为了让uwsig支持使用“-x"选项,能通过xml文件启动项目 295 | ``` shell {.line-numbers} 296 | $ yum gcc-c++ 297 | $ yum install wget openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqlite-devel 298 | $ yum install libxml* 299 | ``` 300 | 下载python3的压缩包之后,解压并安装到/usr/local/python3/路径下,ln命令是为了方便在终端中直接使用python3和pip3命令。 301 | ``` shell {.line-numbers} 302 | $ wget https://www.python.org/ftp/python/3.6.3/Python-3.6.3.tgz 303 | $ tar -zxvf Python-3.6.3.tar.gz 304 | $ ./configure --prefix=/usr/local/python3 305 | $ make -j2 306 | $ make install -j2 307 | $ ln -s /usr/local/python3/bin/python3.5 /usr/bin/python3 308 | $ ln -s /usr/local/python3/bin/pip3 /usr/bin/pip3 309 | ``` 310 | #### 2. 安装uWsgi 311 | ``` shell {.line-numbers} 312 | $ pip3 install uwsgi 313 | ``` 314 | 为了在终端中使用uwsgi命令,执行以下命令 315 | ``` shell {.line-numbers} 316 | $ ln -s /usr/local/python3/bin/uwsgi /usr/bin/uwsgi3 317 | ``` 318 | 编写一个简单的wsgi应用测试uwsgi是否能正常使用,创建一个test.py文件。 319 | ``` python {.line-numbers} 320 | # test.py 321 | def application(env, start_response): 322 | start_response('200 OK', [('Content-Type','text/html')]) 323 | return [b"Hello World"] # python3 324 | ``` 325 | 运行uwsgi,http :8000表示使用http协议,端口号为8000,wigi-file则表示要运行的wsgi应用程序文件。 326 | ``` shell {.line-numbers} 327 | # test.py 328 | $ uwsgi --http :8000 --wsgi-file test.py 329 | ``` 330 | uwsgi运行后打开浏览器,访问http://127.0.0.1:8000/ ,或者是相应服务器地址的8000端口,就可以看到hello world 页面了。 331 | #### 3. 安装Django以及项目依赖,通过uWsgi测试项目是否能正常运行 332 | 安装依赖 333 | ``` shell {.line-numbers} 334 | $ pip3 install django 335 | $ pip3 install djangorestframework 336 | $ pip3 install pycrypto 337 | ``` 338 | Django通过uWsgi测试,如果能正常浏览则运行成功,静态文件无法访问的话,我们这里先忽略,后面会通过Nginx配置静态资源访问 339 | #### 4. 安装MySQL,也可以直接使用SQLite或PostgreSQL、Oracle数据库,Django支持多种数据库,根据配置安装不同的驱动,本项目采用MySQL,数据表结构以及链接配置请参考文档 340 | ``` shell {.line-numbers} 341 | $ pip3 install mysql 342 | $ pip3 install mysql-server 343 | $ pip3 install mysql-devel 344 | ``` 345 | MySQL的安装可以参考文档:[MySQL 安装 | 菜鸟教程](http://www.runoob.com/mysql/mysql-install.html) 346 | #### 5. 连接uwsgi与Django 347 | 该步骤只是检测Django项目能否在uwsgi下运行,下面我们将配置xml的启动配置文件 348 | ``` shell {.line-numbers} 349 | $ uwsgi3 --http :8000 --module attendances.wsgi 350 | ``` 351 | xml的启动配置文件 352 | ``` python {.line-numbers} 353 | 354 | 127.0.0.1:8000 355 | /root/web/attendances/attendances 356 | attendances.wsgi 357 | 4 358 | uwsgi.log 359 | 360 | ``` 361 | 进入项目执行以下命令 362 | ``` shell {.line-numbers} 363 | $ uwsgi3 -x attendances.xml 364 | ``` 365 | #### 6. 安装nginx 366 | ``` shell {.line-numbers} 367 | $ wget http://nginx.org/download/nginx-1.13.7.tar.gz 368 | $ tar -zxvf nginx-1.13.7.tar.gz 369 | $ ./configure 370 | $ make 371 | $ make install 372 | $ nginx 373 | ``` 374 | 通过链接查看nginx是否启动成功:http://192.168.2.110 375 | #### 6. 通过配置nginx.conf文件连接Django、uWsgi与Nginx 376 | 在/etc/nginx/nginx.conf修改nginx.conf 377 | ``` shell {.line-numbers} 378 | server { 379 | listen 80 default_server;#暴露给外部访问的端口 380 | listen [::]:80 default_server; 381 | server_name 127.0.0.1; 382 | index index.py index.html; 383 | root /root/web/attendances/attendances; 384 | 385 | location / { 386 | include uwsgi_params; 387 | uwsgi_pass 127.0.0.1:8000;#外部访问80就转发到内部8000 388 | } 389 | 390 | location /static/ { 391 | alias /root/web/attendances/attendances/static/;#项目静态路径设置 392 | } 393 | } 394 | ``` 395 | 保存nginx.conf执行nginx -t命令先检查配置文件是否有错,没有错就执行以下命令:nginx启动nginx,可以通过链接查看nginx是否启动成功,之前启动过的话重启nginx. 396 | 重启nginx 397 | ``` shell {.line-numbers} 398 | $ nginx -t 399 | $ nginx -s reload 400 | ``` 401 | 以上步骤都没有出错的话,打开你的浏览器,输入以下链接,记得关闭系统防火墙或者开放8000端口 402 | http://192.168.2.110/api/v1/user/?token=21cd4161-2a78-451e-8979-5fbd8538935e&user_id=4 (请将该ip替换成你的服务器ip) 403 | 网站访问成功! 404 | ### 二、相关问题说明 405 | #### 1. Nginx和Django静态文件处理 406 | Django项目可以正常打开,但是静态文件引用路径还有问题,在Django开发时Django自己可以正确处理静态文件的路径,但是部署后Nginx去无法找到静态文件路径。 407 | 408 | 检查Nginx配置文件夹sites-enabled里的nginx-pro文件,确保里面默认的try_files要删掉或者注释掉,否则Nginx会因此检查静态文件是否存在。 409 | 410 | 将Django的静态文件集中起来,Django为此有专门的工具 411 | 412 | 现在Django的Settings文件中加上StATIC_ROOT,把静态文件都集中到这个路径下 413 | 414 | ``` shell 415 | STATIC_ROOT = os.path.join(BASE_DIR, "static/") 416 | ``` 417 | 执行命令 418 | 419 | ``` shell {.line-numbers} 420 | $ python3 ./manage.py collectstatic 421 | ``` 422 | 这样所有Django前后台的静态文件都会集中到项目文件夹pro下static中,另外nginx-pro其中一个配置location /static即可让Nginx来处理静态内容。 423 | #### 2. Nginx权限问题(Nginx 403 forbidden) 424 | 如果nginx用户没有web目录的权限,则会导致该错误。解决办法:修改web目录的读写权限。或者是把nginx的启动用户改成目录的所属用户,重起一下就能解决,在nginx.conf头部加入一行:user root;因为项目是在root用户下建立的,通过这个用户去访问资源就可以访问。 425 | ``` shell 426 | chmod -R 766 /web 427 | ``` 428 | #### 3. Nginx转发出现错误(502 BAD GATEWAY) 429 | 430 | ``` shell 431 | $ setsebool -P httpd_can_network_connect 1 432 | ``` 433 | > 如果访问不了可以查看Nginx下的error.log文件,查看具体的问题,再找解决方法。 434 | ## 分享一些中文文档资料 435 | > 有关Python、Django以及Django REST FrameWork的学习可以参考官方文档,以及以下中文文档,在网上找了很久,感觉以下这几个中文文档还是写的很不错的。 436 | 437 | [**Python:** 廖雪峰老师Python教程,基于最新的Python 3版本](https://www.liaoxuefeng.com/wiki/0014316089557264a6b348958f449949df42a6d3a2e542c000) 438 | 439 | [**Django:** The Django Book 2.0--中文版](http://docs.30c.org/djangobook2/index.html) 440 | 441 | [**Django REST FrameWork:** Django REST FrameWork中文文档目录](http://www.chenxm.cc/post/299.html) 442 | -------------------------------------------------------------------------------- /apis/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apis/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /apis/__pycache__/base.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apis/__pycache__/base.cpython-36.pyc -------------------------------------------------------------------------------- /apis/__pycache__/enum.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apis/__pycache__/enum.cpython-36.pyc -------------------------------------------------------------------------------- /apis/__pycache__/setting.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apis/__pycache__/setting.cpython-36.pyc -------------------------------------------------------------------------------- /apis/__pycache__/urls.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apis/__pycache__/urls.cpython-36.pyc -------------------------------------------------------------------------------- /apis/api_v1/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apis/api_v1/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /apis/api_v1/__pycache__/base.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apis/api_v1/__pycache__/base.cpython-36.pyc -------------------------------------------------------------------------------- /apis/api_v1/__pycache__/enum.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apis/api_v1/__pycache__/enum.cpython-36.pyc -------------------------------------------------------------------------------- /apis/api_v1/__pycache__/setting.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apis/api_v1/__pycache__/setting.cpython-36.pyc -------------------------------------------------------------------------------- /apis/api_v1/__pycache__/urls.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apis/api_v1/__pycache__/urls.cpython-36.pyc -------------------------------------------------------------------------------- /apis/api_v1/base.py: -------------------------------------------------------------------------------- 1 | import hashlib 2 | from apis.api_v1.setting import IS_TEST, APP_VERIFY_CODE 3 | 4 | 5 | class BaseApi(object): 6 | 7 | def authenticate(self, token): 8 | """ 9 | 认证 10 | """ 11 | if IS_TEST: 12 | return True 13 | elif not IS_TEST and self.md5(APP_VERIFY_CODE) == token: 14 | return True 15 | return False 16 | 17 | def authenticate_user(self, token, guid): 18 | """ 19 | 登录用户认证 20 | """ 21 | if IS_TEST: 22 | return True 23 | elif not IS_TEST and self.md5(APP_VERIFY_CODE + guid) == token: 24 | return True 25 | return False 26 | 27 | def md5(self, str): 28 | """ 29 | MD5加密 30 | """ 31 | m = hashlib.md5() 32 | m.update(str.encode("utf8")) 33 | return m.hexdigest() 34 | 35 | def is_in_enum(self, eunm, key=None, value=None): 36 | """ 37 | 判断是否为枚举值的元素 38 | """ 39 | for name, member in eunm.__members__.items(): 40 | if key is not None and name == key or value is not None and value == member.value: 41 | return True 42 | return False 43 | -------------------------------------------------------------------------------- /apis/api_v1/enum.py: -------------------------------------------------------------------------------- 1 | from enum import Enum, unique 2 | 3 | 4 | @unique 5 | class ErrorCode(Enum): 6 | # base 7 | 正确 = 0 8 | 参数错误 = 1 9 | 请求方法错误 = 2 10 | 认证错误 = 3 11 | # user 12 | 微信登录凭证错误 = 1000 13 | 用户不存在 = 1001 14 | 获取微信用户信息错误 = 1002 15 | 账户余额不足 = 1003 16 | 推送状态不正确 = 1004 17 | -------------------------------------------------------------------------------- /apis/api_v1/setting.py: -------------------------------------------------------------------------------- 1 | # 是否测试环境,需要验证认证 2 | IS_TEST = True 3 | -------------------------------------------------------------------------------- /apis/api_v1/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from apis.api_v1.user import views as api_user 3 | from apis.api_v1.scene import views as api_scene 4 | from apis.api_v1.joined_scene import views as api_joined_scene 5 | from apis.api_v1.my import views as api_my 6 | from apis.api_v1.other import views as api_other 7 | 8 | urlpatterns = [ 9 | # user 10 | url(r'^user/$', api_user.user, name='user'), 11 | ] 12 | 13 | app_name = 'api-v1' 14 | -------------------------------------------------------------------------------- /apis/api_v1/user/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apis/api_v1/user/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /apis/api_v1/user/__pycache__/json_models.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apis/api_v1/user/__pycache__/json_models.cpython-36.pyc -------------------------------------------------------------------------------- /apis/api_v1/user/__pycache__/param_models.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apis/api_v1/user/__pycache__/param_models.cpython-36.pyc -------------------------------------------------------------------------------- /apis/api_v1/user/__pycache__/serializers.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apis/api_v1/user/__pycache__/serializers.cpython-36.pyc -------------------------------------------------------------------------------- /apis/api_v1/user/__pycache__/views.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apis/api_v1/user/__pycache__/views.cpython-36.pyc -------------------------------------------------------------------------------- /apis/api_v1/user/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from apps.user.models import User 3 | from apis.api_v1.enum import ErrorCode 4 | from apis.api_v1.base import BaseApi 5 | 6 | 7 | class GetUserSerializer(serializers.Serializer): 8 | """ 9 | 获取用户信息 10 | """ 11 | token = serializers.CharField(max_length=150) 12 | user_id = serializers.IntegerField() 13 | 14 | def get_user(self, validated_data): 15 | result = dict() 16 | base_api = BaseApi() 17 | # 获取用户 18 | try: 19 | user = User.objects.get(user_id=validated_data["user_id"]) 20 | except User.DoesNotExist: 21 | result["error_code"] = ErrorCode.用户不存在.value 22 | result["error"] = "用户不存在" 23 | return result 24 | # 认证 25 | if not base_api.authenticate_user(validated_data["token"], user.user_guid): 26 | result["error_code"] = ErrorCode.认证错误.value 27 | result["error"] = "认证错误" 28 | return result 29 | result["nick_name"] = user.user_name 30 | result["avatar"] = user.avatar 31 | result["error_code"] = ErrorCode.正确.value 32 | result["error"] = "" 33 | return result 34 | 35 | 36 | -------------------------------------------------------------------------------- /apis/api_v1/user/views.py: -------------------------------------------------------------------------------- 1 | import json 2 | from rest_framework.decorators import api_view 3 | from rest_framework.response import Response 4 | from rest_framework import status 5 | from apis.api_v1.user.serializers import * 6 | from apis.api_v1.enum import ErrorCode 7 | 8 | 9 | @api_view(['GET', 'POST']) 10 | def user(request): 11 | """ 12 | GET:获取用户信息 13 | POST:更新用户信息 14 | """ 15 | if request.method == 'GET': 16 | param = dict() 17 | param["token"] = request.GET.get("token", None) 18 | param["user_id"] = request.GET.get("user_id", 0) 19 | serializer = GetUserSerializer(data=param) 20 | if serializer.is_valid(): 21 | result = serializer.get_user(serializer.validated_data) 22 | return Response(result, status=status.HTTP_201_CREATED) 23 | return Response(dict(error_code=ErrorCode.参数错误.value, error=json.dumps(serializer.errors, ensure_ascii=False)), status=status.HTTP_400_BAD_REQUEST) 24 | # elif request.method == 'POST': 25 | # serializer = PostUserSerializer(data=request.data) 26 | # if serializer.is_valid(): 27 | # result = serializer.update_user(serializer.validated_data) 28 | # return Response(result, status=status.HTTP_201_CREATED) 29 | # return Response(dict(error_code=ErrorCode.参数错误.value, error=json.dumps(serializer.errors, ensure_ascii=False)), status=status.HTTP_400_BAD_REQUEST) 30 | -------------------------------------------------------------------------------- /apps/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apps/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /apps/user/__init__.py: -------------------------------------------------------------------------------- 1 | from os import path 2 | from django.apps import AppConfig 3 | 4 | VERBOSE_APP_NAME = "用户管理" 5 | 6 | 7 | def get_current_app_name(file): 8 | path_array = path.dirname(file).replace('\\', '/').split('/') 9 | return path_array[-2] + '.' + path_array[-1] 10 | 11 | 12 | class AppVerboseNameConfig(AppConfig): 13 | name = get_current_app_name(__file__) 14 | verbose_name = VERBOSE_APP_NAME 15 | 16 | 17 | default_app_config = get_current_app_name( 18 | __file__) + '.__init__.AppVerboseNameConfig' 19 | -------------------------------------------------------------------------------- /apps/user/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apps/user/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /apps/user/__pycache__/admin.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apps/user/__pycache__/admin.cpython-36.pyc -------------------------------------------------------------------------------- /apps/user/__pycache__/apps.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apps/user/__pycache__/apps.cpython-36.pyc -------------------------------------------------------------------------------- /apps/user/__pycache__/models.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apps/user/__pycache__/models.cpython-36.pyc -------------------------------------------------------------------------------- /apps/user/__pycache__/urls.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apps/user/__pycache__/urls.cpython-36.pyc -------------------------------------------------------------------------------- /apps/user/__pycache__/views.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apps/user/__pycache__/views.cpython-36.pyc -------------------------------------------------------------------------------- /apps/user/admin.py: -------------------------------------------------------------------------------- 1 | """ 2 | 用户模块 3 | """ 4 | from django.contrib import admin 5 | from apps.user.models import User 6 | 7 | class UserAdmin(admin.ModelAdmin): 8 | """ 9 | 用户表 10 | """ 11 | list_display = ('user_id', 'user_name', 'real_name', 'avatar', 'mobile', 12 | 'balance', 'available_balance', 'frozen_balance') 13 | search_fields = ('user_name', 'mobile') 14 | 15 | admin.site.register(User, UserAdmin) 16 | -------------------------------------------------------------------------------- /apps/user/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class UserConfig(AppConfig): 5 | name = 'user' 6 | -------------------------------------------------------------------------------- /apps/user/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.0 on 2018-01-08 15:34 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | initial = True 9 | 10 | dependencies = [ 11 | ] 12 | 13 | operations = [ 14 | migrations.CreateModel( 15 | name='User', 16 | fields=[ 17 | ('user_id', models.AutoField(primary_key=True, serialize=False, verbose_name='用户id')), 18 | ('user_guid', models.CharField(max_length=32, verbose_name='用户guid')), 19 | ('user_name', models.CharField(blank=True, max_length=100, null=True, verbose_name='用户名')), 20 | ('real_name', models.CharField(blank=True, max_length=50, null=True, verbose_name='真实姓名')), 21 | ('avatar', models.CharField(blank=True, max_length=250, null=True, verbose_name='头像')), 22 | ('mobile', models.CharField(blank=True, max_length=50, null=True, verbose_name='手机')), 23 | ('balance', models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True, verbose_name='账户余额')), 24 | ('available_balance', models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True, verbose_name='可用金额')), 25 | ('frozen_balance', models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True, verbose_name='冻结金额')), 26 | ('all_balance', models.DecimalField(blank=True, decimal_places=2, max_digits=8, null=True, verbose_name='累计金额')), 27 | ('wx_open_id', models.CharField(max_length=150, verbose_name='微信OpenID')), 28 | ('wx_union_id', models.CharField(blank=True, max_length=150, null=True, verbose_name='微信UnionID')), 29 | ('create_date', models.FloatField(blank=True, null=True, verbose_name='创建时间')), 30 | ('last_login_date', models.FloatField(blank=True, null=True, verbose_name='最后登录时间')), 31 | ('ip_address', models.CharField(blank=True, max_length=50, null=True, verbose_name='IP地址')), 32 | ], 33 | options={ 34 | 'verbose_name': '用户', 35 | 'verbose_name_plural': '用户', 36 | 'db_table': 'user', 37 | 'ordering': ['-create_date'], 38 | 'managed': False, 39 | }, 40 | ), 41 | ] 42 | -------------------------------------------------------------------------------- /apps/user/migrations/__pycache__/0001_initial.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apps/user/migrations/__pycache__/0001_initial.cpython-36.pyc -------------------------------------------------------------------------------- /apps/user/migrations/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/apps/user/migrations/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /apps/user/models.py: -------------------------------------------------------------------------------- 1 | """ 2 | 用户模块 3 | """ 4 | from django.db import models, transaction 5 | 6 | class User(models.Model): 7 | """ 8 | 用户信息表 9 | """ 10 | user_id = models.AutoField(primary_key=True, verbose_name='用户id') 11 | user_guid = models.CharField(max_length=150, verbose_name='用户guid') 12 | user_name = models.CharField( 13 | max_length=100, blank=True, null=True, verbose_name='用户名') 14 | real_name = models.CharField( 15 | max_length=50, blank=True, null=True, verbose_name='真实姓名') 16 | avatar = models.CharField( 17 | max_length=250, blank=True, null=True, verbose_name='头像') 18 | mobile = models.CharField( 19 | max_length=50, blank=True, null=True, verbose_name='手机') 20 | balance = models.DecimalField( 21 | max_digits=8, decimal_places=2, blank=True, null=True, verbose_name='账户余额') 22 | available_balance = models.DecimalField( 23 | max_digits=8, decimal_places=2, blank=True, null=True, verbose_name='可用金额') 24 | frozen_balance = models.DecimalField( 25 | max_digits=8, decimal_places=2, blank=True, null=True, verbose_name='冻结金额') 26 | all_balance = models.DecimalField( 27 | max_digits=8, decimal_places=2, blank=True, null=True, verbose_name='累计金额') 28 | wx_open_id = models.CharField(max_length=150, verbose_name='微信OpenID') 29 | wx_union_id = models.CharField( 30 | max_length=150, blank=True, null=True, verbose_name='微信UnionID') 31 | create_date = models.FloatField(blank=True, null=True, verbose_name='创建时间') 32 | last_login_date = models.FloatField( 33 | blank=True, null=True, verbose_name='最后登录时间') 34 | ip_address = models.CharField( 35 | max_length=50, blank=True, null=True, verbose_name='IP地址') 36 | gender = models.IntegerField(blank=True, null=True, verbose_name='性别') 37 | province = models.CharField( 38 | max_length=50, blank=True, null=True, verbose_name='省份') 39 | city = models.CharField( 40 | max_length=50, blank=True, null=True, verbose_name='城市') 41 | session_key = models.CharField( 42 | max_length=150, blank=True, null=True, verbose_name='会话秘钥') 43 | is_notify = models.IntegerField(blank=True, null=True, verbose_name='是否开启打卡通知') 44 | 45 | @classmethod 46 | def update_user_balance(cls, user_id, amount): 47 | # 手动让select for update和update语句发生在一个完整的事务里面 48 | with transaction.atomic(): 49 | user = ( 50 | cls.objects 51 | .select_for_update() 52 | .get(user_id=user_id) 53 | ) 54 | user.available_balance += amount 55 | user.balance = user.available_balance + user.frozen_balance 56 | if amount > 0: 57 | user.all_balance += amount 58 | user.save() 59 | return user 60 | 61 | class Meta: 62 | managed = False 63 | db_table = 'user' 64 | ordering = ['-create_date'] 65 | verbose_name = '用户' 66 | verbose_name_plural = '用户' 67 | -------------------------------------------------------------------------------- /apps/user/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /apps/user/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from apps.user import views 3 | 4 | urlpatterns = [ 5 | url(r'^user-add/(?P\d+)/$', views.add, name='user-add'), 6 | ] 7 | 8 | 9 | app_name = 'user' 10 | -------------------------------------------------------------------------------- /apps/user/views.py: -------------------------------------------------------------------------------- 1 | """ 2 | 用户表 3 | """ 4 | from django.shortcuts import render_to_response 5 | from apps.user.models import User 6 | 7 | 8 | def add(request, id): 9 | """ 10 | add 11 | """ 12 | uid = id 13 | return render_to_response('user/user_add.html', locals()) 14 | -------------------------------------------------------------------------------- /attendances.xml: -------------------------------------------------------------------------------- 1 | 2 | 127.0.0.1:8000 3 | /root/web/attendances/attendances 4 | attendances.wsgi 5 | 4 6 | uwsgi.log 7 | -------------------------------------------------------------------------------- /attendances/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/attendances/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /attendances/__pycache__/settings.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/attendances/__pycache__/settings.cpython-36.pyc -------------------------------------------------------------------------------- /attendances/__pycache__/urls.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/attendances/__pycache__/urls.cpython-36.pyc -------------------------------------------------------------------------------- /attendances/__pycache__/view.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/attendances/__pycache__/view.cpython-36.pyc -------------------------------------------------------------------------------- /attendances/__pycache__/wsgi.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/herry-zhang/Python3-RESTfulAPI/e772e0beee284c50946e94c54a1d43071ca78b74/attendances/__pycache__/wsgi.cpython-36.pyc -------------------------------------------------------------------------------- /attendances/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for attendances project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.0. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.0/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.0/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = '+6$=@6_9ts&1jzyd^fh99k6f_$)5&xixcqv-z8hg1(!sd&9a^3' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = ['127.0.0.1','192.168.2.110'] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'suit', 35 | 'rest_framework', 36 | 'django.contrib.admin', 37 | 'django.contrib.auth', 38 | 'django.contrib.contenttypes', 39 | 'django.contrib.sessions', 40 | 'django.contrib.messages', 41 | 'django.contrib.staticfiles', 42 | 'apps.user', 43 | ] 44 | 45 | 46 | MIDDLEWARE = [ 47 | 'django.middleware.security.SecurityMiddleware', 48 | 'django.contrib.sessions.middleware.SessionMiddleware', 49 | 'django.middleware.common.CommonMiddleware', 50 | 'django.middleware.csrf.CsrfViewMiddleware', 51 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 52 | 'django.contrib.messages.middleware.MessageMiddleware', 53 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 54 | ] 55 | 56 | ROOT_URLCONF = 'attendances.urls' 57 | 58 | TEMPLATES = [ 59 | { 60 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 61 | 'DIRS': [ 62 | os.path.join(BASE_DIR, 'templates') 63 | ], 64 | 'APP_DIRS': True, 65 | 'OPTIONS': { 66 | 'context_processors': [ 67 | 'django.template.context_processors.debug', 68 | 'django.template.context_processors.request', 69 | 'django.contrib.auth.context_processors.auth', 70 | 'django.contrib.messages.context_processors.messages', 71 | ], 72 | }, 73 | }, 74 | ] 75 | 76 | WSGI_APPLICATION = 'attendances.wsgi.application' 77 | 78 | 79 | # Database 80 | # https://docs.djangoproject.com/en/2.0/ref/settings/#databases 81 | 82 | DATABASES = { 83 | 'default': { 84 | 'ENGINE': 'django.db.backends.mysql', 85 | 'NAME': 'attendances', 86 | 'USER': 'root', 87 | 'PASSWORD': '123', 88 | 'HOST': '192.168.2.110', 89 | 'PORT': '3306', 90 | } 91 | # 'default': { 92 | # 'ENGINE': 'django.db.backends.mysql', 93 | # 'NAME': 'attendances', 94 | # 'USER': 'root', 95 | # 'PASSWORD': 'admin!@#$1234', 96 | # 'HOST': '127.0.0.1', 97 | # 'PORT': '3306', 98 | # } 99 | } 100 | 101 | 102 | # Password validation 103 | # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators 104 | 105 | AUTH_PASSWORD_VALIDATORS = [ 106 | { 107 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 108 | }, 109 | { 110 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 111 | }, 112 | { 113 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 114 | }, 115 | { 116 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 117 | }, 118 | ] 119 | 120 | 121 | # Internationalization 122 | # https://docs.djangoproject.com/en/2.0/topics/i18n/ 123 | 124 | LANGUAGE_CODE = 'zh-Hans' 125 | 126 | TIME_ZONE = 'Asia/Shanghai' 127 | 128 | USE_I18N = True 129 | 130 | USE_L10N = True 131 | 132 | USE_TZ = True 133 | 134 | 135 | # Static files (CSS, JavaScript, Images) 136 | # https://docs.djangoproject.com/en/2.0/howto/static-files/ 137 | STATIC_ROOT = os.path.join(BASE_DIR, "static") 138 | 139 | STATIC_URL = '/static/' 140 | 141 | STATICFILES_DIRS = ( 142 | os.path.join(BASE_DIR, "static"), 143 | ) 144 | -------------------------------------------------------------------------------- /attendances/urls.py: -------------------------------------------------------------------------------- 1 | """attendances URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.0/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.conf.urls import url, include 17 | from django.contrib import admin 18 | 19 | urlpatterns = [ 20 | url('admin/', admin.site.urls), 21 | url(r'^user/', include('apps.user.urls', namespace='user')), 22 | url(r'^api/v1/', include('apis.api_v1.urls', namespace='api-v1')), 23 | ] 24 | -------------------------------------------------------------------------------- /attendances/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for attendances 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.0/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", "attendances.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "attendances.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 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Django==2.0 2 | djangorestframework==3.7.7 3 | pycrypto==2.6.1 4 | uwsgi 5 | request -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | def application(env, start_response): 2 | start_response('200 OK', [('Content-Type','text/html')]) 3 | return [b"Hello World"] # python3 --------------------------------------------------------------------------------