django2

Introduction

缓存

什么是缓存?

定义:

缓存是一类可以更快的读取数据的介质统称,也指其它可以加快数据读取的存储方式。一般用来存储临时数据,常用介质的是读取速度很快的内存

意义:

视图渲染有一定成本,数据库的频繁查询过高;所以对于低频变动的页面可以考虑使用缓存技术,减少实际渲染次数;用户拿到响应的时间成本会更低

缓存案例:

from django.shortcuts import render

def index(request):
   # 时间复杂度极高的渲染
   book_list = Book.objects.all()  #-> 此处假设耗时2s
   return render(request, 'index.html', locals())
Copy

优化思想

  • 转自官网

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
Copy

使用缓存场景:

  1. 博客列表页

  2. 电商商品详情页

场景特点:缓存的地方,数据变动频率较少

Django 中缓存配置

  • Django中提供多种缓存方式,如需使用需要在settings.py中进行配置

数据库缓存

Django可以将缓存的数据存储在您的数据库中

说明

  • 尽管存储介质没有更换,但是当把一次负责查询的结果直接存储到表里,比如多个条件的过滤查询结果,可避免重复进行复杂查询,提升效率;

数据库缓存 mysite7 改配置 migrate , 添加缓存配置项 createcachetable

CACHES = {
   'default': {
       'BACKEND': 'django.core.cache.backends.db.DatabaseCache',
       'LOCATION': 'my_cache_table',
       'TIMEOUT': 300,  # 缓存保存时间 单位秒,默认值为300,
       'OPTIONS':{
           'MAX_ENTRIES': 300,  # 缓存最大数据条数
           'CULL_FREQUENCY': 2,  # 缓存条数达到最大值时 删除1/x的缓存数据
      }
  }
}
Copy

创建缓存表

python3 manage.py createcachetable
Copy

本地内存缓存

数据缓存到服务器内存中

CACHES = {
   'default': {
       'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
       'LOCATION': 'unique-snowflake'
  }
}
Copy

文件系统缓存

将缓存的数据存储到本地文件中

CACHES = {
   'default': {
       'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',
       'LOCATION': '/var/tmp/django_cache', # 这个是文件夹的路径
       # 'LOCATION': 'c:\test\cache', # windows下示例
  }
}
Copy

Django 中使用缓存

整体缓存策略

在视图View中使用cache

from django.views.decorators.cache import cache_page

@cache_page(30)  -> 单位s
def my_view(request):
  ...
Copy

在路由中使用

from django.views.decorators.cache import cache_page

urlpatterns = [
   path('foo/', cache_page(60)(my_view) ),
]
Copy

局部缓存策略

缓存案例

from django.shortcuts import render

def index(request):
    # 时间复杂度极高的渲染
    book_list = Book.objects.all() # -> 此处假设耗时 2s
    return render(request, 'index.html', local())
Copy

缓存api 的使用

先引入 cache 对象

方式一:使用caches['CACHE配置key'] 导入具体对象

from django.core.cache import caches

cache1 = caches['myalias']
cache2 = caches['myalias_2']
Copy

方式2 :直接引入 CACHES配置项中的'default'

# 默认配置引入【指的配置中的default项】 等同于 caches['default']
from django.core.cache import cache
Copy

1. cache.set(key, value, timeout) - 存储缓存

# 常规命令 set
# key: 字符串类型
# value: Python对象
# timeout:缓存存储时间  默认值为settings.py CACHES对应配置的TIMEOUT
# 返回值:None
cache.set('my_key', 'myvalue', 30)
Copy

2. cache.get(key) - 获取缓存

# 常规命令 get
# 返回值:为key的具体值,如果没有数据,则返回None
cache.get('my_key')

# 可添加默认值,如果没取到返回默认值
cache.get('my_key', 'default值')
Copy

3.cache.add(key, value) - 存储缓存

# 常规命令 add 只有在key不存在的时候 才能设置成功
# 返回值 True or False
cache.add('my_key', 'value') # 如果my_key已经存在,则此次赋值失效
Copy

4. cache.get_or_set(key, value, timeout)

