天天看點

【Django】 開發:補充知識

有需要上網現查現學的東西。

分頁

  • 分頁是指在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(頁碼)           

複制

  • 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 ():傳回目前頁面對象的個數

  • 說明:

Page 對象是可疊代對象,可以用 for 語句來 通路目前頁面中的每個對象

  • 參考文檔 https://docs.djangoproject.com/en/2.2/topics/pagination/
  • 分頁示例:

視圖函數

from django.core.paginator import Paginato
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())           

複制

模闆設計

<html>
    <head>
        <title>分頁顯示</title>
    </head>
    <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>
</html>

           

複制

檔案下載下傳

Django可直接在視圖函數中生成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.writerow([b.id, b.title])

    return response           

複制

  • 響應獲得一個特殊的MIME類型text / csv。這告訴浏覽器該文檔是CSV檔案,而不是HTML檔案
  • 響應會獲得一個額外的

    Content-Disposition

    标頭,其中包含CSV檔案的名稱。它将被浏覽器用于“另存為…”對話框
  • 對于CSV檔案中的每一行,調用

    writer.writerow

    ,傳遞一個可疊代對象,如清單或元組。

檔案上傳

  • 檔案上傳必須為 POST 送出方式
  • 表單 <form> 中檔案上傳時必須有帶有 enctype="multipart/form-data" 時才會包含檔案内容資料。
  • 表單中用 <input type="file" name="xxx"> 标簽上傳檔案

名字 xxx 對應 request.FILES['xxx'] 對應的記憶體緩沖檔案流對象。可通能過 request.FILES['xxx'] 傳回的對象擷取上傳檔案資料

file=request.FILES['xxx'] file 綁定檔案流對象,可以通過檔案流對象的如下資訊擷取檔案資料

file.name 檔案名

file.file 檔案的位元組流資料

  • 上傳檔案的表單書寫方式
<!-- 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>           

複制

  • 在 setting.py 中設定 MEDIA 相關配置;Django 把使用者上傳的檔案,統稱為 media 資源
# file : settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')           

複制

  • 在目前項目檔案夾下建立 media 檔案夾
$ mkdir media           

複制

  • 上傳檔案的視圖處理函數 方案 1 傳統寫入
# 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 upload_view(request):
    if request.method == 'GET':
        return render(request, 'test_upload.html')
    elif request.method == "POST":
        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("接收檔案:" + a_file.name + "成功")           

複制

  • 上傳檔案的視圖處理函數 方案 2 借助 orm
#test_upload/models.py
from django.db import models
  
# Create your models here.
class Content(models.Model):

    desc = models.CharField(max_length=100)
    myfile = models.FileField(upload_to='myfiles')

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

@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)
        return HttpResponse('----upload is ok-----')           

複制

  • 若要在浏覽器中通路 上傳的資源,runserver 環境下,需要在項目得主路由下添加 media 路由的綁定
from django.conf import settings
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)           

複制

浏覽器可以通路 http://127.0.0.1:8000/media/xxxx

Django中的使用者認證 (使用Django認證系統)

  • Django帶有一個使用者認證系統。 它處理使用者賬号、組、權限以及基于cookie的使用者會話。
  • 作用:
    1. 添加普通使用者和超級使用者
    2. 修改密碼
  • 文檔參見
    • https://docs.djangoproject.com/en/2.2/topics/auth/
  • User模型類
    • 位置:

      from django.contrib.auth.models import User

    • 預設user的基本屬性有:
【Django】 開發:補充知識

auth基本模型操作:

  • 建立使用者

建立普通使用者 create_use

from django.contrib.auth.models import Use
user = User.objects.create_user(username='使用者名', password='密碼', email='郵箱',...)           

複制

  • 建立超級使用者 create_superuse
from django.contrib.auth.models import Use
user = User.objects.create_superuser(username='使用者名', password='密碼', email='郵箱',...)           

複制

  • 删除使用者
from django.contrib.auth.models import Use
try:
    user = User.objects.get(username='使用者名')
    user.is_active = False  # 記目前使用者無效
    user.save()
    print("删除普通使用者成功!")
except:
    print("删除普通使用者失敗")           

