热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

python自定义表单_Python编程(三十二):Django进阶(ModelForm类、Ajax操作)

有的人不管走到哪里,去到哪里,都能给别人带来幸福。本节内容:1、ModelFormModelForm是Model和Form的结合体

有的人不管走到哪里,去到哪里,都能给别人带来幸福。

本节内容:

1、 ModelForm

ModelForm是Model和Form的结合体,使用它可以很方便的增删改查。其中要依赖很多的Model中的类,用于简单的小程序还好,要是用于大型的程序就有些不适用。Model用在定制Django Admin时用得比较多。

2、Ajax

原生的Ajax

通过jQuery操作的Ajax

伪Ajax操作

3、文件上传(预览)

- 基于Form提交,页面会刷新。要上传图片时,不能做预览。实在要做的话,需要浏览器的配合。

- Ajax上传文件

4、图片验证码,需要与Session来配合

5、CKEditor, UEEditor, TinyEditor, KindEditor(***)

- 基本使用

- 文件上传,多文件上传,文件空间管理

- 在博客系统防止XSS攻击(过滤的函数或类,将一些特殊的东西去掉)

上节内容回顾:

Model操作

- 数据库操作

- 验证

Form

- 做验证

- is_vaild -> 每一个字段进行正则(字段内置的正则)+clean_字段 -> clear(__all__) -> _post_clean

- cleand_data

- error

一、ModelForm

ModelForm 是 Model和Form的结合体。

Model + Form -> 数据库操作 + 验证

class LoginModelForm(xxxxx):

利用model.A 中的字段操作

下面来创建一个 Django 项目 michael_05,并创建一个app01,创建过程省略。在 settings.py 中注册 app01,并且添加静态文件路径代码:

STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'),)

接下来在项目文件夹 michael_05 下创建静态文件夹 static。

接着在urls.py中添加一个URL:

from app01 import views

path('index/', views.index),

在app01目录下的 models.py 文件写如下代码:

位置:michael_05\app01\models.py

from django.db import models

class UserType(models.Model):

captions = models.CharField(max_length=32)

class UserInfo(models.Model):

username = models.CharField(verbose_name="用户名",max_length=32)

email = models.EmailField(verbose_name="邮箱")

user_type = models.ForeignKey(to="UserType", to_field="id", on_delete=None)

接着在项目文件夹michael_05的命令行下执行下面的命令:

python manage.py makemigrations

python manage.py migrate

在 views.py 文件中写入下面的代码:

位置:app01\views.py

from django.shortcuts import render

from django import forms

from django.forms import fields

from app01 import models

class UserInfoForm(forms.Form):

username = fields.CharField(max_length=32)

email = fields.EmailField()

user_type = fields.ChoiceField(

choices = models.UserType.objects.values_list("id", "captions"), # 在 Form中做跨表查询是种方式

)

def __init__(self, *args, **kwargs):

"""自动执行更新"""

super(UserInfoForm, self).__init__(*args, **kwargs)

self.fields['user_type'].choices = models.UserType.objects.values_list("id", "captions")

def index(request):

if request.method == "GET":

obj = UserInfoForm()

return render(request, "index.html", {'obj': obj})

elif request.method == "POST":

obj = UserInfoForm(request.POST)

obj.is_valid()

obj.cleaned_data

obj.errors

# models.UserType.objects.create(**obj.cleaned_data) # 写入数据库

# models.UserType.objects.filter(id=1).update(**obj.cleaned_data) # 更新操作

return render(request, "index.html", {'obj': obj})

在index() 函数中,根据GET请求还是POST请求进行相应的操作,如果是POST请求,可以将相应的值写入数据库或者更新操作。接下来还需要在templates目录下新建一个 index.html 文件,该文件代码片断是:

{% csrf_token %}

{{ obj.as_p }}

代码中 obj.as_p 表示生成 p 标签,p 标签内部是 label 和 input 标签,标签的数量由 views.py中 UserInfoForm() 字段数量决定。此时运行 michael_05,在浏览器地址栏访问 http://127.0.0.1:8000/index/,页面呈现出两个输入框和一个选择框。当输入内容不符合规范,就会提示错误,这样就简单的完成了 form 的验证功能。

下面使用 ModelForm来做验证功能。

只需要在Views.py 文件中增加一个ModelForm类,在 index() 函数实例化这个类,并将实例化对象传递到前端即可。

位置:app01\views.py

class UserInfoModelForm(forms.ModelForm):

class Meta:

model = models.UserInfo # model指向数据库的 UserInfo 类,两者建立依赖

fields = '__all__' # 在页面上展示所有的字段

# fields = ["username", "email"] # 还可以写成列表的形式,当有很多个字段时,想要显示某几个字段可用这种方式

# exclude = ['username'] # 当想要某个字段不显示时,可使用exclued排除掉

