Django入门到放弃 学习笔记 09

跟b站武沛齐老师的视频学习
对学习过程进行一些记录以便复习 同时自我督促 :D冲鸭

视频链接: 15天django入门到放弃-哔哩哔哩.

本篇均为 Form 相关内容

Form 验证整理

 - 用户提交数据进行校验
   - Form 提交(刷新,失去上次内容)
     a. LoginForm(Form)
       字段名 = xxxx.xxFields() # 本质验证规则,正则表达式
       字段名 = xxxx.xxFields() # 本质验证规则,正则表达式
       字段名 = xxxx.xxFields() # 本质验证规则,正则表达式
       字段名 = fiedls.RegexField(‘正则表达式’, error_messages={xx})
     b. obj = LoginForm(用户提交的数据)
     c. result = obj.is_valid()
     d. obj.cleaned_data
     e. obj.errors

     - 内部原理
       1. LoginForm 实例化时,
         self.fields={
           ‘user’: 正则表达式
           ‘pwd’: 正则表达式
         }
       2. 循环self.fields
         flag = true
         errors
         cleaned_data
         for k, v in self.fields.items():
           # k是: user, pwd
           # v是: 正则表达式
           # 1. user, 正则表达式
           input_value = request.POST.get(k)
           正则表达式和input_value不匹配
           flag = False
         return flag
   - Ajax 提交(不刷新,上次内容自动保留)
   
   PS:Ajax 提交 > Form 提交

   总结:
     class Foo(Form):
       字段 = 正则表达式
       字段 = 自定义正则表达式

 - 保留上次输入内容

ajax提交 例

views.py

from django.shortcuts import render, redirect, HttpResponse
from django.forms import Form, fields
import json


class LoginForm(Form):
    username = fields.CharField(required=True)
    password = fields.CharField(min_length=18)


def ajax_login(request):
    ret = {'status': True, 'msg': None}
    obj = LoginForm(request.POST)
    if obj.is_valid():
        print(obj.cleaned_data)
    else:
        print(obj.errors)
        ret['status'] = False
        ret['msg'] = obj.errors
    v = json.dumps(ret)
    return HttpResponse(v)

前端收到的v:{“status”: false, “msg”: {“password”: [“Ensure this value has at least 18 characters (it has 12).”]}}
在每个组件后添加提示的办法:

var tag = document.createElement('span');
tag.innerHTML = value[0];
tag.className = 'c1';
$('#f1').find('input[name="' + index + '"]').after(tag);

为了使每次重新提交时之前的 tag 消失,为它添加 c1 类属性,然后再请求处理开头加上 $('.c1').remove();
代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>用户登录</h1>
    <form id='f1' method="POST" action="/login/">
        {% csrf_token %}
        <p>
            用户:<input type="text" name="username"> {{ obj.errors.username.0 }}
        </p>
        <p>
            密码:<input type="password" name="password"> {{ obj.errors.password.0 }}
        </p>
        <a onclick="submitForm();">提交</a>
    </form>

    <script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-1.12.4.min.js"></script>

    <script>
        function submitForm(){
            $.ajax({
                url: '/ajax_login/',
                type: 'POST',
                data: $('#f1').serialize(),
                dataType: 'JSON',
                success: function (arg) {
                    $('.c1').remove();
                    console.log(arg);
                    if (arg.status){

                    } else {
                        $.each(arg.msg, function (index, value) {
                            console.log(value);
                            var tag = document.createElement('span');
                            tag.innerHTML = value[0];
                            tag.className = 'c1';
                            $('#f1').find('input[name="' + index + '"]').after(tag);
                        })
                    }
                }
            })
        }
    </script>
</body>
</html>

相关参数(生成HTML标签)

widget=None,                 HTML插件
label=None,                  用于生成Label标签或显示内容
initial=None,                初始值
help_text='',                帮助信息(在标签旁边显示)
disabled=False,              是否可以编辑
label_suffix=None            Label内容后缀
(自动生成 HTML 标签)

required=True,               是否允许为空
error_messages=None,         错误信息 {'required': '不能为空', 'invalid': '格式错误'}
show_hidden_initial=False,   是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
validators=[],               自定义验证规则
localize=False,              是否支持本地化

示例:
views.py

class TestForm(Form):
    t1 = fields.CharField(
        required=True,
        label='用户名',
        help_text='helptext',
        initial='initial',
        disabled=True,
        label_suffix=':',
        # 只推荐这个:
        widget=forms.Textarea,
    )


def test(request):
    obj = TestForm()
    return render(request, 'test.html', {'obj': obj})

test.html

    <form action="/test/" method="POST">
        {% csrf_token %}
        <p>
            {{ obj.t1.label }}
            {{ obj.t1 }}
            {{ obj.t1.help_text }}
        </p>

        {{ obj.as_p }}
    </form>

