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)@staticmethoddef 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 5if 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 themall in one SQL clause and this function is not enough. To avoiddead 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_tablelogger.debug("Locking table %s" % table)cursor.execute("LOCK TABLES %s WRITE" % table)row = cursor.fetchone()return rowdef unlock(self):""" Unlock the table. """cursor = connection.cursor()table = self.model._meta.db_tablecursor.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 transactionwith transaction.atomisc():# sql1# sql2# sql3# 在with代码块内写的所有orm操作都是属于同一个事务

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

from django.db import transactiondef 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 = Falsewith transaction.atomic():tag = Truechange_obj() # 修改对象变量obj.save()raise DataErrorprint("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 osif __name__ == '__main__':os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")import djangodjango.setup()import datetimefrom app01 import modelstry:from django.db import transactionwith transaction.atomic():new_publisher = models.Publisher.objects.create(name="火星出版社")models.Book.objects.create(title="橘子物语", publish_date=datetime.date.today(), publisher_id=10)  # 指定一个不存在的出版社idexcept 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或者postdata:{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'] = usernameback_dic['message'] = "登录成功"else:back_dic['message']='用户名或密码错误'import jsonreturn 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,如果无需上传文件,还是推荐使用更为精简的编码格式12、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、编码格式2ajax可以设置的数据编码格式有:编码格式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 viewsurlpatterns = [re_path(r'^register/$',views.register),
]

views.py

from django.shortcuts import render,HttpResponsedef 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.nameprint(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 viewsurlpatterns = [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.namewith 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())  # Trueprint(request.body)  # b'{"name":"ly","age":18}'print(request.POST)  # <QueryDict: {}>print(request.FILES)  # <MultiValueDict: {}>import jsondic = 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 Bookfrom django.core import serializersobjs = 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.hqwc.cn/news/8791.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于单片机的智能窗帘智能晾衣架系统的设计与实现

功能介绍 以STM32单片机单片机作为主控系统&#xff1b;OLED液晶显示当前环境温湿度&#xff0c;光照强度&#xff0c;时间&#xff0c;开关状态等信息&#xff1b;雨滴传感器检测当前环境是否下雨&#xff0c;天气下雨检测&#xff0c;天气潮湿时自动收衣服&#xff1b;可以通…

文心一言 VS 讯飞星火 VS chatgpt (54)-- 算法导论6.2 6题

文心一言 VS 讯飞星火 VS chatgpt &#xff08;53&#xff09;-- 算法导论6.2 5题 六、证明:对一个大小为 n的堆&#xff0c;MAX-HEAPIFY 的最坏情况运行时间为 Ω(Ign)。(提示对于n个结点的堆&#xff0c;可以通过对每个结点设定恰当的值&#xff0c;使得从根结点到叶结点路径…

mybatis-plus用法(二)

(5条消息) mybatis-plus用法&#xff08;一&#xff09;_渣娃工程师的博客-CSDN博客 AR模式 ActiveRecord模式&#xff0c;通过操作实体对象&#xff0c;直接操作数据库表。与ORM有点类似。 示例如下 让实体类User继承自Model package com.example.mp.po; import com.bao…

在树莓派上搭建web站点并发布互联网上线【无需公网IP】

文章目录 概述使用 Raspberry Pi Imager 安装 Raspberry Pi OS设置 Apache Web 服务器测试 web 站点安装静态样例站点将web站点发布到公网安装 Cpolar内网穿透cpolar进行token认证生成cpolar随机域名网址生成cpolar二级子域名将参数保存到cpolar配置文件中测试修改后配置文件配…

创新引领未来:RFID技术在汽车装配中的智能革命

射频识别&#xff08;RFID&#xff09;技术作为一种自动识别技术&#xff0c;已经在许多领域得到广泛应用。在汽车装配领域&#xff0c;RFID技术的应用可以提高装配过程的效率、降低人工错误率&#xff0c;并帮助实现自动化和智能化生产。本文将介绍RFID技术在汽车装配中的应用…

C# 在控制台整齐的输出 DataTable

效果: 一、前言 在 Winform 平台,可以用 DataGridView 这样的控件来显示数据库的表单数据,但在 C# 控制台项目中,如果有用到数据库查询,我们想看看查询语句的效果,就比较困难了,比如,我随意写了一个控制台输出,代码如下: using System.Data;namespace CSharpConnec…

scratch 抓狐狸

scratch 抓狐狸 本程序的功能是通过键盘控制“男孩”角色移动&#xff08;程序用的是旋转形式实现上下移动&#xff0c;后来才发现有直接改变y坐标的模块&#xff09;&#xff0c;“狐狸”角色连续在随机位置、随机方向生成&#xff0c;程序显示在场“狐狸”的数量&#xff0c;…

搭建Docker私有仓库

目录 背景介绍 学习目的 学习准备 搭建过程 背景介绍 Docker registry是存储Docker image的仓库&#xff0c;运行push、pull、 search 时,是通过Docker daemon与docker registry通信。 Docker Hub&#xff08;Docker埠&#xff0c;内有大量存储库或叫仓库&#xff09;是D…

Spring源码整体脉络介绍及源码编译

需完成的任务 类------------------------------------------BeanFactory----------------------------------------->Bean【BeanFactory调用getBean()生产出来的】 BeanFactory Spring顶层核心接口&#xff0c;使用了简单工厂模式【根据名字&#xff0c;生产出不同的Bean…

你的隐私被泄漏了吗

近日&#xff0c;某高校毕业生在校期间窃取学校内网数据&#xff0c;收集全校学生个人隐私信息的新闻引发了人们对互联网生活中个人信息安全问题的再度关注。在大数据时代&#xff0c;算法分发带来了隐私侵犯&#xff0c;在享受消费生活等便捷权利的同时&#xff0c;似乎又有不…

Unable to reload Maven project

原因就是IDEA的版本与Maven的版本冲突。 IDEA的日志报错如下&#xff1a; 我当时IDEA是2020版&#xff0c;Maven是3.8.5. 后来把Maven换成3.6.3版本就可以了。

CSS选择器常见用法

目录 一.总体分类 二.基础选择器 &#xff08;1&#xff09;标签选择器 &#xff08;2&#xff09;类选择器 &#xff08;3&#xff09;id选择器 &#xff08;4&#xff09;通配符选择器&#xff08;特殊&#xff09; 三.复合选择器 &#xff08;1&#xff09;后代选择器 &…