此时 index() 函数代码的GET请求时,修改为实例化 UserInfoModelForm 这个类,如下所示:

def index(request):

if request.method == "GET":

obj = UserInfoModelForm() # 注意修改了这个里的实例化对象

return render(request, "index.html", {'obj': obj})

elif request.method == "POST":

obj = UserInfoForm(request.POST)

return render(request, "index.html", {'obj': obj})

这时刷新 http://127.0.0.1:8000/index/ 页面同样能看到之前的效果。这里的 UserInfoModelForm 类中,fields = '__all__' 表示要在页面上显示所有的字段,如果要想显示特定的几个字段可使用 fields = ["username", "email"] 的列表方式,当然还可以使用 exclude = ['username'] 的列表方式排除掉要显示的字段。

在 ModelForm 中是怎样做的验证呢?验证不是由用户定义的类 UserInfo 做的,也不是forms类中定义的,是在 forms.Form 中Form基类 BaseForm 中做的验证,在这个 BaseForm 类中有定义很多的方法,比如 is_vaild() 方法。

所以在做Form验证时,UserInfoForm(自定义类)继承 Form,Form 再继承 BaseForm,BaseForm类中有验证方法,如 is_vaild()。那么 UserInfoModelForm(自定义类)继承的是 ModelForm,ModelForm又继承了 BaseModelForm,在 BaseModelForm 中有定义验证方法。

再一次修改 views.py 中的 index() 函数,这次修改POST请求,POST请求在实例化时也指向 UserInfoModelForm 类,修改后的index() 函数代码如下所示:

def index(request):

if request.method == "GET":

obj = UserInfoModelForm()

return render(request, "index.html", {'obj': obj})

elif request.method == "POST":

obj = UserInfoModelForm(request.POST)

print(obj.is_valid())

print(obj.cleaned_data)

print(obj.errors)

return render(request, "index.html", {'obj': obj})

现在刷新页面 http://127.0.0.1:8000/index/ ,输入对应字段的正确信息后点击提交后,在 index() 函数中的 3条print 语句的输出可知, obj.is_valid() 输出的是 True 或 False,obj.cleaned_data 输出的就是 POST提交是填写的表单内容。

二、 ModelForm组件

在自定义的ModelForm类中的 Meta 类中,除了前面说的3个参数 model,fields,exclude等参数外,其实还有其它参数,相应参数如下所示:

ModelForm

class Meta:

model, # 对应Model的类,如:model = models.UserInfo

fields=None, # 对应字段信息,如对应全部字段:fields = '__all__'

exclude=None, # 排除某个或者某些字段,如:exclude = ['user_type', 'email']

labels=None, # input框左边的提示信息,字典形式,键是字段名,如:labels = {'username': '用户'}

help_texts=None, # 帮助提示信息,如:help_texts = {'email':'比如:michael@root.com'}

widgets=None, # 自定义插件,用法介绍在后面

error_messages=None, # 自定义错误信息(整体错误信息from django.core.exceptions import NON_FIELD_ERRORS)

field_classes=None # 自定义字段类 (也可以自定义字段),用法介绍在后面

localized_fields=('ctime',) # 本地化,如:根据不同时区显示数据,将UTC时间转化为本地时间,ctime 是字段名

如:

数据库中

2016-12-27 04:10:57

setting中的配置

TIME_ZONE = 'Asia/Shanghai'

USE_TZ = True

则显示:

2016-12-27 12:10:57

Meta类中的 widgets 的用法,这个参数是用来修改字段类型的,比如在 UserInfo类中原来的 username 字段的类型是 CharField,现在要将其修改为 Textarea 类型。在使用这个功能前,还需要导入这个 widgets 插件,由于这个插件名与参数同名,所以在导入的时候,还需要指定别名,导入方法如下:

from django.forms import widgets as fwidgets

此时在 Meta 类中就可以使用 fwidgets 修改字段名,具体使用方法是这样:

widgets = {'username': fwidgets.Textarea(attrs={'class':'c1'})}

为了便于说明Meta类中的 error_messages 参数的使用,先将 views.py 中的 index() 函数中的POST请求下的第三条print语句:print(obj.errors),修改为 print(obj.errors.as_json()),就是以 json格式输出错误信息。这时刷新页面,进入到页面的源代码中将三个 input 标签中的 required 属性都去掉,并将邮箱input标签中的 type="email" 改为 type="text",此时的错误信息在后台的输出如下所示:

{"username": [{"message": "This field is required.", "code": "required"}],

"email": [{"message": "This field is required.", "code": "required"}],

"user_type": [{"message": "This field is required.", "code": "required"}]}

从输出的错误信息可以看出,错误信息是一个字典,字典的键是字段名,值是列表,但是列表中又是字典,其中的键值对就对应相应的错误提示信息。但是要在 error_messages 参数中自定义错误提示信息,还得需要使用字典形式,例如下面这样定义:

error_messages = {

"email": {"required": "邮箱不能为空", "invalid": "邮箱格式不正确",}

}

还可以在 error_messages 中定义整体的错误信息,在其中使用 __all__ 即可,如下所示:

error_messages = {

"__all__": {'required': "不能为空", 'invalid': '格式不正确'},

"email": {"required": "邮箱不能为空","invalid": "邮箱格式不正确",}

}

为了便于说明在 Meta 类中 field_classes 参数的使用,将之前导入的 fields 模块使用别名,因为在 field_classes 参数需要使用 fields 模块提供的功能。使用别名导入后,还要记得修改在 views.py 中使用到该模块的地方一并修改。

from django.forms import fields as ffields

这次将提交过来的邮箱使用正则匹配的方式转化成URL。具体实现方法如下代码所示:

field_classes = {'email': ffields.URLField}

此时刷新页面 http://127.0.0.1:8000/index/ 后,在邮箱后面输入正确的邮箱地址点提交,这时提示输入网址,就不在是邮箱。这样可以不在models.py 中的类进行修改,在Meta 类中同样可以修改。要注意的是这里使用的是 ffields.URLField 类,后面没有括号。

在Meta类中的这些参数在生成页面时,只省了做那几个字段的事,也没有省多大的事。能够省事的是在 index() 函数中的 POST提交时做的验证、获取数据、以及 as_json() 等,这些是按照form方式提交时做的。在这里可以先判断 obj.is_vaild() 是否成功。

def index(request):

if request.method == "GET":

obj = UserInfoModelForm()

return render(request, "index.html", {'obj': obj})

elif request.method == "POST":

obj = UserInfoModelForm(request.POST)

if obj.is_valid(): # 判断是否验证成功

obj.save() # 这一句就可以把form提交的全部正确数据保存到数据库

return render(request, "index.html", {'obj': obj})

在上面的 index() 函数中,当POST提交时,通过 if obj.is_valid() 判断提交的数据是否合法,合法就执行 obj.save() 保存到数据库,只需要一条语句即可。要验证是否保存成功,先用navicat连接数据库,在 usertype 表中添加一条数据。此时再刷新http://127.0.0.1:8000/index/ 页面,填写正确数据后点击提交即可完成将全部正确的数据保存到数据,这在做更新操作时是方便的。

这里的数据库中只是做了一个简单的一对多情况,要是有多对多的情况,又该是何种用法呢?在此先在models.py 文件添加一个UserGroup 类,代码如下所示:

位置:app01\models.py

class UserGroup(models.Model):

name = models.CharField(max_length=32)

还需要在models.py 文件中的 UserInfo 类中添加下面这行代码:

u2g = models.ManyToManyField(UserGroup)

此时在命令行执行下面两条命令:

python manage.py makemigrations

python manage.py migrate

接下来在 usergroup 表中添加3条记录后,再次刷新 http://127.0.0.1:8000/index/ 页面,此时在 u2g 后面有多选框,将添加的3条记录全部选上,并填写正确的信息后点击提交。后台的 obj.save() 方法同样可以将多对多关系进行保存。这时刷新数据库连接可以看到 userinfo_u2g 的表上的相应记录。

通过查看 obj.save() 中的 save() 方法源代码可知,在这个方法中有一个参数 commit=True,当 commit=True时,save() 方法中的保存语句才会执行:

if commit:

# If committing, save the instance and the m2m data immediately.

self.instance.save() # 这一句只会保存当前对象,不会保存 ManyToMany

self._save_m2m() # 这一句才会保存 ManyToMany

else:

# If not committing, add a method to the form to allow deferred

# saving of m2m data.

self.save_m2m = self._save_m2m # commit=False时生成一个字段,字段是函数名 _save_m2m

当给save() 方法传递False 参数时,什么都不做,这时可以将save() 方法拆开来做。下面的 index() 函数中的 obj.save() 方法拆开成三步来完成,代码如下所示:

def index(request):

if request.method == "GET":

obj = UserInfoModelForm()

return render(request, "index.html", {'obj': obj})

elif request.method == "POST":

obj = UserInfoModelForm(request.POST)

if obj.is_valid(): # 判断是否验证成功

# obj.save() # 这一句就可以把form提交的全部正确数据保存到数据库

instance = obj.save(False) # 传递 False 参数什么都不做,返回一个对象

instance.save() # 这一句只会保存当前对象,不会保存 ManyToMany

obj.save_m2m() # 这一句才会保存 ManyToMany

return render(request, "index.html", {'obj': obj})

1、ModeForm使用实例

创建两个页面,一个是列出用户信息的页面 user_list,在这个用户信息的页面可以点击相应用户信息后面的编辑后,进入到另一个

编辑页面 edit-(\d+),在编辑页面可以修改用户名、邮箱、User Type、U2g等属性,点击提交后根据用户的id号更新到数据库中。

