1. django简介
Django是一个开放源代码的Web应用框架,由Python写成。其采用了MVC的软件设计模式,即模型(Model)、模板(Template)和视图(Views),因此也被称为 MTV 框架 。在 MTV 开发模式中:
M 代表模型,即数据存取层。该层处理与数据相关的所有事务,如何存取、如何确认有效性、包含哪些行为以及数据之间的关系等。
T 代表模板,即表现层。该层处理与表现相关的决定,如何在页面或其他类型文档中进行显示。
V代表视图,即业务逻辑层。这一层包含访问模型的逻辑和按照模板显示,可以认为它是模型和模板的桥梁。
2. 安装
pip install django
3. 创建项目流程
3.1 创建一个Django项目,名称为faceident
python django-admin.py startproject faceident
3.2 创建APP
一个项目中包含一个或多个APP。APP可以理解为一块功能集合。每个Django APP都有独立的models,views等,具有易移植和可复用特性。
注意:app的名称不能和项目名称faceident一样
cd faceident
python manage.py startapp app
3.3 目录文件说明
(1)子目录faceident下表示工程的全局配置,分别为setttings.py、urls.py和wsgi.py,其中setttings.py包括系统的数据库配置、应用配置和其他配置,urls.py则表示web工程Url映射的配置。
(2)子目录app则是在该工程下创建的APP,包含了models.py、tests.py和views.py等文件;其下新建的templates目录则为模板文件的目录,用来存放web界面模板html文件
(3)manage.py是Django提供的一个管理工具,可以同步数据库、运行项目等等
3.4 逻辑和页面分离
在模板templates目录下创建一个index.html文件,规定web界面格式及算法结果返回显示,为算法与外部数据流的传输定义文件
<!DOCTYPE html>
<html>
<head>
<title>身份识别平台</title>
<script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
<script type="text/javascript">
function ProcessFile(e) {
var file = document.getElementById('file').files[0];
if (file) {
var reader = new FileReader();
reader.onload = function (event) {
var txt = event.target.result;
var img = document.createElement("img");
img.src = txt; //将图片base64字符串赋值给img的src
document.getElementById("result").appendChild(img);
};
}
reader.readAsDataURL(file);
}
function contentLoaded() {
document.getElementById('file').addEventListener('change',
ProcessFile, false);
}
window.addEventListener("DOMContentLoaded", contentLoaded, false);
</script>
</head>
<body>
<h1>信息输入</h1>
<form action="/upload/" method="POST" enctype="multipart/form-data">
编号:<input type="text" name="pig_id" id="pigNumber"/>
<br>
图片上传时间:<input type="date" name="upload_time" id="uploadTime"/>
<br>
猪场ID:<input type="text" name="location" id="location"/>
<br>
左侧脸:
<br>
请选取一个图像文件: <input type="file" id="ltfFile" name="ltf_img" />
<br>
正脸:
<br>
请选取一个图像文件: <input type="file" id="flfFile" name="flf_img" />
<br>
右侧脸:
<br>
请选取一个图像文件: <input type="file" id="rtfFile" name="rtf_img" />
<br>
<div id="result"></div>
<div id="result_new"></div>
<img id="ewmtp" src="https://img-blog.csdnimg.cn/2022010611283655454.png" alt="Red dot" />
<br>
<button type="button" class="btn btn-primary btn-lg" id="lgbut_compute">提交</button>
<br>
<div id="result1"></div>
<div id="result2"></div>
</form>
<script>
function ShowResult(data) {
// var v = data['img64']; // data.img64
// alert(v)
// var obj = JSON.parse(data)
var obj = data
if (obj.identify_return){
document.getElementById('result1').innerHTML = '输出信息:'
document.getElementById('result2').innerHTML = obj.identify_return
}
else if (obj.error_value){
alert(obj.error_value)
}
}
</script>
<script>
$('#lgbut_compute').click(function () {
var formdata = new FormData();
var ltfFile = $("#ltfFile")[0].files[0];
var flfFile = $("#flfFile")[0].files[0];
var rtfFile = $("#rtfFile")[0].files[0];
var pigNumber = $("#pigNumber").val();
var uploadTime = $("#uploadTime").val();
var location = $("#location").val();
formdata.append("ltfFile", ltfFile, 'ltf');
formdata.append("flfFile", flfFile, 'flf');
formdata.append("rtfFile", rtfFile, 'rtf');
formdata.append("pigNumber", pigNumber);
formdata.append("uploadTime", uploadTime);
formdata.append("location", location);
$.ajax({
url: '/identifyDemo/', //调用django服务器计算函数
type: 'POST', //请求类型
data: formdata,
dataType: 'json', //期望获得的响应类型为json
processData: false,
contentType: false,
success: ShowResult //在请求成功之后调用该回调函数输出结果
})
})
</script>
</body>
</html>
3.5 创建数据表
(1)在models.py文件中,新建了一个FeatureDatabase类,继承自models.Model, 例如有猪只编号、猪场ID等属性。
from django.db import models
# Create your models here.
class FeatureDatabase(models.Model): #index是Class的名称与views的相同,也是创建的表名
id = models.CharField(verbose_name="编号", max_length=64, blank=True, null=False, primary_key=True)
#upload_time = models.DateTimeField(verbose_name="添加时间", auto_now_add=True, blank=True, null=False)
upload_time = models.CharField(verbose_name="添加时间", max_length=128, blank=True, null=False)
location = models.CharField(verbose_name="ID", max_length=40, blank=True, null=False)
flf_feature = models.CharField(verbose_name="正脸特征", max_length=1024, null=True)
'''
flf_img = models.ImageField(verbose_name="正脸图片", upload_to='img/%Y/%m/%d/', null=True)
'''
(2)在setting.py文件中指定数据库(默认为sqlite3数据库,也可以替换为mysql数据库)
# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
'''
'default': {
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'FeatureDatabase', # 数据库名,先前创建的
'USER': 'root', # 用户名,可以自己创建用户
'PASSWORD': 'jsjsjxfd', # 密码
'HOST': '179.20.101.20', # mysql服务所在的主机ip
'PORT': '3306', # mysql服务端口
}
'''
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
(3)先 cd 进入 manage.py 所在的那个文件夹下。
(4)输入下面的命令生成迁移文件migration,该文件是描述数据库结构变化的;需要注意这时候数据库还没真正变化,只是生成了描述数据库变化的文件。
python manage.py makemigrations
(5)将结构变化应用到数据库
python manage.py migrate
3.6 常见的数据库操作函数
(1)对数据进行查询
models.FeatureDatabase.objects.all()
models.FeatureDatabase.objects.all().values('user') #只取user列
models.FeatureDatabase.objects.all().values_list('id','user') #取出id和user列,并生成一个列表
models.FeatureDatabase.objects.get(id=1)
models.FeatureDatabase.objects.get(user='三毛')
(2)对数据进行增加
models.FeatureDatabase.objects.create(user='三毛',pwd='123456')
'''
dic = {'user':'三毛','pwd':'123456'}
models.FeatureDatabase.objects.create(**dic)
'''
(3)对数据进行删除
models.FeatureDatabase.objects.filter(user='三毛').delete()
(4)对数据进行更新
models.FeatureDatabase.objects.filter(user='三毛').update(pwd='23456')
'''
obj = models.FeatureDatabase.objects.get(user='三毛')
obj.pwd = '23456'
obj.save()
'''
3.7 响应函数views.py定义,用来定义数据交互、算法执行逻辑
一般深度学习模型在使用过程中需要加载,每次发起post请求响应比较耗时较长(请求一次加载一次模型),可采用模型预加载的方式在django项目初次拉起时将模型加载在缓冲区(只加载一次)
from django.shortcuts import render
# Create your views here.
from django.shortcuts import HttpResponse
from django.views.decorators.csrf import csrf_exempt # 跨站点验证
from django.http import JsonResponse # json字符串返回
import json # json字符串使用
import logging
from logging import handlers
import traceback
import os
from app.src.run import main
class Logger(object):
level_relations = {
'debug': logging.DEBUG,
'info': logging.INFO,
'warning': logging.WARNING,
'error': logging.ERROR,
'crit': logging.CRITICAL
}
def __init__(self, filename, level='info', when='D', backCount=7,
fmt='%(asctime)s - %(pathname)s[line:%(lineno)d] - %(levelname)s: %(message)s'):
self.logger = logging.getLogger(filename)
format_str = logging.Formatter(fmt)
self.logger.setLevel(self.level_relations.get(level))
sh = logging.StreamHandler()
sh.setFormatter(format_str)
th = handlers.TimedRotatingFileHandler(filename=filename, when=when, backupCount=backCount,
encoding='utf-8')
th.setFormatter(format_str)
self.logger.addHandler(sh)
self.logger.addHandler(th)
op = Struct(**parms)
imgFaceRead = cv2.imread('init_data/init.jpg')
_, _ = op.single_process(image_array=imgFaceRead)
log_info = Logger('app/logs/info.log', level='info')
log_error = Logger('app/logs/error.log', level='error')
@csrf_exempt # 用于规避跨站点请求攻击
def identifyDemo(request):
if request.method == "POST":
try:
upload_dict = json.loads(request.body)
except:
log_info.logger.error('uploaded JSON data parse failed!')
log_error.logger.error(traceback.format_exc())
default = {"code": "100600203", "message": "uploaded JSON data parse failed"}
return JsonResponse(default)
'''
results = main()
default = {"code": "100000000",
"message": "Sucess",
"data": results}
return JsonResponse(default)
'''
else:
default = {"code": "100200400", "message": "unsafely executed"} # 初始未执行
log_info.logger.warning('post request failed!')
return JsonResponse(default)
3.8 添加URL路由(用来定位项目执行函数i)
在urls.py文件中,建立每个app的路由方式
(1)配置文件夹下主urls.py
from django.contrib import admin
from django.conf.urls import url, include
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^app1/', include('app1.urls')),
url(r'^app2/', include('app2.urls')),
]
(2)app目录下urls.py(自己新建)
from django.conf.urls import url
from app import views
urlpatterns = [
url(r'^identifyDemo/$', views.identifyDemo, name='identifyDemo'),
]
3.9 在settings.py文件设置
(1)添加所有app,之后可通过“from app.文件名 import 函数名”的模式调用app里面定义好的函数(注意逗号)
注意:DEBUG该项在实际使用中设为False
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app1',
'app2',
]
(2)修改debug方式(调试True,上线False);修改hosts权限
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
ALLOWED_HOSTS = ['*']
(3)修改时区为Asia/Shanghai(防止日志记录时间错误)
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'Asia/Shanghai'#'UTC'
3.10 python脚本so封装
Django认定的根目录为项目目录,经测试若把模型封装函数放在一个文件夹下(如frozen_models),通过“from frozen_models.函数封装文件名”方式无法定位到封装文件中的函数。解决方案有两种:
(1)若只有封装好的so文件,将其放在项目目录下即可
(2)若有源码文件,更改函数调用方式,使用from pigident.frozen_models.函数文件名,然后重新封装即可
import os
import re
import shutil
import argparse
try:
from setuptools import setup, Extension
except ImportError:
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
def get_file_list(pyfile_dir):
files = os.listdir(pyfile_dir)
file_list = []
for file in files:
if re.search('\.py$', file) and (re.match('main.py|soEncapsulation.py', file) is None):
file_list.append(os.path.join(pyfile_dir, file))
else:
pass
return file_list
def encapsulation(file_list, build_dir):
build_tmp_dir = os.path.join(build_dir, 'tmp')
setup(
ext_modules=cythonize(file_list),
script_args=["build_ext", "-b", build_dir, "-t", build_tmp_dir]
)
def delete_c(pyfile_dir, build_dir):
pyfiles = os.listdir(pyfile_dir)
for pyfile in pyfiles:
if re.search('\.c$', pyfile):
os.remove(os.path.join(pyfile_dir, pyfile))
else:
pass
print("All .c files had removed!")
sofiles = os.listdir(build_dir)
for sofile in sofiles:
if re.match('^tmp$', sofile):
shutil.rmtree(os.path.join(build_dir, sofile))
elif re.search('\.so$', sofile):
newfile = sofile.replace('.cpython-37m-x86_64-linux-gnu', '')
os.rename(os.path.join(build_dir, sofile), os.path.join(build_dir, newfile))
else:
pass
print("All .o files had removed!")
def parsers():
parser = argparse.ArgumentParser(description='soEncapsulation, a tool to encapsulate .py to .so.')
parser.add_argument('--pyfile_dir', help='dir with all .py files')
parser.add_argument('--build_dir',help='dir to save .so and .o files')
args = parser.parse_args()
if not args.pyfile_dir:
print('You must supply a pyfile_dir\n')
parser.print_help()
exit(0)
if not args.build_dir:
print('You must supply a build_dir\n')
parser.print_help()
exit(0)
return args
if __name__ == "__main__":
args = parsers()
file_list = get_file_list(pyfile_dir=args.pyfile_dir)
if not os.path.exists(args.build_dir):
os.mkdir(args.build_dir)
encapsulation(file_list=file_list, build_dir=args.build_dir)
delete_c(pyfile_dir=args.pyfile_dir, build_dir=args.build_dir)
3.11 运行项目
调试模式:Python manage.py runserver IP:prot
生产模式:nginx+uwsgi+django
4. nginx+uwsgi生产环境上线django项目
见https://blog.csdn.net/Mugo_Moon/article/details/115248388