Django之ORM的锁,开启事务,Ajax

一、行锁

select_for_update(nowait=False, skip_locked=False)
注意必须用在事务里面,至于如何开启事务,我们看下面的事务一节 Book.objects.select_for_update().filter(nid=3)  # 锁住nid=3的行

select_for_update中的两个参数了解即可,因为在MySQL中压根不支持开启它们

一般情况下如果其他事务锁定了相关行,那么本查询将被阻塞,直到锁被释放。 如果这不想要使查询阻塞 的话,使用select_for_update(nowait=True)。 如果其它事务持有冲突的锁,互斥锁, 那么查询将引发 DatabaseError 异常。你也可以使用select_for_update(skip_locked=True)忽略锁定的行。 nowait和skip_locked是互斥的,同时设置会导致ValueError。

目前,postgresql,oracle和mysql数据库后端支持select_for_update()。 但是,MySQL不支持nowait和skip_locked参数。

二、表锁(了解)

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

    Example::

        class Job(models.Model): #其实不用这么复杂,直接在orm创建表的时候,给这个表定义一个lock和unlock方法,
        # 借助django提供的connection模块来发送锁表的原生sql语句和解锁的原生sql语句就可以了,
        # 不用外层的这个LckingManager(model.Manager)类

            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
        all in one SQL clause and this function is not enough. To avoid
        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

Django中ORM开启事务

一、全局开启

ATOMIC_REQUESTS设置为True,每个请求过来时,Django会在调用视图方法前开启一个事务。如果请求正确处理并正确返回了结果,Django就会提交该事务,否则,Django会回滚该事务。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'mxshop',
        'HOST': '127.0.0.1',
        'PORT': '3306',
        'USER': 'root',
        'PASSWORD': '123',
        'OPTIONS': {
            "init_command": "SET default_storage_engine='INNODB'",
       #'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", #配置开启严格sql模式
        }
        "ATOMIC_REQUESTS": True, # 全局开启事务,绑定的是http请求响应整个过程
        "AUTOCOMMIT":False,      # 全局取消自动提交,慎用
    },
  'other':{
    'ENGINE': 'django.db.backends.mysql', 
            ......
  }  # 还可以配置其他数据

对部分视图函数取消事务

from django.db import transaction

@transaction.non_atomic_requests
def my_view(request):
    do_stuff()

@transaction.non_atomic_requests(using='other')
def my_other_view(request):
    do_stuff_on_the_other_database()

强调:

Django官方文档中提到,不推荐开启全局事务。因为每个HTTP 请求都会捆绑一个事务,当流量上来的时候,性能会有影响。

二、局部开启

使用transaction.atomic(using=None, savepoint=True),atomic即原子性,我们就可以用该方法创建一个具备原子性的代码块。一旦代码块正常运行完毕,所有的修改会被提交到数据库。反之,如果有异常,更改会被回滚。

参数

using='other','other'对应的是settings.py中配置项DATABASES所指定的某个引擎,意思是当你使用other对应的引擎的时,这个事务才生效。


savepoint的意思是开启事务保存点。

用法1:作为上下文管理器来使用

from django.db import transaction

with transaction.atomisc():
    # sql1
    # sql2
    # sql3
    # 在with代码块内写的所有orm操作都是属于同一个事务

上述用法可以写在一个视图函数里

from django.db import transaction

def viewfunc(request):
    # This code executes in autocommit mode (Django's default).
    do_stuff()

    with transaction.atomic():   #保存点
        # This code executes inside a transaction.
        do_more_stuff()

    do_other_stuff()

用法2:给视图函数装饰器

from django.db import transaction

@transaction.atomic
def post(self,request):
    ...
    sid=transaction.savepoint()  #开启事务
    ...
    transaction.savepoint_rollback(sid)  # 回滚
    ...
    transaction.savepoint_commit(sid)  # 提交

也可以进行异常处理

from django.db import IntegrityError, transaction

@transaction.atomic
def viewfunc(request):
    create_parent()

    try:
        with transaction.atomic():
            generate_relationships()
    except IntegrityError:
        handle_exception()

    add_children()

用法3:还可以嵌套使用


函数的事务嵌套上下文管理器的事务,上下文管理器的事务嵌套上下文管理器的事务等

from django.db import IntegrityError, transaction