複制

  • 修改密碼 set_password
from django.contrib.auth.models import Use
try:
    user = User.objects.get(username='xiaonao')
    user.set_password('654321')
    user.save()
    return HttpResponse("修改密碼成功!")
except:
    return HttpResponse("修改密碼失敗!")           

複制

  • 檢查密碼是否正确 check_password
from django.contrib.auth.models import Use
try:
    user = User.objects.get(username='xiaonao')
    if user.check_password('654321'):  # 成功傳回True,失敗傳回False
        return HttpResponse("密碼正确")
    else:
        return HttpResponse("密碼錯誤")
except: 
    return HttpResponse("沒有此使用者!")           

複制

auth擴充字段

如果需要在預設auth表上擴充新的字段,如phone

  1. 添加新的應用
  2. 定義模型類 繼承 AbstractUser
  3. settings.py中 指明 AUTH_USER_MODEL = ‘應用名.類名’
#models.py案例
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='')
    
#settings.py添加配置
AUTH_USER_MODEL = 'user.UserInfo'

#添加使用者
from user.models import UserInfo
UserInfo.objects.create_user(username='guoxiao', password='123456', phone='13488871101')           

複制

電子郵件發送

  • 利用QQ郵箱發送電子郵件
  • django.core.mail 子包封裝了 電子郵件的自動發送SMTP協定
  • 前其準備:
    1. 申請QQ号
    2. 用QQ号登陸QQ郵箱并修改設定
      • 用申請到的QQ号和密碼登陸到 https://mail.qq.com/
      • 修改

        QQ郵箱->設定->帳戶->“POP3/IMAP......服務”

    3. 設定Django伺服器端的,用簡單郵件傳輸協定SMTP(Simple Mail Transfer Protocol) 發送電子郵件
  • settings.py

    設定
# 發送郵件設定
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' # 固定寫法
EMAIL_HOST = 'smtp.qq.com' # 騰訊QQ郵箱 SMTP 伺服器位址
EMAIL_PORT = 25  # SMTP服務的端口号
EMAIL_HOST_USER = '[email protected]'  # 發送郵件的QQ郵箱
EMAIL_HOST_PASSWORD = '******'  # 在QQ郵箱->設定->帳戶->“POP3/IMAP......服務” 裡得到的在第三方登入QQ郵箱授權碼
EMAIL_USE_TLS = True  # 與SMTP伺服器通信時,是否啟動TLS連結(安全連結)預設false           

複制

視圖函數中

from django.core import mail
mail.send_mail(
            subject,  #題目
            message,  # 消息内容
            from_email,  # 發送者[目前配置郵箱]
            recipient_list=['[email protected]'],  # 接收者郵件清單
            )           

複制

項目部署

  • 項目部署是指在軟體開發完畢後,将開發機器上運作的開發闆軟體實際安裝到伺服器上進行長期運作
  • 部署要分以下幾個步驟進行

在安裝機器上安裝和配置同版本的環境

  1. django 項目遷移
  2. $ sudo scp 目前項目源代碼 遠端主機位址和檔案夾
sudo scp /home/tarena/django/mysite1 [email protected]:/home/root/xxx
請輸入root密碼:           

複制

3. 用 uwsgi 替代 python3 manage.py runserver 方法啟動伺服器

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

5.用 nginx 配置靜态檔案路徑,解決靜态路徑問題

uWSGI 網關接口配置 (ubuntu 18.04 配置)

  • WSGI (Web Server Gateway Interface) Web 伺服器網關接口,是 Python 應用程式或架構和 Web 伺服器之間的一種接口,被廣泛使用
  • 使用 python manage.py runserver 通常隻在開發和測試環境中使用。
  • 當開發結束後,完善的項目代碼需要在一個高效穩定的環境中運作,這時可以使用 WSGI
  • uWSGI 是 WSGI 的一種,它實作了 http 協定 WSGI 協定 以及 uwsgi 協定
  • 安裝 uWSGI

終端輸入如下指令

sudo pip3 install uwsgi==2.0.18 -i https://pypi.tuna.tsinghua.edu.cn/simple/           

複制

檢查是否安裝成功