保留上次输入内容

  1. Ajax 仅用验证功能
  2. Form 验证功能 + 生成 HTML 标签
    obj = TestForm(request.POST)

示例

班级管理

models.py

class Classes(models.Model):
    title = models.CharField(max_length=32)

views.py

class ClassForm(Form):
    title = fields.RegexField('1618\d+')


def class_list(request):
    cls_list = models.Classes.objects.all()
    return render(request, 'class_list.html', {'cls_list': cls_list})


def add_class(request):
    if request.method == "GET":
        obj = ClassForm()
        return render(request, 'add_class.html', {'obj': obj})
    else:
        obj = ClassForm(request.POST)
        if obj.is_valid():
            models.Classes.objects.create(**obj.cleaned_data)
            return redirect('/class_list/')
        else:
            return render(request, 'add_class.html', {'obj': obj})


def edit_class(request, nid):
    if request.method == "GET":
        row = models.Classes.objects.filter(id=nid).first()
        # 让页面显示初始值
        obj = ClassForm(initial={'title': row.title})
        return render(request, 'edit_class.html', {'nid': nid, 'obj': obj})
    else:
        obj = ClassForm(request.POST)
        if obj.is_valid():
            models.Classes.objects.filter(id=nid).update(**obj.cleaned_data)
            return redirect('/class_list/')
        else:
            return render(request, 'edit_class.html', {'nid': nid, 'obj': obj})

class_list.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>班级列表</h1>
    <div>
        <a href="/add_class/">添加</a>
    </div>
    <ul>
        {% for row in cls_list %}
            <li>{{ row.title }} <a href="/edit_class/{{ row.id }}/">编辑</a> </li>
        {% endfor %}
    </ul>
</body>
</html>

add_list.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>添加班级</h1>
    <form method="POST" action="/add_class/" novalidate>
        {% csrf_token %}
        <p>
            {{ obj.title }}
            {{ obj.title.errors.0 }}
        </p>
        <input type="submit" value="提交">
    </form>
</body>
</html>

注意,编辑班级对应的 url 为:re_path(r'^edit_class/(\d+)/', views.edit_class),
edit_class.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>编辑班级</h1>
    <form method="POST" action="/edit_class/{{ nid }}/">
        {% csrf_token %}
        <p>
            {{ obj.title }}
            {{ obj.title.errors.0 }}
        </p>
        <input type="submit" value="提交">
    </form>
</body>
</html>

学生管理

models.py

class Student(models.Model):
    name = models .CharField(max_length=32)
    email = models.CharField(max_length=32)
    age = models.IntegerField(max_length=32)
    cls = models.ForeignKey('Classes', on_delete=models.DO_NOTHING)

views.py

class StudentForm(Form):
    name = fields.CharField(min_length=2, max_length=6)
    email = fields.EmailField()
    age = fields.IntegerField(min_value=18, max_value=25)
    cls_id = fields.IntegerField(
        widget=widgets.Select(choices=models.Classes.objects.values_list('id', 'title'))
    )


def student_list(request):

    stu_list = models.Student.objects.all()
    return render(request, 'student_list.html', {'stu_list': stu_list})


def add_student(request):
    if request.method == "GET":
        obj = StudentForm()
        return render(request, 'add_student.html', {'obj': obj})
    else:
        obj = StudentForm(request.POST)
        if obj.is_valid():
            models.Student.objects.create(**obj.cleaned_data)
            return redirect('/student_list/')
        else:
            return render(request, 'add_student.html', {'obj': obj})


def edit_student(request, nid):
    if request.method == "GET":
        # 此处直接取到一个字典,方便初始化,values要写在first前面,因为first不能对values得到的字典做操作
        row = models.Student.objects.filter(id=nid).values('name', 'email', 'age', 'cls_id').first()
        # 让页面显示初始值
        obj = StudentForm(initial=row)
        return render(request, 'edit_student.html', {'nid': nid, 'obj': obj})
    else:
        obj = StudentForm(request.POST)
        if obj.is_valid():
            models.Student.objects.create(**obj.cleaned_data)
            return redirect('/student_list/')
        else:
            return render(request, 'edit_student.html', {'obj': obj, 'nid': nid})

student_list.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>学生列表</h1>
    <a href="/add_student/">添加</a>
    <ul>
        {% for row in stu_list %}
            <li>{{ row.name }}-{{ row.email }}-{{ row.age }}-{{ row.cls_id }}-{{ row.cls.title }}
                <a href="/edit_student/{{ row.id }}/">编辑</a> </li>
        {% endfor %}
    </ul>
</body>
</html>

add_student.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>添加学生</h1>
    <form action="/add_student/" method="POST">
        {% csrf_token %}
        <p>
            用户名:{{ obj.name }}
        </p>
        <p>
            邮箱:{{ obj.email }}
        </p>
        <p>
            年龄:{{ obj.age }}
        </p>
        <p>
            班级:{{ obj.cls_id }}
        </p>
        <input type="submit" value="提交">
    </form>