# 常规命令 get_or_set 如果未获取到数据 则执行set操作
# 返回值 key的值
cache.get_or_set('my_key', 'value', 10)
Copy

5. cache.set_many(dict,timeout) - 批量存储缓存

6. cache.get_many(key_list) - 批量获取缓存数据

# 常规命令 get_many(key_list) set_many(dict,timeout)
# 返回值:
# set_many:返回插入不成功的key数组 
# get_many:取到的key和value的字典
>>> cache.set_many({'a': 1, 'b': 2, 'c': 3})
>>> cache.get_many(['a', 'b', 'c'])
{'a': 1, 'b': 2, 'c': 3}
Copy

7. cache.delete(key) - 删除key的缓存数据

# 常规命令 delete
# 返回值  None
cache.delete('my_key')
Copy

8. cache.delete_many(key_list) - 批量删除

# 常规命令 delete_many
# 返回值  成功删除的数据条数
cache.delete_many(['a', 'b', 'c'])
Copy

浏览器中缓存策略

浏览器也具备缓存技术,对于浏览器来说,每次向服务器发出请求都是耗时的操作,如果本身浏览器内部就具备当前 url 的内容,则一定时间内可以不必给服务器发消息,从而提升网民体验,降低服务器请求压力

 

浏览器缓存分类:

强缓存

不会向服务器发送请求,直接从缓存中读取资源

1. 响应头 - Expires

  • 定义:缓存过期时间,用来指定资源到期的时间,是服务器端的具体的时间点

  • 样例:Expires:Thu, 02 Apr 2030 05:14:08 GMT

2. 响应头 - Cache-Control

在HTTP/1.1中,Cache-Control主要用于控制网页缓存。比如当Cache-Control:max-age=120代表请求创建时间后的120秒,缓存失效

说明:目前服务器都会带着这两个头同时响应给浏览器,浏览器优先使用Cache-Control

协商缓存

强缓存中的数据一旦过期,还需要跟服务器进行通信,从而获取最新数据;

思考?如果强缓存的数据是一些静态文件,大图片等;

解答:考虑到大图片这类比较费带宽且不易变化的数据,强缓存时间到期后,浏览器会去跟服务器协商,当前缓存是否可用,如果可用,服务器不必返回数据,浏览器继续使用原来缓存的数据,如果文件不可用,则返回最新数据

1. Last-Modified响应头 和 If-Modified-Since请求头

说明:

  1. Last-Modified为文件的最近修改时间,浏览器第一次请求静态文件时,服务器如果返回Last-Modified响应头,则代表该资源为需协商的缓存

  2. 当缓存到期后,浏览器将获取到的Last-Modified值做为请求头If-Modified-Since的值,与服务器发请求协商,服务端返回304响应码[响应体为空],代表缓存继续使用,200响应码代表缓存不可用[响应体为最新资源]

2. ETag响应头 和 If-None-Match请求头

说明

  1. Etag是服务器响应请求时,返回当前资源文件的一个唯一标识(由服务器生成),只要资源有变化,Etag就会重新生成;

  2. 缓存到期后,浏览器将 ETag响应头 的值 做为If-None-Match请求头的值,给服务器发请求协商;服务器接到请求头后,比对文件标识,不一致则认为资源不可用,返回200响应码[响应体为最新资源];可用则返回304响应码

Last-Modified VS ETag

  • 精度不一样 - Etag准确率更高

  • 性能上 - Last-Modified 高, Etag需要计算文件标识

  • 优先级 - Etag 高;两个头同时出现,浏览器优先取Etag

 

中间件

定义

  • 中间件是 Django 请求/响应处理的钩子框架。它是一个轻量级的、低级的“插件”系统,用于全局改变 Django 的输入或输出。

  • 中间件以类的形式体现

  • 每个中间件组件负责做一些特定的功能。例如,Django 包含一个中间件组件 AuthenticationMiddleware,它使用会话将用户与请求关联起来。

