django(7)modelform操作及验证、ajax操作普通表单数据提交、文件上传、富文本框基本使用

时间:2023-03-08 22:46:52

一、modelForm操作及验证

1.获取数据库数据,界面展示数据并且获取前端提交的数据,并动态显示select框中的数据

views.py

from django.shortcuts import render,HttpResponse
from app01 import models

from django import forms
from django.forms import fields as Ffields
from django.forms import widgets as Fwidgets
class UserInfoModelForm(forms.ModelForm):
    class Meta:#必须添加
        model = models.UserInfo#指定去UserInfo表里获取数据
        fields = '__all__'  #在界面显示所有字段
        # fields =  ['username','email']#只在界面显示'username','email'字段
        # exclude = ['username']  #不在界面显示'username'字段

class UserInfoForm(forms.Form):
    username = Ffields.CharField(max_length=32)
    email = Ffields.EmailField()
    user_type = Ffields.ChoiceField(
        choices=models.UserType.objects.values_list('id','caption')#以'id','caption'为select框中的数据
    )

    def __init__(self, *args, **kwargs):
        super(UserInfoForm,self).__init__(*args, **kwargs)
        self.fields['user_type'].choices = models.UserType.objects.values_list('id','caption')#加上这个init函数
            #后,就能将新添加到数据库中的数据动态显示到select框啦

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.is_valid()只能为true或false
         print(obj.is_valid())
         print(obj.cleaned_data) #obj.cleaned_data以字典的形式展示前端提交的数据
         print(obj.errors.as_json())#打印错误信息
        return render(request,'index.html',{'obj': obj})

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <form action="/index/" method="POST">
        {% csrf_token %}
        {{ obj.as_p }}  #以p标签的形式展示数据
        <input type="submit" value="提交" />
    </form>
</body>
</html>

二、完整的在界面展示数据、修改数据、提交数据、跳转到新页面

views.py

from django.shortcuts import render,HttpResponse
from app01 import models

from django import forms
from django.forms import fields as Ffields#给导入的fields设置别名为Ffields
from django.forms import widgets as Fwidgets#给导入的widgets设置别名为Fwidgets
class UserInfoModelForm(forms.ModelForm):

    is_rmb = Ffields.CharField(widget=Fwidgets.CheckboxInput())#自定义在界面显示的单选框等,而且不用保存在数据库

    class Meta:#class  Meta里一定不要加逗号
        model = models.UserInfo#指定去UserInfo表里获取数据
        fields = '__all__'  #在界面显示所有字段
        # fields =  ['username','email']#只在界面显示'username','email'字段
        # exclude = ['username']  #不在界面显示'username'字段
        labels = {#设定输入框的标签名
            'username': '用户名',
            'email': '邮箱',
        }
        help_texts = {#在输入框后面自定义帮助信息
            'username': '我是帮助信息'
        }
        widgets = {#自定义输入框的属性,即自定义html标签的class
            'username': Fwidgets.Textarea(attrs={'class': 'c1'})#将输入框设置为text文本框,并添加css:‘c1’
        }
        error_messages = {#自定义错误信息提示
            '__all__':{#给整体设置错误信息,跟form中的all一样

            },
            'email': {#给每个字段设定错误信息
                'required': '邮箱不能为空',
                'invalid': '邮箱格式错误..',
            }
        }
        field_classes = {#将email输入框的格式验证修改为必须输入URL格式才正确
            # 'email': Ffields.URLField
        }

        localized_fields=('ctime',)#将数据库时间转化为北京时间,括号里写需要转化时间的字段,需要在settings里设置
         #timezone

class UserInfoForm(forms.Form):
    username = Ffields.CharField(max_length=32)
    email = Ffields.EmailField()
    user_type = Ffields.ChoiceField(
        choices=models.UserType.objects.values_list('id','caption')#以'id','caption'为select框中的数据
    )

    def __init__(self, *args, **kwargs):
        super(UserInfoForm,self).__init__(*args, **kwargs)
        self.fields['user_type'].choices = models.UserType.objects.values_list('id','caption')#加上这个init函数
            #后,就能将新添加到数据库中的数据动态显示到select框啦

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.is_valid()只能为true或false
            obj.save()#自动将前端提交的数据保存在数据库

        # print(obj.cleaned_data) #obj.cleaned_data以字典的形式获取前端提交的数据
        # print(obj.errors.as_json())
        return render(request,'index.html',{'obj': obj})