sudo pip3 freeze|grep -i 'uwsgi'
#如果成功安裝,則會輸出
uWSGI==2.0.18           

複制

  • 配置 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           

複制

  • 修改 settings.py 将 DEBUG=True 改為 DEBUG=False
  • 修改 settings.py 将 ALLOWED_HOSTS = [] 改為 ALLOWED_HOSTS = [‘網站域名’] 或者 [‘服務監聽的 ip 位址’]
  • uWSGI 的運作管理

啟動 uwsgi

$ 進入到項目同名檔案夾下 【即settings.py所在目錄】
$ sudo uwsgi --ini uwsgi.ini           

複制

停止 uwsgi

$ 進入到項目同名檔案夾下 【即settings.py所在目錄】
$ sudo uwsgi --stop uwsgi.pid           

複制

說明:

  • 當 uwsgi 啟動後,目前 django 項目的程式已變成背景守護程序,在關閉目前終端時此程序也不會停止。
  • 若執行 stop 操作失敗,則需要執行如下操作殺死程序
ps aux|grep 'uwsgi'  -> 檢視uwsgi程序

tarena   103408  0.0  0.9 137172 39984 ?        S    10:02   0:01 uwsgi --ini uwsgi.ini
tarena   103410  0.0  0.9 436200 38552 ?        Sl   10:02   0:00 uwsgi --ini uwsgi.ini

ps -ef | grep 'uwsgi' | grep -v grep | awk '{print $2}' | xargs sudo kill -9           

複制

測試:

  • 在浏覽器端輸入 http://127.0.0.1:8000 進行測試
  • 注意,此時端口号為 8000

nginx 及反向代理配置

  • Nginx 是輕量級的高性能 Web 伺服器,提供了諸如 HTTP 代理和反向代理、負載均衡、緩存等一系列重要特性,在實踐之中使用廣泛。
  • C 語言編寫,執行效率高
  • nginx 作用
  • 負載均衡, 多台伺服器輪流處理請求
  • 反向代理
  • 原理:
  • 用戶端請求 nginx, 再由 nginx 将請求轉發 uWSGI 運作的 django
  • ubuntu 下 nginx 安裝

    $ sudo apt install nginx

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

複制

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

SHELL
1
2
3
$ sudo /etc/init.d/nginx start|stop|restart|status
# 或
$ sudo service nginx start|stop|restart|status           

複制

通過 start,stop,restart,status 可能實作 nginx 服務的啟動、停止、重新開機、操作

  • 修改 uWSGI 配置

修改項目同名檔案夾/uwsgi.ini 下的 Http 通信方式改為 socket 通信方式

[uwsgi]
# 去掉如下
# http=127.0.0.1:8000
# 改為
socket=127.0.0.1:8000           

複制

  • 重新開機 uWSGI 服務
進入到 項目同名檔案夾下
$ sudo uwsgi --stop uwsgi.pid
$ sudo uwsgi --ini uwsgi.ini           

複制

測試:

在浏覽器端輸入 http://127.0.0.1 進行測試

注意 :

1,此時端口号為 80 (nginx 預設值)

2,Django 中有任何修改 需要重新開機 uwsgi , 否則修改不生效

nginx 配置靜态檔案路徑

  • 建立新路徑 - 主要存放 Django 所有靜态檔案 如: /home/tarena/ 項目名_static/
  • 在 Django settings.py 中添加新配置
STATIC_ROOT = '/home/tarena/項目名_static/static  
#注意 此配置路徑為 存放所有正式環境中需要的靜态檔案           

複制

  • 進入項目,執行 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;          
    }
    ...      
}           

複制

404/500 界面

  • 在模闆檔案夾内添加 404.html 模版,當視圖觸發 Http404 異常時将會被顯示
  • 404.html 僅在釋出版中 (即 setting.py 中的 DEBUG=False 時) 才起作用
  • 當向應處理函數觸發 Http404 異常時就會跳轉到 404 界面
from django.http import Http404
def xxx_view( ):
    raise Http404  # 直接傳回404           

複制

郵件告警

報錯郵件中會顯示一些錯誤的追蹤,這些錯誤追蹤中會出現如 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
    ...
#注意:
#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的值會被替換成  ******           

複制