1. ホーム
  2. Python

ORMにおけるトランザクションとロック、Ajaxによる非同期リクエストと部分リフレッシュ、Ajaxによるファイルアップロード、日時指定Json、マルチテーブルクエリブックのシステム

2022-02-08 11:58:02
<パス

I. ORM におけるトランザクションとロック

トランザクション

アトミック性を確保するためのトランザクション

"""
Transactions
	ACID
		Atomicity: the smallest unit that cannot be separated
		Consistency: complementary to atomicity
		Isolation: Transactions do not interfere with each other
		Persistence: Once a transaction is confirmed, it is permanent
	
	Rollback of a transaction
		rollback
	Confirmation of a transaction
		commit
"""
# currently requires
from django.db import transaction

with transaction.atomic(): open transaction
    # sql1
    # sql2
    # All orm operations written in the with block are part of the same transaction
print("perform other operations")
  
import os

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "about_orm.settings")

import django

django.setup()

from app01 import models

from django.db import transaction
from django.db.models import F, Q

# Transactions to ensure atomicity
# Atomicity: An atomic transaction is either executed in its entirety or simply not executed at all. This means that every task in a unit of work must be executed correctly. If any task fails to execute, the entire unit of work or transaction is terminated.
try:
    with transaction.atomic(): # Open the transaction
        # A series of operations
        models.Book.objects.all().update(kucun=F("kucun") - 10)
        models.Book.objects.all().update(sale=F("sale") + 10)
except Exception as e:
    print(e)


ここでは、さらにトランザクションを設定するための小さな原則を紹介します。

1. トランザクションを短くする

2. トランザクションのロールバックを避けるようにする

3. セーブポイントを回避する

4. デフォルトでは、悲観的なロックに依存します。

5. 5. スループットを重視するトランザクションには楽観的ロックを考慮する

6. トランザクションを開くためのステートメントを表示する

7. ロックされる行数は少ないほどよく、またロックされる時間は短いほどよい

ロック

行レベルロック

entries = Entry.objects.select_for_update().filter(author=request.user) # Add a mutually exclusive lock, because mysql automatically adds a shared lock when querying, so we can manually add a mutually exclusive lock. create, update, delete operations, mysql automatically adds row-level mutex lock

SELECT * FROM t1 WHERE id=1 FOR UPDATE;
model.T1.objects.select_for_update().filter(id=1)


テーブルロック

class LockingManager(models.Manager):
    """ Add lock/unlock functionality to manager.
Example::

    Class Job(models.Model): # actually do not have to be so loaded, directly in orm when creating a table, to the table to define a lock and unlock methods, with the connection module provided by django to send a native sql statement to lock the table and unlock the native sql statement can be, without the outer layer of this LckingManager(model.Manager) class

        manager = LockingManager()

        counter = models.IntegerField(null=True, default=0)

        @staticmethod
        def do_atomic_update(job_id)
            ''' Updates job integer, keeping it below 5 '''
            try:
                # Ensure only one HTTP request can do this update at once.
                Job.objects.lock()

                job = Job.object.get(id=job_id)
                # If we don't lock the tables two simultanous
                # requests might both increase the counter
                # going over 5
                if job.counter < 5:
                    job.counter += 1                                        
                    job.save()

            finally:
                Job.objects.unlock()
"""    

def lock(self):
    """ Lock table. 

    Locks the object model table so that atomic update is possible.
    Simulatenous database access request pend until the lock is unlock()'ed.

    Note: If you need to lock multiple tables, you need to do lock them
    Note: If you need to lock multiple tables, you need to do lock them all in one SQL clause and this function is not enough.
    dead lock, all tables must be locked in the same order.

    See http://dev.mysql.com/doc/refman/5.0/en/lock-tables.html
    """
    cursor = connection.cursor()
    table = self.model._meta.db_table
    logger.debug("Locking table %s" % table)
    cursor.execute("LOCK TABLES %s WRITE" % table)
    row = cursor.fetchone()
    return row

def unlock(self):
    """ Unlock the table. """
    cursor = connection.cursor()
    table = self.model._meta.db_table
    cursor.execute("UNLOCK TABLES")
    row = cursor.fetchone()
    return row


II. Ajax

www.cnblogs.com/clschao/art を参照...

1. はじめに