def user_list(request):
    li = models.UserInfo.objects.all().select_related('user_type')#获取UserInfo表中的所有数据,并且跨表将与UserInfo
    # 关联的user_type表中的数据也取出来,注意括号中写UserInfo类中的关联字段名,不可以写多对多的字段名,否则报错,
    #前端html跨表取user_type表数据:{% for row in li %}   {{ row.user_type.caption }}
    return render(request,'user_list.html',{'li': li})

def user_edit(request, nid):
    # 1.获取当前id对应的用户信息
    # 2.显示用户已经存在数据
    if request.method == "GET":#数据展示
        user_obj = models.UserInfo.objects.filter(id=nid).first()#根据nid获取UserInfo表中的相关数据的第一条
        mf = UserInfoModelForm(instance=user_obj)#将mf传到前端就可以自动生成网页,括号中的instance参数是显示默认值
        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)#如果不加这句,就会自动在数据库中新添加数据,而不是修改
        #已存在的数据
        if mf.is_valid():
            mf.save()#将修改的数据写入到数据库中
        else:#如果出现异常,打印错误信息
            print(mf.errors.as_json())
        return render(request,'user_edit.html',{'mf': mf, 'nid': nid})

urls.py

    url(r'^admin/', admin.site.urls),
    url(r'^index/', views.index),
    url(r'^user_list/', views.user_list),
    url(r'^edit-(\d+)/', views.user_edit),

index.html

同上

user_list.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
        <ul>
            {% for row in li %}
                <li>{{ row.username }} - {{ row.user_type.caption }} - <a href="/edit-{{ row.id }}/">编辑</a></li>
            {% endfor %}
        </ul>
</body>
</html>

user_edit.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <form method="POST" action="/edit-{{ nid }}/"> #以新链接的形式提交数据
        {% csrf_token %}
    {{ mf.as_p }}
        <input type="submit" value="提交" />
    </form>

</body>
</html>

 三、ajax操作发送表单,并且获取数据,页面不刷新

1.给后台发送普通数据

views.py

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

def ajax_json(request):
    import time

    print(request.POST)
    ret = {'code': True , 'data': request.POST.get('username')}
    import json
    return HttpResponse(json.dumps(ret))

ajax.html

有两种ajax提交方式:推荐用第一种

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
</head>
<body>
    <input type="text"/>
    <input type="button" value="Ajax1" onclick="Ajax1();" />

    第二种:ifram提交数据
    <form action="/ajax_json/" method="POST" target="ifm1">
        <iframe id="ifm1" name="ifm1" ></iframe>
        <input type="text" name="username" />
        <input type="text" name="email" />
        <input type="submit" onclick="sumitForm();" value="Form提交"/>
    </form>

    <script type="text/javascript" src="/static/jquery-1.12.4.js"></script>
    <script>
        第一种:ajax提交表单数据
        function getXHR(){
            var xhr = null;
            if(XMLHttpRequest){
                xhr = new XMLHttpRequest();
            }else{
                xhr = new ActiveXObject("Microsoft.XMLHTTP");
            }
            return xhr;

        }

        function Ajax1(){
            var xhr = getXHR();
            //var xhr = new XMLHttpRequest();
            xhr.open('POST', '/ajax_json/',true);#括号内参数依次为:数据提交方式、提交去处、是否异步,true为异步
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4){
                    // 接收完毕
                    var obj = JSON.parse(xhr.responseText);
                    console.log(obj);
                }
            };
            xhr.setRequestHeader('k1','v1');
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset-UTF-8');
            xhr.send("name=root;pwd=123");
        }

