独学Python 22日目 - Django フレームワーク (3) AJAX、ファイルアップロード、POSTリクエストタイプ間変換、マルチAPP開発、iframe、検証コード、ページャー、クラスビュー、ミドルウェア、シグナル、ログ、キャッシュ、セロリ非同期

Django 公式ドキュメント

django は AJAX を使用します

Ajax テクノロジーは Django プロジェクトでも使用できます

フロントエンド

フロントエンドは他のWebフレームワークと同様ですが、DjangoがPOSTリクエストを受信した際に検証用にcsrf_tokenが必要となるため、ajaxでcsrf_tokenを取得するのがさらに面倒である点に注意してください。そのため、通常、バックエンドでは csrf_token 検証が免除されます。

バインディングイベントメソッド

{% extends 'layout.html' %}
{% block content %}
    <div class="container">
        <h1>任务管理</h1>
        <input type="button" class="btn btn-primary" value="点击" onclick="clickMe();"/>
    </div>
{% endblock %}
{% block js %}
    <script type="text/javascript">
        function clickMe() {
      
      
            $.ajax({
      
      
                url: "/test/ajax/",
                type: "get",
                data: {
      
      
                	type:'add',
                    n1:123,
                    n2:456
                },
                success: function (res) {
      
      
                    console.log(res);
                }
            })
        }
    </script>
{% endblock %}

jQuery方式で

{% extends 'layout.html' %}
{% block content %}
    <div class="container">
        <h1>任务管理</h1>
        <input type="button" class="btn btn-primary" value="点击" id="btn1" />
    </div>
{% endblock %}
{% block js %}
    <script type="text/javascript">
        $(function (){
      
      
            // 页面框架加载完成后代码自动执行
            bindBtn1Event();        // 绑定事件
        })
        function bindBtn1Event(){
      
      
            $("#btn1").click(function (){
      
             // 绑定到 btn1 的 click 事件的函数
                $.ajax({
      
      
                    url: "/task/ajax/",
                    type: "POST",
                    data: $("#addForm").serialize(),
                    dataType:'JSON',
                    success: function (res) {
      
      
                        console.log(res);
                        // console.log(res.res_add)
                        // console.log(res['res_add'])
                    }
                })
            })
        }
    </script>
{% endblock %}

後部

バックエンド部分では、応答リクエストURLがview関数にバインドされていればview関数内で処理できます。

def task_ajax(request):
    """测试ajax"""
    return HttpResponse('成功')

リクエストボディからデータを取得する

request.POST の使用にはいくつかの制限があります。

  • POST リクエストにのみ使用でき、PUT や DELETE などの非 POST リクエストには使用できません。
  • フォームデータ、つまり次のタイプのリクエストcontent-typeのみ使用できます。application/x-www-form-urlencoded

その他のデータについては、リクエストボディから取得する必要があります。request.bodyのデータはバイト型であるため、最初に解析する必要があります。

import json