@transaction.atomic
def viewfunc(request):
    create_parent()

    try:
        with transaction.atomic():
            generate_relationships()
       # other_task()  #还要注意一点,如果你在事务里面写了别的操作,只有这些操作全部完成之后,事务才会commit,也就是说,如果你这个任务是查询上面更改的数据表里面的数据,那么看到的还是事务提交之前的数据。
    except IntegrityError:
        handle_exception()

    add_children()

上述例子中,即使generate_relationships()中的代码打破了数据完整性约束,你仍然可以在add_children()中执行数据库操作,并且create_parent()产生的更改也有效。需要注意的是,在调用handle_exception()之前,generate_relationships()中的修改就已经被安全的回滚了。因此,如果有需要,你照样可以在异常处理函数中操作数据库。

强调:尽量不要在atomic代码块中捕获异常,原因如下

因为当atomic块中的代码执行完的时候,Django会根据代码正常运行来执行相应的提交或者回滚操作。如果在atomic代码块里面捕捉并处理了异常,就有可能隐盖代码本身的错误,从而可能会有一些意料之外的不愉快事情发生。

担心主要集中在DatabaseError和它的子类(如IntegrityError)。如果这种异常真的发生了,事务就会被破坏掉,而Django会在代码运行完后执行回滚操作。如果你试图在回滚前执行一些数据库操作,Django会抛出TransactionManagementError。通常你会在一个ORM相关的信号处理器抛出异常时遇到这个行为。

捕获异常的正确方式正如上面atomic代码块所示。如果有必要,添加额外的atomic代码块来做这件事情,也就是事务嵌套。这么做的好处是:当异常发生时,它能明确地告诉你那些操作需要回滚,而那些是不需要的。

为了保证原子性,atomic还禁止了一些API。像试图提交、回滚事务,以及改变数据库连接的自动提交状态这些操作,在atomic代码块中都是不予许的,否则就会抛出异常。

下面是Django的事务管理代码:

• 进入最外层atomic代码块时开启一个事务;

• 进入内部atomic代码块时创建保存点;

• 退出内部atomic时释放或回滚事务;注意如果有嵌套,内层的事务也是不会提交的,可以释放(正常结束)或者回滚

• 退出最外层atomic代码块时提交或者回滚事务;

你可以将保存点参数设置成False来禁止内部代码块创建保存点。如果发生了异常,Django在退出第一个父块的时候执行回滚,如果存在保存点,将回滚到这个保存点的位置,否则就是回滚到最外层的代码块。外层事务仍然能够保证原子性。然而,这个选项应该仅仅用于保存点开销较大的时候。毕竟它有个缺点:会破坏上文描述的错误处理机制。

注意:transaction只对数据库层的操作进行事务管理,不能理解为python操作的事务管理

def example_view(request):
    tag = False
    with transaction.atomic():
        tag = True
        change_obj() # 修改对象变量
        obj.save()
        raise DataError
    print("tag = ",tag) #结果是True,也就是说在事务中的python变量赋值,即便是事务回滚了,这个赋值也是成功的

还要注意:如果你配置了全局的事务,它和局部事务可能会产生冲突,你可能会发现你局部的事务完成之后,如果你的函数里面其他的sql除了问题,也就是没在这个上下文管理器的局部事务包裹范围内的函数里面的其他的sql出现了问题,你的局部事务也是提交不上的,因为全局会回滚这个请求和响应所涉及到的所有的sql,所以还是建议以后的项目尽量不要配置全局的事务,通过局部事务来搞定,当然了,看你们的业务场景。

transaction的其他方法

@transaction.atomic
def viewfunc(request):

  a.save()
  # open transaction now contains a.save()
  sid = transaction.savepoint()  #创建保存点

  b.save()
  # open transaction now contains a.save() and b.save()

  if want_to_keep_b:
      transaction.savepoint_commit(sid) #提交保存点
      # open transaction still contains a.save() and b.save()
  else:
      transaction.savepoint_rollback(sid)  #回滚保存点
      # open transaction now contains only a.save()

  transaction.commit() #手动提交事务,默认是自动提交的,也就是说如果你没有设置取消自动提交,那么这句话不用写,如果你配置了那个AUTOCOMMIT=False,那么就需要自己手动进行提交。