{#        第二种:ifram提交表单数据#}
        function sumitForm(){
            $('#ifm1').load(function(){
                var text = $('#ifm1').contents().find('body').text();
                var obj = JSON.parse(text);
            })
        }

    </script>
</body>
</html>

ifram详解:

<body>
    <form action="/ajax_json/" method="POST" target="ifm1">#表单提交到/ajax_json/  target:在指定的框架中打开
        <iframe id="ifm1" name="ifm1" ></iframe>  #加上display:none就看不到ifram框啦
        <input type="text" name="username" />
        <input type="text" name="email" />
        <input type="submit" onclick="sumitForm();" value="Form提交"/>
    </form>

    <script>
        function sumitForm(){#获取ifram框架中的数据
            $('#ifm1').load(function(){
                var text = $('#ifm1').contents().find('body').text(); #.contents():由于ifram生成的是完整的html网页,通过contents()属性提取出网页内容,再找到body标签,                                                                      #再通过text方法找到后台返回的数据
                var obj = JSON.parse(text);  #将获取到的后台数据json序列化
            })
        }

    </script>

2.给后台上传文件,上传文件有三种方式,优先使用ifram,再次使用jQuery ajax

views.py

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

def upload_file(request):
    username = request.POST.get('username')
    fafafa = request.FILES.get('fafafa')
    import os
    img_path = os.path.join('static/imgs/',fafafa.name)#生成图片保存路径
    with open(img_path,'wb') as f:
        for item in fafafa.chunks():#保存文件,必须用chunks()
            f.write(item)

    ret = {'code': True , 'data': img_path}#将图片路径返回给前端
    import json
    return HttpResponse(json.dumps(ret))

upload.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title></title>
    <style>
        .upload{
            display: inline-block;padding: 10px;
            background-color: brown;
            position: absolute;
            top: 0;
            bottom: 0;
            right: 0;
            left: 0;
            z-index: 90;
        }
        .file{
            width: 100px;height: 50px;opacity: 0;
            position: absolute;
            top: 0;
            bottom: 0;
            right: 0;
            left: 0;
            z-index: 100;
        }
    </style>
</head>
<body>
    <div style="position: relative;width: 100px;height: 50px;">
        <input class="file" type="file" id="fafafa" name="afafaf" />
        <a class="upload">上传</a>
    </div>
    第一种上传方式:原生ajax
    <input type="button" value="提交XHR" onclick="xhrSubmit();" />
    第二种上传方式:jQuery ajax
    <input type="button" value="提交jQuery" onclick="jqSubmit();" />
    <hr/>  分隔符
    第三种上传方式:ifram  最好用第三和第二种
    <form id="form1" action="/upload_file/" method="POST" enctype="multipart/form-data" target="ifm1">
        <iframe id="ifm1" name="ifm1" style="display: none;"></iframe>
        <input type="file" name="fafafa" onchange="changeUpalod();" />
        <input type="submit" onclick="iframeSubmit();" value="Form提交"/>
    </form>

    <div id="preview"></div>

    <script src="/static/jquery-1.12.4.js"></script>
    <script>
        这一步可以不要,选中图片后自动提交,并且生成图片预览
        function changeUpalod(){
            $('#ifm1').load(function(){
                var text = $('#ifm1').contents().find('body').text();
                var obj = JSON.parse(text);  获取图片路径

                $('#preview').empty();  #将预览图片标签清空
                var imgTag = document.createElement('img');  生成图片标签
                imgTag.src = "/" + obj.data;  生成图片路径
                $('#preview').append(imgTag);
            });
            $('#form1').submit();
        }
        以下有三种文件提交方式,优先使用ifram,再次使用jQuery提交文件
         以下这一步是:jQuery提交文件
        function jqSubmit(){
            // $('#fafafa')[0]
            var file_obj = document.getElementById('fafafa').files[0];

            var fd = new FormData();
            fd.append('username','root');
            fd.append('fafafa',file_obj);

            $.ajax({
                url: '/upload_file/',
                type: 'POST',
                data: fd,
                processData: false,  // tell jQuery not to process the data
                contentType: false,  // tell jQuery not to set contentType
                success:function(arg,a1,a2){
                    console.log(arg);
                    console.log(a1);
                    console.log(a2);
                }
            })
        }
        以下这一步是:原生ajax提交文件
        function xhrSubmit(){
            // $('#fafafa')[0]
            var file_obj = document.getElementById('fafafa').files[0];

            var fd = new FormData();
            fd.append('username','root');
            fd.append('fafafa',file_obj);

            var xhr = new XMLHttpRequest();
            xhr.open('POST', '/upload_file/',true);
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4){
                    // 接收完毕
                    var obj = JSON.parse(xhr.responseText);
                    console.log(obj);
                }
            };
            xhr.send(fd);
        }
        以下这一步是:ifram提交文件,优先使用ifram提交
        function iframeSubmit(){
            $('#ifm1').load(function(){
                var text = $('#ifm1').contents().find('body').text();
                var obj = JSON.parse(text);  获取图片路径

                $('#preview').empty();  #将预览图片标签清空
                var imgTag = document.createElement('img');  生成图片标签
                imgTag.src = "/" + obj.data;  生成图片路径
                $('#preview').append(imgTag);
            });
        }

    </script>
