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

news2024/11/26 20:39:10

一、行锁

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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/718437.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习入门

AI人工智能 ANI 弱人工智能&#xff0c;狭义人工智能&#xff0c;指的是一种针对特定任务或领域进行优化的人工智能&#xff0c;例如语音识别、图像识别、自然语言处理、推荐系统 AGI 通用人工智能&#xff0c;强人工智能&#xff0c; ASI 超级人工智能&#xff0c;超人工智…

Erupt框架学习

Erupt框架学习 Erupt框架Erupt简介学习EruptEruptFieldErupt的逻辑删除Erupt的自定义按钮多数据源配置 Erupt框架 Erupt简介 最近因为工作所以接触到了一个低代码框架Erupt。这是一个通用的配置管理框架&#xff0c;主打就是零前端代码&#xff0c;急速开发通用管理框架。 Er…

C# 如何调用python,避免重复造轮子

文章目录 原因资源调用python文件需求解决方案1、C#里面运行python引入python文件&#xff0c;再调用其中的方法启动python脚本&#xff0c;监听返回值改造一下&#xff0c;可以入参的python调用查看是否等待python运行完成之后再运行C#如果参数比较复杂 开一个python网络后端 …

如何用Airtest脚本无线连接Android设备?

1. 前言 之前我们已经详细介绍过如何用AirtestIDE无线连接Android设备&#xff1a; 手把手教你用AirtestIDE无线连接手机&#xff01; &#xff0c;它的关键点在于&#xff0c;需要先 adb connect 一次&#xff0c;才能点击 connect 按钮无线连接上该设备&#xff1a; 但是有很…

​浅谈大型语言模型

大型语言模型&#xff08;Large Language Models&#xff0c;LLMs&#xff09;是一类强大的人工智能模型&#xff0c;具有出色的自然语言处理能力。它们在许多任务中表现出色&#xff0c;如机器翻译、文本摘要、对话生成和情感分析等。下面我们将介绍大型语言模型的训练和生成过…

海康监控摄像机接入NTV GBS GB28181平台实现远程调取监控视频

海康威视各种型号监控摄像头或硬盘录像机(NVR/HVR)接入NTV GBS GB28181平台配置过程都非常简单明了&#xff0c;但有些细节需要注意&#xff0c;避免走弯路踩泥坑。 1、基本要求 1)网络要求 总体来说&#xff0c;只要监控设备和GB28181平台的网络是连通的&#xff0c;设备可以主…

MATLAB基础篇(上)

一、MATLAB简介 MATLAB(Matrix Laboratory, 即矩阵实验室)是MathWork公司推出的一套高效率的数值计算和可视化软件.MATLAB是当今科学界最具影响力、也是最具活力的软件, 它起源于矩阵运算, 并已经发展成一种高度集成的计算机语言.它提供了强大的科学运算、灵活的程序设计流程、…

保姆级python环境配置(anaconda+pycharm+cuda+cudnn+pytorch)

文章目录 前言一、如何下载anaconda1、下载网址2、版本选择3、下载流程4、注意事项 二、如何下载pycharm1、下载网址2、下载流程 三、更新NVIDIA驱动1、下载网址2、选择相应配置进行下载 四、如何下载cuda1、查看可安装的cuda版本号2、下载网址3、下载流程4、注意事项 五、如何…

牛客网数据库sql实战基础知识

sql基础知识 1.concat(a,“-”,b)&#xff1a; 将a和b列&#xff0c;连接成字符串&#xff0c;用-分割后输出成一列。&#xff08;分隔符在参数中间&#xff09; 2.insert into values(),()&#xff1a; 插入多列时用逗号分割&#xff0c;不需要在最外层加再加括号。 3.inse…

工信部新材料大数据创新联盟成立,龙讯旷腾作为首批会员单位参与大会