def data_form_body(request):
	req_dict = json.loads(request.body)
	category = req_dict.get('category)

jsonデータを返す

json の dumps メソッドを使用して、辞書を json に変換して返すことができます。

import json

def task_ajax(request):
	res_dict = {
    
    'res':'ok', 'data':{
    
    'k1': 'v1', 'k2': 'v2'}}
	res_dict = json.dumps(res_dict)
    return HttpResponse(res_dict)

JsonResponse を直接使用してデータを返すこともできます

from django.http import JsonResponse

def task_ajax(request):
	res_dict = {
    
    'res':'ok', 'data':{
    
    'k1': 'v1', 'k2': 'v2'}}
    return JsonResponse(res_dict)

csrf検証チェックを無効にする

フロントエンドが POST リクエストの送信時に csrf_token をロードしない場合、バックエンドは csrf 検証チェックを無効にする必要があります。

from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def task_ajax(request):
	return HttpResponse('成功')

またはルート登録時にご指定ください

from django.views.decorators.csrf import csrf_exempt

urlpatterns = [
	path('goods/', csrf_exempt(views.goods), name='goods'),
]

または、settings.pyのミドルウェアにあるcsrfミドルウェアをキャンセルします(非推奨)。

Ajax と ModelForm の組み合わせ

Ajax と ModelForm を組み合わせると、フロントエンドにはほとんど変更がありませんが、ボタンが Ajax 関数にバインドされcsrf_token が使用されないことに注意してください

バックエンドではModelFormが受け取るデータもフォームの辞書形式になっており、検証や保存などの処理方法は変わりませんが、戻り値が変わります。

移転情報の取り扱い

Ajax はデータを受信して​​処理するため、バックエンドから返される再配置情報をジャンプすることはできません。ジャンプしたい場合は、json を返す必要があります。この特定の json データを受け取った後、ajax は js を使用してページにジャンプします。

フォームのエラーメッセージを処理する

さらに、ModelForm エラー メッセージが返された場合は、form.字段.errors辞書が取得されます。これを json 形式に編成して、処理のために ajax に返すことができます。

<!-- novalidate 可以禁止浏览器自身的有效性检测 -->
<form id="addForm" novalidate>
    <div class="clearfix">
        {% for field in form %}
            <!-- field.label 是从数据表定义中的 verbose_name 取的 -->
            <div class="col-xs-6">
                <div class="form-group">
                    <label>{
   
   { field.label }}</label>
                    {
   
   { field }}
                    <!-- 因为不会使用 ModelForm 返回错误信息,所以不再这样写 -->
                    <!-- <span style="color: red">{
    
    { field.errors.0 }}</span> -->
                    <span style="color: red"></span>
                </div>
            </div>
        {% endfor %}
        <div class="col-xs-12">
            <button type="button" id="btnAdd" class="btn btn-primary">添加</button>
        </div>
    </div>
</form>
<script type="text/javascript">
    $(function () {
      
      
        // 页面框架加载完成后代码自动执行
        bindBtnAddEvent();
    })
    function bindBtnAddEvent() {
      
      
        $("#btnAdd").click(function () {
      
      
        	$(".error-msg").empty();        // 清空上一次的错误信息
            $.ajax({
      
      
                url: "/task/add/",
                type: "POST",
                data: $("#addForm").serialize(),
                dataType: 'JSON',
                success: function (res) {
      
      
                    if(res.status){
      
      
                        alert("添加成功!");
                    }else{
      
      
                        console.log(res);
                        $.each(res.error,function (name,data){
      
            // 循环每一个错误,获取键值
                            // 拼接 id_ 和 name,获取对应文本框的 id
                            // 查找文本框元素的下一个元素(span),使其文本(text)为错误信息
                            $("#id_" + name).next().text(data[0]);
                        })
                    }
                }
            })
        })
    }
</script>

ファイルのアップロード

簡単なファイルアップロード

フロントエンドはファイルをポストリクエストとしてバックエンドに送信し、バックエンドは request.FILES を使用してファイルを受信できます。form には enctype 属性が必要であることに注意してください。

<form method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <input type="text" name="filename">
    <input type="file" name="file">
    <input type="submit" value="提交">
</form>
file_obj = request.FILES.get('file')  # 获取上传文件的对象
filename = request.POST.get('filename', file_obj.name)      # 获取设置的文件名,如果没有则为原文件名。
with open(filename, mode='wb') as f:
    for chunk in file_obj.chunks():  # 将上传文件分块读取
        f.write(chunk)
    f.flush()	# 文件写入完成,冲刷缓冲区

ajaxアップロードファイル

ajax によるファイルのアップロードと json の送信の違いは、送信されるデータが FormData オブジェクトであることです。このオブジェクトを作成するときに、フォームの dom オブジェクトを渡すことができます。

$("#upload").click(function () {
    
    
   var formData = new FormData($('#uploadForm')[0]);
   // 或使用 FormDate 对象添加文件对象的方式
   // var formData = new FormData();
   // formDate.append('file', this.file[0]);		//这里的 this 指向上传文件的 input 标签的dom对象
   // formDate.append('key', value);		// 可以添加其他的数据
   $.ajax({
    
    
    type: 'post',
    url: "https://****:****/fileUpload", //上传文件的请求路径必须是绝对路劲
     data: formData,
     cache: false,
     processData: false,
     contentType: false,
      }).success(function (data) {
    
    
        console.log(data);
        alert("上传成功"+data);
        filename=data;
      }).error(function () {
    
    
         alert("上传失败");
     });
    });

バックエンドが受信するとき、受信フィールドはフロントエンドによって定義された名前であり、ファイルはrequests.FILESから取得されることに注意してください。

uid = requests.POST.get('uid')		# 获取数据
file = requests.FILES.get('file')	# 获取文件
# files = requests.FILES.getlist('files')	# 获取多个文件

Form コンポーネントを使用してファイルをアップロードする

Form を使用する場合、ファイル フィールド FileField を定義できます。定義したら、ファイルをアップロードできます。

class UpForm(forms.Form):
	name = forms.CharField(label='姓名')
	age = forms.IntegerField(label='年龄')
	img = forms.FileField(label='头像')

def upload_form(request):
	form = UpForm(data=request.POST, files=request.FILES)
	if form.is_valid():
		print(form.cleaned_data)
		# 文件在 form.cleaned_data 的相应字段(img)内
		img = form.cleaned_data.get('img')
		file_path = os.path.join('app01', 'static', 'img', img.name)	# 拼接文件路径
		with open(file_path,'wb') as f:		# 写入文件
			for chunk in img.chunks():
				f.write(chunk)

	else:
		return render(request, 'upload.html', {
    
    'form': form})
<form method="post" enctype="multipart/form-data" novalidate>
	.
	.
	.
</form>

ファイルをアップロード ディレクトリ メディアにアップロードします

一般に使用される静的リソースは静的フォルダーにありますが、ユーザーがプロジェクトのルート ディレクトリにあるメディア フォルダーなどの特定のフォルダーにファイルをアップロードできるようにするには、urls.py の設定を有効にする必要があります。まずいくつかのリソースを導入し、次に urls.py ファイルの urlpatterns フィールドに追加します。

from django.views.static import serve
from django.urls import path, re_path
from django.conf import settings

re_path(r'^media/(?P<path>.*)$', serve, {
    
    'document_root': settings.MEDIA_ROOT}, name='media'),

次に、settings.py で設定します。

import os

MEDIA_ROOT = os.path.join(BASE_DIR, 'media')		# 项目根目录下的 media 目录
MEDIA_URL = '/media/'

このとき、アップロードしたファイルを保存するパスは次のように記述できます。

media_file_path = os.path.join('media', image_obj.name)

このように、ユーザーがアップロードしたファイルはメディア ディレクトリに配置され、/media/文件名http://127.0.0.1:8000/media/001.png などの URL を通じてアクセスすることもできます。

ModelForm コンポーネントを使用してファイルをアップロードする

ModelForm コンポーネントは、ファイルを自動的にアップロードしてデータベースへの保存パスを保存できるため、対応する保存コードを記述する必要がなく、重複した名前を自動的に処理できます。アップロードの保存ディレクトリを設定するだけです。

なお、データベースに保存されるファイルパスもメディア配下の相対パスとなり、メディアは含まれませんので、使用する際はメディアの追加に注意してください。

# models.py

class City(models.Model):
	"""城市"""
	name = models.CharField(verbose_name='名称', max_length=32)
	count = models.IntegerField(verbose_name='人口')

	# 本质上数据库存储的是文件路径,也是CharField,可以自动保存数据
	# upload_to 指的就是上传文件到哪个目录,是 media 目录下的相对路径
	img = models.FileField(verbose_name='logo', max_length=128, upload_to='city/')
# views.py

class UpModelForm(forms.ModelForm)
	class Meta:
		model = models.City
		fields = '__all__'
	
def upload_modal_form(request):
	form = UpModelForm(data=request.POST, files=request.FIELS)
	if form.is_valid():
		# 保存文件,保存 form 字段信息和文件路径并写入数据库
		form.save()
		return HttpResponse('成功')

POSTリクエストタイプ間の変換

POST リクエストと GET リクエストは、最も一般的に使用される 2 つのリクエスト メソッドです。通常、単純なリクエストには GET が使用されますが、大量のパラメータやデータを追加する必要がある場合は POST が選択されます。その後、POSTの機能も具体的に区別され、PUT、DELETE、OPTIONSなどのリクエストタイプが登場しました。

4 つの一般的なエンコード方法

HTTP プロトコルでは、POST によって送信されたデータをメッセージ本文 (entity-body) に配置する必要があると規定していますが、データがどのようなエンコード方式を使用する必要があるかはプロトコルで指定されていません。一般的なエンコード方法は次の 4 つです。

  1. application/x-www-form-urlencoded

POST 経由でデータを送信する最も一般的な方法。ブラウザのネイティブ フォームの場合、enctype 属性が設定されていない場合、application/x-www-form-urlencodedデータは最終的にフォームで送信されます。

  1. マルチパート/フォームデータ

POST データを送信するもう 1 つの一般的な方法。フォームを使用してファイルをアップロードする場合、フォームの enctyped をこの値と等しくする必要があります。

  1. アプリケーション/json

application/json は伝説的な json 形式で、実際、メッセージ本文がシリアル化された JSON 文字列であることをサーバーに伝えるリクエスト ヘッダーとして使用する人が増えています。JSON 仕様の普及により、IE の下位バージョンを除くすべての主要ブラウザは JSON.stringify をネイティブでサポートしており、サーバーサイド言語にも JSON を処理する機能があります。

  1. テキスト/xml

トランスポートプロトコルとしてHTTP、エンコード方式としてXMLを使用するリモート呼び出し仕様です。

Pythonのさまざまなタイプのリクエストでデータを読み取る

変換は読み取りと送信に分けられますが、実際に送信される応答パケットの種類は、基本的にブラウザでは正常に認識できます(リクエストヘッダーの content-type に応じて)。一般的に使用されるメッセージは、text/javascript、text/html、application/json、および image/png などの一部のメディア応答メッセージです。

ここで使用する環境は django です。

バックエンドから返される応答が json 形式を使用している場合は、json.dumps()それをシリアル化するために を使用する必要があることに注意してください。

application/x-www-form-urlencoded

このフォーム形式はPOSTリクエストを送信しますが、実際にはオブジェクトやシリアル化された文字ではなく、GETリクエストに似たメソッドとしてデータ本体を送信します。

url: 'www.test.com/'
body: "id=1&name=zhangsan&age=23"

たとえば、jquery は json オブジェクトを直接処理して送信できますが、fetch は処理しません。

Django はrequest.POST.get()メソッドを直接使用して取得することができ、取得した POST データは辞書の形式で保存されますが、辞書を辞書内にネストすることはできません。

フロントエンドがネストされたオブジェクトを送信する場合、Django はネストされたオブジェクトを次のように変換します。

// 前端发送的数据
{
    
    
	status:true,
	person:{
    
    
				name:'张三',
				age:18,
				sex:'male'
			}
}
# django 会将数据解析成这样,类型为字典
{
    
    	
	'status':'true',
	'person[name]':'张三',
	'person[age]':'18',
	'person[sex]':'male'
}

フロントエンドがシリアル化を実行すると、バックエンドはデシリアライズして対応するデータを取得できます。方法はjsonデータのシリアル化・逆シリアル化と同じです。

マルチパート/フォームデータ

フロントエンドで注意が必要なのは、multipart/form-data型のフロントエンドはオブジェクトを送信する必要があり、FormData文字列テキストやjsonオブジェクトなどを直接送信すると正常に処理できません。データ オブジェクト (文字列または json オブジェクトを含む) をFormDataオブジェクトに追加して送信できます。jqueryを使用して送信する場合はcontent-type=falseリクエストヘッダが自動で付加されるように設定する必要がありますcontent-type:multipart/form-data; boundary=----WebKitFormBoundaryyr7L6TwhhOAIoEyn同時に設定が必要となりprocessData=false、変換データは処理されません。

この形式では、メソッドを使用して各パラメータの値を直接取得できrequest.POST.get() 、型は文字列型です。

request.POST はQueryDictオブジェクトであり、直接逆シリアル化できないため、get()メソッドなどの第 1 レベルのプロパティを直接取得する必要があります。このメソッドは、文字列の内容が辞書型 (フロントエンドによって送信されたデータ オブジェクト) であっても、すべての文字列を取得します。

ブラウザがデータ オブジェクト (json に似たオブジェクト) をFormDataオブジェクトの属性値として送信すると、取得されるのは文字列型です[object Object]このときフロントエンドはJSON.stringify()jsのメソッドなどを使ってシリアル化(データオブジェクト)する必要があります。バックエンドが受け取る値のタイプは文字列で、内容は辞書データです。

バックエンドがそれを受信すると、取得された値はシリアル化された文字列であり、 をjson.loads()使用して辞書形式に変換できます。

アップロードされるファイルもこの形式である必要があり、 をrequests.FILES.get('file')使用して取得できます。

アプリケーション/json

フロントエンドがjsonを送信する場合は、JSON.stringify()送信されたjsonオブジェクトをバックエンドが正常に取得できるように を使用してシリアル化するように注意してください。

リクエスト本文をjson.loads()逆シリアル化してディクショナリ タイプを取得します。

if request.method == 'POST' and request.content_type == 'application/json':
	dic = json.loads(request.body)
	param1 = dic.get('param1')		# 如果不知道字典中是否有 param1 参数,可以使用字典的 get 方法,否则会报错
	param2 = dic.get('param2')

マルチアプリ開発

複数の人が Django の開発に協力する場合、各人が異なるアプリを開発する際に、同じ名前のテンプレートや静的リソースの使用などの問題が発生する可能性があります。Django は、アプリがインストールおよび登録された順序で静的リソースとテンプレートを検索するため、各アプリ ディレクトリでテンプレートまたは静的リソース ファイルを検索します。同名のテンプレートや静的リソースファイルが存在する場合、後から登録するアプリは最初に登録したアプリと同じ名前のファイルを使用します。

したがって、テンプレート ファイルについては、各アプリの templates フォルダーの下にアプリ名のフォルダーを作成し、テンプレート ファイルを作成します。これにより、各テンプレート ファイルが参照されるときに、それぞれのアプリの名前が追加されます。同じパスを持つ参照は存在しません。問題です。

静的リソース ファイルの場合、パブリックの静的リソースが含まれるため、静的リソースのパスを登録する必要があります。

# settings.py

STATIC_URL = '/static/'		
STATICFILES_DIRS = [
  os.path.join(BASE_DIR, "static"),			# 主静态文件目录
  os.path.join(BASE_DIR, "main", "static"),		# main app 静态文件目录
  os.path.join(BASE_DIR, "login", "static"),	# login app 静态文件目录
]

次に、スタイルJS CSSなどをlogin/static/login/に置くなど、各APPの下のstaticの下に同じAPP名のフォルダーを作成できます。

呼び出し時に静的構造体とアプリ名を使用する

{% static 'main/img/firefox-logo-small.jpg' %}

{% static 'login/img/name.png' %}

さらに、静的ファイルのパッケージ化と統合については、次の記事を参照してください。

複数の Django アプリで静的ファイルにアクセスできない問題を解決する

iframeを使用する

django で iframe を使用するとブラウザエラーが発生する場合があり、プロンプト情報によると が原因であることが分かりましたX-Frame-Options=deny

Refused to display xxx in a frame because it set 'X-Frame-Options' to 'deny'.

クリックジャッキング保護に関する公式ドキュメント

クリックジャック保護

<frame>X-Frame-Options HTTP 応答ヘッダーは、ページを、<iframe><embed>または<object>で表示できるかどうかをブラウザに示すために使用されるフラグです。サイトは、そのサイトが他の人のサイト内に埋め込まれないようにすることで、クリックジャッキング攻撃を回避できます。これには 3 つの値があります。

  • DENY: 同じドメイン名のページにネストされている場合でも、ページをフレーム内に表示できないことを示します。
  • SAMEORIGIN: 同じドメイン名のページのフレーム内にページを表示できることを示します
  • ALLOW-FROM uri: 指定されたソースのフレーム内にページを表示できることを示します

上記の X-Frame-Options の 3 つの値の説明によれば、django の X-Frame-Options を SAMEORIGIN に変更すれば、フレーム内に同じドメイン名のページを表示することができます。

Xフレームを有効にする

settings.py のミドルウェアでは、django.middleware.clickjacking.XFrameOptionsMiddlewareミドルウェアを有効にするということは X-Frame をオンにすることを意味し、django はデフォルトでオンになります。

MIDDLEWARE = [
    ...
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ...
]

同じドメイン名で iframe を使用できるようにする

デフォルトでは、ミドルウェアは送信ヘッダーごとHttpResponse将X-Frame-Optionsに設定されますDENY他の値に設定したい場合は、settings.py で X_FRAME_OPTIONS を設定できます。

X_FRAME_OPTIONS = 'SAMEORIGIN'

ビューが X フレームを使用しないことを指定します

このミドルウェアを使用するときに個々のビューで X-Frame を使用しないようにしたい場合は、デコレーターを使用できます。xframe_options_exempt

from django.http import HttpResponse
from django.views.decorators.clickjacking import xframe_options_exempt

@xframe_options_exempt
def ok_to_load_in_a_frame(request):
    return HttpResponse("此页面可以使用 iframe 加载")

注: フレームまたは iframe 内でフォームを送信したり、セッション Cookie にアクセスしたりする場合は、 または のCSRF_COOKIE_SAMESITE設定を変更する必要がある場合がありますSESSION_COOKIE_SAMESITE

X-Frameを使用するビューを指定します

指定したビューで X-Frame を使用します。デコレータも使用できます

from django.http import HttpResponse
from django.views.decorators.clickjacking import xframe_options_deny
from django.views.decorators.clickjacking import xframe_options_sameorigin

@xframe_options_deny
def view_one(request):
    return HttpResponse("不能在 iframe 中使用")

@xframe_options_sameorigin
def view_two(request):
    return HttpResponse("可以在同域的 iframe 中使用")

検証コード

クローラーやその他の人為的操作を防ぐために、検証コードを使用してチェックします。一般的に使用される検証コードの方法は次のとおりです。

  • 画像認証コード
  • スライド認証コード
  • SMS認証コード
  • 電子メール認証コード

枕図書館

ピロー ライブラリは、Python 用の比較的シンプルで便利なグラフィック ツール ライブラリです。

pip installpillow ピローを
使用する場合、PIL ライブラリが導入されることに注意してください

Pillow には、Image、ImageDraw、ImageFont クラスという 3 つの主要なクラスがあります。

CanvasImage クラス

Image クラスはキャンバスを作成でき、すべてのグラフィックはキャンバス上に描画されます。キャンバスを作成する場合は、キャンバスのサイズや色などを指定する必要があります。

BrushImageDraw クラス

ImageDrawクラスはキャンバス上に模様を描画するために使用され、使用する際にはブラシの色、描画方法(特定のグラフィックや描画パス)、描画の開始座標、終了座標などを指定する必要があります。 。

FontImageFontクラス

ImageFontクラスは文字を画像として描画することができ、使用する際にはフォントやサイズ、文字の内容などを指定する必要があります。

描画工程

描画プロセスには通常、キャンバスの作成、キャンバス上へのテキストの描画、キャンバス上への干渉画像の描画が含まれます。

from PIL import Image, ImageDraw, ImageFont

def new_code_img(request):
	# 创建画布
	bg = (220, 220, 180)	# 画布背景色,RGB
	img = Image.new('RGB', (120, 30), bg)	# 创建画布,使用RGB模式,画布大小为120*30,指定背景色
	# 在画布上创建画笔对象
	draw = ImageDraw.Draw(img, 'RGB')		# 指定画布,创建画笔对象,使用RGB模式
	# 随机生成4位字符
	chars = ''
	while len(chars) < 4:
		flag = random.randint(0, 2)
		char = chr(random.randrange(48, 57) if flag == 0 else random.randrange(65, 90) if flag == 1 else random.randrange(97, 122))
		if len(chars) == 0 or chars.count(char) == 0:
			chars += char
	# 保存验证字符到 session
	request.session['code'] = chars
	# 创建字体
	font = ImageFont.truetype(font='static/font/Fangz.ttf',size=25)
	# 绘制内容
	for char in chars:
		# 指定字体颜色
		font_color = (random.randrange(255), random.randrange(255), random.randrange(255))
		# 字符坐标
		xy = (15 + chars.index(char) * 25, random.randint(0, 5))
		# 绘制字符
		draw.text(xy, char,font=font,fill=font_color)
	# 画干扰点
	for i in range(50):
		# 干扰点坐标
		xy = (random.randrange(120), random.randrange(30))
		# 干扰点颜色
		p_color = (random.randrange(255), random.randrange(255), random.randrange(255))
		# 绘制干扰点
		draw.point(xy, p_color)
	# 删除画笔和字体,释放资源
	del draw
	del font
	# 生成图片对象,返回响应
	import io
	# 创建缓冲对象
	buf = io.BytesIO()
	# 保存图片到缓冲区
	img.save(buf, 'png')
	# 从缓冲区获取图片,添加到响应对象中,并返回
	return HttpResponse(content=buf.getvalue(), content_type='image/png')

フロントエンド使用確認コード

フロントエンドで img タグを使用して検証コード画像を取得し、クリックを追加してイベントを再取得できます。

<img src='/new_code_img/' onclick='this.src="/new/code_img/?t="+new Date()*1'>

このパラメータは、リクエストが行われるたびにパスが異なることを保証するために追加され、新しい検証コード イメージが取得されます。

ページネータ

Django は、非常に便利なデータ ページング ツールである Paginator を提供します。これは、データ モデルのページネーションに使用されます。これを使用するとき (通常はビュー処理関数で)、クエリされたデータ モデルを渡すだけです。ページネータを使用すると、現在のページ番号、前ページと次ページのページ番号、前後ページの有無などの情報を簡単に取得できます。

from django.core.paginator import Paginator

def order_list(request):
	# 获取查询关键字
	kw = request.GET.get('kw', '')
	# 获取展示页码
	page = request.GET.get('p', 1)
	# 获取查询数据
	orders = Order.objects.filter(Q(title__icontains=kw)).all()
	# 创建分页器,参数1是数据模型对象,参数2是每页显示记录数量
	paginator = Paginator(orders, 5)
	# 获取分页后的数据,即第几页内的数据
	pager = paginator.page(page)
	return render(request, 'list.html', locals())

フロントエンドは、pager.object_list を使用してデータを取得し、paginator.page_range を使用してすべてのページ番号を取得します。その他のページング情報も簡単に取得できます。

<html lang="en">
	<head>
		<meta charset="UTF-8">
		<title>商品列表</title>
		<style>
			.page {
      
      
				text-decoration: none;
				cursor: pointer;
				padding: 5px;
				margin: 5px;
			}
			.active {
      
      
				background-color: lightblue;
			}
		</style>
	</head>
	<body>
		<form method="GET">
			<input type="text" name='kw' placeholder="商品名称">
			<button>搜索</button>
		</form>
		<h3>订单查询结果</h3>
		<table border="1" cellspacing="0" cellpadding="2" width="100%">
			<thead>
				<th>ID </th>
				<th>名称</th>
				<th>单价</th>
				<th>支付状态</th>
			</thead>
			<tbody>
				{% for order in pager.object_list %}
				<tr>
					<td>{
   
   { order.id }}</td>
					<td>{
   
   { order.title }}</td>
					<td>{
   
   { order.price }}</td>
					<td>{
   
   { order.pay_status }}</td>
				</tr>
				{% empty %}
				<tr>
					<td colspan="">没有查到数据</td>
				</tr>
				{% endfor %}
			</tbody>
		</table>
		<p style="text-align: center;">
			<a {% if pager.has_previous %} href="?p={
     
     { pager.previous_page_number }}&kw={
     
     { kw }}" {% endif %}>&lt;</a>
			{% for p in paginator.page_range %}
				{% if p == pager.number %}
				<a class="page active">{
   
   { p }}</a>
				{% else %}
				<a class="page" href="?p={
     
     { p }}&kw={
     
     { kw }}">{
   
   { p }}</a>
				{% endif %}
			{% endfor %}
			<a {% if pager.has_next %} href="?p={
     
     { pager.next_page_number }}&kw={
     
     { kw }}" {% endif %}>&gt;</a>
		</p>
	</body>