编写中间件类

  • 中间件类须继承自 django.utils.deprecation.MiddlewareMixin

  • 中间件类须实现下列五个方法中的一个或多个:

    • def process_request(self, request): 执行路由之前被调用,在每个请求上调用,返回None或HttpResponse对象

    • def process_view(self, request, callback, callback_args, callback_kwargs): 调用视图之前被调用,在每个请求上调用,返回None或HttpResponse对象

    • def process_response(self, request, response): 所有响应返回浏览器 被调用,在每个请求上调用,返回HttpResponse对象

    • def process_exception(self, request, exception): 当处理过程中抛出异常时调用,返回一个HttpResponse对象

    • def process_template_response(self, request, response): 在视图函数执行完毕且试图返回的对象中包含render方法时被调用;该方法需要返回实现了render方法的响应对象

    注:中间件中的大多数方法在返回None时表示忽略当前操作进入下一项事件,当返回HttpResponese对象时表示此请求结束,直接返回给客户端

编写中间件类:

# file : middleware/mymiddleware.py
from django.http import HttpResponse
from django.utils.deprecation import MiddlewareMixin

class MyMiddleWare(MiddlewareMixin):
    def process_request(self, request):
        print("中间件方法 process_request 被调用")

    def process_view(self, request, callback, callback_args, callback_kwargs):
        print("中间件方法 process_view 被调用")

    def process_response(self, request, response):
        print("中间件方法 process_response 被调用")
        return response
Copy

注册中间件:

settings.py中需要注册一下 自定义的中间件

# file : settings.py
MIDDLEWARE = [
    ...
       ]
Copy

注意:配置为数组,中间件被调用时 以 '先上到下' 再 '由下到上'的顺序调用

练习1

用中间件实现强制某个IP地址只能向/test开头的地址 发送 5 次请求

提示:

  • request.META['REMOTE_ADDR']可以得到远程客户端的IP地址

  • request.path_info 可以得到客户端访问的请求路由信息

中间件执行总流程

 

csrf攻击

跨站请求伪造攻击 CSRF

定义

某些恶意网站上包含链接、表单按钮或者JavaScript,它们会利用登录过的用户在浏览器中的认证信息试图在你的网站上完成某些操作,这就是跨站请求伪造(CSRF,即Cross-Site Request Forgey)。

CSRF 防范

  • django采用 '比对暗号' 机制 防范攻击

  • Cookies中存储暗号1, 模板中 表单里藏着 暗号2, 用户只有在本网站下提交数据,暗号2才会随表单提交给服务器,django对比两个暗号,对比成功,则认为是合法请求,否则是违法请求-403响应码

配置步骤

  1. settings.py中确认 MIDDLEWAREdjango.middleware.csrf.CsrfViewMiddleware是否打开

  2. 模板中,form标签下添加如下标签

     

  3. 如果某个视图不需要django进行csrf保护,可以用装饰器关闭对此视图的检查

    from django.views.decorators.csrf import csrf_exempt
    
    @csrf_exempt
    def my_view(request):
        return HttpResponse('Hello world')
    Copy

 

分页

分页

  • 分页是指在web页面有大量数据需要显示,为了阅读方便在每个页页中只显示部分数据。

  • 好处:

    1. 方便阅读

    2. 减少数据提取量,减轻服务器压力。

  • Django提供了Paginator类可以方便的实现分页功能

  • Paginator类位于django.core.paginator 模块中。

Paginator对象

  • 负责分页 数据整体 的管理

对象的构造方法

  • paginator = Paginator(object_list, per_page)

  • 参数

    • object_list 需要分类数据的对象列表

    • per_page 每页数据个数

  • 返回值:

    • Paginator的对象

Paginator属性

  • count:需要分类数据的对象总数

  • num_pages:分页后的页面总数

  • page_range:从1开始的range对象, 用于记录当前面码数

  • per_page 每页数据的个数

Paginator方法

  • page(number)
    • 参数 number为页码信息(从1开始)

    • 返回当前number页对应的页信息

    • 如果提供的页码不存在,抛出InvalidPage异常

  • Paginator

    异常

    exception
    • InvalidPage

      :总的异常基类,包含以下两个异常子类

      • PageNotAnInteger:当向page()传入一个不是整数的值时抛出

      • EmptyPage:当向page()提供一个有效值,但是那个页面上没有任何对象时抛出