为保证事务的隔离性,我们还可以结合上面的锁来实现,也就是说在事务里面的查询语句,咱们使用select_for_update显示的加锁方式来保证隔离性,事务结束后才会释放这个锁,例如:(了解)

@transaction.atomic ## 轻松开启事务
def handle(self):
    ## 测试是否存在此用户
    try:
        ## 锁定被查询行直到事务结束
        user = 
    User.objects.select_for_update().get(open_id=self.user.open_id)
        #other sql 语句
    except User.DoesNotExist:
        raise BaseError(-1, 'User does not exist.')

通过Django外部的python脚本来测试一下事务:

import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
    import django
    django.setup()

    import datetime
    from app01 import models

    try:
        from django.db import transaction
        with transaction.atomic():
            new_publisher = models.Publisher.objects.create(name="火星出版社")
            models.Book.objects.create(title="橘子物语", publish_date=datetime.date.today(), publisher_id=10)  # 指定一个不存在的出版社id
    except Exception as e:
        print(str(e))

Ajax

一、Ajax介绍

1、什么是ajax

简单地讲,ajax就是一种前端技术,用于朝着后端程序发送请求和数据

# 总结下来,前端向后端发送请求的方式无非以下几种,ajax技术便是其中一种
1、浏览器地址栏输入url地址
	请求方式默认并且只能是:get请求

2、a标签
	请求方式默认并且只能是:get请求

3、form表单
	请求方式可以是:get与post请求
    
4、ajax技术
	请求方式可以是:get与post请求
    
	AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步Javascript和XML”。
    即使用Javascript语言与服务器进行异步交互,传输的数据为XML
    当然,传输的数据不只是XML,现在更多使用json数据。

其实Ajax不是具体的某一个技术,而是下述几种技术的融合,每种技术都有其独特之处,合在一起就成了一个功能强大的新技术

• (1)XHTML和CSS的基于标准的表示技术

• (2)DOM进行动态显示和交互

• (3)XML和XSLT进行数据交换和处理

• (4)XMLHttpRequest进行异步数据检索

• (5)Javascript将以上技术融合在一起

所以ajax依赖依赖浏览器的 JavaScript 和XML

2、为何要用ajax

在我们之前的开发,每当用户向服务器发送请求,哪怕只是需要更新一点点的局部内容,服务器都会将整个页面进行刷新,这么做的问题有两点

1、性能会有所降低(一点内容,刷新整个页面!)

2、用户的操作页面会中断(整个页面被刷新了)

而我们基于ajax可以使用Javascript技术向服务器发送异步请求,因为异步请求,这可以使我们在不刷新页面的前提下拿到后端数据,完成页面的局部刷新,所以总结为何用ajax呢,牢牢记住两个关键词即可

• 1、异步请求

• 2、局部刷新

例如:我们可以尝试一下博客园的注册页面,那些局部刷新的部分背后都是ajax请求

3、ajax的原理

XMLHttpRequest对象是Ajax中最重要的一个对象使用Ajax更多的是编写客户端代码,而不是服务端的代码。

我们使用AJAX之后,浏览器是先把请求发送到XMLHttpRequest异步对象之中,异步对象对请求进行封装,然后再与发送给服务器。服务器并不是以转发的方式响应,而是以流的方式把数据返回给浏览器

XMLHttpRequest异步对象会不停监听服务器状态的变化,得到服务器返回的数据,就写到浏览器上【因为不是转发的方式,所以是无刷新就能够获取服务器端的数据】

二、基于jquery实现ajax

注意

如果请求方法是post,需要先注释掉settings.py中的一行代码,后续我们会详细介绍该行代码的作用
MIDDLEWARE = [
    ...
    # 'django.middleware.csrf.CsrfViewMiddleware',
   	...
]


1、案例一:计算器

通过Ajax,实现前端输入两个数字,服务器做加法,返回到前端页面,实现如下

index.html

<input type="text" id="num1">+<input type="text" id="num2">=<input type="text" id="sum">
<button id="submit">我是按钮</button>

<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script>
    $("#submit").click(function () {
        $.ajax({
            url:"/test_ajax/", // 朝着哪个地址发送ajax请求,不写则默认当前地址
            type:"post",  // 请求方法可以是get或者post
            data:{
                n1:$("#num1").val(),
                n2:$("#num2").val()
            },  // 朝后端发送的数据
            success:function (data) {
                console.log(data);
                $("#sum").val(data)
            }
        })
    })