</body>
</html>

四、生成图片验证码

check_code.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-

import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter

_letter_cases = "abcdefghjkmnpqrstuvwxy"  # 小写字母,去除可能干扰的i,l,o,z
_upper_cases = _letter_cases.upper()  # 大写字母
_numbers = ''.join(map(str, range(3, 10)))  # 数字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))

def create_validate_code(size=(120, 30),
                         chars=init_chars,
                         img_type="GIF",
                         mode="RGB",
                         bg_color=(255, 255, 255),
                         fg_color=(0, 0, 255),
                         font_size=18,
                         font_type="Monaco.ttf",
                         length=4,
                         draw_lines=True,
                         n_line=(1, 2),
                         draw_points=True,
                         point_chance=2):
    """
    @todo: 生成验证码图片
    @param size: 图片的大小,格式(宽,高),默认为(120, 30)
    @param chars: 允许的字符集合,格式字符串
    @param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG
    @param mode: 图片模式,默认为RGB
    @param bg_color: 背景颜色,默认为白色
    @param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF
    @param font_size: 验证码字体大小
    @param font_type: 验证码字体,默认为 ae_AlArabiya.ttf
    @param length: 验证码字符个数
    @param draw_lines: 是否划干扰线
    @param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效
    @param draw_points: 是否画干扰点
    @param point_chance: 干扰点出现的概率,大小范围[0, 100]
    @return: [0]: PIL Image实例
    @return: [1]: 验证码图片中的字符串
    """

    width, height = size  # 宽高
    # 创建图形
    img = Image.new(mode, size, bg_color)
    draw = ImageDraw.Draw(img)  # 创建画笔

    def get_chars():
        """生成给定长度的字符串,返回列表格式"""
        return random.sample(chars, length)

    def create_lines():
        """绘制干扰线"""
        line_num = random.randint(*n_line)  # 干扰线条数

        for i in range(line_num):
            # 起始点
            begin = (random.randint(0, size[0]), random.randint(0, size[1]))
            # 结束点
            end = (random.randint(0, size[0]), random.randint(0, size[1]))
            draw.line([begin, end], fill=(0, 0, 0))

    def create_points():
        """绘制干扰点"""
        chance = min(100, max(0, int(point_chance)))  # 大小限制在[0, 100]

        for w in range(width):
            for h in range(height):
                tmp = random.randint(0, 100)
                if tmp > 100 - chance:
                    draw.point((w, h), fill=(0, 0, 0))

    def create_strs():
        """绘制验证码字符"""
        c_chars = get_chars()
        strs = ' %s ' % ' '.join(c_chars)  # 每个字符前后以空格隔开

        font = ImageFont.truetype(font_type, font_size)
        font_width, font_height = font.getsize(strs)

        draw.text(((width - font_width) / 3, (height - font_height) / 3),
                  strs, font=font, fill=fg_color)

        return ''.join(c_chars)

    if draw_lines:
        create_lines()
    if draw_points:
        create_points()
    strs = create_strs()

    # 图形扭曲参数
    params = [1 - float(random.randint(1, 2)) / 100,
              0,
              0,
              0,
              1 - float(random.randint(1, 10)) / 100,
              float(random.randint(1, 2)) / 500,
              0.001,
              float(random.randint(1, 2)) / 500
              ]
    img = img.transform(size, Image.PERSPECTIVE, params)  # 创建扭曲

    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)  # 滤镜,边界加强(阈值更大)

    return img, strs