Page对象

  • 负责具体 某一页 的数据的管理

创建对象

Paginator对象的page()方法返回Page对象

page = paginator.page(页码)
Copy

Page对象属性

  • object_list:当前页上所有数据对象的列表

  • number:当前页的序号,从1开始

  • paginator:当前page对象相关的Paginator对象

Page对象方法

  • has_next():如果有下一页返回True

  • has_previous():如果有上一页返回True

  • has_other_pages():如果有上一页或下一页返回True

  • next_page_number():返回下一页的页码,如果下一页不存在,抛出InvalidPage异常

  • previous_page_number():返回上一页的页码,如果上一页不存在,抛出InvalidPage异常

  • len():返回当前页面对象的个数

 

下载csv文件

生成csv文件

定义

逗号分隔值(Comma-Separated Values,CSV,有时也称为字符分隔值,因为分隔字符也可以不是逗号),其文件以纯文本形式存储表格数据(数字和文本)

说明:可被常见制表工具 ,如excel等直接进行读取

python 中生成 csv 文件

Python提供了内建库 - csv ; 可直接通过该库操作csv文件

案例如下:

import csv

with open('eggs.csv', 'w', newline='') as csvfile:
    writer = csv.writer(csvfile)
    writer.writerow(['a', 'b', 'c'])
Copy

csv 文件下载

在网站中,实现下载CSV,注意如下:

  • 响应Content-Type类型需修改为 text/csv。这告诉浏览器该文档是CSV文件,而不是HTML文件

  • 响应会获得一个额外的 Content-Disposition 标头,其中包含CSV文件的名称。它将被浏览器用于开启 “另存为...”对话框

import csv
from django.http import HttpResponse
from .models import Book


def make_csv_view(request):

    # 响应头
    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.writerrow([b.id, b.title])

    return response
Copy

练习

结合分页功能,在 test_page 页面中添加 ‘生成csv’ 的链接;在指定页中点击该链接,生成当前页的csv数据,供用户下载

 

上传文件&下载文件

文件服务

定义

  • 用户可以通过浏览器将图片等文件传到网站

场景:

  • 用户上传头像

  • 上传流程性的文档[pdf, txt等]

上传规范

前端[HTML]

  • 文件上传必须为POST提交方式

  • 表单<form>中文件上传时必须有enctype="multipart/form-data"伴随时会包含文件内容数据。

  • 表单中用<input type="file" name="xxx">标签上传文件

<!-- file: index/templates/index/upload.html -->
<html>
<head>
    <meta charset="utf-8">
    <title>文件上传</title>
</head>
<body>
    <h3>上传文件</h3>
    <form method="post" action="/test_upload" enctype="multipart/form-data">
        <input type="file" name="myfile"/><br>
        <input type="submit" value="上传">
    </form>
</body>
</html>
复制

现在[Django]

视图功能中,用request.FILES取文件框的内容file=request.FILES['xxx']

说明:

  1. FILES的key对应页面中文件框的name值

  2. file 绑定文件流对象,

  3. 文件名 文件名

  4. file.file 文件的字节流数据

配置 文件的 访问路径和存储路径

setting.py中设置MEDIA相关配置;Django把用户上传的文件,统统media资源

# file : settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
复制

在当前项目文件夹下创建 media 文件夹

$ mkdir media
复制

MEDIA_URL和MEDIA_ROOT需要手动绑定

步骤:主语音中添加语音

from django.conf import settings
from django.conf.urls.static import static

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
复制

说明:等价于最初MEDIA_URL的了解,Django得到该特征请求后去MEDIA_ROOT路径寻找资源

文件写入方案1: 传统的open方式

# file views.py
from django.http import HttpResponse
from django.conf import settings
from django.views.decorators.csrf import csrf_exempt
import os


