一、Django基础
1. 配置静态文件
STATIC_URL = '/static/' # 这个路径访问静态文件
STATICFILESDIRS=(os.path.join(BASEDIR,"static"),) # 配置静态文件路径
2. APP使用
python3 manage.py startapp 应用名称(必须是标识符命令规则)
INSTALLED_APPS = [ ... ..., '自定义应用名称' ] #配置app
# 应用的分布式路由:主路由配置:url(r’^music/’,includ(‘music.urls’))
3. 数据库的使用
模型继承于django.db.models.Model。
DATABASE配置mysql相关信息:‘NAME’、‘USER’、‘PASSWORD’、‘HOST’、‘PORT’。
数据类型:DecimalField,Bollean,char,datetime,float,email,integer,url,image保存图片路径,text。
基础操作
#### 查询
MyModel.objects.filter(属性1=值1, 属性2=值2).get(id=10)
#### 查询返回指定列(元组表示)
MyModel.objects.values_list(...) # 指定字段查询
books = Book.objects.values_list("title", "pub")
#### 排序查询
MyModel.objects.order_by('-列','列') # 反向正向排序
#### 实现批量修改
update(属性 = 值)
#### 除了符合条件的,其他都需要
MyModel.objects.exclude(条件)
聚合查询
#### 不带分组聚合
result = Book.objects.aggregate(myAvg=Avg('price')) # 返回结果为字典{"结果变量名": 值}
#### 带分组聚合
pubset = Book.objects.values('pub').annotate(mycount=Count(‘pub’))
查询谓词
__exact:等值匹配 __contains:包含指定值 __startswith:以...开始
__endswith:以...结束 __gt:大于指定值 __gte:大于等于 __lt:小于
__lte:小于等于 __in:查找数据是否在指定范围内 __range:查找数据是否在指定的区间范围内
F对象
通常是对数据库中的字段值在不获取的情况下进行操作。模型内多字段比较。
from django.db.models import F
F('列名')
models.Book.objects.all().update(market_price=F('market_price')+10) # 例子
Q对象
Q对象通常会与另一个Q对象进行比较,使用逻辑或 |
、 逻辑非 ~
来连接。
models.Book.objects.filter(Q(price__lt=20)|Q(pub="清华大学出版社"))
数据表关联关系映射
一对一关系:
class Author(models.Model):
'''作家模型类'''
name = models.CharField('作家', max_length=50)
class Wife(models.Model):
'''作家妻子模型类'''
name = models.CharField("妻子", max_length=50)
author = models.OneToOneField(Author)
#### 创建
author1 = Author.objects.create(name='王老师')
wife1 = Wife.objects.create(name='王夫人', author=author1)
#### 正向查询
wife = Wife.objects.get(name='王夫人')
#### 反向查询
author1 = Author.objects.get(name='王老师')
wife = author1.wife.name
一对多关系:
class Publisher(models.Model):
'''出版社'''
name = models.CharField('名称', max_length=50, unique=True)
class Book(models.Model):
title = models.CharField('书名', max_length=50)
publisher = ForeignKey(Publisher)
#### 数据查询
abook = Book.objects.get(id=1)
pub1 = Publisher.objects.get(name='清华大学出版社')
books = pub1.book_set.all() # 通过book_set 获取pub1对应的多个Book数据对象
多对多映射:
class Author(models.Model):
...
class Book(models.Model):
...
authors = models.ManyToManyField(Author)
#### 数据查询
author = book.authors.get() -> 获取book对应的所有的author的信息
author.book_set.all()
author.book_set.create(...) # 创建新书并关联author
author.book_set.add(book) # 添加已有的书给当前作者author
author.book_set.clear() # 删除author所有并联的书
3. Admin后台
创建后台管理账号
创建管理员账号:python3 manage.py createsupperuser
后台管理的登录地址:http://127.0.0.1:8000/admin
$ python3 manage.py createsuperuser
Username (leave blank to use 'tarena'): tarena # 此处输入用户名
Email address: laowei@tedu.cn # 此处输入邮箱
Password: # 此处输入密码(密码要复杂些,否则会提示密码太简单)
Password (again): # 再次输入重复密码
Superuser created successfully.
$
修改后台Models展现形式
在自定义模型类中重写**__ str __ (self)**方法返回显示文字内容:
class Book(models.Model):
def __str__(self):
return "书名" + self.title
模型管理器类
先在<应用app>/admin.py
里定义模型管理器类,再注册管理器与模型类关联。
from django.contrib import admin
from . import models
class BookManager(admin.ModelAdmin):
list_display = ['id', 'title', 'price', 'market_price']
admin.site.register(models.Book,BookManager) # 注册models.Book 模型类与管理器类BookManager关联
高级管理功能:
- list_display去控制哪些字段会显示在Admin 的修改列表页面中。
- list_display_links 可以控制 list_display中的字段是否应该链接到对象的“更改”页面。
- list_filter 设置激活Admin 修改列表页面右侧栏中的过滤器
- search_fields 设置启用Admin 更改列表页面上的搜索框。
- list_editable 设置为模型上的字段名称列表,这将允许在更改列表页面上进行编辑。
后台模型名显示
class Book(models.Model):
title = CharField(....)
class Meta:
db_table = '数据表名' # 该模型所用的数据表的名称。(设置完成后需要立马更新同步数据库)
4. Cookie和Session
cookies:存储在浏览器中。按域存储,彼此隔离,有生命周期。每次访问地址时,只要cookie中有数据,浏览器都会在请求头中都会携带上cookie内容。服务器中添加cookie告知浏览器,触发存储cookies。删除cookie实质是修改cookie有效时间。
HttpResponse.set_cookie(key, value='', max_age=None, expires=None)
# key:cookie的名字
# value:cookie的值
# max_age:cookie存活时间,秒为单位
# expires:具体过期时间
# 当不指定max_age和expires 时,关闭浏览器时此数据失效
HttpResponse.delete_cookie(key)
# 删除指定的key 的Cookie。如果key 不存在则什么也不发生。
session:存储在服务端数据库中,将生成的id加入cookie中发送给浏览器。记住密码,需要在session和cookie中分别加入username,请求时先验证session,再验证cookie。
Django中使用session:在settings.py中INSTALLED_APPS列表中添加应用,MIDDLEWARE列表中添加中间件。
INSTALLED_APPS = [ 'django.contrib.sessions' ] # 启用 sessions 应用
MIDDLEWARE=['django.contrib.sessions.middleware.SessionMiddleware'] # 启用Session中间件
request.session[‘uname’]=uname
VALUE = request.session['KEY']
del request.session['KEY']
5. HttpRequest对象
request.path_info # 获取URL字符串;
request.scheme # 获取请求协议('http'/'https')
request.getfullpath() # 获取请求的完整路径
request.get_host() # 获取请求的主机
request.META # 获取请求中的元数据(消息头)
request.META['REMOTE_ADDR'] # 获取客户端IP地址
Request.body # 获取请求体的内容(POST或PUT),字符串。
6. HttpResponse对象
'text/html'(默认的,html文件)
'text/plain'(纯文本)
'text/css'(css文件)
'text/javascript'(js文件)
'multipart/form-data'(文件提交)
'application/json'(json传输)
'application/xml'(xml文件)
HttpResponse(content = 响应体, content_type = 响应体数据类型, status = 状态码)
7. 缓存
数据库缓存
Django可以将其缓存的数据存储在您的数据库中,需要在settings.py中进行配置。
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
'LOCATION': 'my_cache_table',
'TIMEOUT': 300, #缓存保存时间 单位秒,默认值为300,
'OPTIONS':{
'MAX_ENTRIES': 300, #缓存最大数据条数
'CULL_FREQUENCY': 2,#缓存条数达到最大值时删除1/2的缓存数据
}
}
}
创建缓存表
python manage.py createcachetable
本地内存缓存
CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
'LOCATION': 'unique-snowflake'
}
}
使用缓存存储中间值
from django.core.cache import cache
cache.set('my_key', 'myvalue', 30) #30秒
cache.get('my_key','default值')
cache.get_or_set('my_key', 'value', 10)
cache.set_many({'a': 1, 'b': 2, 'c': 3})
cache.get_many(['a', 'b', 'c'])
cache.delete('my_key')
在视图View中使用缓存
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('foo/', cache_page(60)(my_view)) ]
自定义缓存方法
通过三次装饰器函数来实现自定义缓存存储。Django.cache会触发浏览器缓存。
像商品细节这种变化多的通过装饰器 @cachecheck(keyprefix=‘gd’, keyparam=‘sku_id’, cache=‘goods_detail’, ex=300)来处理数据缓存。
def cachecheck(**cachekwargs):
def outter(func):
def inner(self, request, *args, **kwargs):
CACHE = caches['default']
if 'cache' in cachekwargs:
CACHE = caches[cachekwargs.get('cache')]
ex = cachekwargs.get('ex', 60)
keyprefix = cachekwargs.get('keyprefix')
keyparam = cachekwargs.get('keyparam')
if keyparam not in kwargs:
raise
cachekey = keyprefix + kwargs.get(keyparam)
res = CACHE.get(cachekey)
if res:
return res
res = func(self, request, *args, **kwargs)
CACHE.set(cachekey, res, ex)
return res
return inner
return outter
8. 中间件
注册中间件:
# file : settings.py
MIDDLEWARE = [
...
]
使用中间件:
django.utils.deprecation.MiddlewareMixin
# 执行路由之前被调用,在每个请求上调用,返回None或HttpResponse对象
def process_request(self, request):
# 调用视图之前被调用,在每个请求上调用,返回None或HttpResponse对象
def process_view(self, request, callback, callback_args,
callback_kwargs):
# 所有响应返回浏览器 被调用,在每个请求上调用,返回HttpResponse对象
def process_response(self,request, response):
# 当处理过程中抛出异常时调用,返回一个HttpResponse对象
def process_exception(self, request, exception):
# 在视图刚好执行完毕之后被调用,在每个请求上调用,返回实现了render方法的响应对象
def process_template_response(self, request, response):
9. 分页
视图函数:
from django.core.paginator import Paginator
def book(request):
bks = Book.objects.all()
paginator = Paginator(bks, 10)
cur_page = request.GET.get('page', 1) # 得到默认的当前页
page = paginator.page(cur_page)
return render(request, 'bookstore/book.html', locals())
模板设计:
<body>
{% for b in page %}
<div>{{ b.title }}</div>
{% endfor %}
{# 分页功能 #}
{# 上一页功能 #}
{% if page.has_previous %}
<a href="{% url 'book' %}?page={{ page.previous_page_number }}">上一页</a>
{% else %}
上一页
{% endif %}
{% for p in paginator.page_range %}
{% if p == page.number %}
{{ p }}
{% else %}
<a href="{% url 'book' %}?page={{ p }}">{{ p }}</a>
{% endif %}
{% endfor %}
{#下一页功能#}
{% if page.has_next %}
<a href="{% url 'book' %}?page={{ page.next_page_number }}">下一页</a>
{% else %}
下一页
{% endif %}
</body>
10. 文件上传与下载
文件上传必须为POST提交方式。
<form method="post" action="/upload" enctype="multipart/form-data">
<input type="file" name="myfile"/><br>
<input type="submit" value="上传">
</form>
Setting中设置:
MEDIA_ROOT = os.path.join(BASE_DIR, 'static/files')
Views中:
a_file = request.FILES['myfile']
filename =os.path.join(
settings.MEDIA_ROOT
, a_file.name)
with open(filename, 'wb') as f:
data = a_file.file.read()
f.write(data)
文件下载通过改变response头的方式。
response = HttpResponse(content_type ='text/csv')
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.title])
return response
二、项目
1. Token令牌
为了解决前后端分离后,http无状态,同时web、安卓、苹果能公用一套后台。Cookie是web技术。
base64加密
-
将字符串拆成每三个字符一组。
-
计算每一个字符对应的ASCII码二进制。
-
将8位的二进制码,按照每6位一组重新分组,不足6位的在后面补0。
-
计算对应的十进制编码。
-
不足四组的,用“=”表示。
生成Token令牌
base64加密:
import base64
s = b'jasonchu'
b_s = base64.b64encode(s) #base64加密
ss = base64.b64decode(b_s) #base64解密
sha256安全散列算法:
import hashlib
s = hashlib.sha256() #创建sha256对象
s.update(b'xxxx') #添加欲hash的内容,类型为 bytes
s.digest() #获取最终结果
HMAC-SHA256结合散列与秘钥:
import hmac
#生成hmac对象
#第一个参数为加密的key,bytes类型,
#第二个参数为欲加密的串,bytes类型
#第三个参数为hmac的算法,指定为SHA256
h = hmac.new(key, str, digestmod='SHA256 ')
h.digest() #获取最终结果
Jwt-token的生成:
import hmac
headers={‘alg’:’HS256’, # 算法
’typ’:’JWT’} # 类型
payload={ #公有声明
'exp':xxx, #过期时间的时间戳
'iss':xxx, #token的签发者
'aud':xxx, # token签发面向群体
'iat':xxx } # 创建的时间戳
signature=hmac.new(key,base64后的header+'.'+ base64后的payload) #最后返回三部分拼接出来的字节串。
2. Cors跨域
简单请求:
1.请求方法如下:
GET or HEAD or POST
2.请求头仅包含如下:
Accept
Accept-Language
Content-Language
Content-Type
3.Content-Type 仅支持如下三种:
application/x-www-form-urlencoded
multipart/form-data
text/plain
步骤:
1.请求:请求头中携带Origin,该字段表明自己来自哪个域。
2.响应:如果请求头中的Origin在服务器接受范围内,则返回如下头:
Access-Control-Allow-Origin|服务器接受得域
预检请求:
请求头:必须有!!!
Origin | 表明此请求来自哪个域
Access-Control-Request-Method|此次请求使用方法
Access-Control-Request-Headers | 此次请求使用的头
响应头:
Access-Control-Allow-Origin |同简单请求
Access-Control-Allow-Methods |告诉浏览器,服务器接受得跨域请求方法
Access-Control-Allow-Headers | 返回所有支持的头部
Access-Control-Allow-Credentials |同简单请求
Access-Control-Max-Age | OPTION请求缓存时间,单位s 过期时间设定!!!
主请求阶段:
Origin | 表明此请求来自哪个域
主请求响应阶段:
Access-Control-Allow-Origin | 当前服务器接受得域。
每次都需要先预检请求,再进行主请求!!!
所以一般设置请求缓存时间为一天。
Django中应用cors:
1. INSTALLED_APPS 中添加 corsheaders
2. MIDDLEWARE 中添加 corsheaders.middleware.CorsMiddleware,官方建议
‘django.middleware.common.CommonMiddleware’ 上方
3. CORS_ORIGIN_ALLOW_ALL 布尔值 如果为True 白名单不启用
4. CORS_ORIGIN_WHITELIST =[ "https://example.com" ]
5. CORS_ALLOW_METHODS = ( 'DELETE', 'GET', 'OPTIONS', 'PATCH', 'POST', 'PUT', )
6. CORS_ALLOW_HEADERS = ( 'accept-encoding', 'authorization', 'content-type', 'dnt',
'origin', 'user-agent', 'x-csrftoken', 'x-requested-with', )
7. CORS_PREFLIGHT_MAX_AGE 默认 86400s
8. CORS_EXPOSE_HEADERS []
CORS_ALLOW_CREDENTIALS 布尔值, 默认False
3. Django+redis的使用
# 在settings中配置:导入from django_redis import get_redis_connect
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
4. Celery使用
Task就是需要执行的任务,broker消息中间件(RQ,redis),backend用于储存结果,worker消费执行任务。
5. 第三方授权登录
先向授权页面发送请求,再通过后,三方发送过来code,接受到code再组合成固定url,去三方服务器请求数据。
微博授权
配置项:
WEIBO_CLIENT_ID = '415587****'
WEIBO_CLIENT_SECRET = 'cd8f41ba1cb718572386912dd57*****'
# 获取code成功后,跳转的地址
WEIBO_REDIRECT_URI = 'http://127.0.0.1:7000/dadashop/templates/callback.html'
请求授权code:
params = {'response_type': 'code',
'client_id': settings.WEIBO_CLIENT_ID,
'redirect_uri': settings.WEIBO_REDIRECT_URI }
weibo_url = 'https://api.weibo.com/oauth2/authorize?'
url = weibo_url + urlencode(params)
请求数据:
req = {'client_id': settings.WEIBO_CLIENT_ID,
'client_secret': settings.WEIBO_CLIENT_SECRET,
'grant_type': 'authorization_code',
'redirect_uri': settings.WEIBO_REDIRECT_URI,
'code': code }
response = requests.post('https://api.weibo.com/oauth2/access_token', data=req)
6. 邮件发送
配置:
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' # 固定写法
EMAIL_HOST = 'smtp.qq.com' # 腾讯QQ邮箱 SMTP 服务器地址
EMAIL_PORT = 25 # SMTP服务的端口号
EMAIL_HOST_USER = 'xxxx@qq.com' # 发送邮件的QQ邮箱
EMAIL_HOST_PASSWORD = '******' # “POP3/IMAP的授权码
View中实现:
from django.core import mail
mail.send_mail( subject, #题目
message, # 消息内容
from_email, # 发送者[当前配置邮箱]
recipient_list=['xxx@qq.com'] ) # 接收者邮件列表
7. 上传图片
# 在debug=True的时候,当请求到达后(127.0.0.1:8000/media/a.jpg),django去media_root配置下找相应的资源文件。
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
三、项目上线
1. 项目部署
配置uWSGI:
[uwsgi]
# 套接字方式的 IP地址:端口号
# socket=127.0.0.1:8000
# Http通信方式的 IP地址:端口号
http=127.0.0.1:8000
# 项目当前工作目录
chdir=/home/tarena/.../my_project 这里需要换为项目文件夹的绝对路径
# 项目中wsgi.py文件的目录,相对于当前工作目录
wsgi-file=my_project/wsgi.py
# 进程个数
process=4
# 每个进程的线程个数
threads=2
# 服务的pid记录文件
pidfile=uwsgi.pid
# 服务的目志文件位置
daemonize=uwsgi.log
# 开启主进程管理模式
master=true
settings.py中:
将 DEBUG=True 改为DEBUG=False
将 ALLOWED_HOSTS = [] 改为 ALLOWED_HOSTS = ['网站域名'] 或者 ['服务监听的ip地址']
终端启动、停止、查看
Sudo/uwsgi --ini uwsgi.ini 启动uwsgi
Sudo/uwsgi --stop uwsgi.pip 停止
Ps -aux|grep ‘uwsgi’查看进程情况
2. nginx 配置
每次更改后重启sudo /etc/init.d/nginx start|stop|restart|status
- nginx 配置 :
# 修改nginx的配置文件 /etc/nginx/sites-enabled/default
# 在server节点下添加新的location项,指向uwsgi的ip与端口。
server {
location / {
uwsgi_pass 127.0.0.1:8000; # 重定向到127.0.0.1的8000端口
include /etc/nginx/uwsgi_params; # 将所有的参数转到uwsgi下
}
}
- 修改uWSGI配置,重启uwsgi服务:
[uwsgi]
# http=127.0.0.1:8000
# 改为
socket=127.0.0.1:8000
- nginx配置静态文件路径:
(1)创建新路径-主要存放Django所有静态文件如: /home/tarena/项目名_static/
(2)在Django settings.py中添加新配置:
#注意 此配置路径为 存放所有正式环境中需要的静态文件
STATIC_ROOT = '/home/tarena/项目名_static/static
(3)Django项目中执行执行python3 manage.py collectstatic,将项目重所有静态文件复制到 STATIC_ROOT中。
(4)Nginx配置中添加新配置:
# 新添加location/static 路由配置,重定向到指定的第一步创建的路径即可
server {
location /static {
# root 第一步创建文件夹的绝对路径,如:
root /home/tarena/项目名_static;
}
}
3. 邮件告警
当正式服务器上代码运行有报错时,可将错误追溯信息发至指定的邮箱。
配置如下settings.py中:
#在基础邮件配置之后 添加如下
#关闭调试模式
DEBUG = False
#错误报告接收方
ADMINS = [('jasonchu', 'xxxx@example.com'), ('jasonchu', 'xxxx@example.com')]
#发送错误报告方,默认为 root@localhost账户,多数邮件服务器会拒绝
SERVER_EMAIL = 'email配置中的邮箱'
过滤账号、密码等敏感信息:
1.视图函数中的局部变量
from django.views.decorators.debug import sensitive_variables
@sensitive_variables('user', 'pw', 'cc')
def process_info(user):
pw = user.pass_word
cc = user.credit_card_number
name = user.name
#注意:
#1 若报错邮件中牵扯到user,pw,cc等局部变量的值,则会将其替换成 *****, 而name变量还显示其真实值
#2 多个装饰器时,需要将其放在最顶部
#3 若不传参数,则过滤所有局部变量的值
2.POST提交中的数据
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的值会被替换成 ******