</body>
</html>

edit_student.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1>编辑学生</h1>
    <form method="POST" action="/edit_student/{{ nid }}/" novalidate>
        {% csrf_token %}
        <p>
            用户名:{{ obj.name }} {{ obj.name.errors.0 }}
        </p>
        <p>
            邮箱:{{ obj.email }} {{ obj.email.errors.0 }}
        </p>
        <p>
            年龄:{{ obj.age }} {{ obj.age.errors.0 }}
        </p>
        <p>
            班级:{{ obj.cls_id }} {{ obj.cls_id.errors.0 }}
        </p>
        <input type="submit" value="提交">
    </form>
</body>
</html>

教师管理

多选:

xx = fields.CharField(
	widget=widgets.Select(
		choices=models.Classes.objects.values_list('id', 'title'),
		attrs={'multiple': 'multiple'}
	),
)

使用这种方法是不能达到多选的目的的,因为 Select 的实现是用 request.get(''),只能拿到一个数据,应该用 SelectMultiple
拿到数据格式为:
{'tname': 'xxx', 'xx': "['1', '2']"}
仍然不好处理,改为使用 MultipleChoiceFields

xx = fields.MultipleChoiceField(
choices=models.Classes.objects.values_list('id', 'title')
)

总结:

  单选:CharFields + Select
     ChoiceFields + choices
  多选:MultipleChoice + choices

编辑老师中,实现初始化:

row = models.Teacher.objects.filter(id=nid).first()
class_ids = row.c2t.values_list('id')

得到数据格式为:<QuerySet [(1,), (2,)]>
zip 方法:

因此加上:

id_list = list(zip(*class_ids))[0] if list(zip(*class_ids)) else []
obj = TeacherForm(initial={'tname': row.tname, 'xx': id_list})

这样的到的数据形式为:(1, 2)

代码:
models.py

class Teacher(models.Model):
    tname = models.CharField(max_length=32)
    c2t = models.ManyToManyField('Classes')

views.py

def teacher_list(request):
    tea_list = models.Teacher.objects.all()
    return render(request, 'teacher_list.html', {'tea_list': tea_list})


def add_teacher(request):
    if request.method == "GET":
        obj = TeacherForm()
        return render(request, 'add_teacher.html', {'obj': obj})
    else:
        obj = TeacherForm(request.POST)
        if obj.is_valid():
            print(obj.cleaned_data)
            xx = obj.cleaned_data.pop('xx')
            row = models.Teacher.objects.create(**obj.cleaned_data)
            row.c2t.add(*xx)
            return redirect('/teacher_list/')
        else:
            print(obj.errors)
            return render(request, 'add_teacher.html', {'obj': obj})


def edit_teacher(request, nid):
    if request.method == "GET":
        row = models.Teacher.objects.filter(id=nid).first()
        class_ids = row.c2t.values_list('id')
        id_list = list(zip(*class_ids))[0] if list(zip(*class_ids)) else []
        print(id_list)
        obj = TeacherForm(initial={'tname': row.tname, 'xx': id_list})
        return render(request, 'edit_teacher.html', {'obj': obj, 'nid': nid})
    else:
        pass

teacher_list

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>教师列表</h1>
<div>
    <a href="/add_teacher/">添加</a>
</div>
<table border="1">
    <thead>
    <tr>
        <th>ID</th>
        <th>姓名</th>
        <th>任教班级</th>
        <th>操作</th>
    </tr>
    </thead>
    <tbody>
    {% for row in tea_list %}
        <tr>
            <td>{{ row.id }}</td>
            <td>{{ row.tname }}</td>
            <td>
                {% for item in row.c2t.values %}
                {{ item.title }}
                {% endfor %}
            </td>
            <td><a href="/edit_teacher/{{ row.id }}/">编辑</a> </td>
        </tr>
    {% endfor %}
    </tbody>
</table>
</body>
</html>

add_teacher.html;

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form method="POST" action="/add_teacher/">
    {% csrf_token %}
    <p>
        姓名:{{ obj.tname }}
    </p>
    <p>
        班级:{{ obj.xx }}
    </p>
    <input type="submit" value="提交">
</form>
</body>
</html>

edit_teacher.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<h1>编辑老师</h1>
<form method="POST" action="/edit_teacher/{{ nid }}">
    {% csrf_token %}
    {{ obj.tname }}
    {{ obj.xx }}
</form>
</body>
</html>

数据无法动态显示

TeacherForm 进行了一次对象实例化,生成一个对象,不再更新。导致 Class 中添加了新的班级,增加教师页面的班级选择并未更新。
解决办法:为 TeacherForm 类增加 __init__ 方法

    def __init__(self, *args, **kwargs):
        super(TeacherForm, self).__init__(*args, **kwargs)
        self.fields['xx'].widget.choices = models.Classes.objects.values_list('id', 'title')