@csrf_exempt
def test_file_upload(request):
    """
        文件上传
        传统方案: 使用open()
    """
    if request.method == 'GET':
        return render(request, 'file/test_file_upload.html')
    elif request.method == 'POST':
        # 文件: file_obj = request.FILES['xx']
        # file.file
        a_file = request.FILES['myfile']
        print('上传文件名是:', a_file.name)

        filename = os.path.join(settings.MEDIA_ROOT, a_file.name)

        # 写入
        with open(filename, 'wb') as f:
            data = a_file.file.read()
            f.write(data)
        return HttpResponse('上传成功!')
复制

文件写入方案2: 借助ORM

  • 内容: FileField(upload='子目录名')

#test_upload/models.py
from django.db import models

# Create your models here.
class File(models.Model):
    title = models.CharField(max_length=20)
    content = models.FileField(verbose_name="文件名称", upload_to="myfiles")



# test_upload/views.py
from test_upload.models import *
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse


@csrf_exempt
def test_file_upload1(request):
    """
        文件上传
        方案二: 利用 ORM 进行上传
    """
    if request.method == 'GET':
        return render(request, 'file/test_file_upload.html')

    elif request.method == 'POST':
        title = request.POST['title']
        file_obj = request.FILES['myfile']

        File.objects.create(title=title, content=file_obj)

        return HttpResponse('upload file is ok!')
复制

下载规则

HTML

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <form id="_form" method="post" action="/file/download">
            <input type="hidden" name="name" value="value" />
            <a onclick="document.getElementById('_form').submit();">点击提交</a>
        </form>
    </body>
</html>
复制

DJNGO

# models.py

from django.db import models


# Create your models here.
class File(models.Model):
    title = models.CharField(max_length=20)
    content = models.FileField(verbose_name="文件名称", upload_to="myfiles")
复制
# views.py
from django.shortcuts import render
from django.http import HttpResponse
from django.conf import settings
from django.http import StreamingHttpResponse

@csrf_exempt
def download_view(request):
    """
        文件下载 
    """

    if request.method == "GET":
        return render(request, 'op/download_file.html')

    elif request.method == "POST":

        file = File.objects.get(id=1)
        file_path = os.path.join(settings.MEDIA_ROOT, str(file.content))
        print('--->', file_path)

        files = open(file_path, 'rb+')
        resp = StreamingHttpResponse(files)
        resp['Content-Type]'] = 'application/octet-stream'

        filename = file_path.split('/')[-1]

        # 下方是解决 浏览器编码问题 下载中文文件乱码
        resp['Content-Disposition'] = 'attachment; filename=' + filename.encode('utf-8').decode('ISO-8859-1')

        return resp
复制

 

用户认证系统

内建用户系统

Django中的用户认证 (使用Django认证系统)

定义

  • Django带有一个用户认证系统。它的用户账号、组、权限以及基于cookie的用户会话。

  • 用户可以直接使用Django自带的用户表

作用:

  1. 添加普通用户和超级用户

  2. 修改密码

官方文档:https://docs.djangoproject.com/en/2.2/topics/auth/

基本字段

User模型类 from django.contrib.auth.models import User

属性名类型是否必选
用户名 用户名
密码 密码
电子邮件 邮箱 任选
 
 
is_superuser 是否是管理员管理员(/admin)  
is_staff 是否可以访问admin管理界面  
活跃 是否经常用户,默认为True。一般删除用户,频率将用户的is_active 设为False。  
上次登录 上一次的登录时间  
加入日期 用户创建的时间  

基本模型操作

创建用户

1. 创建普通用户create_user

from django.contrib.auth.models import User

user = User.objects.create_user(username='用户名', password='密码', email='邮箱',...)
复制

2. 创建超级用户create_superuser

from django.contrib.auth.models import User

user = User.objects.create_superuser(username='用户名', password='密码', email='邮箱',...)
复制

删除用户

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)
复制

说明:如果用户名密码成功则返回对应的用户对象,否则返回None

修改密码

from django.contrib.auth.models import User

try:
    user = User.objects.get(username='xiaonao')
    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)
    login(request, user)
复制

登陆状态检查

from django.contrib.auth.decorators import login_required


@login_required
def index_view(request):
    """
        该视图必须为用户登录状态下才可访问
          当前登陆用户可通过 request.user获取
    """

    login_user = request.user 
    .....
