"""
Django settings for mydemo project.
Generated by 'django-admin startproject' using Django 3.1.7.《django版本》
For more information on this file, see
https://docs.djangoproject.com/en/3.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.1/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent """《显示当前项目根目录》"""
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'jb^(bip2g_@%r*5ni-8@#yz%*crzw7sqpq_qcbhw48lgk+ctxo'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
"""
调试模式开关,默认为True(调试模式)
为True时:1.检测代码改动后立刻重启服务;2.显示报错页面
为False时(正式启动模式/上线模式):项目上线时一定要更改为False
"""
ALLOWED_HOSTS = []
"""
允许的域名,只有列表中添加的域名可以访问服务器,默认会允许127.0.0.1或者localhost访问
设置为"*":表示允许所有域名访问
设置当前局域网内域名可进行访问:
1.项目启动时设置:python manage.py runserver 0.0.0.0:8000
2.添加局域网IP
"""
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'mydemo.urls'
"""
项目主路由位置,默认为项目文件夹下的urls.py
"""
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
"""
模版配置
"""
WSGI_APPLICATION = 'mydemo.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# Password validation
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
"""
主页语言配置,可设置为中文
LANGUAGE_CODE = 'zh-hans'
"""
TIME_ZONE = 'UTC'
"""
时区设置,主要影响数据库时间显示
TIME_ZONE = 'Asia/shanghai'
"""
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.1/howto/static-files/
STATIC_URL = '/static/'
"""
静态文件路由
"""
URL 结构:protocol://hostname[:port]/path[?query][#fragment]
key=value
Django如何处理URL请求
定义:用于接收浏览器请求( HttpRequest对象),并通过HttpResponse对象返回响应的函数
语法:return必须是HttpResponse对象
def demoview(request[,其他参数]):
return HttpResponse('需要返回的数据')
示例:
在项目同名目录下创建views.py
from django.http import HttpResponse
def page1_view(requet):
html = "hallo world
"
return HttpResponse(html)
添加路由url.py
from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('admin/', admin.site.urls),
path('page1/', views.page1_view),
]
from django.urls import path
path(route,views,name=None)
path('page/',views.xxx)
,如果匹配到page路径后面时int类型,则会将接收到的路径中的数据赋值给变量page,然后以关键值传参的方式传递到视图中转换器类型 | 作用 | 样例 |
---|---|---|
str | 匹配除了‘/’之外的非空字符串 | ”v1/users/ |
int | 匹配0和任何正整数,返回一个int | ”v1/users/ |
slug | 匹配任意有ASCII字母或数字以及连字符和下划线组成的短标签 | ”v1/users/ |
path | 匹配非空字段,包括路径分隔符”/“ | ”v1/users/ |
re_path(reg,view,name=xxx)
?Ppattern
;匹配成功后用关键字传参的方式传递给视图函数url(r'^weather/(?P[a-z]+)/(?P\d{4})/$', views.weather),
HttpRequest属性:
属性名 | 说明 |
---|---|
path_info | URL字符串 |
method | 字符串,表示Http请求方法:GET、POST、PUT等 |
GET | QueryDict查询字典的对象,包含get请求方式的所有数据 |
POST | QueryDict查询字典的对象,包含post请求方式的所有数据 |
FILES | 类似于字典的对象,包含所有的上传文件信息 |
COOKIES | python字典,包含所有的cookie,键和值都为字符串 |
session | 类似于字典对象,表示当前的会话 |
body | 字符串请求体的内容 |
scheme | 请求协议(http或者https) |
request.get_full_path() | 请求的完整路径,会将查询字符串一并取出 |
request.META | 请求中的元数据(消息头) |
requset.META[‘REMOTE_ADDR’] | 客户端IP地址 |
类型 | 作用 | 状态码 |
---|---|---|
HttpResponseRedirect | 重定向 | 302 |
HttpResponseNotModified | 未修改 | 304 |
HttpResponseBadRequest | 错误请求 | 400 |
HttpResponseNotFound | 没有对应的资源 | 404 |
HttpResponseForbidden | 请求被禁止 | 403 |
HttpResponseServerError | 服务器错误 | 500 |
if request.method == 'GET':
处理get请求时的业务逻辑
elif request.method == 'POST':
处理POST请求时的业务逻辑
else:
处理其他请求业务的逻辑
request.GET['参数名'] #得到该参数名的值
request.GET.get('参数名',’默认值‘) #得到该参数名的值,如果该参数名不存在则返回默认值
request.GET.getlist(’参数名‘) #如果该参数名存在多个返回值,则用getlist来接收```
<form method='post' action="/login"> #action指明post请求发给哪个路由
姓名:<input type="text" name="username">
<input type='submit' value='登陆'>
form>
request.POST['参数名'] #得到该参数名的值
request.POST.get('参数名',’默认值‘) #得到该参数名的值,如果该参数名不存在则返回默认值
request.POST.getlist(’参数名‘) #如果该参数名存在多个返回值,则用getlist来接收
模板定义:
模板配置
模板的加载方式
from django.template import loader
t = loader.get_template("模板文件名") #通过loader加载模板,得到一个loader对象
html = t.render(字典数据) #将t(loader对象)转换成HTML字符串
return HttpResponse(html) #用响应对象的方式将转换的字符串内容返回给浏览器
from django.shortcuts import render
return render(request,'模板文件名',字典数据)
视图层与模板层之间的交互
def xxx_view(request):
dic = {
"变量1":"值1",
"变量2":"值2",
}
return render(request,'xxx.html',dic)
模板层-变量
类型名 | 注释 |
---|---|
str | 字符串 |
int | 整型 |
list | 列表 |
tuple | 元组 |
dict | 字典 |
func | 方法 |
obj | 类实例化对象 |
作用:将一些服务器端的功能嵌入到模板中,例如流程控制等
语法:
{% 标签 %}
…………
{% 结束标签 %} #django中大部分标签都需要使用结束标签封口
IF标签
{% if 条件表达式1 %}
……
{% elif 条件表达式2 %}
……
{% else %}
……
{% endif %} #必须使用endif标签进行结束
<form action='/text' method="post">
<input type="text" name="x" value='{{ x }}'>
<select name="op">
<option value="add" {%if op == 'add' %}selected{%endif%}> +加</option>
<option value="sub" {%if op == 'sub' %}selected{%endif%}> -减</option>
<option value="mul" {%if op == 'mul' %}selected{%endif%}> *乘</option>
<option value="div" {%if op == 'div' %}selected{%endif%}> /除</option>
</select>
<input type="text" name="y" value='{{ y }}'> = <span>{{ result }}</span>
<div><input type="submit" value="开始计算">
</div>
</form>
for 标签
{% for 变量 in 可迭代对象 %}
…… 循环语句
{% empty %}
…… 可迭代对象为空时显示的语句
{% endfor %}
{% for i in x %}{{ forloop.counter }}this is in for {{i}} {%endfor%}
# x由视图函数传入
内置变量 - forloop
变量 | 描述 |
---|---|
forloop.counter | 返回当前循环的索引(从1开始索引) |
forloop.counter0 | 返回当前循环的索引(从0开始索引) |
forloop.revcounter | 返回当前循环的索引(从1开始索引counter值的倒序) |
forloop.revcounter0 | 返回当前循环的索引(从0开始索引counter值的倒序) |
forloop.first | 如果当前循环是第一次循环,则返回True |
forloop.last | 如果当前循环是最后一次循环,则返回True |
forloop.parentloop | 如果为嵌套循环,parentloop则表示外层循环 |
定义:在变量输出时,对变量的值进行处理
作用:可以通过使用过滤器来改变变量的输出显示
语法:{{变量|过滤器1:‘参数值1’ | 过滤器2:‘参数值2’}}
关于过滤器的语法参考官方文档
常用过滤器:
过滤器 | 说明 |
---|---|
lower | 将字符串转换为全部小写 |
upper | 将字符串转换为全部大写 |
safe | 禁用转义,告诉模板这个变量是安全的,可以解释执行 |
add:“n” | 将value的值增加n |
default:“默认值” | 变量不存在时,返回默认值 |
truncatechars:‘n’ | 如果字符串的字符多余指定的字符数量,那么会被截断,截断的字符串将以可翻译的省略号序列("…")结尾 |
{% extends 'base.html '%}
#继承父模板base.html{% block block_name %}
子模板块用来覆盖父模板中 block_name 块的内容
{% endblock block_name %} #block_name可省略
{% block block_name %}
<h4>this is in parent</h4>
{% endblock %}
子模板中{% extends 'test_html.html' %}
{% block block_name %}
<h3> this is in base.html </h3>
{% endblock %}
超链接
#点击后页面跳转至url
#form表单中的数据用post方法提交值URL{% url '别名' %}
{% url '别名' '参数值1' '参数值2' %}
,参数值1:向路由中传入的参数{% url 'page1' '100' %}
#解析name=page1的路由,并传入参数100{% url 'page2' key1='1' key2='2' %}
#解析name=page2的路由,并关键字传参key1,key2 注意 路由解析时传递的参数均为字符串方式传入from django.urls import reverse
reverse('别名', args=[ ], kwargs={ })
#例 :
reverse('page1',args=[100]) #解析name=page1的路由,并传入参数100
reverse('page2',kwargs={key1=1,key2=2})
#file:settings.py
STATICFILES_DIRS = (
os.path.join(BASE_DIR,'static'),
)
<img src="http://127.0.0.1:8000/static/image1.jpg" width="200" height="200"> #绝对路径访问
<img src="static/image2.jpg" width="200" height="200" > #相对路径访问
访问static文件夹下images文件夹下的lena.jpgfrom django.urls import psth,include
from . import views
urlpatterns = [
path('admin/',admin.site.urls),
path('users/',include('users.urls')) #分发至子应用users中的urls.py
]
from django.urls import path
from . import views
urlpatterns = [
#http://127.0.0.1:8000/users/index
path('index',views.index_view)
]
from django.shortcuts import render
# Create your views here.
def index_view(request):
return render(request,'news/index.html')
sudo apt-get install gcc
create database 数据库名 default charset utf8
#创建数据库设置默认编码格式为utf8DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', #指定数据库的存储引擎
'NAME': 'mysql_demo', #指定要连接的数据库的名称
'USER': 'root', #指定登陆到数据库的用户名
'PASSWORD': 'mysql', #数据库登录密码
'HOST': '127.0.0.1', #连接数据库的IP
'PORT': '3306', #连接数据库的端口
}
}
ENGINE:常见的引擎还有:django.db.models.Model
派生出的子类(即它必须继承至django.db.models.Model
)from django.db import models
# Create your models here.
class Book(models.Model):
book_name = models.CharField(verbose_name='书名',max_length=50,default='')
price = models.DecimalField(verbose_name='价格',max_digits=7,decimal_places=2)
python manage.py makemigrations
将应用下的models.py文件生成一个中间文件,并保存在migrations文件夹中python manage.py migrate
执行迁移程序实现迁移,将每个应用下的migrations目录中的中间文件同步到数据库应用名_模型类类名
from django.db import models
class ModelName(models.Model):
FieldName = models.FieldType(field-options)
"""
FieldName:字段名,迁移后对应数据表中字段名
FieldType:字段类型
field-options:字段选项
"""
字段类型 | 数据库类型 | 作用 | 参数 |
---|---|---|---|
BooleanField() | tinyint(1) | 使用True或False来表示值 | - |
CharField() | varchar | - | max_length:字符最大长度(必填参数) |
DateField() | date | 表示日期 | auto_now:每次保存对象时, 自动设置该字段为当前时间(True/False); auto_now_add:当对象第一次被创建时自动设置当前时间(True/False); default:设置当前默认时间(取值:字符串格式时间,如’2021-7-1’) * 以上三个参数只能多选一 |
DateTimeField() | datetime(6) | 表示日期和时间 | 参数通DateField |
FloatField() | double | 使用小数表示值 | - |
DecimalField() | decimal(x,y) | 使用小数表示值 | max_digits:位数总长度, 包括小数点后的位数, 该值必须大于等于decimal_places; decimal_places:小数点后的数字长度(即小数位数) *以上两个参数必选 |
EmailField() | varchar | 表示邮箱 | - |
IntegerField() | int | 表示整数 | - |
ImageField() | varchar(100) | 在数据库中为了保存图片的路径 | - |
TextField() | longtext | 表示不定长的字符数据 | - |
注:更多字段类型说明参考官方文档
字段选项 | 说明 |
---|---|
primary_key | 如果设置为True,表示该列为主键,如果指定一个字段为主键,则此数据库不会创建id字段 |
blank | 设置为True时,该字段可以为空(在管理后台中编辑数据的时候可以为空),设置为False时,该字段时必须填写的 |
null | 设置为True时,表示该列值允许为空,默认为False,如果此项为False时建议加入default选项来设置默认值 |
default | 设置所在列的默认值,如果字段null=false建议添加此项 |
db_index | 设置为True时,表示为该列增加索引 |
unique | 设置为True时,表示该字段在数据库中的值必须是唯一不能重复出现的 |
db_column | 指定列的名称,如果不指定的话则采用类属性名作为列名 |
verbose_name | 设置此字段在admin界面显示的名称,不设置则默认以类属性名显示 |
注:更多字段选项说明参考官方文档
模型类 - Meta类(控制表相关属性)
属性 | 作用 |
---|---|
db_table=‘数据表名’ | 修改该模型所用的数据表的名称,设置完成后需要立马更新同步数据库 |
verbose_name=‘单数名’ | 给模型一个易于理解的名称(单数),用于显示在/admin管理界面中 |
verbose_name_plural=‘复数名’ | 该对象复数形式的名称,用于显示在/admin管理界面中 |
#file:models.py
from django.db import models
class Books(models.Model):
class Meta:
db_table = 'book' #改变当前模型类对应的表名为book
class MyModel(models.Model):
···
MyModel.objects.create(···) #objects就是管理器对象
创建数据 -> 创建数据中每一条记录就是创建一个数据对象
obj = MyModel()
obj.属性1 = 值1
obj.属性2 = 值2
obj.save() #执行save()后数据才会提交到数据库中
查询数据 -> 数据库的查询也需要使用管理器对象进行(可用QuerySet对象.query显示ORM转换为SQL语句的查询方法)
通过MyModel.objects管理器方法调用查询方法
方法 | 用法 | 作用 | 返回值 |
---|---|---|---|
all() | MyModel.objects.all() | 查询MyModel表中的所有数据,等同于select * from table | QuerySet容器对象,内部存放MyModel实例 |
values (‘列1’, ’列2‘) |
MyModel.objects.values() | 查询部分列的数据并返回,等同于select 列1,列2 from table | QuerySet容器对象,内部存放字典,每个字典代表一条数据;格式为{‘列1’:值1,‘列2’:值2} |
values_list (‘列1’, ’列2‘) |
MyModel.objects.values_list() | 返回元组形式的查询结果,等同于select 列1,列2 from xxx | QuerySet容器对象,内部存放元组,会将查询出来的数据封装到元组中,再封装到查询集合QuerySet中,如果需要将查询结果取出,需要使用索引的方式取值 |
order_by (‘列1’, ’列2‘) |
MyModel.objects.order_by() | 与all()方法不同,它会用SQL语句的ORDER BY子句对查询结果进行根据某个字段选择性的进行排序,默认是按照升序排序,降序排序需要在列前面增加’-‘表示 | QuerySet容器对象,内部存放MyModel实例,将实例按指定字段进行排序(等同于MyModel.objects.all().order_by(‘列1’)) |
filter(条件) | MyModel.objects.filter(属性1=值1,属性2=值2)多个属性在一起时为”与“关系 | 返回包含此条件的全部的数据集 | QuerySet容器对象,内部存放MyModel实例 |
exclude(条件) | MyModel.objects.exclude(条件)多个属性在一起时为”与“关系 | 返回不包含此条件的全部的数据集 | QuerySet容器对象,内部存放MyModel实例 |
get(条件) | MyModel.objects.get(条件) | 返回满足条件的唯一一条数据,查询结果多余一条数据则抛出Model.MultipleObjectsReturned异常,查询结果如果没有数据则抛出Model.DoesNotExist异常 | QuerySet容器对象,内部存放MyModel实例 |
查询谓词 | 说明 | 示例 |
---|---|---|
__exact | 等值匹配 | Author.objects.filter(id__exact==1) 等同于 select * from author where id = 1 |
__contains | 包含指定值 | Author.objects.filter(name__contains='w') 等同于 select * from author where name like '%w%' |
__startwith | 以xxx开始 | Author.objects.filter(name__startwith='w') 等同于 select * from author where name like 'w%' |
__endwith | 以xxx结束 | Author.objects.filter(name__endwith='w') 等同于 select * from author where name like '%w' |
__gt | 大于指定值 | Author.objects.filter(age__gt=50) 等同于 select * from author where age > 50 |
__gte | 大于等于指定值 | Author.objects.filter(age__gte=50) 等同于 select * from author where age >= 50 |
__lt | 小于指定值 | Author.objects.filter(age__lt=50) 等同于 select * from author where age < 50 |
__lte | 小于等于指定值 | Author.objects.filter(age__lte=50) 等同于 select * from author where age <= 50 |
__in | 查找数据是否在指定范围内 | Author.objects.filter(country__in=['中国','日本','韩国']) 等同于 select * from author where country in ['中国','日本','韩国'] |
__range | 查找数据是否在指定的区间范围内 | Author.objects.filter(age__range=(30,50)) 等同于 select * from author where author between 35 and 50 |
更多查询谓词参考官方文档
ORM 更新操作
#示例
#将id大于3的所有图书价格定位0元
books = Book.objects.filter(id__gt==3)
books.update(price=0)
ORM 删除操作
try:
auth = Author.objects.get(id=1)
auth.delete()
except:
print("删除失败")
#删除年龄≥65的全部信息
auths = Author.objects.filter(age__gt=65)
auths.delete()
F对象
from django.db.models import F
f('列名')
Q对象
from django.db.models import Q
Q(条件1) | Q(条件2) #条件1或者条件2成立
Q(条件1) & Q(条件2) #条件1和条件2同时成立
Q(条件1) &~ Q(条件2) #条件1成立且条件2不成立
F对象与Q对象举例见链接
聚合查询和原生数据库操作
from django.db.models import *
Mymodel.objects.aggregate(结果变量名=聚合函数('列'))
{"结果变量名":“值”}
MyModel.objects.values('列1','列2')
QuerySet.annotate(结果变量名=聚合函数('列'))
from books.models import Book
from django.db.models import Count
pub_set = Book.objects.values('pub')
#pub_set =
pub_count_set = pub_set.annotate(mycount=Count('pub'))
#pub_count_set =
原生数据库操作 - raw():django也支持直接用sql语句的方式通信数据库 不建议使用
books = models.Book.objects.raw('select * from bookstore_book')
for book in books:
print(book)
‘1 or 1 = 1’
s1 = Book.objects.raw('select * from bookstore_book where id = s%'%('1 or 1 = 1'))
MyModel.objects.raw(sql语句,拼接参数)
中的拼接参数,拼接参数是一个列表,将所有需要拼接的值写入列表中s1= Book.objects.raw('select * from bookstore_book where id = %s',['1 or 1 = 1'])
原生数据库操作 - cursor():django也支持直接用sql语句的方式通信数据库
from django.db import connection
from django.db import connection
with connection.cursor() as cur:
cur.execute('SQL语句','拼接参数')
#用SQL语句将id为10的书的出版社改为“xxx出版社”
from django.db import connection
with connection.cursor() as cur:
cur.execute('update bookstore_book set pub = "xxx出版社" where id = 10;')
拓展知识
#在Book模型类中定义:
def __str__(self):
return '%s_%s_%s_%s'%(self.title,self.price,self.pub,self.market_price) #自定义输出格式
#在shell模式下得到如下显示
>>> from books.models import Book
>>> a1 = Book.objects.all()
>>> a1
<QuerySet [<Book: python_20.00_清华大学出版社_25.00>, <Book: Django_70.00_清华大学出版社_75.00>, <Book: JQuery_90.00_机械工业出版社_85.00>, <B出版社_65.00>, <Book: HTML5_90.00_清华大学出版社_105.00>]>
You are trying to add a non-nullable field 'pub' to book without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows with a null value for this column)
2) Quit, and let me add a default in models.py
Select an option:
sql>drop database mywebdb;
sql>create datebase mywebdb default charset=utf8;
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
#命令行执行
>>>python manage.py createsuperuser
用户名 (leave blank to use 'sixstar'): topgh #此处输入用户名
电子邮件地址: 634890284@qq.com #此处输入邮箱
Password: #此处输入密码 (密码过于简单则会提示密码太简单)
Password (again): #此处再次输入密码
密码长度太短。密码必须包含至少 8 个字符。
这个密码太常见了。
密码只包含数字。
Bypass password validation and create user anyway? [y/N]: y #绕过密码验证并创建用户
Superuser created successfully.
作用:为后台管理界面添加便于操作的新功能
说明:后台管理器继承自django.contrib.admin 里的ModelAdmin类
使用方法:1.在子应用app下的admin.py文件中定义模型管理器类,模型管理器类的名字可自定义,一般按照一个模型对应一个模型管理器类
class XXXXManager(admin.ModelAdmin):
"自定义模型类属性"
绑定注册模型管理器和模型类:
from django.contrib import admin
from .models import *
admin.site.register(YYYY,XXXXManager) #绑定YYYY模型类与管理器类XXXManger
常用模型管理器类的类属性
类属性 | 说明 | 示例 |
---|---|---|
list_display=[field1,field2…] | 用于控制哪些字段会显示在Admin的修改列表页面中 | list_display = ['id','title','pub','price'] |
list_display_links=[field1] | 控制list_display中的字段,哪些可以链接到修改页,默认情况下link加在id上 | list_display_links = ['title'] |
list_filter=[field1,field2…] | 添加过滤器(指定的字段作为过滤条件),添加后会在页面的右侧显示过滤器 | list_filter = ['pub'] |
search_fields=[field1,field2…] | 添加搜索框(指定的字段可进行模糊查询) | search_fields = ['title'] |
list_editable=[field1,field2…] | 用于添加可在列表页编辑的字段,添加的字段必须在list_display中,并且该字段不能在list_display_links中 | list_display = ['id','title','pub','price'] |
例
#file:books/admin.py
from django.contrib import admin
from django.contrib import admin
from .models import Book, Author
admin.site.register(Book) #注册Book模型类
class AuthorManager(admin.ModelAdmin): #自定义author模型管理器类
list_display = ['id','name','age'] #admin后台中修改列表页面显示字段
list_editable = ['age'] #age字段可在列表页编辑
class Meta:
verbose_name = '作者' #admin中显示的模型类名字
verbose_name_plural = verbose_name #单复数同名
admin.site.register(Author,AuthorManager) #绑定模型类与模型管理器类,并注册
更多关于模型管理器类的类属性参考官方文档
一对一【创建表】:
class A(model.Model):
……
class B(model.Model):
属性 = models.OneToOneField(A, ondelete=xxx)
from django.db import models
class Author(models.Model):
name = models.CharField(verbose_name='作者',max_length=11)
class AuthorWife(models.Model):
name = models.CharField(verbose_name='妻子',max_length=11)
author = models.OneToOneField(Author,verbose_name='妻子的丈夫',on_delete=models.CASCADE) #通常外键属性就叫关联的模型类类名小写,在数据库中外键字段名跟类属性名有区别,默认是以类属性名 + '_' +id作为字段名
一对一【创建数据】
author1 = Author.objects.create(name = '王老师')
一对一【查询数据】
#通过wife查找author
from .models import AuthorWife
wife = Wife.objects.get(name='王夫人')
print(wife.name,'的老公是',wife.author.name) #wife.author返回的是wife所关联的author对象,调用author对象的name属性获得对应的值
实例对象.引用类名(小写)
,如:作家的反向引用为’作家对象.wife’;当反向引用不存在时,则会触发异常author1 = Author.objects.get(name='王老师')
author1.wife.name
一对多【创建表】
class A(models.Model): #一
···
class B(models.Model): #多
属性 = models.Foreignkey("一"的模型类,on_delete=xxx) #on_delete指明在删除A时,B表对应字段如何处理
#file:otm/models.py
from django.db import models
class Publisher(models.Model):
"""出版社【一】"""
name = models.CharField('名称',max_length=50,unique=True)
class Book(models.Model):
"""书【多】"""
title = models.CharField('书名',max_length=50)
publisher = models.ForeignKey(Publisher,on_delete=models.CASCADE)
一对多【创建数据】
from .models import *
pub1 = Publisher.objects.create(name="清华大学出版社")
Book.objects.create(title="C++",publisher=pub1) #以类属性名创建数据,需要传入关联外键的对象
Book.objects.create(titel="python",publisher_id=1) #以字段名创键数据,需要传入关联对象的主键值
一对多【查询数据】
正向查询:直接通过外键属性查询 [通过有外键的表查询没有外键的表]
#通过Book查询publisher
abook = Book.objects.get(id=1)
print(abook.title,'的出版社是',abook.publisher.name) #abook.publisher返回的是abook所关联的publisher对象,直接调用publisher对象的name属性
反向查询:没有外键属性的一方,可以调用反向属性(有外键的模型类类名(小写)_set
)查询到关联的另一方
pub1 = Publisher.objects.get(name='清华大学出版社')
books = pub1.book_set.all() #通过book_set获取pub1对应的多个Book数据对象,pub.book_set得到的是RelatedManager关联管理器对象
#books = Book.objects.filter(Publisher=pub1) #也可用此方式获取
print("清华大学出版社出版的书有:")
for book in books:
print(book.title)
多对多【创建表】
属性名 = models.ManyToManyField(MyModel)
#MyModel:多对多关联的模型类
#创建一本书对应多个作家,一个作家对应多本书
class Author(models.Model):
"""作家模型类"""
name = models.CharField('作家',max_length=20)
def __str__(self):
return self.name
class Book(models.Model):
"""书模型类"""
title = models.CharField('书名',max_length=20)
authors = models.ManyToManyField(Author) #设置多对多属性,设置在任意模型类中均可
def __str__(self):
return self.title
多对多【创建数据】
author1 = Author.objects.create(name='王老师')
author2 = Author.objects.create(name='李老师')
#王老师和李老师共同写了一本书python
book1 = author1.book_set.create(title='python')
author2.book_set.add(book1) #add一个obj
book = Book.objects.create(title='python1')
#郭老师和李老师都参与了python1 的创作
author3 = book.authors.create(name='郭老师')
book.authors.add(author1)
多对多【查询数据】
book.authors.all() -> 获取 book 对应的所有的author的信息
book.authors.filter(age__gt=80) -> 获取book对应的作者中年龄大于80岁的作者的信息
author.book_set.all()
author.book_set.filter()
HttpResponse.set_cookie(key,value='',max_age=None,expires=None)
#为浏览器添加键为my_var1,值为123,过期时间为1小时的cookie
response = HttpReponse('已添加名为my_var1的cookie')
response.set_cookie('may_var1','123',3600)
return response
#为浏览器添加键为my_var1,修改值为456,过期时间为2小时的cookie
response = HttpResponse('已修改名为my_var1的cookie')
response.set_cookie('may_var1','456',3600*2)
return response
HttpResponse.delete_cookie(key)
value = request.COOKIES.get('cookies名','默认值')
#可用get()方法获取也可用索引方式获取定义:session是在服务器上开辟一段空间用于保留浏览器和服务器交互的重要数据
实现方式:
settings.py 中配置session(默认是自动配置的)
'django.contrib.sessions'
python manage.py clearsessions
来删除过期的session数据session的使用
request.session['KEY']:VALUE
value = request.session['KEY']
;value = request.session.get('KEY',默认值)
del request.session['KEY']
Cookies与Session对比
种类 | 存储位置 | 安全性 | 用途 |
---|---|---|---|
Cookies | 浏览器 | 相对不安全 | 一般用于存储长期数据 |
session | 服务器 | 相对安全 | 一般用于存储短期数据 |
缓存
from django.shortcuts import render
def index(request):
#时间复杂度极高的渲染
book_list = Book.objects.all() #-> 此处假设用时2s
return render(request,'index.html',local()) #此处的页面渲染会在book_list查询到结果后才执行
given a URL, try finding that page in the cache
if the page is in the cache:
return the cached page
else:
generate the page
save the generated page in the cache (for next time)
return the generated page
django中设置缓存 - 数据库缓存
CACHES = {
'default':{
'BACKEND':'django.core.cache.backends.db.DatabaseCache', #引擎
'LOCATION':'my_cache_table',#设置缓存使用的数据表,设置表名为:my_cache_table
'TIMEOUT':300 #设置缓存保存时间 单位秒,默认值为300
'OPTIONS':{
'MAX_ENTRIES':300, #缓存最大数据条数
'CULL_FREQUENCY':2 #缓存达到最大条数时,删除1/X的缓存条数
}
}
}
python manage.py createcachetable
,表名为CACHES里面设置的表名django中设置缓存 - 本地内存缓存
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake', #内存寻址 - 使用的是雪花算法
}
}
django中设置缓存 - 文件系统缓存
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
'LOCATION': '/var/tmp/django_cache', #缓存文件夹的路径
# 'LOCATION': 'c:\test\cache', #windows 下示例
}
}
django中使用缓存 - 整体缓存策略
from django.views.decorators.cache import cache_page
@cache_page(30) -> 单位s,当前视图缓存有效期
def my_view(request):
···
from django.views.decorators.cache import cache_page
urlpatterns = [
path('page/',cache_page(60)(my_view)),#逻辑与在试图函数中使用相同
]
django中使用缓存 - 局部缓存
使用缓存API引入
from django.core.cache import caches
cache1 = caches['default'] #default -> settings文件中配置的CACHES的键名
cache2 = caches['myalias']
from django.core.cache import cache
缓存API相关方法:
作用 | 参数 | 返回值 |
---|---|---|
cache.set(key,value,timeout) | 存储缓存 | key : 缓存的key,字符串类型 value : python对象 timeout : 缓存存储时间(s),默认为CACHES中的TIMEOUT值 |
cache.get(key) | 获取缓存 | key : 缓存的key,字符串类型 |
cache.add(key, value) | 存储缓存,只在key不存在时生效 | key : 缓存的key,字符串类型 value : python对象 |
cache.get_or_set(key,value,timeout) | 如果未获取到数据则执行set操作 | key : 缓存的key,字符串类型 value : python对象 timeout : 缓存存储时间(s),默认为CACHES中的TIMEOUT值 |
cache.set_many(dict,timeout) | 批量存储缓存 | dict : 缓存的key和value的字典 timeout : 缓存时间(s) |
cache.get_many(key_list) | 批量获取缓存 | key_list : 包含key的列表 |
cache.delete(key) | 删除key的缓存数据 | key : 缓存的key,字符串类型 |
cache.delete_many(key) | 批量删除缓存 | key_list : 包含key的列表 |
浏览器缓存策略
编写中间件:
django.utils.deprecation.MiddlewareMixin
类process_request(self,request)
:
process_view(self,request,callback,callback_args,callback_kwargs)
:
process_response(self,request,response)
:
process_exception(self,request,exception)
:
process_template_response(self,request,response)
:
注册中间件:
#file:settings.py
MIDDLEWARE= [
···
'MyMiddleWare',
]
示例:
#file:mymiddleware.py
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin
import re
class MWare(MiddlewareMixin):
count_dict = {} #创建用于统计次数的字典
def process_request(self,request):
request_ip = request.META['REMOTE_ADDR'] #获取请求IP
request_url = request.path_info #获取请求URL
if re.match(r'^/test',request_url): #匹配请求是否以/test开头
times = self.count_dict.get(request_ip,0) #查询当前IP的请求次数,默认为0
self.count_dict[request_ip]= times + 1 #请求次数 + 1
if times < 5: #如果请求次数<5次,请求正常通过
return
else: #如果请求次数>5次,则返回HttpResponse,阻止请求
return HttpResponse("访问次数超过5次,请求失败")
else: #如果不是以/test开头则直接正常通过
return
#file:settings.py
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'middleware.mymiddleware.MWare',
]
{% csrf_token %}
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>CSRF-TEXTtitle>
head>
<body>
<form action="/test_csrf" method="post">
{% csrf_token %}
<input type="text" name="test">
<input type="submit" value="提交" name="key">
form>
body>
html>
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt #使用装饰器关闭csrf对此试图的检查
def my_view(request):
return HttpResponse('hello world')
django.core.paginator
模块中paginator = Paginator(object_list , per_page)
#file:urls.py
from django.urls import path
from . import views
urlpatterns = [
path('test_page', views.test_page), #创建路由
]
#file:views.py
def test_page(request):
#请求页数从URL中获取
p = request.GET.get('page',1)
data_list = [str(i) for i in range(20)] #生成列表数据
#创建Paginator对象
paginator = Paginator(data_list,per_page=2)
#创建page对象
page = paginator.page(int(p))
return render(request,'test_page.html',locals())
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>分页显示title>
head>
<body>
{% for p in page.object_list %}
{{ p }}
{% endfor %}
{% if page.has_previous %}
<a href="/test_page?page={{ page.previous_page_number }}"> 上一页a>
{% else %}
<p>上一页p>
{% endif %}
{% for pg in paginator.page_range %}
{% if pg == page.number %}
{{ pg }}
{% else %}
<a href="test_page?page={{ pg }}">{{ pg }}a>
{% endif %}
{% endfor %}
{% if page.has_next %}
<a href="/test_page?page={{ page.next_page_number }}"> 下一页a>
{% else %}
<p>下一页p>
{% endif %}
body>
html>
import csv
with open('eggs.csv','w',newline='') as csvfile:
writer = csv.writer(csvfile) #创建writer对象
writer.writerow(['a','b','c']) #以行的方式写入数据,
import csv
from django.http import HttpResponse
from django.shortcuts import render
from books.models import Book
def make_csv_view(request):
response = HttpResponse(content_type = 'text/csv') #修改content-type
response['Content-Disposition'] = 'attachment;filename="mybook.csv"'
all_book = Book.objects.all()
writer = csv.writer(response)
writer.writerow(['id','title'])
for b in all_book:
writer.writerow([b.id,b.book_name])
return response
定义:django带有一个用户认证系统,用于处理用户账号、组、权限以及基于cookie的用户会话
用户可直接使用django自带的用户表
详细信息参考官方文档
基本字段:模型位置:from django.contrib.auth.models import User
Field | Type | Null | Key | Default | Extra | info |
---|---|---|---|---|---|---|
id | int(11) | NO | PRI | NULL | auto_increment | id |
password | varchar(128) | NO | NULL | 密码 | ||
last_login | datetime(6) | YES | NULL | 上一次的登陆时间 | ||
is_superuser | tinyint(1) | NO | NULL | 是否是管理员账号(/admin) | ||
username | varchar(150) | NO | UNI | NULL | 用户名 | |
first_name | varchar(150) | NO | NULL | 名 | ||
last_name | varchar(150) | NO | NULL | 姓 | ||
varchar(254) | NO | NULL | 邮箱 | |||
is_staff | tinyint(1) | NO | NULL | 是否可以访问admin管理界面 | ||
is_active | tinyint(1) | NO | NULL | 是否是活跃用户,默认为True, 一般不删除用户,而是将用户的is_active设为False |
||
date_joined | datetime(6) | NO | NULL | 用户创建的时间 |
模型操作 - 创建用户
from django.contrib.auth.models import User
user = User.objects.create_user(username, email, password) #username、password为必填项
from django.contrib.auth.models import User
user = User.objects.create_superuser(username, email, password) #username、password为必填项
模型操作 - 删除用户
from django.contrib.auth.models import User
try:
user = User.objects.get(username='用户名')
user.is_active = False
user.save()
print("删除普通用户成功")
except:
print("删除普通用户失败")
模型操作 - 校验密码
from django.contrib.auth import authenticate
user = authenticate(username=username,password=password)
模型操作 - 修改密码
from django.contrib.auth.models import User
try:
user = User.objects.get(username='用户名')
user.set_password('654321')
user.save()
return HttpResponse("密码修改成功!")
except:
return HttpResponse("密码修改失败!")
模型操作 - 登陆状态保持
from django.contrib.auth import login
def login_view(request):
user = authenticate(username=username,password=password) #返回校验后的user对象
login(request,user) #传入request和校验后的user对象
模型操作 - 登陆状态校验
LOGIN_URL = '跳转url'
from django.contrib.auth.decorators import login_required
@login_required #装饰的视图必须为用户登陆状态下才可访问
def index_view(request):
login_user = request.user #获取当前登陆的用户
···
模型操作 - 登陆状态取消
from django.contrib.auth import logout
def logout_view(request):
logout(request) #直接传入request即可
扩展内建用户表的字段
from django.db import models
from django.contrib.auth.models import AbstractUser
class UserInfo(AbstractUser): #继承自AbstractUser
phone = models.CharField(max_length = 11,default = '') #新添加的字段
#file:settings.py
AUTH_USER_MODEL = 'user.UserInfo'
添加用户(与原User使用方法相同)from user.models import UserInfo
UserInfo.onjects.create_user(username='xxx',password='123456',phone='13344445555')
中文件上传时必须带有enctype="multipart/form-data"
时才会包含文件内容数据
标签上传文件request.FILES
获取文件框的内容request.POST
去取#file:settings.py
MEDIA_URL = '/media/' #配置上传文件的访问路径
MEDIA_ROOT = os.path.join(BASE_DIR,'media') #配置文件存储路径
from django.conf import settings
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL,document_root = settings.MEDIA_ROOT)
@csrf_exmpt #取消csrf对以下视图函数的验证
def upload_view(request):
if request.method == 'GET':
return render(request,'test_upload.html')
elif request.methof == 'POST':
a_file = request.FILES['myfile'] #获取上传文件的文件流对象
print("上传的文件名是:",a_file.name)
filename = os.path.join(settings.MEDIA_ROOT,a_file.name) #j将上传的文件名与文件存储路径进行拼接
with open(filename,'w') as f:
data = a_file.file.read() #使用read()方法将a_file.file字节流数据读取出来
f.write(data) #写入读取出来的数据
return HttpResponse("接收文件:",a_file.name + "成功")
@csrf_exempt
def upload_view_dj(request):
if request.method == 'GET':
return render(request,'test_upload.html')
elif request.method == 'POST':
title = request.POST['title']
a_file = request.FILES['myfile']
Content.objects.create(desc=title,myfile=a_file) #desc、myfile为Content模型类中的字段名
return HttpResponse("----upload is ok----")
# 1.file:settings.py , 配置MEDIA_URL和MEDIA_ROOT
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR,'media') #在项目根目录下创建media 文件夹用于存放上传的文件
# 2.file:urls.py , 绑定上传文件的路由与存放目录
from django.conf.urls.static import static
from django.urls import path
from test_upload import views
from django.conf import settings
urlpatterns = [
path('test_upload',views.upload_view)
]
urlpatterns += static(settings.MEDIA_URL,document_root = settings.MEDIA_ROOT)
# 3.file:models.py , 创建模型类
from django.db import models
class test_upload(models.Model):
title = models.CharField(verbose_name='标题',max_length=20)
mfile = models.FileField(verbose_name='media')
# 4.file:views.py , 创建视图函数
from django.http import HttpResponse
from django.shortcuts import render
from django.views.decorators.csrf import csrf_exempt
from .models import test_upload
@csrf_exempt
def upload_view(request):
if request.method == 'GET':
return render(request,'test_upload.html')
elif request.method == 'POST':
title = request.POST['title']
m_file = request.FILES['myfile']
test_upload.objects.create(title=title,mfile=m_file)
return HttpResponse('----文件上传成功----')
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传title>
head>
<body>
<form action="test_upload" method="post" enctype="multipart/form-data">
<input type="text" name="title">
<input type="submit" value="提交">
<input type="file" name="myfile">
form>
body>
html>
#file:settings.py 发送邮件相关配置
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' # 发送邮件的引擎
EMAIL_HOST = 'smtp.qq.com' # 发送邮件的SMTP服务器地址
EMAIL_PORT = 25 #SMTP服务的端口号
EMAIL_HOST_USER = '[email protected]' #发送邮件的邮箱账号
EAMIL_HOST_PASSWORD = '********' #第三方登录邮箱的授权码
EMAIL_USE_TLS = False #与SMTP服务器通信时,是否启动TLS链接(安全链接)默认为False
from django.core import mail
mail.send_mail(
subject, #邮件主题
message, #邮件内容
from_email, #发送人【当前配置邮箱】
recipient_list=['[email protected]','[email protected]'],#邮件接收人列表
)
sudo scp /home/path1········/mysite1 [email protected]:/home/root/path2
path1 为本地项目路径,path2为迁移到服务器中存放的路径定义:是WSGI中的一种,它实现了http协议、WSGI协议以及uwsgi协议,uWSGI功能完善,支持协议众多,主要以学习配置为主
uWSGI安装:ubuntu执行 sudo pip install uwsgi==2.0.18 -i https://pypi.tuna.tsinghua.edu.cn/simple/
sudo pip freeze|grep -i 'uwsgi'
如果成功安装则会显示对应的版本配置uWSGI
mysite1/mysite1/uwsgi.ini
;正式项目的时候一般将这个配置文件命名为跟项目同名.ini#file:uwsgi.ini
[uwsgi] #文件开头第一行必须写这个
socket = 127.0.0.1:8000 #套接字方式的IP地址:端口号,此模式必需要有nginx
http = 127.0.0.1:8000 #http通信方式的IP地址:端口号
chdir = /home/···/my_project #项目当前工作目录
wsgi-file = my_project/wsgi.py #项目中wsgi.py文件的目录,相对于当前工作目录;my_project是项目目录下的同名文件夹
process = 4 #进程数
threads = 2 #每个进程的线程个数
pidfile = uwsgi.pid #pid->主进程的ID,uwsgi启动后会pid写进一个文件里,pidfile用于指明文件的位置,启动后会在启动目录下生成一个uwsgi.pid文件
daemonize = uwsgi.log #服务日志文件的位置,以后台启动,并且将日志文件输出到启动目录下的uwsgi.log中
master = True #开启主进城管理模式
启动uwsgi:切换到uWSGI配置文件所在目录执行uwsgi --ini uwsgi.ini
‘–ini’ 为初始化uwsgi
停止uwsgi:切换到uWSGI配置文件所在目录执行uwsgi --stop uwsgi.pid
查看是否启动成功:ps aux|grep 'uwsgi'
查看是否有进程启动,无论是启动还是关闭,都需要执行此命令确认是否符合预期
注意:启动成功后,进程在后台执行,所有日志均输出在配置文件所在目录的uwsgi.log中;Django中代码有任何修改,均需要重新启动uwsgi
uWSGI常见问题汇总
问题 | 原因 | 解决方案 |
---|---|---|
启动失败 | 端口被占用,其它进程占用了uWSGI启动的端口 | 执行sudo lsof -i:端口号 查询出具体进程;执行sudo kill -9 端口号 ,杀掉进程,重新启动uWSGI即可 |
停止失败 | 重复启动uWSGI导致pid文件中的进程号失准 | ps 出uWSGI进程,手动kill掉 |
定义:nginx是轻量级的高性能web服务器,提供了诸如http代理和反向代理、负载均衡等一系列重要特性;采用c语言编写,执行效率高
作用:
原理:客户端请求nginx,再由nginx将请求转发uWSGI运行的django
安装:sudo apt install nginx
(更换国内源 : vim /etc/apt/sources.list 更改国内源 sudo apt-get update
) -> 安装完毕后在ubuntu终端中输入nginx -v
会显示nginx相应的版本号;安装完毕后nginx会自动启动,并占用80端口
配置:修改nginx的配置文件:/etc/nginx/sites-enabled/default
; sudo vim default
server{
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
# try_files $uri $uri/ =404; # nginx可以通过请求路由自动去查找html文件,查找目录默认为: root /var/www/html,如果未找到会报404错误
uwsgi_pass 127.0.0.1:8000; #所有用‘/’开头的路由都重定向到127.0.0.1的8000端口,
include /etc/nginx/uwsgi_params; #如果要使用uwsgi协议就必须将所有的参数转到uwsgi下
}
}
启动/停止 nginx
sudo /etc/init.d/nginx/ start|stop|restart|status
或sudo service nginx start|stop|restart|status
uWSGI修改配置:nginx负责接收请求,并把请求转发给后面的uWSGI,此模式下,uWSGI需要以socket模式启动;
#file:uwsgi.ini
[uwsgi] #文件开头第一行必须写这个
socket = 127.0.0.1:8000 #套接字方式的IP地址:端口号,此模式必需要有nginx
# http = 127.0.0.1:8000 #http通信方式的IP地址:端口号
常见问题排查:
502响应代表nginx反向代理配置成功,但是对应的uWSGI未启动
静态文件配置
/home/···/项目名_static/
#file:settings.py
#配置后,django在收集静态文件时会在项目名_static文件夹下自动新建static文件夹用于收集静态文件
STATIC_ROOT = '/home/···/项目名_static/static'
python manage.py collectstatic
执行该命令后,django会将项目中的所有静态文件收集到STATIC_ROOT中,包括django内建的静态文件
#file:/etc/nginx/site-enabled/default
# 添加localtion /static 路由配置,重定向到静态文件存放目录即可
location /static {
root /home/···/day04_static;
}
DEBUG = False #关闭调试模式
ADMINS = [('收件人姓名','收件人邮箱'),('收件人2姓名','收件人2邮箱')] #错误报告接收方
SERVER_EMAIL = 'email配置中的邮箱' #发送错误报告邮件发件人,默认为root@localhost账户,多数邮件服务器会拒绝此邮件
from django.views.decorators.debug import sensitive_variables
@sensitive_variables('user','pw','cc') #使用装饰器的方式,将需要过滤的字段作为参数传递给装饰器
def process_info(user):
pw = user.password
cc = user.credit_card_number
name = user.name
from django.views.decorators.debug import sensitive_post_parameters
@sensitive_post_parameters('password','username')
def index(request):
s = request.POST['username'] + request.POST['abcd']
# abcd并不存在,此时引发error
#POST中username及password的值会被替换称 ****