为此先在urls.py 中添加两个URL,代码如下:

位置:michael_05\ursl.py

from django.urls import path, re_path

path('user_list/', views.user_list),

re_path('edit-(\d+)/', views.user_edit),

接下来在views.py 中增加两个相应的处理函数user_list()和user_edit(),这里 user_list() 函数执行数据库查询操作,将查询结果返回给前端,由前端根据需要在页面上展示用户信息数据。当点击用户信息数据后面的编辑后就跳转到对应用户的编辑页面,在编辑页面首先要显示用户原来的信息,当修改完信息后,点击提交(此时是POST请求),后台首先验证数据是否规范,规范就执行保存动作。user_list()和user_edit() 函数的代码如下所示:

位置:app01\views.py

def user_list(request):

li = models.UserInfo.objects.all().select_related('user_type') # 这里不能查询多对多的关系

return render(request, 'user_list.html', {"li": li})

def user_edit(request, nid):

# 获取当前id 对应的用户信息

# 显示用户已经存在的数据

if request.method == "GET":

user_obj = models.UserInfo.objects.filter(id=nid).first()

# 使用参数 instance,并将上一步的查询结果对象user_obj传递给instance即可,这时在编辑页面就有用户对应的原信息

mf = UserInfoModelForm(instance=user_obj)

return render(request, "user_edit.html", {"mf": mf, "nid": nid})

elif request.method == "POST":

user_obj = models.UserInfo.objects.filter(id=nid).first()

mf = UserInfoModelForm(request.POST, instance=user_obj) # 加上instance参数,下面的save()方法才会是更新,要不然是创建

if mf.is_valid(): # 判断提交的数据是否正确

mf.save() # 必须要将查询对象user_obj传递给mf,这里才是更新操作,否则是创建操作

else:

print(mf.errors.as_json())

return render(request, "user_edit.html", {"mf": mf, "nid": nid})

在上面的 user_edit() 函数的代码中,要注意 instance参数的用法,instance 参数指向的是查询对象,在POST请求的时候,实例化UserInfoModelForm对象时,要将查询对象传递给instance参数,后面的save()方法才是更新操作,否则就是创建操作。

接下来的user_list.html和user_edit.html的代码如下所示:

位置:michael_05\templates\user_list.html

{% for row in li %}

{{ row.username }} - {{ row.user_type.captions }} - 编辑

{% endfor %}

位置:michael_05\templates\user_edit.html

{% csrf_token %}

{{ mf.as_p }}

2、ModeForm验证

前面做的ModeForm只是用来生成标签,对提交过来的数据通过 is_valid() 进行验证,通过 save() 方法进行保存。现在对用户提交过来的数据进行验证,就只用了一个 is_valid() 进行验证判定,在 ModeForm 中还可以定义方法进行验证。通过查看 is_valid()方法的源代码可知,这个方法是在 BaseForm 类中定义的。ModelForm 类也继承了 BaseForm 类,在 BaseForm 类中有定义clean方法,所以在用户自定义的 ModeForm类中也可以定义 clean 开头的方法对用户数据进行验证。例如在 UserInfoModelForm 自定义类中增加一个 clean_username() 验证方法,增加后的 UserInfoModelForm 类完整代码如下所示:

位置:app01\views.py中的 UserInfoModelForm 类

class UserInfoModelForm(forms.ModelForm):

class Meta:

model = models.UserInfo # model指向数据库的 UserInfo 类,两者建立依赖

fields = '__all__' # 在页面上展示所有的字段

# fields = ["username", "email"] # 还可以写成列表的形式,当有很多个字段时,想要显示某几个字段可用这种方式

# exclude = ['user_type'] # 当想要某个字段不显示时,可使用exclued排除掉

labels = {'username': '用户'}

help_texts = {'email':'比如:michael@root.com'}

widgets = {'username': fwidgets.Textarea(attrs={'class':'c1'})}

error_messages = {

"__all__": {'required': "不能为空", 'invalid': '格式不正确'},

"email": {

"required": "邮箱不能为空",

"invalid": "邮箱格式不正确",

}

}

# field_classes = {

# 'email': ffields.URLField

# }

def clean_username(self):

"""本次主要增加了 clean_username 验证方法"""

old = self.cleaned_data['username'] # 获取 username 原来的值

# 中间可以对原来的值进行各种操作,最后可根据情况返回原来的值还是操作后的新值,这样就提交过去了

return old

3、在自定义的ModeForm类中添加字段

添加字段的操作都是在 models.py 文件中的类中添加的,这些字段都是保存在数据库中,在用户自定义的 ModeForm 类也可以添加字段,这些字段同样可以在页面上显示。例如在自定义类 UserInfoModelForm 中添加一个单选标签字段 is_rmb,代码如下所示:

位置:app01\views.py 文件中的 UserInfoModelForm

class UserInfoModelForm(forms.ModelForm):