复制

登陆状态取消

from django.contrib.auth import logout


def logout_view(request):
    logout(request)
复制

思考:内建用户表用词怎么办?比如想添加手机号字段?

方案1:通过建立新表,跟内建表做1对1

2:继承内建方案抽象用户模型类

内建用户表 — 继承内建抽象类

步骤:

  1. 添加新的应用

  2. 定义模型类继承 AbstractUser

  3. settings.py中指定 AUTH_USER_MODEL = '应用名.类名'

注意:此操作要在第一次Migrate之前进行

示例

1. user/models.py - 添加user应用

from django.db import models
from django.contrib.auth.models import AbstractUser

# Create your models here.
class UserInfo(AbstractUser):

    phone = models.CharField(max_length=11, default='')
复制

2. settings.py添加配置

AUTH_USER_MODEL = 'user.UserInfo'
复制

3. 添加用户

from user.models import UserInfo

UserInfo.objects.create_user(username='guoxiao', password='123456', phone='13488871101')
复制

 

发送邮件

业务场景

  • 业务告警

  • 邮件验证

  • 密码找回

邮件相关协议

SMTP

  • SMTP的全称是“Simple Mail Transfer Protocol”,即简单邮件传输协议(25号端口)。

  • 它是一组用于从源地址到目的地址传输邮件的规范,通过它来控制邮件的中转

  • 属于 “推送” 协议

IMAP

  • IMAP全称是Internet Mail Access Protocol,即交互式邮件访问协议,是一个应用层协议(端口是143)。

  • 用来从本地邮件客户端(Outlook Express、Foxmail、Mozilla Thunderbird等)访问远程服务器上的邮件。

  • 属于 “拉取” 协议

POP3

  • POP3是Post Office Protocol 3的简称,即邮局协议的第3个版本,是TCP/IP协议族中的一员(默认端口是110)。

  • 本协议主要用于支持使用客户端远程管理在服务器上的电子邮件

  • 属于 “拉取” 协议

IMAP VS POP3

两者均为 “拉取”型协议,负责从邮件服务器中 下载邮件

  • IMAP 具备 摘要浏览功能,可预览部分摘要,再下载整个邮件

  • IMAP 为双向协议,客户端操作可反馈给服务器

  • POP3 必须下载全部邮件,无摘要功能

  • POP3 为单向协议,客户端操作无法同步服务器

Django发邮件

Django中配置邮件功能,主要为SMTP协议,负责发邮件

原理:

  • 给Django授权一个邮箱

  • Django用该邮箱给对应收件人发送邮件

  • django.core.mail 封装了 电子邮件的自动发送SMTP协议

授权步骤 - 以QQ邮箱为例

  1. 申请QQ号

  2. 用QQ号登陆QQ邮箱并修改设置

    • 用申请到的QQ号和密码登陆到 https://mail.qq.com/

    • 修改 QQ邮箱->设置->帐户->“POP3/IMAP......服务”

  3. 设置Django服务器端的,用简单邮件传输协议SMTP(Simple Mail Transfer Protocol) 发送电子邮件

Django 配置

settings.py 设置

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 = '******'  # 在QQ邮箱->设置->帐户->“POP3/IMAP......服务” 里得到的在第三方登录QQ邮箱授权码
EMAIL_USE_TLS = False  # 与SMTP服务器通信时,是否启动TLS链接(安全链接)默认False
Copy

函数调用

视图函数中

from django.core import mail

mail.send_mail(
            subject,  #题目
            message,  # 消息内容
            from_email,  # 发送者[当前配置邮箱]
            recipient_list=['xxx@qq.com'],  # 接收者邮件列表
            )
Copy

邮箱警告

用中间件实现抓取视图函数的异常,并以邮件的形式将异常信息发送给 指定联系人

  • 邮件主题: 'mysite7异常告警'

  • 内容:自定义即可,要求带有 异常信息

  • 收件人要求可灵活配置

 

