Python-Django其他组件②之分页、Form、序列化、model进阶、ajax

来自:Pixiv 画师( 紫李鳥 )

  • 目录
    • 分页
    •  
    • Form
    •  
    • 序列化
    •  
    • model进阶
    •  
    • Ajax

分页

Django内置分页:

views.py

def index(request):
    BookList=[]
    for i in range(1,100):
        BookList.append('Book'+str(i))
        #Book.objects.bulk_create(BookList)     #批量导入数据
    paginator = Paginator(BookList, 10)
    page = request.GET.get('page', 1)
    currentPage = int(page)

    try:
        print(page)
        book_list = paginator.page(page)
    except PageNotAnInteger:
        book_list = paginator.page(1)
    except EmptyPage:
        book_list = paginator.page(paginator.num_pages)
    return render(request,'index.html',locals())

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>分页</title>
    <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
    integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<ul>
    {% for book in book_list %}
        <li>{{ book }}</li>
    {% endfor %}

</ul>

 <ul class="pagination" id="pager">

                 {% if book_list.has_previous %}
                    <li class="previous"><a href="/index/?page={{ book_list.previous_page_number }}">上一页</a></li>
                 {% else %}
                    <li class="previous disabled"><a href="#">上一页</a></li>
                 {% endif %}


                 {% for num in paginator.page_range %}

                     {% if num == currentPage %}
                       <li class="item active"><a href="/index/?page={{ num }}">{{ num }}</a></li>
                     {% else %}
                       <li class="item"><a href="/index/?page={{ num }}">{{ num }}</a></li>

                     {% endif %}
                 {% endfor %}



                 {% if book_list.has_next %}
                    <li class="next"><a href="/index/?page={{ book_list.next_page_number }}">下一页</a></li>
                 {% else %}
                    <li class="next disabled"><a href="#">下一页</a></li>
                 {% endif %}

            </ul>a
</body>
</html>

内置分页扩展:

views.py


def index(request):
    BookList=[]
    for i in range(1,100):
        BookList.append('Book'+str(i))
    paginator = Paginator(BookList, 15)
    page = request.GET.get('page', 1)
    currentPage = int(page)

    #  如果页数十分多时,换另外一种显示方式
    if paginator.num_pages > 30:

        if currentPage - 5 < 1:
            pageRange = range(1, 11)
        elif currentPage + 5 > paginator.num_pages:
            pageRange = range(currentPage - 5, paginator.num_pages + 1)

        else:
            pageRange = range(currentPage - 5, currentPage + 5)

    else:
        pageRange = paginator.page_range

    try:
        book_list = paginator.page(page)
    except PageNotAnInteger:
        book_list = paginator.page(1)
    except EmptyPage:
        book_list = paginator.page(paginator.num_pages)

    return render(request, "index.html", locals())

自定制分页组件:

组件:pager.py