插件及样式定制

views.py 中添加样式后传给前端:

class StudentForm(Form):
    name = fields.CharField(min_length=2, max_length=6, widget=widgets.TextInput(attrs={'class': 'form-control'}))
    email = fields.EmailField(widget=widgets.TextInput(attrs={'class': 'form-control'}))
    age = fields.IntegerField(min_value=18, max_value=25, widget=widgets.TextInput(attrs={'class': 'form-control'}))
    cls_id = fields.IntegerField(
        widget=widgets.Select(choices=models.Classes.objects.values_list('id', 'title'),
                              attrs={'class': 'form-control'})
    )

引用 bootstrap:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="/static/plugins/bootstrap-3.4.1-dist/css/bootstrap.css"/>
    <link rel="stylesheet" href="/static/plugins/bootstrap-3.4.1-dist/css/bootstrap.min.css"/>
</head>
<body>
<div style="width: 500px; margin: 0 auto;">
    <h1>编辑学生</h1>
    <form class="form-horizontal" method="POST" action="/edit_student/{{ nid }}/" novalidate>
        {% csrf_token %}
        <div class="form-group">
            <label class="col-sm-2 control-label">姓名:</label>
            <div class="col-sm-10">
                {{ obj.name }} {{ obj.name.errors.0 }}
            </div>
        </div>
        <div class="form-group">
            <label class="col-sm-2 control-label">邮箱:</label>
            <div class="col-sm-10">
                {{ obj.email }} {{ obj.email.errors.0 }}
            </div>
        </div>
        <div class="form-group">
            <label class="col-sm-2 control-label">年龄:</label>
            <div class="col-sm-10">
                {{ obj.age }} {{ obj.age.errors.0 }}
            </div>
        </div>
        <div class="form-group">
            <label class="col-sm-2 control-label">班级:</label>
            <div class="col-sm-10">
                {{ obj.cls_id }} {{ obj.cls_id.errors.0 }}
            </div>
        </div>
        <div class="form-group">
            <div class="col-sm-offset-2 col-sm-10">
                <button type="submit" class="btn btn-default">提交</button>
            </div>
        </div>
    </form>
</div>
</body>
</html>

出现如下提示,是浏览器自带的验证功能,给 form 加上 novalidate 即可阻止:

常用插件

  • checkBox
  • radio
  • iput
  • textarea
  • File

验证扩展

在原有验证规则基础上添加其他正则,会逐个进行验证:

from django.core.validators import RegexValidator
class MyForm(Form):
    user = fields.CharField(
        validators=[
            RegexValidator(r'^[0-9]+$', '请输入数字'), 
            RegexValidator(r'^159[0-9]+$', '数字必须以159开头')
        ]
    )

总结

  1. 使用
    class Foo:
      xx = xxxx() # 正则,插件

      def clean_xx():
        …

      def clean():
        pass
  2. 页面展示
    obj = Foo()

    # 灵活
      <form>
        {{ obj.xx }}
        {{ obj.xx }}
        …
      <form>
    # 简单
      {{ obj.as_p }}
      <ul>
        {{ obj.as_ul }}
      </ul>
      <table>
        {{ obj.as_table }}
      </table>
  3. 后台
    is_valid()
    clean_data
    errors

补充:文件上传

方式一:

import os
from django.core.files.uploadedfile import InMemoryUploadedFile
def f1(request):
    if request.method == "GET":
        return render(request, 'f1.html')
    else:
        file_obj = request.FILES.get('fafafa')
        print(file_obj.name)
        print(file_obj.size)
        f = open(os.path.join('static', file_obj.name), 'wb')
        for chunk in file_obj.chunks():
            f.write(chunk)
        f.close()
        return render(request, 'f1.html')

f1.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="POST" action="/f1/" enctype="multipart/form-data">
        {% csrf_token %}
        <input type="file" name="fafafa">
        <input type="submit" value="提交">
    </form>
</body>
</html>

方式二:

class F2Form(Form):
    user = fields.CharField()
    fafafa = fields.FileField()


def f2(request):
    if request.method == "GET":
        obj = F2Form()
        return render(request, 'f2.html', {'obj': obj})
    else:
        obj = F2Form(data=request.POST, files=request.FILES)
        if obj.is_valid():
            print(obj.cleaned_data.get('fafafa').name)
            print(obj.cleaned_data.get('fafafa').size)
        return render(request, 'f2.html', {'obj': obj})

f2.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <form method="POST" action="/f2/" enctype="multipart/form-data">
        {% csrf_token %}
        {{ obj.user }}
        {{ obj.fafafa }}
        <input type="submit" value="提交">
    </form>
</body>
</html>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值