项目部署

 

  • 项目部署是指在软件开发完毕后,将开发机器上运行的开发板软件实际安装到服务器上进行长期运行

  • 部署要分以下几个步骤进行

    1. 在安装机器上安装和配置同版本的环境

    2. django 项目迁移

      sudo scp /home/tarena/django/mysite1 root@88.77.66.55:/home/root/xxx
      请输入root密码:
      Copy
    3. 用 uwsgi 替代python3 manage.py runserver 方法启动服务器

    4. 配置 nginx 反向代理服务器

    5. 用nginx 配置静态文件路径,解决静态路径问题

uWSGI 网关接口配置 (ubuntu 18.04 配置)

WSGI 定义

  • WSGI (Web Server Gateway Interface)Web服务器网关接口,是Python应用程序或框架和Web服务器之间的一种接口,被广泛使用

  • 使用 python manage.py runserver 通常只在开发和测试环境中使用。

  • 当开发结束后,完善的项目代码需要在一个高效稳定的环境中运行,这时可以使用WSGI

uWSGI 定义

  • uWSGI是WSGI的一种, 它实现了 http协议 WSGI协议 以及 uwsgi协议

  • uWSGI功能完善,支持协议众多,在python web圈热度极高

  • uWSGI 主要以学习配置为主

安装uWSGI

  • 终端输入如下命令

    sudo pip3 install uwsgi==2.0.18 -i https://pypi.tuna.tsinghua.edu.cn/simple/
    Copy
  • 检查是否安装成功

    sudo pip3 freeze|grep -i 'uwsgi'
    
    # 如果成功安装,则会输出
    uWSGI==2.0.18
    Copy

配置uWSGI

  • 添加配置文件 项目同名文件夹/uwsgi.ini

    如: mysite1/mysite1/uwsgi.ini

    [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
    Copy

特殊说明

特殊说明:Django 的 settings.py需要做如下配置

  1. 修改settings.pyDEBUG=True 改为DEBUG=False

  2. 修改settings.pyALLOWED_HOSTS = []改为ALLOWED_HOSTS = ['网站域名'] 或者 ['服务监听的ip地址']

uWSGI的运行管理

  • 启动 uwsgi

    $ 进入到项目同名文件夹下 【即settings.py所在目录】
    $ uwsgi --ini uwsgi.ini
    Copy
  • 停止 uwsgi

    $ 进入到项目同名文件夹下 【即settings.py所在目录】
    $ uwsgi --stop uwsgi.pid
    Copy

uWSGI 测试:

  • 在浏览器端输入http://127.0.0.1:8000 进行测试

  • 注意,此时端口号为8000

  • 如果当前有预期返回,则uWSGI启动成功

uWSGI 常见问题汇总

  1. 启动失败:端口被占用

    • 原因:有其他进程占用 uWSGI启动的端口;

    • 解决方案:可执行 sudo lsof -i:端口号 查询出具体进程;杀掉进城后,重新启动uWSGI即可

  2. 停止失败:stop无法 关闭uWSGI

    • 原因:重复启动uWSGI,导致pid文件中的进程号失准

    • 解决方案: ps出uWSGI进程,手动kill掉

nginx 及反向代理配置

  • Nginx是轻量级的高性能Web服务器,提供了诸如HTTP代理和反向代理、负载均衡、缓存等一系列重要特性,在实践之中使用广泛。

  • C语言编写,执行效率高

  • nginx 作用

    • 负载均衡, 多台服务器轮流处理请求

    • 反向代理

  • 原理:客户端请求nginx,再由nginx 将请求转发 uWSGI 运行的django

nginx 安装

sudo apt install nginx

如果下载速度很慢,考虑更换为国内源

vim /etc/apt/sources.list
更改国内源
sudo apt-get update
Copy

安装完毕后,ubuntu终端中 输入 nginx -v 显示如下:

nginx version: nginx/1.14.0(ubuntu)
Copy

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下
    }
    ...
}
Copy
  • nginx服务控制

    $ sudo /etc/init.d/nginx start|stop|restart|status
    # 或
    $ sudo service nginx start|stop|restart|status
    Copy

    通过 start,stop,restart,status可能实现nginx服务的启动、停止、重启、状态