</html>

クラスビュー (CBV)

Django は、ビュー処理関数 (tornado と同様) を置き換える多くのビュー処理クラスを提供します。

  • ビュー
  • テンプレートビュー
  • リダイレクトビュー
  • リストビュー
  • 編集ビュー
  • フォームビュー
  • 詳細ビュー
  • 削除ビュー

簡単なビューの例:

from django.views import View

class GoodsView(View):
	# 处理 get 请求
	def get(self, request):
		pass
	# 处理 post 请求
	def post(self, request):
		pass

ルートを登録するときは、ルート処理クラスを登録し、その as_view() メソッドを使用することに注意してください。

urlpatterns = [
	path('goods/', views.GoodsView.as_view(), name='goods')
]

具体的な使用方法については公式ドキュメントを参照してください。

公式ドキュメント - 組み込みのクラスベースのビュー

ジャンゴミドルウェア

ミドルウェアはAOP(Aspect-Oriented Programming)の設計思想に基づいており、その目的は事業の拡大、つまり元の業務を変更せずに新しい機能を追加することであり、デコレータに似ています。

Django はリクエストを受け取ると、一連のミドルウェアを経由して view 関数に到達し、view 関数で処理されたデータはこれらのミドルウェアを経由してユーザーのブラウザに返されます。