AJAX(Asynchronous Javascript And XML)は、中国語に訳すと「非同期型JavaScriptとXML」です。つまり、Javascript言語を使ってサーバーと非同期でやり取りし、転送されるデータはXMLです(もちろん、転送されるデータはXMLだけでなく、現在はjsonデータも多く使用されています)。

AJAXは新しいプログラミング言語ではなく、既存の標準を利用した新しい方法です。

AJAXの最大の利点は、ページ全体を再読み込みすることなく、サーバーとデータのやり取りを行い、ページの一部を更新できることである。(この機能により、ユーザーにはリクエストとレスポンスの処理が知らず知らずのうちに行われているような印象を与えることができる)。

AJAXはブラウザのプラグインを必要としませんが、ユーザーがブラウザ上でJavaScriptを実行することを許可する必要があります。

a. 同期型インタラクション:クライアントがリクエストを送信した後、サーバーの応答が終了するのを待ってから2回目のリクエストを送信する必要があります。

b.非同期インタラクション。クライアントがリクエストを行った後、サーバーの応答が終了するのを待たずに2回目のリクエストを行う。

AJAXの特徴として、非同期の他に、ブラウザのページが部分的に更新されるというものがあります(この機能は、ユーザーにリクエストとレスポンスの処理が知らない間に行われているような印象を与えます)。