</script>

urls.py

urlpatterns = [
    path('index/',views.index),
    path('test_ajax/',views.test_ajax),
]

views.py

def index(request):
    return render(request, 'index.html')


def test_ajax(request):
    request.is_ajax()
    num1 = request.POST.get("n1")
    num2 = request.POST.get("n2")
    res = int(num1) + int(num2)
    return HttpResponse(res)

Ajax—->服务器——>Ajax执行流程图

2、案例二:登录验证

用户在表单输入用户名与密码,通过Ajax提交给服务器,服务器验证后返回响应信息,客户端通过响应信息确定是否登录成功,成功,则跳转到首页,否则,在页面上显示相应的错误信息

login.html

用户名:<input type="text" id="user">
密码:<input type="text" id="pwd">
<button id="btn">登录</button><span id="err"></span>
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script>
    $("#btn").click(function(){
        $.ajax({
            url:"",
            type:"post",
            data:{
                username:$("#user").val(),
                password:$("#pwd").val(),
            },
            success:function(data){
                var data = JSON.parse(data);
                if (data.user) {
                    location.href="/index/";
                }else {
                    $("#err").text(data.message).css("color","red")
                }
            }
        })
    })
</script>

urls.py

urlpatterns = [
    path('index/',views.index),
    path('login/',views.login),
]
def login(request):
    if request.method == "GET":
        return render(request, 'login.html')
    elif request.method == "POST":
        username = request.POST.get('username')
        password = request.POST.get('password')

        back_dic = {'user': None, 'message': None}
        if username == "egon" and password == "123":
            back_dic['user'] = username
            back_dic['message'] = "登录成功"
        else:
            back_dic['message']='用户名或密码错误'

        import json
        return HttpResponse(json.dumps(back_dic))

三、文件上传



1、储备知识HttpRequest.body

 

前端的请求发送到django后都会被封装到HttpRequest对象中,而该对象下有一个非常重要的属性
HttpRequest.body,即请求体

# 一 当请求方法为GET
当浏览器基于http协议的GET方法提交数据时,没有请求体一说,
数据会按照k1=v1&k2=v2&k3=v3的格式放到url中,
然后发送给django,django会将这些数据封装到request.GET中,
注意此时的请求体request.body为空、无用

# 二 当请求方法为POST时
当浏览器基于http协议的POST方法提交数据时,数据会放在请求体内,然后发送给django
django会将接收到的请求体数据存放于HttpRequest.body属性中.
但该属性的值为Bytes类型(套接字数据传输都是bytes类型),
而通常情况下直接处理Bytes、并从中提取有用数据的操作是复杂而繁琐的,
好在django会对它做进一步的处理与封装以便我们更为方便地提取数据,
具体如何处理呢?
  
当前端采用POST提交数据时,数据有三种常用编码格式,编码格式不同Django会有不同的处理方式
  # 编码格式1:application/x-www-form-urlencoded,是form表单默认编码格式
  # 编码格式2:multipart/form-data,上传文件专用格式
  # 编码格式2:application/json,提交jason格式字符串

  #====》POST请求体数据的编码格式1:application/x-www-form-urlencoded时《====
      HttpRequest.body中的数据格式为b'a=1&b=2&c=3'
      django会将其提取出来封装到request.POST中
      request.FILES此时为空
        
      如:
      print(request.body) # b'a=1&b=2&c=3'
      print(request.POST) # <QueryDict: {'a': ['1'],'b': ['2'],'c': ['3']}
      print(request.FILES) # <MultiValueDict: {}>

        
  #====》POST请求体数据的编码格式2:multipart/form-data时《====      
      HttpRequest.body中的数据格式为b'------WebKitFormBoundaryKtcwuksQltpNprep\r\nContent-Disposition: form-data;......',注意,文件部分数据是流数据,所以不在浏览器中显示是正常的
      django会将request.body中的非文件数据部分提取出来封装到request.POST中
      将上传的文件部分数据专门提取出来封装到request.FILES属性中
     
      如:
      print(request.body) # 不要打印它,打印则报错,因为它是数据流
      print(request.POST) # <QueryDict: {'a': ['1'],'b': ['2'],'c': ['3']}
      print(request.FILES) # <MultiValueDict: {'head_img': [<InMemoryUploadedFile: 11111.jpg (image/jpeg)>]}>

      强调:
        1、毫无疑问,编码格式2的数据量要大于编码格式1,如果无需上传文件,还是推荐使用更为精简的编码格式1
    	2、FILES will only contain data if the request method was POST and the <form> that posted to the request had enctype="multipart/form-data". Otherwise, FILES will be a blank dictionary-like object.
        
  #===》POST请求体数据的编码格式3:application/json时《====
      此时在django后台,request.POST和request.FILES中是没有值的,都放到request.body中了,需要用json.loads对其进行反序列化
        
      如:
      print(request.body) # b'{"a":1,"b":2,"c":3}'
	  print(request.POST) # <QueryDict: {}>
	  print(request.FILES) # <MultiValueDict: {}>
        
  