ミドルウェアのリクエスト処理メソッドは process_request 、レスポンス処理メソッドprocess_response です。処理シーケンスは次のとおりです。受信したリクエストは process_request に渡され、次に URL ディストリビュータに渡され、続いて process_view に渡され、次にビュー処理関数に渡され、その後 process_template_response (一般的には使用されません) に渡され、その後データ モデルが処理され、テンプレートが処理されます。はビューを通じてレンダリングされ、次に process_response が来て、最終的にブラウザーに応答を送り返します。リクエストからレスポンスまでの過程でエラーが発生した場合、process_Exceptionで処理され、ブラウザにエラー情報が返されます。

リクエストがミドルウェアを通過するときに、 process_request が値を返さない (または戻り値が None の場合) 場合は、次のステップに処理を続行します。戻り値がある場合 (戻り値は HttpResponse、render、redirect です)。リクエストを実行し、戻り値をこのクラスの process_response メソッドに渡すことで、リクエストをクライアントに直接返すことができるため、ミドルウェアの view 関数の前後にいくつかの動作を記述することができます。

ミドルウェアの処理順序はsettings.pyに登録されている順序に従い、リクエストはその順に処理され、レスポンスはその逆の順序で処理されます。

ミドルウェアを使用する

django で使用されるミドルウェアは、settings.py の MIDDLEWARE フィールドで定義できます。このフィールドは文字列のリストであり、各文字列は参照されるミドルウェアクラスが配置される場所です。