class Pagination(object):

    def __init__(self, current_page, all_count, per_page_num=10, pager_count=8):
        """
        封装分页相关数据
        :param current_page: 当前页
        :param all_count:    数据库中的数据总条数
        :param per_page_num: 每页显示的数据条数
        :param pager_count:  最多显示的页码个数

        用法:
        queryset = model.objects.all()
        page_obj = Pagination(current_page,all_count)
        page_data = queryset[page_obj.start:page_obj.end]
        获取数据用page_data而不再使用原始的queryset
        获取前端分页样式用page_obj.page_html
        """
        try:
            current_page = int(current_page)
        except Exception as e:
            current_page = 1

        if current_page < 1:
            current_page = 1

        self.current_page = current_page

        self.all_count = all_count
        self.per_page_num = per_page_num

        # 总页码
        all_pager, tmp = divmod(all_count, per_page_num)
        if tmp:
            all_pager += 1
        self.all_pager = all_pager

        self.pager_count = pager_count
        self.pager_count_half = int((pager_count - 1) / 2)

    @property
    def start(self):
        return (self.current_page - 1) * self.per_page_num

    @property
    def end(self):
        return self.current_page * self.per_page_num

    def page_html(self):
        # 如果总页码 < 11个:
        if self.all_pager <= self.pager_count:
            pager_start = 1
            pager_end = self.all_pager + 1
        # 总页码  > 11
        else:
            # 当前页如果<=页面上最多显示11/2个页码
            if self.current_page <= self.pager_count_half:
                pager_start = 1
                pager_end = self.pager_count + 1

            # 当前页大于5
            else:
                # 页码翻到最后
                if (self.current_page + self.pager_count_half) > self.all_pager:
                    pager_end = self.all_pager + 1
                    pager_start = self.all_pager - self.pager_count + 1
                else:
                    pager_start = self.current_page - self.pager_count_half
                    pager_end = self.current_page + self.pager_count_half + 1

        page_html_list = []
        BaseUrl = '/myindex'
        # 添加前面的nav和ul标签
        page_html_list.append('''
                    <nav aria-label='Page navigation>'
                    <ul class='pagination'>
                ''')
        first_page = '<li><a href="%s?page=%s">首页</a></li>' % (BaseUrl,1)
        page_html_list.append(first_page)

        if self.current_page <= 1:
            prev_page = '<li class="disabled"><a href="#">上一页</a></li>'
        else:
            prev_page = '<li><a href="%s?page=%s">上一页</a></li>' % (BaseUrl,self.current_page - 1,)

        page_html_list.append(prev_page)

        for i in range(pager_start, pager_end):
            if i == self.current_page:
                temp = '<li class="active"><a href="%s?page=%s">%s</a></li>' % (BaseUrl,i, i,)
            else:
                temp = '<li><a href="%s?page=%s">%s</a></li>' % (BaseUrl,i, i,)
            page_html_list.append(temp)

        if self.current_page >= self.all_pager:
            next_page = '<li class="disabled"><a href="#">下一页</a></li>'
        else:
            next_page = '<li><a href="%s?page=%s">下一页</a></li>' % (BaseUrl,self.current_page + 1,)
        page_html_list.append(next_page)

        last_page = '<li><a href="%s?page=%s">尾页</a></li>' % (BaseUrl,self.all_pager,)
        page_html_list.append(last_page)
        # 尾部添加标签
        page_html_list.append('''
                                           </nav>
                                           </ul>
                                       ''')
        return ''.join(page_html_list)

views.py

from app.pager import Pagination

def myindex(request):
    BookList=[]
    for i in range(1,100):
        BookList.append('Book'+str(i))
    pager_obj = Pagination(request.GET.get('page', 1), len(BookList))
    data_list = BookList[pager_obj.start:pager_obj.end]
    html = pager_obj.page_html()
    return render(request,'myindex.html',locals())

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>自定义分页组件</title>
      <link rel="stylesheet" href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css"
    integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
</head>
<body>
<ul>
    {% for data in data_list %}
        <li>{{ data }}</li>
    {% endfor %}
    <br>
</ul>
{{ html |safe}}

</body>
</html>

参考blog:

cnblog-yuanchenqi

Form

验证用户请求;生成Html代码

作用过程:

1、定制Form类check() [约束条件、自定义valid信息]
2、url对应函数:处理数据
①GET:obj=check()
生成html代码
②POST:obj=check(request.POST)
if checkFrom(request.POST).is_valid():
return HttpResponse(‘验证成功!’)
else:
obj=checkFrom(request.POST)
return render(request,’f1.html’,locals())
前端数据交给Form类处理,返回valid信息到前端
3、html页面
将生成的html代码与valid信息显示即可

表单提交:(显示用户列表;添加/修改用户)

#urls.py>>>
        urlpatterns = [
                    path('admin/', admin.site.urls),
                    path('user/', views.user),
                    path('add/', views.add),
                    path('edit/', views.edit),
                ]
#models.py>>>
        class User(models.Model):
        username=models.CharField(max_length=12)
        password=models.CharField(max_length=16)
        email=models.EmailField()