# 如何设定POST提交数据的编码格式 
    前端往后台POST提交数据,常用技术有form表单和ajax两种
    form表单可以设置的数据编码格式有:编码格式1、编码格式2
    ajax可以设置的数据编码格式有:编码格式1、编码格式2、编码格式3

2、form表单上传文件


form表单可以通过属性enctype进行设置编码格,如下

编码格式1(默认的编码格式):enctype="application/x-www-form-urlencoded"
编码格式2(使用form表单上传文件时只能用该编码):enctype="multipart/form-data"

针对编码格式2我们来写一个form表单上传文件的案例,如下

在templates目录下新建register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
</head>
<body>

<form action="" method="POST" enctype="multipart/form-data" >
    {% csrf_token %}
    <p>
        用户名:<input type="text" name="name">
    </p>
    <p>
        头像:<input type="file" name="header_img">
    </p>
    <p>
        <input type="submit" value="提交">
    </p>
</form>
</body>
</html>

urls.py

from django.urls import re_path
from app01 import views

urlpatterns = [
    re_path(r'^register/$',views.register),
]

views.py

from django.shortcuts import render,HttpResponse

def register(request):
    '''
    保存上传文件前,数据需要存放在某个位置。默认当上传文件小于2.5M时,django会将上传文件的全部内容读进内存。从内存读取一次,写磁盘一次。
    但当上传文件很大时,django会把上传文件写到临时文件中,然后存放到系统临时文件夹中。
    '''
    if request.method == 'GET':
        return render(request, 'register.html')
    elif request.method == 'POST':
        # print(request.body) 

        # 从request.POST中获取用户名
        name = request.POST.get('name')

        # 从request.FILES获取文件对象
        file_obj = request.FILES.get('header_img')
        # 从文件对象中获取文件名
        filename = file_obj.name
        print(filename)

        file_obj.chunks()
        # 上传的文件存放于templates文件夹下
        with open('templates/%s' %filename, 'wb') as f:
            for line in file_obj.chunks():
                f.write(line)

        return HttpResponse('注册成功')
    
    
    # 强调:
    '''
    file_obj.read() 当文件过大时,会导致内存溢出,系统崩溃
    应该使用file_obj.chunks()读取,官网解释如下
    uploadedFile.chunks(chunk_size=None)¶
    A generator returning chunks of the file. If multiple_chunks() is True, you should use this method in a loop instead of read().

    In practice, it’s often easiest simply to use chunks() all the time. Looping over chunks() instead of using read() ensures that large files don’t overwhelm your system’s memory.
   
    注意:直接for循环file_obj也不行,因为直接for读取文件是按照行分隔符来依次读取的,不同平台下的文件行分隔符不同,有可能一行的数据就很大,所以还是推荐file_obj.chunks()来读取,官网解释如下
    Like regular Python files, you can read the file line-by-line simply by iterating over the uploaded file:

    for line in uploadedfile:
        do_something_with(line)
        
    Lines are split using universal newlines. The following are recognized as ending a line: the Unix end-of-line convention '\n', the Windows convention '\r\n', and the old Macintosh convention '\r'.
    '''

3、ajax上传文件

ajax指定编码格式如下

 

# 1、指定编码格式为:application/x-www-form-urlencoded
contentType:"application/x-www-form-urlencoded"  // 不指定默认就是这种格式
    