近日&#xff0c;“新材料大数据创新联盟”成立大会暨第一届理事会在京召开。为深入贯彻国家创新驱动发展战略&#xff0c;联盟在工业和信息化部指导下&#xff0c;由北京科技大学、中国钢研科技集团有限公司、中关村材料试验技术联盟联合材料和信息技术领域重点高校、科研院所…

浅学CSS

目录 CSS 是什么 基本语法规范 引入方式 内部样式表 行内样式表 外部样式 代码风格 样式格式 样式大小写 空格规范 选择器 选择器的功能 选择器的种类 基础选择器 标签选择器 类选择器 id 选择器 通配符选择器 基础选择器小结 复合选择器 后代选择器 子选…

<C语言> 指针(上)

1.指针是什么&#xff1f; 指针&#xff08;Pointer&#xff09;是一种特殊的变量类型&#xff0c;它存储了一个内存地址。可以将指针视为存储了另一个变量的地址的变量。通过指针&#xff0c;可以直接访问和修改内存中的数据。 指针提供了一种间接访问内存的方式&#xff0c…

SpringCloud源码探析(七)-整合Elasticsearch

1.概述 ElasticSearch是一个基于Lucene的搜索服务器&#xff0c;提供了一个分布式多用户能力的全文搜索引擎。它是基于JAVA语言开发&#xff0c;并且是基于RESTful web接口进行查询和结果返回&#xff0c;是一款非常流行的企业级搜索引擎。Elasticsearch的核心功能包括存储数据…

Redis缓存问题与缓存更新机制

目录 ​编辑 一、缓存问题 1.1 缓存穿透 1.1.1 问题来源 1.1.2 解决方案 1.1.2.1 缓存空对象 1.1.2.2 使用布隆过滤器 1.2 缓存击穿 1.2.1 问题来源 1.2.2 解决方案 1.2.2.1 设置热点数据永远不过期 1.2.2.2 新增后台定时更新缓存线程&#xff08;逻辑不过期&#xff09; 1.2.…

详解Java Synchronized锁升级原理

✅作者简介&#xff1a;热爱Java后端开发的一名学习者&#xff0c;大家可以跟我一起讨论各种问题喔。 &#x1f34e;个人主页&#xff1a;Hhzzy99 &#x1f34a;个人信条&#xff1a;坚持就是胜利&#xff01; &#x1f49e;当前专栏&#xff1a;JAVA多线程 &#x1f96d;本文内…

蓝桥杯专题-试题版含答案-【猴子吃桃问题】【小光棍数】【九九乘法表】【谁是最好的Coder】

点击跳转专栏>Unity3D特效百例点击跳转专栏>案例项目实战源码点击跳转专栏>游戏脚本-辅助自动化点击跳转专栏>Android控件全解手册点击跳转专栏>Scratch编程案例点击跳转>软考全系列点击跳转>蓝桥系列 &#x1f449;关于作者 专注于Android/Unity和各种游…

(Linux) 套接字socket基础

文章目录 前言基本原理 Codeserverclient 核心函数socketbindlistenacceptrecvsendconnectclose 多线程改进END 前言 本文将以纯C语言描述&#xff0c;编译器gcc。 C/C没有标准的网络库&#xff0c;因为都需要用到各个平台的接口才行。 本文讲解Linux下最基础的socket编程&a…

uniapp 常用提示弹框整理

一. 加载提示弹框 在执行数据查询、页面数据渲染等过程中弹出提示。以页面渲染为例&#xff1a; //前端数据请求时&#xff0c;显示加载提示弹框 uni.showLoading({title: 加载中... }); // 数据从后端接口返回后&#xff0c;提示弹框关闭 uni.hideLoading();效果如下&#x…

Sui Builder House京都站|创意大赛获奖名单公布

Sui Builder House京都站于6月30日&#xff08;周五&#xff09;圆满结束&#xff0c;这是一次Sui生态系统项目演示和展示各自产品的良好机会。构建者们向大家展示了游戏、NFT、DeFi和基础设施赛道的项目&#xff0c;同时现场演讲还介绍了Sui的最新进展以及有关AI和用户体验设计…