#checkForm.py>>>
        class checkFrom(forms.Form):
            username=fields.CharField(max_length=12,min_length=4,required=True,label='用户名',
                                      error_messages={
                                          'required':'用户名不能为空',
                                          'max_length':'长度最多12位',
                                          'min_length':'长度最少4位',
                                      })
            password=fields.CharField(max_length=16,min_length=8,required=True,label='密码',
                                      error_messages={
                                          'required': '密码不能为空',
                                          'max_length': '长度最多16位',
                                          'min_length': '长度最少8位',
                                      })
            email=fields.EmailField(required=True,label='邮箱',
                                    error_messages={
                                        'required': '邮箱不能为空',
                                        'invalid': '请输入正确的邮箱',
                                    })
#views.py>>>
            def user(request):
            user_list=User.objects.all()
            return render(request,'user.html',locals())
        def add(request):
            if request.method == 'GET':
                obj = checkFrom()
                return render(request, 'add.html', locals())
            else:
                username = request.POST.get('username')
                password = request.POST.get('password')
                email = request.POST.get('email')
                obj=checkFrom(request.POST)
                if obj.is_valid():
                    User.objects.create(**obj.cleaned_data)
                    return redirect('/user/')
                else:
                    obj = checkFrom(request.POST)
                    return render(request, 'add.html', locals())
        def edit(request):
            if request.method=='GET':
                id = request.GET.get('id')
                userById = User.objects.get(id=id)
                print(userById)
                obj=checkFrom({'username':userById.username,
                               'password':userById.password,
                               'email':userById.email})
                return render(request,'edit.html',locals())
            else:
                obj=checkFrom(request.POST)
                username = request.POST.get('username')
                if obj.is_valid():
                    User.objects.filter(username=username).update(**obj.cleaned_data)
                    return redirect('/user/')
                else:
                    return render(request,'edit.html',locals())