views.py

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from io import BytesIO
from django.shortcuts import HttpResponse
from django.shortcuts import render
from utils.check_code import create_validate_code

def check_code(request):#该函数对应login.html中的src=/check_code.html
    """
    验证码
    :param request:
    :return:
    """
    # stream = BytesIO()
    # img, code = create_validate_code()
    # img.save(stream, 'PNG')
    # request.session['CheckCode'] = code
    # return HttpResponse(stream.getvalue())

    # data = open('static/imgs/avatar/20130809170025.png','rb').read()
    # return HttpResponse(data)

    # 1. 创建一张图片 pip3 install Pillow
    # 2. 在图片中写入随机字符串
    # obj = object()
    # 3. 将图片写入到制定文件
    # 4. 打开制定目录文件,读取内容
    # 5. HttpResponse(data)

    stream = BytesIO()#生成一个内存对象
    img, code = create_validate_code() #生成图片和验证码
    img.save(stream,'PNG')#将图片保存到内存中,格式为png
    request.session['CheckCode'] = code  #将验证码放到session里
    return HttpResponse(stream.getvalue())#stream.getvalue()将图片从内存中取出,返回给前端

def login(request):
    """
    登陆
    :param request:
    :return:
    """
    # if request.method == "POST":
    #     if request.session['CheckCode'].upper() == request.POST.get('check_code').upper():
    #         pass
    #     else:
    #         print('验证码错误')
    if  request.method == 'POST':
        code = request.POST.get('check_code')
        if code.upper() == request.session['CheckCode'].upper():
            print('验证码正确')
        else:
            print('验证码错误')
    return render(request, 'login.html')

def register(request):
    """
    注册
    :param request:
    :return:
    """
    return render(request, 'register.html')

def logout(request):
    """
    注销
    :param request:
    :return:
    """
    pass

login.html

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title></title>
    <link rel="stylesheet" href="/static/plugins/bootstrap/css/bootstrap.css"/>
    <link rel="stylesheet" href="/static/plugins/font-awesome/css/font-awesome.css"/>
    <link rel="stylesheet" href="/static/css/edmure.css"/>
    <link rel="stylesheet" href="/static/css/commons.css"/>
    <link rel="stylesheet" href="/static/css/account.css"/>
    <style>

    </style>
</head>
<body>
<div class="login">
    <div style="font-size: 25px; font-weight: bold;text-align: center;">
        用户登陆
    </div>
    <form role="form" action="/login.html" method="POST">
        {% csrf_token %}
        <div class="form-group">
            <label for="username">用户名</label>
            <input type="text" class="form-control"  placeholder="请输入用户名">
        </div>
        <div class="form-group">
            <label for="password">密码</label>
            <input type="password" class="form-control"  placeholder="请输入密码">
        </div>
        <div class="form-group">
            <label for="password">验证码</label>

            <div class="row">
                <div class="col-xs-7">
                    <input type="text" class="form-control" placeholder="请输入验证码" name="check_code">
                </div>
                <div class="col-xs-5">
                    <img src="/check_code.html" onclick="changeCheckCode(this);">
                  #绑定事件,点击图片生成新的验证码
                </div>
            </div>

        </div>
        <div class="checkbox">
            <label>
                <input type="checkbox"> 一个月内自动登陆
            </label>
            <div class="right">
                <a href="#">忘记密码?</a>
            </div>
        </div>
        <button type="submit" class="btn btn-default">登 陆</button>
    </form>
</div>
    <script>
        function changeCheckCode(ths){
            ths.src = ths.src +  '?';
{#            通过在url后边加问号的形式,实现点击验证码图片刷新页面获取新的验证码#}

        }
    </script>
</body>
</html>

五、富文本框使用

http://www.cnblogs.com/wupeiqi/articles/6307554.html   视频24天-17-KindEditor基本使用和文件操作