カスタムミドルウェア

すべてのカスタム ミドルウェア (クラス) は、django.utils.deprecation.MiddlewareMixinクラスを継承する必要があります。そして、settings.pyに登録を追加します

from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import redirect

class AuthMiddleware(MiddlewareMixin):
    """访问验证中间件"""
    def process_request(self, request):
        # 如果请求访问 login 页面则不进行检测
        if request.path_info == '/login/':          # request.path_info 能获取请求URL
            return
        # 1. 读取当前访问用户的 session 信息,如果能读到,说明已经登录过
        username = request.sesssion.get('username')
        if username:
            return
        # 2. 如果没有登录过,
        else:
            return redirect('/login/')
    
    def process_view(self, request):
    	pass

	def process_template_response(self, request, response):
		pass

    def process_response(self, request, response):
        return response

信号伝達機構

Django は、フレームワーク内の特定の操作の実行を監視し、ビジネスとモデルまたはビューの間の分離を実現するために使用されるシグナル スケジューリングメカニズムを提供します。Django で特定のアクションが発生すると、システムはシグナルで定義された関数に基づいて対応する操作を実行します。

シグナルの送信は、関数を呼び出してパラメーターを渡すことに少し似ていますが、シグナルを送信するときにどの関数を呼び出す必要があるかわからないため、シグナル メカニズムがセットアップされます。必要な場合にのみシグナルを受信する関数またはメソッドを作成します。