# 在 UserInfoModelForm 类中还可以定义字段,例如下面这样:

is_rmb = ffields.CharField(widget=fwidgets.CheckboxInput) # CheckboxInput 是单选标签

class Meta:...(这里的代码与前面的一样)

def clean_username(self):...(这里的代码与前面的一样)

这时访问 index页面或者 edit-(\d+) 页面都会有一个 Is rmb的单选框。这个新增字段与保存在数据库中的字段没有关系,这时也可以在 user_edit() 等函数中的 is_valid()方法后面做一些相应的操作。

4、ModeForm总结

(1)、可以生成HTML标签:class Meta: ...

(2)、要在页面上显示默认值:mf = xxxModeForm(instance=Modelobj)

(3)、还可以加额外的标签,例如:

is_rmb = ffields.CharField(widget=fwidgets.CheckboxInput)

(4)、各种验证 is_valid() -> 各种钩子...

(5)、保存数据 mf.save(),可以拆开来做,例如:

instance = mf.save(False)

instance.save()

mf.save_m2m()

三、Ajax

1、 原生AJAX

Ajax主要就是使用 【XmlHttpRequest】对象来完成请求的操作,该对象在主流浏览器中均存在(除早期的IE),Ajax首次出现,IE5.5中存在(ActiveX控件)。

1.1、 XmlHttpRequest对象介绍

XmlHttpRequest对象的主要方法:

在浏览器的 Console 下面创建一个 XmlHttpRequest 对象方法:

xmlobj = new XMLHttpRequest(),创建对象后,可以对下面的方法进行调用

a. void open(String method,String url,Boolen async)

用于创建请求

参数:

method: 请求方式(字符串类型),如:POST、GET、DELETE...

url: 要请求的地址(字符串类型)

async: 是否异步(布尔类型)

xmlobj.open()

b. void send(String body)

用于发送请求

参数:

body: 要发送的数据(字符串类型)

c. void setRequestHeader(String header,String value)

用于设置请求头

参数:

header: 请求头的key(字符串类型)

vlaue: 请求头的value(字符串类型)

d. String getAllResponseHeaders()

获取所有响应头

返回值:

响应头数据(字符串类型)

e. String getResponseHeader(String header)

获取响应头中指定header的值

参数:

header: 响应头的key(字符串类型)

返回值:

响应头中指定的header对应的值

f. void abort()

终止请求

XmlHttpRequest对象的主要属性:

a. Number readyState

状态值(整数)

详细:

0-未初始化,尚未调用open()方法;

1-启动,调用了open()方法,未调用send()方法;

2-发送,已经调用了send()方法,未接收到响应;

3-接收,已经接收到部分响应数据;

4-完成,已经接收到全部响应数据;

b. Function onreadystatechange

当readyState的值改变时自动触发执行其对应的函数(回调函数)

c. String responseText

服务器返回的数据(字符串类型)

d. XmlDocument responseXML

服务器返回的数据是Xml对象,可以用这个对象,根据节点名称取值

e. Number states

状态码(整数),如:200、404...

f. String statesText

状态文本(字符串),如:OK、NotFound...

1.2、 XmlHttpRequest实例

为了对上面的 XmlHttpRequest 对象加深理解,下面用实例来做一做。首先在urls.py文件中添加两个URL,代码如下:

位置:michael_05\urls.py

re_path('ajax/$', views.ajax),

re_path('ajax_json/$', views.ajax_json),

接下来在 views.py 文件中创建两个函数 ajax() 和 ajax_json() ,这两个函数的代码如下所示:

位置:app01\views.py文件中 ajax() 函数和 ajax_json() 函数:

def ajax(request):

return render(request, 'ajax.html')

def ajax_json(request):

ret = {'status': True, 'data': None}

import json

return HttpResponse(json.dumps(ret))

#return HttpResponse(json.dumps(ret), status=404, reason='Not Found') # 在返回的时候还可以发送状态码

接着在templates文件夹下增加一个 ajax.html 文件,该文件的代码如下所示:

