# dj_drone
**Repository Path**: duans/dj_drone
## Basic Information
- **Project Name**: dj_drone
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2026-05-07
- **Last Updated**: 2026-05-22
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# 空中快滴小程序项目-数据接口
## 项目初始化
1. 安装`django`
```shell
pip install django
```
2. 创建`django`项目
```shell
django-admin startproject dj_dorne
```
3. 创建一个子应用
```shell
py manage.py startapp client
```
4. 配置项目
> `settings.py`
```python
INSTALLED_APPS=[
# 注册子应用
"client"
]
# 配置数据库连接参数
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": "drone_db", # 数据库名称
"USER": "root", # 数据库用户名
"PASSWORD": "root", # 数据库密码
"HOST": "localhost", # 数据库主机地址
"PORT": "3306", # 数据库端口号
}
}
```
5. 安装`mysql`数据库连接引擎`mysqlclient`
```shell
pip install mysqlclient
```
`mysqlclient`是C语言编写的一个`mysql`的基础链接库, 如果操作系统缺失一些C语言的基础库, 可能会下载失败
## 根据数据表自动生成模型类
```shell
py manage.py inspectdb > models.py
```
## 集成`djangorestframework`
简称`DRF`, 是`django`框架的一个插件, 可以帮助开发者快速构建`api`数据接口
### 安装
```shell
pip install djangorestframework
```
### 配置
`settings.py`
```python
INSTALLED_APPS=[
# 注册DRF
"rest_framework",
# 注册子应用
"client"
]
# DRF配置
REST_FRAMEWORK = {
# 配置分页类
"DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.PageNumberPagination",
# 配置分页大小: 每页显示的数据条目数量
"PAGE_SIZE": 10,
}
```
### 使用步骤
1. 编写数据表模型类的序列化器类
```python
from rest_framework import serializers
# 导入数据表模型类
from client.models import Photography, PhotographyClassify
# 航拍服务分类序列化器
class PhotographyClassifySerializer(serializers.ModelSerializer):
class Meta:
# 指定数据表模型类
model = PhotographyClassify
# 指定序列化字段
fields = '__all__'
# 航拍服务数据表序列化器
class PhotographySerializer(serializers.ModelSerializer):
# 关联分类序列化器
classify = PhotographyClassifySerializer()
class Meta:
# 指定数据表模型类
model = Photography
# 指定序列化字段
fields = '__all__'
```
2. 编写数据接口类
```python
from rest_framework import viewsets,mixins
# 导入数据表模型类
from client.models import Photography, PhotographyClassify
# 导入序列化器
from client.serializers import PhotographySerializer, PhotographyClassifySerializer
# 航拍服务分类数据接口类
# ListModelMixin: 会自动生成list接口方法
class PhotographyClassifyViewSet(viewsets.GenericViewSet,mixins.ListModelMixin):
# 指定数据表模型类的查询集
queryset = PhotographyClassify.objects.all()
# 指定序列化器
serializer_class = PhotographyClassifySerializer
# 禁用分页功能
pagination_class = None
# 航拍服务数据接口类
# RetrieveModelMixin: 会自动生成retrieve接口方法
class PhotographyViewSet(viewsets.GenericViewSet,mixins.ListModelMixin,mixins.RetrieveModelMixin):
# 指定数据表模型类的查询集
queryset = Photography.objects.all()
# 指定序列化器
serializer_class = PhotographySerializer
```
3. 配置路由
`urls.py`
```python
from django.urls import path,include
# 导入drf默认路由类
from rest_framework.routers import DefaultRouter
# 导入接口类
from . import views
# 创建路由对象
router = DefaultRouter()
# 注册路由
router.register(r'photography-classify',views.PhotographyClassifyViewSet)
router.register(r'photography',views.PhotographyViewSet)
urlpatterns = [
path('',include(router.urls))
]
```
## djangorestframework-simplejwt
### 下载安装
```shell
pip install djangorestframework-simplejwt
```
### 配置
> settings.py
```python
from datetime import timedelta
# djangorestframework-simplejwt 配置
SIMPLE_JWT = {
# 访问令牌过期时间
"ACCESS_TOKEN_LIFETIME": timedelta(hours=1),
# 刷新令牌过期时间
"REFRESH_TOKEN_LIFETIME": timedelta(days=1),
# 加密算法
"ALGORITHM": "HS256"
}
```
### 使用
**生成令牌**
```python
# 生成访问令牌(登录凭证)和刷新令牌(用于刷新访问令牌)
# user: 当前登录用户对象
def gen_access_token(user):
from rest_framework_simplejwt.tokens import RefreshToken
# 生成刷新令牌
refresh = RefreshToken.for_user(user)
# 返回访问令牌和刷新令牌
return str(refresh.access_token), str(refresh)
```
**解析令牌**
```python
from rest_framework_simplejwt.tokens import AccessToken
from rest_framework_simplejwt.exceptions import ExpiredTokenError,InvalidToken,AuthenticationFailed
# 验证访问令牌
try:
# {"user_id":"当前登录用id"}
payload = AccessToken(access_token)
except ExpiredTokenError:
print('令牌过期')
except InvalidToken:
print('登录凭证不合法')
except AuthenticationFailed:
print('登录凭证验证失败')
```
###
## 集成`redis`
1. 下载安装`redis`数据库软件
https://github.com/redis-windows/redis-windows/releases
2. 下载安装`redis`连接引擎(python连接`redis`的一个基础库)
```shell
pip install redis
```
3. 在`django`项目中配置`redis`连接参数
> `settings.py`
```python
# 配置redis链接参数
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
```
4. 通过cache对象操作`redis`
```python
from django.core.cache import cache
# 写入数据
cache.set('数据名称','数据值','有效期')
# 读取数据
cache.get('数据名称')
```
## `requirements.txt`
作用: 记录项目依赖项, 需要手动生成
### 方式1
```shell
pip freeze > requirements.txt
```
缺点: 会将当前运行环境中安装过的所有依赖写入`requirements.txt`, 无论在项目中是否引入过
### 方式2
使用第三方工具包`pipreqs`, 需要下载安装
#### 安装`pipreqs`
```shell
pip install pipreqs
```
#### 生成`requirements.txt`
```shell
# --force: 表示强制生成, 可以覆盖之前生成的requirements.txt
pipreqs ./ --force
```
## 日志配置
日志可以记录项目运行情况, 如果项目运行出错, 可以通过分析日志找出出错原因, 及时修正代码.
> `settings.py`
```python
# 日志配置
LOG_DIR = os.path.join(BASE_DIR, "logs")
# 确保日志目录存在
os.makedirs(LOG_DIR, exist_ok=True)
LOGGING = {
"version": 1,
"disable_existing_loggers": False,
# 配置日志格式
'formatters': {
# 详细格式:日志级别、包含时间、日志级别、模块名、进程/线程ID、具体消息
'verbose': {
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{', # 使用 {} 风格的字符串格式化
},
# 简单格式:只包含时间和具体的日志消息
'simple': {
'format': '{asctime} {levelname} {message}',
'style': '{',
},
},
"handlers": {
# 文件日志
"file": {
"level": "INFO", # 日志级别
"class": "logging.FileHandler", # 日志处理工具
"filename": os.path.join(LOG_DIR, "django.log"), # 日志文件的保存位置
"encoding": "utf-8", # 日志编码格式
'formatter': 'verbose' # 使用上面定义的简单格式
},
# 控制台日志
"console": {
"level": "INFO", # 日志级别
"class": "logging.StreamHandler", # 日志处理工具
}
},
# 日志记录器配置
"loggers": {
# django框架运行日志记录器
"django": {
# 日志处理程序
# file: 文件日志
# console: 控制台日志
"handlers": ["file","console"],
# 日志级别
"level": "INFO",
# 是否传递日志给父记录器
# True: 传递给父记录器
# False: 不传递给父记录器
# 默认值: True
"propagate": True,
},
}
}
```
### 按照日期对日志进行切割
> `settings.py`
```python
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
# 确保存放日志的文件夹存在
LOG_DIR = os.path.join(BASE_DIR, 'logs')
os.makedirs(LOG_DIR, exist_ok=True)
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'INFO',
# 核心配置:使用 TimedRotatingFileHandler 按时间切割日志
'class': 'logging.handlers.TimedRotatingFileHandler',
'filename': os.path.join(LOG_DIR, 'django.log'), # 基础日志文件名
'when': 'midnight', # 每天凌晨(午夜)进行切割
'interval': 1, # 间隔1个when单位(即每1天)
'backupCount': 30, # 保留最近30天的日志文件,超出的会自动删除
'encoding': 'utf-8', # 防止中文乱码
},
},
'loggers': {
# django框架运行产生的日志
'django': {
'handlers': ['file'],
'level': 'INFO', # 日志级别
'propagate': True,
},
},
}
```
生产环境多进程下会出现日志写入错误来拿甚至日志丢失的问题
### 生产环境多进程部署日志方案
如果你的 `Django `项目在生产环境中使用 **`Gunicorn`** 或 **`uWSGI`** 等多进程方式部署,直接使用 `TimedRotatingFileHandler` 可能会出现问题。
**原因**:Python 的 logging 模块是线程安全的,但**不是进程安全的**。在每天凌晨切割日志的那一瞬间,多个进程可能会同时尝试重命名或删除日志文件,导致日志写入错乱,甚至出现部分日志丢失的情况。
**解决方案**:
如果是多进程环境,建议将 `class` 替换为第三方库提供的多进程安全处理器,例如 `ConcurrentLogHandler`:
1. 安装库:`pip install concurrent-log-handler`
2. 修改配置:
> `settings.py`
```python
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
# 确保存放日志的文件夹存在
LOG_DIR = os.path.join(BASE_DIR, 'logs')
os.makedirs(LOG_DIR, exist_ok=True)
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'file': {
'level': 'INFO',
# 替换为多进程安全的Handler
'class': 'cloghandler.ConcurrentRotatingFileHandler',
'filename': os.path.join(LOG_DIR, 'django.log'),
# 按大小切割(例如50MB)
'maxBytes': 1024 * 1024 * 50,
'backupCount': 10,
'encoding': 'utf-8',
},
},
'loggers': {
# django框架运行产生的日志
'django': {
'handlers': ['file'],
'level': 'INFO', # 日志级别
'propagate': True,
},
},
}
```
### 日志级别
在 Python 的 `logging` 模块中,内置了 5 个标准的日志级别。它们本质上是带有整数值的常量,**数值越大,级别越高,代表的问题越严重**。
按照严重程度**从低到高**排列如下
| 日志级别 | 对应数值 | 适用场景说明 |
| :----------- | :------- | :----------------------------------------------------------- |
| **DEBUG** | 10 | **调试信息**。非常详细的诊断信息,通常包含变量值、执行步骤等,仅在开发和排查问题时开启。 |
| **INFO** | 20 | **一般信息**。证明程序按预期运行,例如程序启动、关闭、关键操作成功完成等。 |
| **WARNING** | 30 | **警告信息**。发生了一些意想不到的事情(如磁盘空间不足),或者预示着将来可能会出现某些问题,但程序目前仍能正常工作。 |
| **ERROR** | 40 | **错误信息**。由于发生严重问题,程序的某些功能未能正常执行(如网络请求失败、文件读取异常)。 |
| **CRITICAL** | 50 | **严重错误**。发生了极其严重的错误,可能导致程序无法继续运行甚至崩溃(如数据库彻底连不上)。 |
## 微信小程序相关接口
- [微信小程序-登录凭证校验](https://developers.weixin.qq.com/miniprogram/dev/server/API/user-login/api_code2session.html)
- [微信小程序-开放数据校验与解密](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html)
### 获取微信用户手机号码
```html
```
```jsx
// pages/phone/phone.js
Page({
getPhoneNumber(event){
// encryptedData: 手机号码加密数据
// iv: 加密向量
const {encryptedData,iv}=event.detail
wx.login({
success:(res)=>{
// code: 换取session_key会话令牌的凭证码
const {code}=res
// 调用自己的后端接口实现手机号码机密
wx.request({
url:'http://localhost:8000/api/client/wechat/phone_decode/',
method:"POST",
data:{
encryptedData,iv,code
},
success:(result)=>{
console.log(result);
}
})
}
})
},
})
```
### 微信一键登录
```html
```
```jsx
// pages/phone/phone.js
Page({
getPhoneNumber(event){
// encryptedData: 手机号码加密数据
// iv: 加密向量
const {encryptedData,iv}=event.detail
wx.login({
success:(res)=>{
// code: 换取session_key会话令牌的凭证码
const {code}=res
// 调用自己的后端接口实现手机号码机密
wx.request({
url:'http://localhost:8000/api/client/wechat/wechat_login/',
method:"POST",
data:{
encryptedData,iv,code
},
success:(result)=>{
console.log(result);
}
})
}
})
}
})
```
## 通过`.env`文件保存项目配置信息
1. 下载安装`python-dotenv`
```python
pip install python-dotenv
```
2. 在项目根目录中创建一个`.env`的文件, 将相关配置信息写入该文件
```python
# 环境变量配置文件
# 数据库配置
MYSQL_HOST=localhost
MYSQL_PORT=3306
MYSQL_USER=root
MYSQL_PASSWORD=root
MYSQL_DATABASE=drone_db
# redis配置
REDIS_HOST=localhost
REDIS_PORT=6379
REDIS_DB=0
# 微信小程序配置
WECHAT_APPID=wxfa47c341f928dcc5
WECHAT_APPSECRET=900f40d011824a8d3f0fb81fd19bafb5
```
3. 在`settings.py`文件中,通过`dotenv.load_dotenv()`将`.env`文件中的配置项载入到环境变量中, 通过`os.getenv()`动态获取环境变量中的配置信息
```python
from dotenv import load_dotenv
import os
# 加载环境变量: 从.env文件中加载环境变量(django项目的运行内存中)
load_dotenv()
DATABASES = {
"default": {
"ENGINE": "django.db.backends.mysql",
"NAME": os.getenv('MYSQL_DATABASE'), # 数据库名称
"USER": os.getenv('MYSQL_USER'), # 数据库用户名
"PASSWORD": os.getenv('MYSQL_PASSWORD'), # 数据库密码
"HOST": os.getenv('MYSQL_HOST'), # 数据库主机地址
"PORT": os.getenv('MYSQL_PORT'), # 数据库端口号
}
}
# 配置redis链接参数
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": f"redis://{os.getenv('REDIS_HOST')}:{os.getenv('REDIS_PORT')}/{os.getenv('REDIS_DB')}",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
# 小程序配置信息
WECHAT_APPID=os.getenv('WECHAT_APPID')
WECHAT_APPSECRET=os.getenv('WECHAT_APPSECRET')
```
## 参考文档
- [django官网](https://docs.djangoproject.com/zh-hans/6.0/)
- [redis-windows](https://github.com/redis-windows/redis-windows/releases)
- [mysql数据库](https://dev.mysql.com/downloads/installer/)
- [djangrestframework参考文档](https://www.django-rest-framework.org)
- [djangorestframework-simplejwt](https://django-rest-framework-simplejwt.readthedocs.io/en/latest/)
- [python-dotenv](https://pypi.org/project/python-dotenv/)
- [微信小程序-登录凭证校验](https://developers.weixin.qq.com/miniprogram/dev/server/API/user-login/api_code2session.html)
- [微信小程序-开放数据校验与解密](https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html)