さらに、シグナルを使用する場合、シグナル メカニズムはスレッドセーフですが、複数のプロセスにわたって使用できないことに注意してください。

通常、シグナルはアプリケーション app の __init__.py ファイルに書き込まれます。ただし、アプリは登録中に読み込まれるため、モデル、URL、ビューを導入できず、エラーが報告されます。したがって、通常、エラーを生成する信号はアプリ構成に記述され、アプリの準備ができた後に信号がインポートされます。

# app.apps.py
from django.apps import AppConfig

class MyAppConfig(AppConfig):
    name = 'myApp'

    def ready(self):
        # 这里写入信号,或导入信号文件
        import myApp.signals

よく使用される内蔵信号

Django にはいくつかの組み込みシグナルがあります。

  • モデルと移行 django.db.models.signals
信号 説明する
pre_init モデルオブジェクトがコンストラクターメソッドを実行する前に自動的にトリガーされます。
post_init モデルオブジェクトがその構築メソッドを実行した後に自動的にトリガーされます。
事前保存 モデル オブジェクトが保存される前に自動的にトリガーされます。
後保存 モデルオブジェクトが保存された後に自動的にトリガーされます
事前削除 モデルオブジェクトが削除される前に自動的にトリガーされます
post_delete モデルオブジェクトが削除された後に自動的にトリガーされます
m2m_changed モデル オブジェクトは、ManyToMany フィールドを使用してデータベースの 3 番目のテーブルを操作します (追加/削除)。
クラス_準備済み プログラムが開始されると、登録されているモデル クラスが検出され、クラスごとに自動的にトリガーされます。
事前移行 移行コマンドを実行する前に自動的にトリガーされます
移行後 移行コマンドの実行後、自動的にトリガーされます。
  • リクエストの応答と設定ファイル django.core.signals