function Ajax1() {

var xhr = new XMLHttpRequest(); // 创建Ajax对象 xhr.open('GET', '/index/', true); // 使用open()方法以GET请求方式打开index页面 xhr.send("name=root;pwd=123"); // 向打开的页面发送数据 }

此时访问 http://127.0.0.1:8000/ajax/ 页面,在页面上进入查看源代码模式,点击 NetWork 选项卡,这时在页面上的输入框中随便输入内容,点击 Ajax1 按钮,在Network选项卡下面的 Name 一栏可以看到请求的网址,在右边还有请求的头部(Headers)信息,请求页面的视图(Preview)信息,请求页面的源代码(Response)信息等。

为了更直观的说明请求页面的源代码信息,将ajax.html的代码修改如下:

function Ajax1() {

var xhr = new XMLHttpRequest(); // 创建Ajax对象 //xhr.open('GET', '/index/', true); // 使用open()方法以GET请求方式打开index页面 xhr.open('GET', '/ajax_json/', true); // 修改了这里 xhr.send("name=root;pwd=123"); // 向打开的页面发送数据 }

再次刷新页面 http://127.0.0.1:8000/ajax/ 后,点击页面上的按钮,在Network选项卡下的 Response 子选项中,就可以看到ajax_json页面的源代码,该源代码正是在 views.py 文件中的 ajax_json() 函数中定义的 ret 字典内容。

现在已经能看到页面源代码的内容了,可以使用 XMLHttpRequest 对象的responseText属性获取源代码内容,例如下面修改后的ajax.html代码所示:

function Ajax1() {

var xhr = new XMLHttpRequest(); // 创建Ajax对象 //xhr.open('GET', '/index/', true); // 使用open()方法以GET请求方式打开index页面 xhr.onreadystatechange = function(){

// 这个函数的位置不重要,只要xhr的readyState属性值发生变化,就执行这个函数 if(xhr.readyState == 4){

// readyState等于4表示接收完毕 console.log(xhr.responseText); // 通过xhr的responseText属性获取返回值 // var xml = xhr.responseXML // responseXML 返回的是xml对象,可以用这个对象根据节点取值 };

};

xhr.open('GET', '/ajax_json/', true); // 使用open()方法以GET请求方式打开ajax_json页面 xhr.send("name=root;pwd=123"); // 向打开的页面发送数据 }

还可以将获取的源代码转化为JSON格式,方法如下:

var obj = JSON.parse(xhr.responseText); // 将获取的源代码转化为JSON格式

console.log(obj);

还可以设置请求头,还可以拿到csrf_token的值来发送:

xhr.setRequestHeader('k1','v1') // 还可以设置请求头

1.3、 发送数据

把xhr.open()方法的请求方式改为POST后,xhr.send()的数据可以发送到后台,这里需要在ajax请求中设置请求头才能发送过去。下面的 ajax.html 代码中的ajax请求改成了POST,并设置了请求头,如下面代码所示:

function Ajax1() {

var xhr = new XMLHttpRequest(); // 创建Ajax对象 xhr.onreadystatechange = function(){

// 这个函数的位置不重要,只要xhr的readyState属性值发生变化,就执行这个函数 if(xhr.readyState == 4){

// readyState等于4表示接收完毕 var obj = JSON.parse(xhr.responseText); // 将获取的源代码转化为JSON格式 console.log(obj);

};

};

xhr.open('POST', '/ajax_json/', true); // 这里使用 POST请求 xhr.setRequestHeader('k1','v1'); // 还可以设置请求头 // 设置下面的请求头后send方法发送的数据后端才能接收到 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');

xhr.send("name=root;pwd=123"); // 向后台发送数据 }

要让xhr.send() 方法的数据能发送到后台,必须要在ajax请求中设置下面这个请求头信息,这个请求头信息由Django框架决定的:

xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');

在views.py 文件中的 ajax_json()函数中增加一条输出语句,输出POST请求的数据,代码如下所示:

def ajax_json(request):

print(request.POST) # 主要添加这一条语句

ret = {'status': True, 'data': None}

import json

return HttpResponse(json.dumps(ret))

现在刷新 http://127.0.0.1:8000/ajax/ 页面,点击页面上的按钮,后台输出ajax请求中 xhr.send()方法发送的数据,如下所示:

在ajax.html 的代码中,var xhr = new XMLHttpRequest(); 是在创建ajax对象,其实还可以这样 var xhr = window.XMLHttpRequest();此外 alert 方法前面也可以加 window,例如 window.alert("123"),在DOM中,对象都是在window下。要创建 ajax 对象还可以这样操作 var xhr = window['XMLHttpRequest'],当然也可以直接这样使用 aler方法 window['alert']("hello")。

ajax对低版本的浏览器要进行兼容,可在创建 ajax 对象前,先进行判断,以便于创建低版本的浏览器的 ajax 请求。可将 ajax.hmtl文件中的 script 标签中的代码修改如下:

function getXHR(){ // 根据不同的浏览器对象,创建不同的 ajax 对象 var xhr = null;

if(XMLHttpRequest){

xhr = new XMLHttpRequest();

}else{

xhr = new ActiveXObject("Microsoft.XMLHTTP");

}

return xhr;

}

function Ajax1() {

var xhr = getXHR();

xhr.onreadystatechange = function(){

// 这个函数的位置不重要,只要xhr的readyState属性值发生变化,就执行这个函数 if(xhr.readyState == 4){

// readyState等于4表示接收完毕 //console.log(xhr.responseText); // 通过xhr的responseText属性获取返回值 // var xml = xhr.responseXML // responseXML 返回的是xml对象,可以用这个对象根据节点取值 var obj = JSON.parse(xhr.responseText); // 将获取的源代码转化为JSON格式 console.log(obj);

};

};

xhr.open('POST', '/ajax_json/', true); // 使用open()方法以GET请求方式打开ajax_json页面 xhr.setRequestHeader('k1','v1'); // 还可以设置请求头 // 设置请求头后send方法发送的数据后端才能接收到 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');

xhr.send("name=root;pwd=123"); // 向打开的页面发送数据 }

2、 jQuery 的 Ajax

jQuery 的 Ajax 与原生的 Ajax 没有多大的区别,调用 ajax 的方法是 $.ajax({})。在用 $.ajax({}) 发送请求时,内部有

success对应的函数,该函数有貌似有3个参数,其中第2还是第3个参数就是 XMLHttpRequest 对象,所以要用原生的 ajax 返回值,可以访问第2个还是第3个参数即可实现。例如下面这样:

$.ajax({

success: function (arg, a1, a2) {

}

})

3、 伪ajax(iframe标签)

iframe标签有一功能是,这个标签的src属性指向某个网站,当运行含有 iframe 标签的HTML代码后,在页面上就能看到 iframe 标签的src属性指向的网站页面。结合在 ajax 请求中就是,当在页面的输入框中输入一个网址,通过jQuery 的方法将这个输入框的内容传给 iframe 标签的 src 属性,再给提交按钮绑定一个事件,这样就能做到在我们的页面上访问输入框中的网址,但页面并没有刷新。例如下面的修改后的 ajax.html 代码所示,在这次的 ajax.html 代码要引入 jquery 文件。详细代码如下所示:

位置:michael_05\templates\ajax.html

function getXHR(){

var xhr = null;

if(XMLHttpRequest){

xhr = new XMLHttpRequest();

}else{

xhr = new ActiveXObject("Microsoft.XMLHTTP");

}

return xhr;

}

function Ajax1() {

var xhr = getXHR();

xhr.onreadystatechange = function(){

// 这个函数的位置不重要,只要xhr的readyState属性值发生变化,就执行这个函数 if(xhr.readyState == 4){

// readyState等于4表示接收完毕 //console.log(xhr.responseText); // 通过xhr的responseText属性获取返回值 // var xml = xhr.responseXML // responseXML 返回的是xml对象,可以用这个对象根据节点取值 var obj = JSON.parse(xhr.responseText); // 将获取的源代码转化为JSON格式 console.log(obj);

};

};

xhr.open('POST', '/ajax_json/', true); // 使用open()方法以GET请求方式打开ajax_json页面 xhr.setRequestHeader('k1','v1'); // 还可以设置请求头 // 设置请求头后send方法发送的数据后端才能接收到 xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');

xhr.send("name=root;pwd=123"); // 向打开的页面发送数据 }

function iframeRequest() {

// 更新 iframe 标签的 src 属性的值为输入框的值 var url = $("#url").val();

$("#ifm").attr('src', url);

}

有了上面的 iframe 标签的功能介绍,怎样才能用 iframe 做伪Ajax请求呢?先来看下面这段在ajax.html文件中HTML代码:

在这段HTML代码中,body 内部有一个form标签,以 POST 方式提交请求,跳转的地址是 /ajax_json/ ,但是在这个 form 标签中有一个属性 target="ifrm1",这个 target属性的值与下面form标签内部的 iframe 标签内的 name属性值一样。这样写上后就表示提点“Form提交”时,不跳转到 /ajax_json/ 页面,而是直接在当前页面的 iframe 框内返回目标页面的内容。当在页面的两个 input框中输入相应内容点击“Form提交”时,页面上的 iframe 框返回目标页面的内容,后台同时也在输出两个输入框的内容。这里后台的代码仍然是前面写在 app01\views.py 文件中的 ajax()函数和ajax_json() 函数,这两个函数的代码在这里不在示出。后台的输出内容如下所示:

注意这里的输出内容,要取决于在页面上的两个输入框中填入的内容。

4、 原生Ajax、jQuery的Ajax、伪Ajax的使用时机

在做开发时,允许使用jQuery时,就使用jQuery的Ajax。

情景1:如果发送的是【普通数据(key-value)】 -> 推荐使用顺序:jQuery, XMLHttpRequest, iframe

前面的伪ajax中,使用的 iframe 标签,当点击请求提交按钮后,返回的数据是在页面的 iframe 标签框内,不能直接获取其内容,查看页面的源代码可知 iframe 标签下面嵌套了 document,相当于是嵌套了页面,要获取其下面的文本内容,还需要做一些特殊操作才行,具体代码如下所示:

位置:michael_05\templates\ajax.html

function submitForm() {

$("#ifrm1").load(function () {

//console.log($("#ifrm1").contents()); var s = $("#ifrm1").contents().find("body").text(); // 获取 iframe 标签的值 var obj = JSON.parse(s); // 拿到值后转化为 json 对象,就可以做各种操作 console.log(s)

})

}

/*function iframeLoad() {console.log(123);}*/

在这段代码中,iframe 标签本身有一个 onload 属性,可以给该属性绑定一个事件,这里假设绑定的是 iframeLoad() 事件,在每次刷新页面后,在 Console 可以看这个 iframeLoad未定义的提示,这是因为js代码写在下面的原因。可以对提交按钮绑定onclick事件submitForm(),在这个 submitForm() 事件中去执行 iframe 的事件。后台的ajax()函数和ajax_json()函数的代码可保持不变,运行上面的 HTML代码后,在输入框中填入相应的值点击提交,在 Console 端可以看到返回页面的文本信息。

(下一篇博文继续本节内容)



推荐阅读
  • 第一种<script>$(".eq").on(&qu ... [详细]
  • Java实战之电影在线观看系统的实现
    本文介绍了Java实战之电影在线观看系统的实现过程。首先对项目进行了简述,然后展示了系统的效果图。接着介绍了系统的核心代码,包括后台用户管理控制器、电影管理控制器和前台电影控制器。最后对项目的环境配置和使用的技术进行了说明,包括JSP、Spring、SpringMVC、MyBatis、html、css、JavaScript、JQuery、Ajax、layui和maven等。 ... [详细]
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
  • 本文介绍了一种处理AJAX操作授权过期的全局方式,以解决Asp.net MVC中Session过期异常的问题。同时还介绍了基于WebImage的图片上传工具类。详细内容请参考链接:https://www.cnblogs.com/starluck/p/8284949.html ... [详细]
  • 本文介绍了使用FormData对象上传文件同时附带其他参数的方法。通过创建一个表单,将文件和参数添加到FormData对象中,然后使用ajax发送POST请求进行文件上传。在发送请求时,需要设置processData为false,告诉jquery不要处理发送的数据;同时设置contentType为false,告诉jquery不要设置content-Type请求头。 ... [详细]
  • Request对象和Response对象request:(请求)当一个页面被请求时,Django就会创建一个包含本次请求原信息的HttpRequest对象。Djang ... [详细]
  • 本文内容皆为作者原创,如需转载,请注明出处:https:www.cnblogs.comxuexianqip13045462.html1.自定义分页器的拷贝及使用当我们需要使用 ... [详细]
  • web.py开发web 第八章 Formalchemy 服务端验证方法
    本文介绍了在web.py开发中使用Formalchemy进行服务端表单数据验证的方法。以User表单为例,详细说明了对各字段的验证要求,包括必填、长度限制、唯一性等。同时介绍了如何自定义验证方法来实现验证唯一性和两个密码是否相等的功能。该文提供了相关代码示例。 ... [详细]
  • 本文介绍了前端人员必须知道的三个问题,即前端都做哪些事、前端都需要哪些技术,以及前端的发展阶段。初级阶段包括HTML、CSS、JavaScript和jQuery的基础知识。进阶阶段涵盖了面向对象编程、响应式设计、Ajax、HTML5等新兴技术。高级阶段包括架构基础、模块化开发、预编译和前沿规范等内容。此外,还介绍了一些后端服务,如Node.js。 ... [详细]
  • 本文介绍了小程序商城引进流量的优化策略与方法。首先,通过附近小程序功能可以增加周围门店的方位并展示,吸引附近用户。其次,利用微信群聊功能,将小程序分享到多个微信群聊中,扩大影响力。最后,通过设置一些固定的活动机制,打造仪式感来吸引用户。这些方法能够有效提升小程序商城的流量,增加用户数量。 ... [详细]
  • 恶意软件分析的最佳编程语言及其应用
    本文介绍了学习恶意软件分析和逆向工程领域时最适合的编程语言,并重点讨论了Python的优点。Python是一种解释型、多用途的语言,具有可读性高、可快速开发、易于学习的特点。作者分享了在本地恶意软件分析中使用Python的经验,包括快速复制恶意软件组件以更好地理解其工作。此外,作者还提到了Python的跨平台优势,使得在不同操作系统上运行代码变得更加方便。 ... [详细]
  • 14亿人的大项目,腾讯云数据库拿下!
    全国人 ... [详细]
  • 起因由于我录制过一个小程序的课程,里面有消息模板的讲解。最近有几位同学反馈官方要取消消息模板,使用订阅消息。为了方便大家容易学 PythonFlask构建微信小程序订餐系统 课程。 ... [详细]
  • java io换行符_Java IO:为什么从stdin读取时,换行符的数字表示出现在控制台上?...
    只是为了更好地理解我在讲座中听到的内容(关于Java输入和输出流),我自己做了这个小程序:publicstaticvoidmain(String[]args)thro ... [详细]
  • jqueryajax怎么通过header传递参数?
    /这个是全局的ajax请求头设置,所有的ajax请求都会加上这个请求头 ... [详细]
author-avatar
手机用户2502907603
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有