修改uWSGI配置

  • 说明 nginx负责接收请求,并把请求转发给后面的uWSGI

  • 修改项目同名文件夹/uwsgi.ini下的Http通信方式改为socket通信方式

 [uwsgi]
# 去掉如下
# http=127.0.0.1:8000
# 改为
socket=127.0.0.1:8000
Copy
  • 重启uWSGI服务

进入到 项目同名文件夹下
$ uwsgi --stop uwsgi.pid
$ uwsgi --ini uwsgi.ini
Copy
  • 测试:

    • 在浏览器端输入http://127.0.0.1 进行测试

    • 注意 :

      1,此时端口号为80(nginx默认值)

      2,Django中有任何修改 需要重启 uwsgi , 否则修改不生效

nginx+uwsgi排错

排查问题宗旨 -> 看日志! 看日志!! 看日志!!!

nginx 日志位置:

  • 异常信息 /var/log/nginx/error.log

  • 正常访问信息 /var/log/nginx/access.log

uwsgi 日志位置:

项目同名目录下,uwsgi.log

问题:

  1. 访问127.0.0.1:80 地址,502响应

    502响应 代表nginx反向代理配置成功,但是对应的uWSGI未启动

  2. 访问127.0.0.1:80/url 404响应

    1. 路由的确不在django配置中

    2. nginx配置错误,未禁止掉 try_files

nginx 配置静态文件路径

Django settings.py 中的debug=False后,静态文件就无法正常加载了;可以访问 http://127.0.0.1/admin 测试

admin样式丢失 - 静态文件加载失败

解决方案:静态文件交由 nginx 统一处理,让nginx加载静态文件

静态文件配置步骤

  1. 创建新路径-主要存放Django所有静态文件 如:/home/tarena/项目名_static/

  2. 在Django settings.py 中添加新配置

    STATIC_ROOT = '/home/tarena/项目名_static/static  
    # 注意 此配置路径为 存放所有正式环境中需要的静态文件
    Copy
  3. 进入项目,执行 python3 manage.py collectstatic 。执行该命令后,Django将项目重所有静态文件 复制到 STATIC_ROOT 中 ,包括Django内建的静态文件【如admin后台的样式】

Nginx配置中添加新配置

# file : /etc/nginx/sites-enabled/default
# 新添加location /static 路由配置,重定向到指定的 第一步创建的路径即可
server {
    ...
    location /static {
        # root 第一步创建文件夹的绝对路径,如:
         root /home/tarena/项目名_static;          
    }
    ...      
}

img src='/static/a.jpg'
Copy

测试

重启nginx - sudo /etc/init.d/nginx restart

浏览器访问 - http://127.0.0.1/admin/

404/500 界面 定义和配置

  • 在模板文件夹内添加 404.html 模版,当视图触发Http404 异常时将会被显示

  • 404.html 仅在发布版中(即setting.py 中的 DEBUG=False时) 才起作用

  • 当向应处理函数触发Http404异常时就会跳转到404界面

from django.http import Http404

def xxx_view( ):
    raise Http404  # 直接返回404
Copy

邮件告警

  • 当正式服务器上代码运行有报错时,可将错误追溯信息发至指定的邮箱

  • 配置如下 settings.py

    # 在基础邮件配置之后 添加如下
    
    # 关闭调试模式
    DEBUG = False  
    
    # 错误报告接收方
    ADMINS = [('guoxiaonao', 'xxxx@example.com'), ('wanglaoshi', 'xxxx@example.com')]
    
    # 发送错误报告方,默认为 root@localhost账户,多数邮件服务器会拒绝
    SERVER_EMAIL = 'email配置中的邮箱'
    Copy

过滤敏感信息

报错邮件中会显示一些错误的追踪,这些错误追踪中会出现如 password等敏感信息,Django已经将配置文件中的敏感信息 过滤修改为 多个星号,但是用户自定义的视图函数需要用户手动过滤敏感信息

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
    ...
Copy

注意:

  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的值会被替换成  ******
Copy
 
posted @ 2022-05-31 10:45  我不知道取什么名字好  阅读(31)  评论(0编辑  收藏  举报