信号 説明する
request_started リクエストが来る前に自動的にトリガーされる
リクエスト_終了しました リクエストが完了すると、自動的にトリガーされます。
got_request_例外 リクエスト例外時に自動的にトリガーされます
設定変更済み 構成ファイルが変更されると自動的にトリガーされます
  • django.test.signals をレンダリングするテンプレート
信号 説明する
template_rendered テンプレートがレンダリングを実行すると自動的にトリガーされます
  • データベース django.db.backends.signals
信号 説明する
接続_作成済み データベース接続の作成時に自動的にトリガーされます

信号の使用法

シグナル関数のパラメータ定義は固定されており、例えば、sender は情報送信オブジェクトを表します。

connect()信号モニタリングでは、次のように定義される信号メソッドが使用されますSingal.connect(receiver, sender=None, weak=True, dispatch_uid=None)パラメータの意味は次のとおりです。

  • レシーバー : このシグナルに接続されるコールバック ハンドラー関数。
  • sender: 監視する信号の送信元を指定できます。送信者が指定されていない場合、シグナルをトリガーする可能性のあるすべての送信者が監視されます。
  • weak: Django はデフォルトでシグナル ハンドラーを弱い参照として保存します。つまり、ハンドラーがローカル関数の場合、ガベージ コレクションされる可能性があるため、ハンドラーを追加する必要がありますweak=Falseこのパラメータの最大の役割は、信号処理関数がオブジェクトのメソッドである場合、オブジェクトが削除された場合に、エラーを発生させることなく信号を再利用できることです。
  • dispatch_uid : 重複したシグナルが送信される可能性がある場合に備えて、シグナル受信者の一意の識別子。
from dango.db.models.signals import post_save

def post_save_func(sender, **kwargs):
	# 输出相关信息
	print('保存信息:', sender, kwargs)

# 连接信号和处理函数
post_save.connect(post_save_func, weak=False)

デコレータも使用できます

from django.db.models.signals import post_save
from django.dispatch import receiver
from models import Test

@receiver(post_save, weak=False)
def post_save_func(sender, **kwargs):
	# 输出相关信息
	print('保存信息:', sender, kwargs)

情報の伝達、受信機能

def singal_handler(sender, **kwargs):
	pass

Django 信号処理関数には 2 つの主なパラメータがあります。sender は信号送信者、kwargs は渡される基本情報を格納する辞書です。たとえば、instance はシグナルを生成したインスタンス オブジェクト、signal は渡されたシグナル オブジェクトなどです。

カスタム信号

Django のプリセット信号に加えて、いくつかの信号をカスタマイズすることもできます

信号を定義する

信号の定義は、

from django import dispatch

# providing_args 发送信息的参数列表
action = dispatch.Signal(providing_args=['name', 'age'])

レジスタ信号

シグナルの登録とは、シグナルを受信関数に送信することを意味します

@receiver(action)
def post_action(sender, **kwargs):
	print(sender, kwargs)

信号を送る

動作が発生したとき (関数またはメソッドが実行されているとき) にシグナルを送信します。送信者を除く、送信されるデータは、シグナルの定義時に定義されます。

action.send(sender='sender', name='Joe', age=18)

信号受信機の管理

Sign は、受信機を管理するためのいくつかのインスタンス メソッドを提供します。

  • connect(self, receiver, sender=None, weak=True, dispatch_uid=None)
    受信機を接続します。
  • disconnect(self, receiver=None, sender=None, dispatch_uid=None)
    受信機を取り外します。
  • has_listeners(self, sender=None)
    シグナルの送信者に受信者がいるかどうかを判断する
  • send(self, sender, **named)
    送信者はシグナルを送信します。Return [(受信者, 応答), … ]
  • send_robust(self, sender, **named)
    send_robust違いはsend、例外が発生したときのレシーバーの動作にあります: send を呼び出すとき、レシーバーで例外が発生した場合、例外は引き続き送信でスローされます。現在のレシーバーの背後に他のレシーバーがある場合、例外はスローされません。例外の発生により呼び出されました。send_robust呼び出し時、受信側で例外excが発生した場合はそれを捕捉し結果としてsend_robustに(receiver,exc)を返し、例外が無い場合は結果として(receiver,response)を返します。送信するのと同じように。

ロギング

Django のログは、バージョン、フォーマッタ、ハンドラ、ロガー、フィルタの 5 つの部分で構成されます。Django のロガーには、「django.server」と「django.request」という 2 つのデフォルトのロガーがあります。

ロギングを設定するには、辞書である settings.py で LOGGING を宣言する必要があります。

LOGGING = {
    
    
	'version': 1,	# 版本号
	'disable_existing_logger': False,	# 是否禁用已经存在的记录器
	'formatters': {
    
    		# 声明 格式化输出
		'simple': {
    
    		# 声明格式化的名称,后面在记录器中会使用
			'format': "%(asctime)s %(module)s.%(funcName)s: %(message)s",	# 输出格式
			'datefmt': '%Y-%m-%d %H:%M:%S'	# 时间格式
		}
	},
	'handlers': {
    
    	# 声明处理器,如文件输出、控制台输出、发送邮件等
		'inf': {
    
    
			'class': 'logging.handlers.TimedRotatingFileHandler',	# 处理器类
			'filename': f'{
      
      BASE_DIR}/out.log',		# 输出文件名
			'when': 'WO',	# 每周一切割日志
			'backupCount': 5,	# 备份数量
			'formatter': 'simple',	# 使用的格式
			'level': 'DEBUG' if DEBUG else 'INFO',	# 处理级别
		},
		'err': {
    
    
			'class': 'logging.handlers.TimedRotatingFileHandler',	# 处理器类
			'filename': f'{
      
      BASE_DIR}/err.log',		# 输出文件名
			'when': 'D',	# 每天切割日志
			'backupCount': 5,	# 备份数量
			'formatter': 'verbose',	# 使用的格式
			'level': 'WARNING',
		},
		'out': {
    
    
			'class': 'logging.StreamHandler',	# 处理器类
			'formatter': 'simple',	# 使用的格式
			'level': 'INFO',
		},
		'file': {
    
    
			'class': 'logging.FileHandler',	# 处理器类
			'formatter': 'simple',	# 使用的格式
			'level': 'WARNING',
			'filename': f'{
      
      BASE_DIR}/warn.log',
			'when': 'D',	# 每天切割日志
			'backupCount': 7,	# 备份数量
		}
	},
	'loggers': {
    
    	# 日志记录器
		'inf': {
    
    
			'handlers': ['inf'],	# 使用的处理器,一个记录器能使用多个处理器
			'level': 'DEBUG',		# 记录级别
			'propagate': True,		# 是否传播
		},
		'err': {
    
    
			'handlers': ['err'],
			'level': 'DEBUG',
			'propagate': True,
		},
		'django': {
    
    
			'handlers': ['out', 'file'],	# 使用的处理器,一个记录器能使用多个处理器
			'level': 'INFO',		# 记录级别
			'propagate': True,		# 是否传播
		}
	}
}

