luffy后台
一 luffy后台创建
# 创建django项目,两种方式
-命令行
前提:进入目标目录
cd python project
django-admin startproject 项目名
-pycharm创建
命令行
1 workon luffy
2 pip install django==3.2.12
3 django-admin startproject luffy_api # 切换到你想要创建项目的目录下
pycharm 创建项目
-要选中咱们的虚拟环境---> 如果虚拟环境不在,要新增进去
# 打开的项目没有使用虚拟环境,在pycharm中如何配置
二 调整目录结构
├── luffyapi
├── logs/ # 项目运行时/开发时日志目录 - 包
├── manage.py # 脚本文件
├── luffyapi/ # 项目主应用,开发时的代码保存 - 包
├── apps/ # 开发者的代码保存目录,以模块[子应用]为目录保存 - 包
├── libs/ # 第三方类库的保存目录[第三方组件、模块] - 包
├── settings/ # 配置目录 - 包
├── dev.py # 项目开发时的本地配置
└── prod.py # 项目上线时的运行配置
├── urls.py # 总路由
└── utils/ # 多个模块[子应用]的公共函数类库[自己开发的组件]
└── scripts/ # 保存开发项目的脚本文件 - 文件夹
步骤
# 1、调整目录后,以后app全都放在apps文件夹下
-创建app,进入到apps的路径
-python ../../manage.py startapp user # 在apps目录下创建出一个userapp # ***********
-注册到配置文件中
- 全路径 (咱们不用,太长了)
luffy_api.apps.应用名
- 把apps所在路径加入到环境变量后,直接写app名字即可
# 2、设置两套配置文件
#### 所有项目,都会有多套配置文件###
-开发阶段配置:dev.py
-上线阶段配置:prod.py
# 3、django项目运行,优先运行settings.py 配置文件
命令运行 python manage.py runserver--->所以,manage中的配置文件路径要正确
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffy_api.settings.dev')
一旦把dev.py 移动到settings文件夹下后,配置文件中BASE_DIR路径变了,apps拼接的路径也变了,要相应的修改
# 4、把小luffy_api和apps加入环境变量
sys.path.insert(0, str(BASE_DIR)) # 要str一下,以后导入模块,路径短一些
apps = os.path.join(BASE_DIR, 'apps')
sys.path.insert(1, apps)
# 5、重要:项目上线,不适用manage.py 运行---> 使用uwsgi运行wsgi.py 文件----> 修改这个文件的配置
-asgi.py
-wsgi.py
配置文件指定 prod.py 以后上线使用这个配置文件
三 开发阶段本地配置文件dev.py
from pathlib import Path
import os
import sys
# 以后就是小luffy_api 作为项目根路径
BASE_DIR = Path(__file__).resolve().parent.parent # 因为dev被移动到了更深一层,所以 BASE_DIR 变成了小luffy_api
# print(BASE_DIR) # E:\python project\luffy_api\luffy_api 变成了小路飞
# print(type(BASE_DIR)) # <class 'pathlib.WindowsPath'>
# 1. 把小luffy_api加入环境变量中,以后修改项目基本是修改小luffy中的文件,这样以后导入模块的路径短一些
sys.path.insert(0, str(BASE_DIR)) # 要str一下
# 2.把apps所在路径,加入到环境变量,以后引入app直接写app名字即可
APPS_DIR = os.path.join(BASE_DIR, 'apps')
sys.path.insert(1, APPS_DIR)
修改目录结构,不能启动
-
我们把所有的目录结构都修改好后,点击绿色箭头不能顺利启动
-
先进入编辑配置中,删除掉Django Server,再重新加入进去。
- 如果还不能启动,进入到settings中
- ---> Languages&Framework ---> Django中,
- 把配置文件路径跟启动文件路径选择好,再重新启动
四 数据库配置
软件开发模式
-学bbs,设计创建出所有表,直接迁移,后期没改过 -->瀑布开发模式
-学路飞,先设计,开发一点,测试上线一点-----> 敏捷开发
创建表模型
如果用户表想用 auth的user表扩写,在一开始就要定好
# 1. 使用mysql 数据库,创建一个库,navicate创建即可
# 2. 新建一个user app,基于auth的user表扩写用户表
from django.db import models
from django.contrib.auth.models import AbstractUser
# 扩写auth的user表,新增两个字段
class User(AbstractUser):
mobile = models.CharField(max_length=11, unique=True)
# 需要pillow包的支持
icon = models.ImageField(upload_to='icon', default='icon/default.png')
class Meta:
db_table = 'luffy_user' # 指定表名
verbose_name = '用户表' # 后台管理看到的中文
verbose_name_plural = verbose_name
def __str__(self): # 打印对象,显示的
return self.username
3. 迁移数据(mysql-->配置文件配置)
链接数据库和创建luffy用户
-项目的数据库用户,不使用root用,新建一个mysql用户给项目用
-因为root用户权限太大了,新建用户权限小一些
-创建一个luffy用户,授权,只授予luffy库的权限
cmd链接数据库
# 管理员连接数据库
mysql -uroot -proot
# 查看当前数据库有哪些用户
select user,host,password from mysql.user;
# 5.7往后的版本
select user,host,authentication_string from mysql.user;
为指定数据库配置指定账户
设置权限账号密码
# 授权账号命令:grant 权限(create, update) on 库.表 to '账号'@'host' identified by '密码'
# 创建luffy用户
# 创建一个用户叫luffy,密码是:Luffy123?,可以本地链接,对luffy库所有表有权限
grant all privileges on luffy.* to 'luffy'@'localhost' identified by 'Luffy123?';
# 创建一个用户叫luffy,密码是:Luffy123?,可以远程地链接,对luffy库所有表有权限
grant all privileges on luffy.* to 'luffy'@'%' identified by 'Luffy123?';
项目配置文件配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'luffy', # 数据库名字
'HOST': '127.0.0.1',
'PORT': 3306,
'USER': 'luffy',
'PASSWORD': 'Luffy123?',
'CHARSET': 'utf8',
}
}
链接mysql的第三方模块
1、pymysql模块
# django项目如果使用pymsql链接mysql,需要加两句话,加在哪不重要,重要的是它一定要执行
import pymysql
pymysql.install_as_MySQLdb()
django 2 高一点的版本就会报错,需要改源码,麻烦
2、mysqlclient模块
# 咱们以后,使用mysqlclient 操作mysql pip install mysqlclient,不需要任何配置,就可以操作mysql
-win:看人品,也有解决方案
-mac:很难装,也有,比较麻烦
-linux:有解决方案
其他配置
dev.py中 注册user表
# 自定义User表
AUTH_USER_MODEL = 'user.User'
Terminal 中执行命令
# 安装pillow包
pip install pillow
# 执行迁移文件
python manage.py makemigrations
python manage.py migrate
项目数据库之隐藏密码
# 我们直接把mysql的用户名和密码 写死在了代码中----> 后期可能会存在风险----> 代码如果泄露----> mysql的用户密码泄露----> 可以远程登录----> 脱裤(拖库)----> 所有数据会被黑客获取到----> 卖钱
# 华住--->在代码中把数据库用户名和密码写死了----> 源代码泄露了---> 导致数据泄露
# 上海随身办数据泄露----> 新员工写博客---> 把密钥放到了往上
# 我们不把敏感信息写死在代码中
-使用从环境变量中获取
-PATH: 任意路径下敲可执行文件能找到
-其它key value 是该机器的全局配置,可以直接拿到
-使用python代码拿到
python代码获取环境变量的值
# 配置完环境变量,重启一下pycharm
# 如果环境变量没配置,就用默认的
# 如果配置了,就用配置的(项目上线时候,运维配置环境变量--->运维配置的环境变量的值---->开发根本不知道)
import os
res=os.environ.get('PWD','Luffy123?')
print(res)
res=os.environ.get('USER','luffy')
print(res)
具体操作
# python代码获取环境变量的值
# res = os.environ.get('WORKON_HOME')
# print(res) # E:\Virtualenvs
user = os.environ.get('USER', 'luffy')
pwd = os.environ.get('PWD', 'Luffy123?')
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'luffy', # 数据库名字
'HOST': '127.0.0.1',
'PORT': 3306,
'USER': user,
'PASSWORD': pwd,
'CHARSET': 'utf8',
}
}
五 封装logger
- 项目运行,会产生很多日志
- 日志作用:记录程序运行过程中 错误,警告,程序员的输出,观察项目运行情况
- 每个项目都需要记录日志
- django中加入记录日志的功能
日志模块 loguru:https://blog.csdn.net/freeking101/article/details/52448580
1.把如下代码,copy到配置文件中
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
},
'simple': {
'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
},
},
'filters': {
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': {
'console': {
# 实际开发建议使用WARNING
'level': 'WARNING',
'filters': ['require_debug_true'],
'class': 'logging.StreamHandler',
'formatter': 'simple'
},
'file': {
# 实际开发建议使用ERROR
'level': 'ERROR',
'class': 'logging.handlers.RotatingFileHandler',
# 日志位置,日志文件名,日志保存目录必须手动创建,注:这里的文件路径要注意BASE_DIR代表的是小luffyapi
'filename': os.path.join(os.path.dirname(BASE_DIR), "logs", "luffy.log"),
# 日志文件的最大值,这里我们设置300M
'maxBytes': 300 * 1024 * 1024,
# 日志文件的数量,设置最大日志数量为10
'backupCount': 10,
# 日志格式:详细格式
'formatter': 'verbose',
# 文件内容编码
'encoding': 'utf-8'
},
},
# 日志对象
'loggers': {
'django': {
'handlers': ['console', 'file'],
'propagate': True, # 是否让日志信息继续冒泡给其他的日志处理系统
},
}
}
2.utils/common_logger.py
- 获取logger对象:
# 日志对象获取,以后想用日志的地方,直接导入,使用 logger.info error....
import logging
logger=logging.getLogger('django')
3. 日志使用
- 以后想用日志的地方,直接导入使用即可
# DEBUG < INFO < WARNING < ERROR < CRITICAL
logger.debug('debug级别')
logger.info('info级别')
logger.warning('warning级别')
logger.error('error级别')
logger.critical('CRITICAL级别')
# 以后想用print的位置,都用logger.info,以后项目上线,调高日志输出级别,虽然代码中写了日志输出,实际上并不会输出
在写项目直接导入utils文件夹也不''错误提示''
日志测试
# 应用的views.py
from django.shortcuts import render, HttpResponse
# 小luffy_api已经加入到环境变量中了,就可以直接导入utils文件夹,但是有错误提示
# 想要取消错误提示,右击小luffy_api ---> Mark Diectory as ---> Sources Root
from utils.common_logger import logger
### 日志测试
def index(request):
# DEBUG < INFO < WARNING < ERROR < CRITICAL
logger.debug('debug级别')
logger.info('info级别')
logger.warning('warning级别')
logger.error('error级别')
logger.critical('critical级别')
logger.info('1111')
a = [1, 23, 3]
logger.info('2222')
# print(a[9])
logger.info('333') # 控制台没有输出333的info日志,说明是这个日志和上个日志中间出了问题
# 以后想用print的位置,都用logger.info,以后项目上线,调高日志输出级别,虽然代码中写了日志输出,实际上并不会输出
return HttpResponse('ok')
六 封装全局异常处理
安装
pip install djangorestframework==3.14.0
utils/common_exceptions.py
from rest_framework.views import exception_handler
from rest_framework.response import Response
from utils.common_logger import logger
#### 加入日志记录,只要走到这,说明程序出error了,程序的error,咱们都要记录日志,方便后期排查
### 日志记录尽量详细:ip;如果用户登录了,记录用户;请求地址是;执行那个视图类出的错
def common_exception_handler(exc, context):
# print(exc) # 错误信息
# print(context) # {'view': <user.views.ExceptionTestView object at 0x00000174CC0E0C40>, 'args': (), 'kwargs': {}, 'request': <rest_framework.request.Request: GET '/exception/'>}
request = context.get('request')
view = context.get('view')
view_str = str(view)
ip = request.META.get('REMOTE_ADDR')
try:
user_id = request.user.pk
except:
user_id = '[未登录用户]' # 匿名用户的id是None
path = request.get_full_path()
res = exception_handler(exc, context)
# 记录日志
# logger.info('用户地址为:%s,用户id号为:%s,请求地址为:%s,执行的视图函数为:%s' % (ip, user_id, path, view_str))
if res:
# drf的异常,一种是从res.data这个字典的detail中取,一种是 直接取data
if isinstance(res.data, dict):
data = {'code': 999, 'msg': res.data.get('detail', '系统错误,请联系系统管理员')}
else:
data = {'code': 998, 'msg': res.data}
else:
# django的异常
data = {'code': 888, 'msg': str(exc)}
# 记录日志
logger.error('用户地址为:%s,用户id号为:%s,请求地址为:%s,执行的视图函数为:%s,错误详细是:%s。' % (ip, user_id, path, view_str, exc))
return Response(data)
配置文件配置
REST_FRAMEWORK = {
'EXCEPTION_HANDLER': 'utils.common_exceptions.common_exception_handler',
}
全局异常测试
### view.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.exceptions import APIException, ValidationError
class ExceptionTestView(APIView):
def get(self, request):
## django 异常
# raise Exception('django的异常')
# a = [9, 4, 2]
# print(a[9])
## drf 异常
# raise APIException('drf异常')
# raise ValidationError('认证失败') # 列表套字典形式,
return Response('ok')
七 封装Response
# 咱们之前使用drf提供的Response类,用它的时候,需要传很多参数,基于它做封装,方便我们的使用
data=None,
status=None,
headers=None,
# 封装后达成的效果是 APIResponse
return APIResponse()
----->前端收到的是 {code:100,msg:'成功'}
return APIResponse(token=12q4dfasdf,username=lqz)
----->前端收到的是 {code:100,msg:成功,token:12q4dfasdf,username:lqz}
return APIResponse(data=[{name:红楼梦,price:33},{name:西游记,price:33}])
----->前端收到的是 {code:100,msg:成功,data:[{name:红楼梦,price:33},{name:西游记,price:33}]}
return APIResponse(code=101,msg='失败')
----->前端收到的是 {code:101,msg:失败}
return APIResponse(headers={'xx':'xx'})
----->前端收到的是 {code:100,msg:成功},但是相应中有xx:xx
return APIResponse(name=lqz,status=201)
----->前端收到的是 {code:100,msg:成功,name:lqz},但是响应状态码是201
utils/common_response.py
from rest_framework.response import Response
class APIResponse(Response):
def __init__(self, code=100, msg='成功', status=None, headers=None, **kwargs):
data = {'code': code, 'msg': msg}
# kwargs 有可能是 {token:asdfad,name:lqz}
if kwargs: # kwargs中有值
data.update(kwargs)
# 调用父类(Response)的__init__完成初始化
super().__init__(data=data, status=status, headers=headers)
测试响应对象
#### 测试自己封装的响应对象
from rest_framework.views import APIView
from utils.common_response import APIResponse
class MyResponseView(APIView):
def get(self, request):
return APIResponse()
# return APIResponse(code=101, msg='请求失败')
# return APIResponse(result=[{'name': 'lqz', 'price': 19}])
# return APIResponse(name='lqz', status=201, headers={'xx': 'xx'})
八 开启media访问
- 头像,课程图片,放在项目的某个目录下 (media),后期需要能够访问
- 需要开启media的访问
urls.py
urlpatterns = [
path('admin/', admin.site.urls),
path('media/<path:path>', serve,{'document_root':settings.MEDIA_ROOT}),
]
dev.py
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
九、导出项目依赖
以后所有python项目的根路径下,都会有个 requirements.txt 【约定俗称的名字】,这里面记录了当前项目所有的依赖。格式如下:
Django==3.2.12
redis # 最新版
我们也要自己设置
9.1 手动创建
笨办法:直接手动建立
9.2 一键导出
- 一键导出(虚拟环境中:虚拟环境用的模块,就是当前项目的模块,一般不会少,不会多,如果多,可以手动删掉)
-pip list
-pip freeze >requirements.txt
- 文件中直接显示
Django==3.2.12
django-cors-headers==4.1.0
django-simpleui==2023.3.1
djangorestframework==3.14.0
mysqlclient==2.1.1
Pillow==9.5.0