#html页面>>>
            #user.html:
            <h2>用户列表</h2><hr>
            <ul>
                {% for user in user_list %}
                    <li>{{ user.username }}---{{ user.email }}
                        <a href="/edit/?id={{ user.id }}">修改</a></li>
                {% endfor %}
            </ul>
            <a href="/add/">添加</a>

            #add.html:
            <form action="/add/" method="post" >
                {% csrf_token %}
            {#    <p>{{ obj.username.label }}{{ obj.username }}{{ obj.errors.username.0 }}</p>#}
            {#    <p>{{ obj.password.label }}{{ obj.password }}{{ obj.errors.password.0 }}</p>#}
            {#    <p>{{ obj.email.label }}{{ obj.email }}{{ obj.errors.email.0 }}</p>#}
                {{ obj.as_p }}
                <input type="submit" value="提交">

            #edit.html:
            <form action="/edit/" method="post" novalidate>
                {% csrf_token %}
            {#    <p>{{ obj.username.label }}{{ obj.username }}{{ obj.errors.username.0 }}</p>#}
            {#    <p>{{ obj.password.label }}{{ obj.password }}{{ obj.errors.password.0 }}</p>#}
            {#    <p>{{ obj.email.label }}{{ obj.email }}{{ obj.errors.email.0 }}</p>#}
                {{ obj.as_p }}
                <input type="submit" value="提交">
            </form>

ajax提交:

#views.py
            def f1(request):
                if request.method=='GET':
                    obj=checkFrom()
                    return render(request,'f1.html',locals())
                else:
                    obj = checkFrom(request.POST)
                    ret={'msg':False}
                    if checkFrom(request.POST).is_valid():
                        ret['msg']=True
                        return HttpResponse(json.dumps(ret))
                    else:
                        from django.forms.utils import ErrorDict
                        ret['msg']=obj.errors
                        return HttpResponse(json.dumps(ret))
#html页面
            <form id="form" action="/f1/" method="post" novalidate>
                {{ obj.as_p }}
                <input type="button" value="ajax提交" id="btn"><br>
            </form>
            <script>
                $(function () {
                    $('#btn').click(function () {
                        $.ajax({
                            url:'/f1/',
                            type:'POST',
                            data:$('#form').serialize(),
                            datatype:"JSON",
                            success:function (arg) {
                                if(arg.msg==true){
                                    window.location.href='http://www.baidu.com'
                                }
                                console.log(arg);

                            }
                        })
                    })
                })
            </script>

与表单提交区别:手动设置页面跳转;错误信息手动展示

Form字段和插件:

创建Form类时,主要涉及到 【字段】 和 【插件】,字段用于对用户请求数据的验证,插件用于自动生成HTML。

Django内置字段:

Field
    required=True,               是否允许为空
    label=None,                  用于生成Label标签或显示内容
    initial=None,                初始值
    help_text='',                帮助信息(在标签旁边显示)
    error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
    show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
    validators=[],               自定义验证规则
    localize=False,              是否支持本地化
    disabled=False,              是否可以编辑
    label_suffix=':'            Label内容后缀


CharField(Field)
    max_length=None,             最大长度
    min_length=None,             最小长度
    strip=True                   是否移除用户输入空白

IntegerField(Field)
    max_value=None,              最大值
    min_value=None,              最小值

FloatField(IntegerField)
    ...

DecimalField(IntegerField)
    max_value=None,              最大值
    min_value=None,              最小值
    max_digits=None,             总长度
    decimal_places=None,         小数位长度

BaseTemporalField(Field)
    input_formats=None          时间格式化

DateField(BaseTemporalField)    格式:2015-09-01
TimeField(BaseTemporalField)    格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12

DurationField(Field)            时间间隔:%d %H:%M:%S.%f
    ...

RegexField(CharField)
    regex,                      自定制正则表达式
    max_length=None,            最大长度
    min_length=None,            最小长度
    error_message=None,         忽略,错误信息使用 error_messages={'invalid': '...'}

EmailField(CharField)
    ...

FileField(Field)
    allow_empty_file=False     是否允许空文件

ImageField(FileField)
    ...
    注:需要PIL模块,pip3 install Pillow
    #以上两个字典使用时,需要注意两点:
        - form表单中 enctype="multipart/form-data"
        - view函数中 obj = MyForm(request.POST, request.FILES)

URLField(Field)
    ...


BooleanField(Field)
    ...

NullBooleanField(BooleanField)
    ...

ChoiceField(Field)
    ...
    choices=(),                选项,如:choices = ((0,'上海'),(1,'北京'),)
    required=True,             是否必填
    widget=None,               插件,默认select插件
    label=None,                Label内容
    initial=None,              初始值
    help_text='',              帮助提示


ModelChoiceField(ChoiceField)
    ...                        django.forms.models.ModelChoiceField
    queryset,                  # 查询数据库中的数据
    empty_label="---------",   # 默认空显示内容
    to_field_name=None,        # HTML中value的值对应的字段
    limit_choices_to=None      # ModelForm中对queryset二次筛选

ModelMultipleChoiceField(ModelChoiceField)
    ...                        django.forms.models.ModelMultipleChoiceField



TypedChoiceField(ChoiceField)
    coerce = lambda val: val   对选中的值进行一次转换
    empty_value= ''            空值的默认值

MultipleChoiceField(ChoiceField)
    ...

TypedMultipleChoiceField(MultipleChoiceField)
    coerce = lambda val: val   对选中的每一个值进行一次转换
    empty_value= ''            空值的默认值

ComboField(Field)
    fields=()                  使用多个验证,如下:即验证最大长度20,又验证邮箱格式
                               fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])

MultiValueField(Field)
    PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用

SplitDateTimeField(MultiValueField)
    input_date_formats=None,   格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
    input_time_formats=None    格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']

FilePathField(ChoiceField)     文件选项,目录下文件显示在页面中
    path,                      文件夹路径
    match=None,                正则匹配
    recursive=False,           递归下面的文件夹
    allow_files=True,          允许文件
    allow_folders=False,       允许文件夹
    required=True,
    widget=None,
    label=None,
    initial=None,
    help_text=''

GenericIPAddressField
    protocol='both',           both,ipv4,ipv6支持的IP格式
    unpack_ipv4=False          解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用

SlugField(CharField)           数字,字母,下划线,减号(连字符)
    ...

UUIDField(CharField)           uuid类型
            ...

常用选择插件:

# 单radio,值为字符串
 user = fields.CharField(
     initial=2,
     widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
 )

# 单radio,值为字符串
 user = fields.ChoiceField(
     choices=((1, '上海'), (2, '北京'),),
     initial=2,
     widget=widgets.RadioSelect
 )

# 单select,值为字符串
 user = fields.CharField(
     initial=2,
     widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
 )

# 单select,值为字符串
 user = fields.ChoiceField(
     choices=((1, '上海'), (2, '北京'),),
     initial=2,
     widget=widgets.Select
 )

# 多选select,值为列表
 user = fields.MultipleChoiceField(
     choices=((1,'上海'),(2,'北京'),),
     initial=[1,],
     widget=widgets.SelectMultiple
 )


# 单checkbox
 user = fields.CharField(
     widget=widgets.CheckboxInput()
 )


# 多选checkbox,值为列表
 user = fields.MultipleChoiceField(
     initial=[2, ],
     choices=((1, '上海'), (2, '北京'),),
     widget=widgets.CheckboxSelectMultiple
 )

动态绑定数据:

#方式一:重构构造方法(推荐)
class ClassesForm(Form):
    name = fields.CharField()
    user_id = fields.IntegerFiled(
        widget=widgets.Select()
        )
    #重写init方法,时时更新
    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        self.fields["user_id"].widget.choices =User.objects.values_list('id', "username")
#方式二:ModelChoiceField(不推荐)
from django.forms.models import ModelChoiceField
class ClassesForm(Form):
    name = fields.CharField()
    user_id = ModelChoiceField(
        queryset=User.objects.all(),
        to_field_name='id'
        )
#models.py
class User(models.Model):
    name=models.charField()
    user_id=models.IntegerField()
    #显示name而不是object:__str__()返回name
    def __str__(self):
        return self.name

校验数据库字段:

不需要在视图函数中编写校验代码。

#自定制Form类中
class CheckForm(forms.Form):
    username=fields.CharField()
    userid=fields.IntField(
        widget=widgets.Select(choices=[(0,'alex'),(1,'egon')])
            )
#定义 clean_'字段' 方法(单字段)
def clean_username(self):
v=self.cleaned_data['username']
if User.objects.filter(username=v).count()>0:
    raise ValidationError('用户名已存在')
return v
#定义clean方法(整体)
def clean(self):
    value_dict=self.cleaned_data
    v1=value_dict.get('username')
    v2=value_dict.get('userid')
    if xxx:
        raise ValidationError('整体错误')
    return cleaned_data

序列化

关于Django中的序列化主要应用在将数据库中检索的数据返回给客户端用户,特别的Ajax请求一般返回的为Json格式。

原始方式:

#views.py
def getuser(request):
    user_list = User.objects.all()
    return render(request, 'getuser.html', locals())
#xu.html
<h1>用户列表</h1><hr>
<table id="tb" >
</table>
<script>
    $(function () {
        initData();
    });
    function initData() {
        $.ajax({
            url:'/get_user/',
            type:'GET',
            success:function (ret) {
                $('#tb').append(ret);
            }
        })
    }
#getuser.html
 {% for user in user_list %}
       <tr>
       <td>{{ user.id }}</td>
       <td>{{ user.username }} </td>
       <td>{{ user.age }} </td>
       </tr>
 {% endfor %}

serializers :[object]

#views.py
from django.core import serializers
def getuser(request):
    ret={'status':True,'data':None}
    try:
        user_list = User.objects.all()
        ret['data'] = serializers.serialize('json', user_list)
    except Exception as e:
        ret['status']=False
    return HttpResponse(json.dumps(ret))
#xu.html
<h1>用户列表</h1><hr>
<table id="tb" >
</table>
<script>
    $(function () {
        initData();
    });
    function initData() {
        $.ajax({
            url:'/get_user/',
            type:'GET',
            dataType:'JSON',
            success:function (ret) {
                if(ret.status){
                    var val=JSON.parse(ret.data);
                    for (let i = 0; i <val.length; i++) {
                        $('#tb').append(val[i].pk+'\t');
                        $('#tb').append(val[i].fields.username+'\t');
                        $('#tb').append(val[i].fields.age+'\t');
                        $('#tb').append('<br>');
                    }
                }

            }
        })
    }

json.dumps :[tuple]

#views.py
def getuser(request):
    ret={'status':True,'data':None}
    try:
        user_list = User.objects.all().values('id','username','age')
        ret['data']=list(user_list)
    except Exception as e:
        ret['status']=False
    return HttpResponse(json.dumps(ret))
#xu.html
<h1>用户列表</h1><hr>
<table id="tb" >
</table>
<script>
    $(function () {
        initData();
    });
    function initData() {
        $.ajax({
            url:'/get_user/',
            type:'GET',
            dataType:'JSON',
            success:function (ret) {
                if(ret.status){
                    for (let i = 0; i <ret.data.length; i++) {
                         $('#tb').append(ret.data[i].id);
                         $('#tb').append(ret.data[i].username);
                         $('#tb').append(ret.data[i].age);
                         $('#tb').append('<br>');
                    };
                }

            }
        })
    }

model进阶

参考博客:yuan先生

Ajax

1、发送请求

①基于jquery

#GET
function submit1() {
    $.ajax({
        url:'/submit1/',
        type:'GET',
        data:{'msg':'123'},
        success:function (arg) {

        }
    })
}
#POST
function submit1() {
    $.ajax({
        url:'/submit1/',
        type:'POST',
        data:{'msg':'123'},
        success:function (arg) {

        }
    })
}

②基于XMLHttpRequest

#GET
function submit2() {
    var xhr=new XMLHttpRequest();
    xhr.onreadystatechange=function(){
        if (xhr.readyState==4){
            console.log(xhr.responseText)
        }
    };
    xhr.open('GET','/submit?msg=234');
    xhr.send(null);

}
#POST
function submit2() {
    var xhr=new XMLHttpRequest();
    xhr.onreadystatechange=function(){
        if (xhr.readyState==4){
            console.log(xhr.responseText)
        }
    };
    xhr.open('POST','/submit/');
    xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded','charset-UTF-8')
    xhr.send('msg=345');
 }

③Iframe伪造ajax请求

<form id="fm" action="/submit/" method="post" target="iframe">
    <input type="text" >
    <a onclick="submit3()">提交</a>
</form>
<iframe id="iframe" name="iframe"></iframe>
function submit3() {
    document.getElementById('iframe').onload=reloadiframe;
    document.getElementById('fm').submit();
}
#伪造回调函数
function reloadiframe() {
    var content=this.contentWindow.document.body.innerHTML;
    var obj=JSON.parse(content);
    if (obj.status){
        alert(obj.msg)
    }
}

2、上传文件

①基于jquery

#html页面
<input type="file" id="img"><br>
<a onclick="upload1()">上传</a><br>
    function upload1() {
        var data=new FormData();
        data.append('img',document.getElementById('img').files[0]);
        $.ajax({
            url:'/upload/',
            type:'POST',
            data:data,
            processData:false,
            contentType:false,
            success:function (arg) {

            }
        })
    }
#views.py
def upload(request):
    img = request.FILES.get('img')
    with open('upload/' + img.name, 'wb') as f:
        for line in img.chunks():
            f.write(line)
    return HttpResponse('up...')

②基于XMLHttpRequest

#html页面
function upload2() {
    var xhr=new XMLHttpRequest();
    var data=new FormData();
    data.append('img',document.getElementById('img').files[0]);
    xhr.onreadystatechange=function(){
        if (xhr.readyState==4){
            console.log(xhr.responseText)
        }
    };
xhr.open('POST','/upload/');
xhr.send(data);
}

③基于iframe+form表单

#html页面
<iframe style="display: none" id="iframe" name="iframe"></iframe>
<form id="fm" action="/upload/" method="post" enctype="multipart/form-data" target="iframe">
    <input type="file" id="img" name="img"><br>
    <a onclick="upload3()">上传</a><br>
</form>
 function upload3() {
    document.getElementById('iframe').onload=reloadiframe;
    document.getElementById('fm').submit();
 }
 function reloadiframe() {
    var content=this.contentWindow.document.body.innerHTML;
    var obj=JSON.parse(content);
    if (obj.status){
        alert(obj.msg)
    }
 }
 #views.py
 def upload(request):
    img = request.FILES.get('img')
    print(request.FILES)
    print(request.POST)
    with open('upload/' + img.name, 'wb') as f:
        for line in img.chunks():
            f.write(line)
    data = {'status': True, 'msg': 'ok'}
    return HttpResponse(json.dumps(data))

优化:上传即提交,上传预览

#views.py
 def upload(request):
    img = request.FILES.get('img')
    with open('app01/static/' + img.name, 'wb') as f:
        for line in img.chunks():
            f.write(line)
    data = {'status': True, 'url': os.path.join('/static/',img.name)}
    return HttpResponse(json.dumps(data))
 #html页面
 <iframe style="display: none" id="iframe" name="iframe"></iframe>
 <form id="fm" action="/upload/" method="post" enctype="multipart/form-data" target="iframe">
     <input type="file" id="img" name="img" onchange="upload()"><br>
 </form>
 <div id="preview"></div>
 function upload() {
    document.getElementById('iframe').onload=reloadiframe;
    document.getElementById('fm').submit();
 }
 function reloadiframe() {
    var content=this.contentWindow.document.body.innerHTML;
    var obj=JSON.parse(content);
    if (obj.status){
       console.log(obj.url);
       var preview=document.createElement('img');
       preview.src=obj.url;
       $('#preview').empty().append(preview);
    }
 }

3、JSONP跨域请求

浏览器同源策略:XMLHttpRequest只能向本地发送数据;jsonp巧妙解决

jquery集成的jsonp:

<input type="button" value="发送跨域请求" onclick="jsonp()">
function jsonp() {
    $.ajax({
        url:'[路径]',
        type:'GET',
        DATA: 'jsonp',
    })
}
function list(arg) {
    $('#content').html(arg)
}

本质上:

function jsonp() {
    var req=document.createElement('script');
    req.src='[路径]',
    document.head.appendChild(req);
    document.head.removeChild(req);
}
function list(arg) {
    $('#content').html(arg)
}

实例:跨域请求江西卫视节目表

<input type="button" value="发送跨域请求" onclick="jsonp()">
<div id="list"></div>
function jsonp() {
    $.ajax({
        url: 'http://www.jxntv.cn/data/jmd-jxtv2.html',
        type: 'GET',
        dataType: 'jsonp',
        jsonp: 'callback',
        jsonpCallback: 'list',
    })
}
function list(arg) {
    {#console.log(arg);#}
    {#console.log(arg.data[0].list[0].time);#}
    for (let i = 0; i < arg.data.length ; i++) {
        $('#list').append('<p>'+arg.data[i].week+'</p>');
        $('#list').append('<hr>');
        for (let j = 0; j <arg.data[i].list.length ; j++) {
            $('#list').append(arg.data[i].list[j].time+'&emsp;'+arg.data[i].list[j].name+'&emsp;'+arg.data[i].list[j].link);
            $('#list').append('<br>');
        }
    }
}

实现效果:

另:CORS跨站资源共享(了解)

#被请求端的views.py
def list(request):
    data={'name':'alex','age':22……}
    obj=HttpResponse(json.dumps(data))
    #设置响应头为*,则绕过了浏览器同源策略
    obj['Access-Control-Allow-Origin']='*'
    return obj

——————————————以上——————————————

发表回复