これは、構成後に、通常はリクエストのロギングなどのミドルウェアで使用できます。

import logging

class LoggingMiddleware(MiddlewareMixin):
	def process_request(self, request):
		ip = request.META.get('REMOTE_ADDR')
		path = request.get_raw_uri()
		msg = "%s 访问 %s" %(ip, path)
		# 获取日志记录器 django,并记录 INFO 级消息
		logging.getLogger('django').info(msg)

キャッシュ

Django には、レンダリングされたページ、セッション、その他のデータを呼び出されるのを待っているキャッシュに保存できるさまざまなキャッシュ ソリューションがあります。キャッシュ、ローカル、ファイル サーバー、Redis サーバー、データベース サーバーなどのオプションも多数あります。詳細については、公式ドキュメントを参照してください。

公式ドキュメントキャッシュフレームワーク

キャッシュの構成

カスタム キャッシュは settings.py で構成する必要があります。たとえば、

ファイルキャッシュ:

CACHES = {
    
    
	'default': {
    
    	# 缓存方案名称
		'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',	# 缓存方案(使用哪种缓存技术)
		'LOCATION': 'c:/foo/bar',		# 数据存储地点名称
		'TIMEOUT': 300,		# 超时时间,单位是秒
		'OPTIONS': {
    
    		# 其他的一些选项
			'MAX_ENTRIES': 300,		# 最大的实体数
		},
	}
}

メモリキャッシュ:

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

ネイティブキャッシュ

ネイティブ キャッシュは、セッションに似た一時データを保存するために使用されますが、クライアントに依存したり、クライアントを区別したりしません。タイムアウト時間の単位は秒です。

from django.core.cache import cache

cache.add(key, value, timeout=DEFAULT_TIMEOUT, version=None)			# 添加缓存,如果 key 已存在则放弃
cache.set(key, value, timeout=DEFAULT_TIMEOUT, version=None)			# 设置缓存 key、value、timeout
cache.set_many(dict, timeout)		# 传入字典,一次设置多个缓存
cache.get(key, default=None, version=None)		# 获取缓存 key 的 value
cache.get_many(keys, version=None)		# keys 是列表,返回字典,返回多个缓存
cache.delete(key, version=None)		# 显性删除 key 和其 value
cache.clear()		# 清空所有缓存

詳しい使用方法については、公式ドキュメントをご覧ください。

公式ドキュメント - キャッシュ フレームワーク - 基盤となるキャッシュ API

キャッシュスキーム

キャッシュ キャッシュはdefaultデフォルトでそのスキームを使用します。他のスキーム名を使用する場合、またはキャッシュされたデータを他のスキーム名で操作する必要がある場合は、次のようにキャッシュ スキームを指定できます。

from django.core.cache import caches

cache = caches['other_cache']		# 指定缓存方案

これで、キャッシュを正常に操作できるようになります。

キャッシュされたビュー

キャッシュを使用してレンダリングされたビューの結果を保存するには、デコレータ @cache_page を使用できます。これにより、ビューの応答が自動的にキャッシュされます。

from django.views.decorators.cache import cache_page

@cache_page(timeout=60,cache='default',key_prefix=None)		# cache为settings.py中设置的缓存方案
def index(request):
	pass

Redisキャッシュを使用する

Redis の特性により、キャッシュされた分散データベースであることが決定されるため、ほとんどのフレームワーク キャッシュは Redis を使用します。Django は Redis を使用してプラグインを使用しdjango-redis、それを settings.py の CACHES に設定します。

pip インストール django-redis

CACHES = {
    
    
	'redis': {
    
    	# 缓存方案名称
		'BACKEND': 'django_redis.cache.RedisCache',	# 缓存方案
		'LOCATION': 'redis://127.0.0.1:6379/1',		# redis 的主机地址、端口和数据库编号
		'OPTIONS': {
    
    		# 其他的一些选项
			'CLIENT_CLASS': 'django_redis.client.DefaultClient',	# 连接客户端
			'PASSWORD': 'mysecret',		# 口令
			'SOCKET_CONNECT_TIMEOUT': 5,	# 连接超时时间,单位秒
			'SOCKET_TIMEOUT': 5,	# 读写超时时间,单位秒
			'CONNECTION_POOL_KWARGS': {
    
    'max_connections': 10,}		# 连接池参数
		},
	}
}

django-redis 接続プール

キャッシュ情報を構成した後、コード内で接続プールを使用できるようになります。

from django.core.cache import get_cache
from django_redis import get_redis_connection

r = get_redis_connection('redis')
connection_pool = r.connection_pool

おすすめ

転載: blog.csdn.net/runsong911/article/details/127936118
おすすめ