# 2、指定编码格式为:multipart/form-data,不是直接指定,而是通过下述方式
	var formdata=new FormData();  // 创建一个formdata对象
    formdata.append('user',$("#user").val());  
    formdata.append('img',$("#img")[0].files[0]);
    $.ajax({
        url:'',
        type:'post',
        processData:false, //告诉jQuery不要去处理发送的数据
        contentType:false,// 告诉jQuery不要去设置Content-Type请求头
        data:formdata,
        success:function (data) {
            console.log(data)
        }
    })

    
# 3、指定编码格式为:application/json
contentType:'application/json'
data:JSON.stringify(你的数据),  // 记住将数据转换成json字符串格式

针对编码格式2我们来写一个ajax上传文件的案例,如下

在templates目录下新建register.html

<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
</head>
<body>

用户名:<input type="text" id="user">
头像:<input type="file" id="img">
<button id="btn">提交</button>
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script>
    $("#btn").click(function () {
        var formdata=new FormData();  // 创建一个formdata对象
        formdata.append('user',$("#user").val());
        formdata.append('img',$("#img")[0].files[0]);
        $.ajax({
            url:"",
            type:"post",
            data: formdata,
            processData: false,
            contentType:false,
            success:function (data) {
                console.log(data);
            }
        })
    })
</script>

</body>
</html>

urls.py

from django.urls import re_path
from app01 import views

urlpatterns = [
    re_path(r'^register/$',views.register),
]

views.py

def register(request):
    if request.method == "POST":
        print('==ajax==>')
        # print(request.body)  # 打印有可能会报错
        print(request.POST)
        print(request.FILES)
        file_obj = request.FILES.get("img")
        filename = file_obj.name
        with open('templates/%s' % filename, mode='wb') as f:
            for line in file_obj.chunks():
                f.write(line)
        return HttpResponse("上传完毕") 
    return render(request, 'register.html')

4、ajax发送json数据

register.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
</head>
<body>
<button id="btn">提交</button>
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script>
    $("#btn").click(function () {
        var dic = {"name":"ly","age":18};
        $.ajax({
            url:"",
            type:"post",
            contentType:"application/json",
            data: JSON.stringify(dic),
            success:function (data) {
                var res = JSON.parse(data);  // 如果返回的数据也是json格式,那么需要先反序列化
                console.log(res.x);  // 然后才能访问属性
                
                // console.log(data.x);  // 如果后台返回的是JsonResponse,那么直接使用就好
            }
        })
    })
</script>
</body>
</html>

urls.py

urlpatterns = [
    re_path("^register/$",views.register)
]

views.py

def register(request):
    if request.method == "POST":
        print('==ajax==>')
        print(request.is_ajax())  # True
        print(request.body)  # b'{"name":"ly","age":18}'
        print(request.POST)  # <QueryDict: {}>
        print(request.FILES)  # <MultiValueDict: {}>
        
        import json
        dic = json.loads(request.body)
        print(dic["name"])

        # from django.http import JsonResponse
        # return JsonResponse({"x":1,"y":2})  # 可以直接返回JsonRResponse前端就不需要反序列化了

        return HttpResponse(json.dumps({"x":1,'y':2}))  # 前端需要反序列化才可以使用
    return render(request, 'register.html')

5、Django内置的serializers

如果我的前端想拿到由ORM得到的数据库里面的一个个用户对象,我的后端想直接将实例化出来的数据对象直接发送给客户端,那么这个时候,就可以用Django给我们提供的序列化方式

def register(request):
    if request.method == "POST":
        from app01.models import Book
        from django.core import serializers

        objs = Book.objects.all()
        res = serializers.serialize('json',objs)
        
        return HttpResponse(res)
    return render(request, 'register.html')

前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>注册页面</title>
</head>
<body>
<button id="btn">提交</button>
<script src="https://code.jquery.com/jquery-3.6.0.js"></script>
<script>
    $("#btn").click(function () {
        var dic = {"name":"ly","age":18};
        $.ajax({
            url:"",
            type:"post",
            contentType:"application/json",
            data: JSON.stringify(dic),
            success:function (data) {
                var books = JSON.parse(data);  // 如果返回的数据也是json格式,那么需要反序列化
                books.forEach(function (book) {
                    console.log(book.fields.name);
                    console.log(book.fields.price);
                    console.log(book.fields.publish_date);
                });
            }
        })
    })
</script>
</body>
</html>

猜你喜欢

转载自blog.csdn.net/weixin_47035302/article/details/131534381