Features.
	1. Asynchronous request
	2. partial refresh
	<script>
    $('#btn').click(function () {
        $.ajax({
            {#url:'/login/',#}
            url:"{% url 'login' %}",
            type:'post',
            data:{
                csrfmiddlewaretoken:$('[name=csrfmiddlewaretoken]').val(),
                {#csrfmiddlewaretoken:"{

{ csrf_token }}",#}
                name:$('#username').val(),
                pwd:$('#password').val(),
            },
            success:function (res) {
                var resStr = JSON.parse(res)
                if (resStr['code'] == 0){
                    location.href=resStr['redirect_url']
                }else{
                    if(resStr['code'] == 3){
                        var spanEle = document.createElement('span');
                        $(spanEle).text(resStr['warning'])
                        $('form').append(spanEle)
                    }
                }

                console.log(resStr)
            }
        })
    })


2. ajaxのリクエストとレスポンス

# urls.py
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^login/', views.LoginView.as_view(),name='login'),
    url(r'^index/', views.IndexView.as_view()),
]
# views.py
class LoginView(View):

    def get(self, request):

        # return render(request, reverse('login'))
        return render(request, 'login.html')

    def post(self, request):
        # name = request.POST.get('username')
        # password = request.POST.get('passwoame')
        name = request.POST.get('name')
        password = request.POST.get('pwd')
        print(name, password)
        if name == 'laowang' and password == '123':
            ret = {'code': 0, 'redirect_url': '/index/'}
            ret = json.dumps(ret)
            return HttpResponse(ret)
        else:
            ret = {'code':3,'warning': 'username or password error!}
            ret = json.dumps(ret)
            # return HttpResponse(ret)
        	return HttpResponse(ret, content_type='application/json') # Add this to ajax and you don't have to do json parsing

class IndexView(View):
    def get(self,request):
        return render(request, 'index.html')
# login.html
<body>
<h1> Hello, world! </h1>
<form action="/login/" method="post">
    {% csrf_token %}
    Username: <input type="text" id="username" name="username">
    password: <input type="password" id="password" name="password">
{# submit and button will then trigger the form request #}
{# <input type="submit" id="btn">#}
{# <button></button>#}
    <input type="button" id="btn" value="submit">
</form>
<! -- jQuery (all of Bootstrap's JavaScript plugins rely on jQuery, so it must be in front of them) -->
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script>
<script>
    $('#btn').click(function () {
        $.ajax({
            {#url:'/login/',#}
            url:"{% url 'login' %}",
            type:'post',
            data:{
                csrfmiddlewaretoken:$('[name=csrfmiddlewaretoken]').val(),
                {#csrfmiddlewaretoken:"{
{ csrf_token }}",#}
                name:$('#username').val(),
                pwd:$('#password').val(),
            },
            success:function (res) {
                // var resStr = JSON.parse(res)
                if (resStr['code'] == 0){
                    location.href=resStr['redirect_url']
                }else{
                    if(resStr['code'] == 3){
                        var spanEle = document.createElement('span');
                        $(spanEle).text(resStr['warning'])
                        $('form').append(spanEle)
                    }
                }

                console.log(resStr)
            }
        })
    })
</script>
</script>
</body>
  
from django.http import JsonResponse
# This kind of response is the most convenient, direct Json, and no need to import Json
if name == 'laowang' and password == '123':
    ret = {'code': 0, 'redirect_url': '/index/'}
    # ret = json.dumps(ret)
    return JsonResponse(ret)
    # return HttpResponse(ret, ontent_type='application/json') # Add this and you don't need to parse json in ajax


III. ファイルをアップロードするためのフォーム

{#enctype="multipart/form-data" must specify contenttype#}
<form action="/upload/" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    avatar: <input type="file" name="headicon">
    Username: <input type="text" name="name">
    Password: <input type="password" name="pwd">
    <input type="submit">
</form>
  
# views.py
class UploadView(View):    
    def get(self, request):        
        return render(request, 'upload.html')    
    def post(self, request):        
        print(request.FILES) # <MultiValueDict: {'headicon': [<InMemoryUploadedFile: pech.png (image/png)>]}>        
        file_obj = request.FILES.get('headicon') # The file data needs to be taken with request.FILES        
        print(file_obj)        
        file_name = file_obj.name        
        path = os.path.join(settings.BASE_DIR, 'static', 'img', file_name) # with open('static/img/'+file_name, 'wb') as f:        
        with open(path, 'wb') as f:           
        # for i in file_obj:              
        # f.write(i)      		
            for chunck in file_obj.chuncks() # return 65536B at a time, you can set the size of chunck_size            
            f.write(chunck)        
            return HttpResponse('ok')



ファイルのアップロード II

views.py
from django.shortcuts 
import render, HttpResponsefrom django.views 
import View# using the CBV pattern class Upload(View):    
def get(self, request):        
    return render(request, "Upload.html")    
def post(self, request): # Get the file before you must change the form form inside the enctype="multipart/form-data"        
    print(request.FILES) # <MultiValueDict: {'file_name': [<InMemoryUploadedFile: 1.jpg (image/jpeg)>]}> # Get the file uploaded by the user        
    file_path = request.FILES.get("file_name")        
    print(file_path, type(file_path)) # get is the file name 1.jpg<class 'django.core.files.uploadedfile.InMemoryUploadedFile'>       
    print(file_path.name, type(file_path.name)) # 1.jpg <class 'str'>        
    with open(file_path.name, "wb")as f: # Because file_path gets a file object, so to .name a bit            
        for i in file_path:                
            f.write(i)        
        return HttpResponse("ok")
  
Upload.html
{# Be sure to change the enctype="multipart/form-data, and don't forget the csrf_token #}
<form action="" method="post" enctype="multipart/form-data"> {% csrf_token %}    
<input type="file" name="file_name" >    
<button>upload</button></form>
  
urls.pyurlpatterns = [    
url(r'^admin/', admin.site.urls),    
url(r'^upload/', views.Upload.as_view()), # don't forget the brackets
]


file_path.name: アップロードされたファイルの名前

フォーム用紙の注意点

  1. formタグのaction属性は、投稿先アドレス(デフォルトのカレントアドレスは書かれていない)、リクエストメソッド(デフォルトはget)を指定します。

  2. inputタグにはname属性が必要で、タグによってはvalueも必要です

  3. type="submit"のボタンまたはinputタグを持つ(submitはsubmitのみ)。

    # ここで method="get" であれば、元のurlを変更します。

    メールアドレスを入力してください。

    <ラベル メールアドレス <ラベル パスワード
    <ラベル 選択した項目を記憶する
    サインイン

novalidate:フォーマットバリデーションなし

ここで、method="get"とすると、元のurl http://127.0.0.1:8000/login/?user=abc&pwd=123 : セキュリティが厳しくなくなる。

POST リクエストを送信するために必要な現在のアクションです。

setings.pyの中間キーをコメントアウトします。

MIDDLEWARE = [ # middle key 'django.middleware.security,
'django.contrib.sessions.middleware,    
'django.middleware.common.CommonMiddleware', 
# 'django.middleware.csrf.CsrfViewMiddleware', commented out to submit POST requests
'django.contrib.auth.middleware.AuthenticationMiddleware',    
'django.contrib.messages.middleware,    
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]


urls.pyのデータを操作する(ログインに成功した場合はページを返し、ログインに失敗した場合は元のページを返します)

def login(requests):    
    print(requests, type(requests)) # Here's requests, a class    
    print(requests.method, type(requests.method)) # Get its method, POST GET    
    print(requests.POST, type(requests.POST.get("user"))) # Turn off an intermediate key before the POST request   
    # Logic for handling POST requests    
    if requests.method == "POST": # Get the user and password submitted by the user        
        user = requests.POST.get("user")        
        pwd = requests.POST.get("pwd") # Set a fixed value first (will be imported into the database later)        
        if user == "abc" and pwd == "123": # check success, return            
            return render(requests, "login2.html") # failed to verify    
   return render(requests, "login.html")


IV. ajax

ajaxファイルアップロード

Ajax avatar: <input type="file" id="file">
Ajax username<input type="text" id="uname">
<button id="btn">submit</button>
<! -- jQuery (all of Bootstrap's JavaScript plugins rely on jQuery, so it must be in front) -->
<script src="{% static 'js/jquery.js' %}"></script>
<script>    
$('#btn').click(function () {        
    var formdata = new FormData(); // can carry file data        
    formdata.append('name', $('#uname').val());        
    formdata.append('file_obj', $('#file')[0].files[0]);        
    formdata.append('csrfmiddlewaretoken', 
    $('[name=csrfmiddlewaretoken]').val());        
    $.ajax({            
        url:'/upload',            
        type:'post',            
        {#data:{#}            
        {# name:$('#uname').val(),#}            
        {# file_obj:$('#file')[0].files[0],#}            
        data:formdata,            
        processData:false, // do not process data            
        contentType:false, // don't set the content type            
        success:function (res){                
            console.log(res)            
        }        
})    
})
    </script>
  
class UploadView(View):    
    def get(self, request):        
        return render(request, 'upload.html')    
    def post(self, request):        
        print(request.FILES) 
        # <MultiValueDict: {'headicon': [<InMemoryUploadedFile: pech.png (image/png)>]}>        
        # file_obj = request.FILES.get('headicon') # file data needs to be taken with request.FILES        
        file_obj = request.FILES.get('file_obj') # The file data needs to be taken with request.        
        print(file_obj)        
        file_name = file_obj.name        
        path = os.path.join(settings.BASE_DIR, 'static', 'img', file_name)        
        # with open('static/img/'+file_name, 'wb') as f:        
        with open(path, 'wb') as f:            
            # for i in file_obj:            
            # f.write(i)            
            for chunk in file_obj.chunks(): # once rice hi65536B                
                f.write(chunk)        
        return HttpResponse('ok')



ajaxによる追加操作の完了

  1. urls.py

    urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
    url(r'^calc/', views.calc),
    ]

  2. views.py

    def index(request):
    return render(request, "index.html", locals())
    def calc(request):
    x1 = request.GET.get("x1")
    x2 = request.GET.get("x2")です。
    print(x1)
    print(x2)
    time.sleep(3) # 非同期テストのために3秒間休止する。
    l3 = int(x1) + int(x2)
    return HttpResponse(l3) # 計算結果はレスポンス内で返されます。

  3. index.html

    + = 計算する

パラメータを受け取るためのajax操作

views.py

import json
from django.http.response 
import JsonResponse
def text(request):    
    print(request.GET)    
    xqu = json.loads(request.GET.get("xqu")) # receive ajax parameters, pass the received string as json   
    print(xqu, type(xqu))    
    return JsonResponse({"status":200,"msg":"ok"}) # return information to ajax


html

 $("#b3").click(function () {       
     $.ajax({            
         url:"/text/",            
         type:"get",            
         data:{ #ajax pass information to function                
             name: 'aaa',                
             age: 15,                
             xqu: JSON.stringify(['smoke','drink','perm']), # can pass string directly },  
             success:function (data) {	
                 # Receive the information returned by the function                
                 console.log(data)                
                 console.log(data.msg)                
                 console.log(data.status)           
         },        
     });    
 });


ajaxで削除する

アドレスジャンプで削除(ジャンプはページを更新することです)

htmlコード

{#implement ajax for delete function#}
<button url="{% url 'del' 'publisher' i.pk %}" class="btn btn-danger btn-sm" >delete</button>
   
$(".btn-danger").click(function () {        
    $.ajax({            
        url:$(this).attr('url'),            
        type:"get",            
        success:function (data) { // receive can't redirect, so receive the address // jump with address                
        location.href = data           
        }        
    })    
})


views.py

@is_cookies
def delete(request, name, pk): # code    
    return HttpResponse(reverse(name)) # String returned


ジャンプせずに削除

htmlコード

{#implement ajax for delete function#}
<button url="{% url 'del' 'publisher' i.pk %}" class="btn btn-danger btn-sm" >delete</button>
ajax
<script>    
    $(".btn-danger").click(function () {        
    $.ajax({            
        url:$(this).attr('url'),            
        type:"get",           
        success:function (data) { //Receive can't redirect, so it receives the address //Jump with address                
        {#location.href = data#}                
        if(data.status===200){                    
        //backend delete successful, front delete the line                    
            _this.parent().parent().remove()                
        }            
       }        
    })    
})


views.py

@is_cookies
def delete(request, name, pk): # code    
    return JsonResponse({"status":200}) # return a dictionary for ajax to extract if it's 200, then delete the th tag on this page


sweetalertの使用(ポップアップ効果)

Ajax削除時のSweetalertポップアップ効果、追加。

  1. インポートスクリプト

複数のポップアップスタイルを持つ他のウェブサイト:sweetalert.js.org/guides/

AJAX素材サイト:www.cnblogs.com/maple-shaw/...

V. JSON

軽量テキストデータ交換フォーマット

パイソン

対応するデータ型

String Data Boolean List Dictionary None


シリアライズ

Data types for python --> json string


デシリアライズされた単語

json string --> python's data type


ビュー関数へのcsrfチェックサムの追加

方法1

from django.views.decorators.csrf 
import csrf_exempt,csrf_protect@csrf_exempt Add to view This view does not require csrf checksum @csrf_protect Add to view This view requires csrf checksum @ensure_csrf_cookie Add to view Ensure that the return sets csrftoken's cookie


csrfチェックサムの原則

Get the csrftoken value from the cookie Get the csrfmiddlewaretoken value from request.POST or get the x-csrftoken value from the request header Compare the two values, accept the request if the comparison is successful, and reject it if not


ajax が django の csrf チェックサムを通過するようにします。

htmlのajaxにcsrfのチェックサムを追加する。

方法2

  1. ajaxでcsrfmiddlewaretokenのキーと値のペアを直接データに追加します。

    data:{ <未定義
    csrfmiddlewaretoken:$('[name="csrfmiddlewaretoken]').val()を使用します。
    name: 'aaa',
    年齢 15,
    xqu: JSON.stringify(['smoking','drinking','perm']),
    },

前提条件:csrftokenのクッキーが必要です。

  • csrf_token%}を使用します。

  • ビューに追加された ensure_csrf_cookie デコレータを使用します。
    from django.views.decorators.csrf import ensure_csrf_cookie

    1. x-csrftokenのキーと値のペアをヘッダーに追加する(ファイルをインポートすることで)。

    1. ヘッダーをajaxに追加する
    $.ajax({ <未定義
    url:"/calc2/"。
    タイプ: "post",
    ヘッダを表示します。{"X-CSRFToken": KaTeX のパースエラーです。Expected 'EOF', got '}' at position 38: ...etoken]').val()}̲, // fetch c... ('[name="l11"]').val(),
    "x2"。 KaTeX のパースエラーです。予想される 'EOF' は 43 番目の位置に '}' があります: ... }̲, ... /.test(method));}.

最終的な推奨:インポートファイル+クッキーがあることを確認する(この最後の1つ)

import json
from datetime import datetime
from datetime import date#Converts json data with date format data
class JsonCustomEncoder(json.JSONEncoder):    
    def default(self, field):        
        if isinstance(field,datetime):            
            return field.strftime('%Y-%m-%d %H:%M:%S')        
        elif isinstance(field,date):            
            return field.strftime('%Y-%m-%d')        
        else:            
            return json.JSONEncoder.default(self,field)d1 = datetime.now()
            dd = json.dumps(d1,cls=JsonCustomEncoder)
            print(dd)