我们前面都是手工在HTML文件中编写表单form元素,然后在views.py的视图函数中接收表单中的用户数据,再编写验证代码进行验证,最后使用ORM进行数据库的增删改查。这样费时费力,整个过程比较复杂,而且有可能写得不太恰当,数据验证也比较麻烦。设想一下,如果我们的表单拥有几十上百个数据字段,有不同的数据特点,如果也使用手工的方式,其效率和正确性都将无法得到保障。有鉴于此,Django在内部集成了一个表单功能,以面向对象的方式,直接使用Python代码生成HTML表单代码,专门帮助我们快速处理表单相关的内容。
Django的表单给我们提供了下面三个主要功能:
编写Django的form表单,非常类似我们在模型系统里编写一个模型。在模型中,一个字段代表数据表的一列,而form表单中的一个字段代表<form>
中的一个<input>
元素。
在项目根目录的login文件夹下,新建一个forms.py
文件,也就是/login/forms.py
,又是我们熟悉的Django组织文件的套路,一个app一套班子!
在/login/forms.py
中写入下面的代码,是不是有一种编写数据model模型的既视感?
from django import forms class UserForm(forms.Form): username = forms.CharField(label="用户名", max_length=128) password = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput)
说明:
<form>
内的一个input元素。这一点和Django模型系统的设计非常相似。<label>
标签max_length
限制字段输入的最大长度。它同时起到两个作用,一是在浏览器页面限制用户输入不可超过字符数,二是在后端服务器验证用户输入的长度也不可超过。widget=forms.PasswordInput
用于指定该字段在form表单里表现为<input type='password' />
,也就是密码输入框。使用了Django的表单后,就要在视图中进行相应的修改:
# login/views.py from django.shortcuts import render from django.shortcuts import redirect from . import models from . import forms # Create your views here. def index(request): pass return render(request, 'login/index.html') def login(request): if request.method == 'POST': login_form = forms.UserForm(request.POST) message = '请检查填写的内容!' if login_form.is_valid(): username = login_form.cleaned_data.get('username') password = login_form.cleaned_data.get('password') try: user = models.User.objects.get(name=username) except : message = '用户不存在!' return render(request, 'login/login.html', locals()) if user.password == password: return redirect('/index/') else: message = '密码不正确!' return render(request, 'login/login.html', locals()) else: return render(request, 'login/login.html', locals()) login_form = forms.UserForm() return render(request, 'login/login.html', locals()) def register(request): pass return render(request, 'login/register.html') def logout(request): pass return redirect("/login/")
说明:
from . import forms
is_valid()
方法一步完成数据验证工作;cleaned_data
数据字典中获取表单的具体值;另外,这里使用了一个小技巧,Python内置了一个locals()函数,它返回当前所有的本地变量字典,我们可以偷懒的将这作为render函数的数据字典参数值,就不用费劲去构造一个形如{'message':message, 'login_form':login_form}
的字典了。这样做的好处当然是大大方便了我们,但是同时也可能往模板传入了一些多余的变量数据,造成数据冗余降低效率。
Django的表单很重要的一个功能就是自动生成HTML的form表单内容。现在,我们需要修改一下原来的login.html
文件:
{% load static %} <!doctype html> <html lang="en"> <head> <!-- Required meta tags --> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <!-- 上述meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! --> <!-- Bootstrap CSS --> <link href="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/css/bootstrap.min.css" rel="stylesheet"> <link href="{% static 'login/css/login.css' %}" rel="stylesheet"/> <title>登录</title> </head> <body> <div class="container"> <div class="col"> <form class="form-login" action="/login/" method="post"> {% if message %} <div class="alert alert-warning">{{ message }}</div> {% endif %} {% csrf_token %} <h3 class="text-center">欢迎登录</h3> {{ login_form }} <div> <a href="/register/" class="text-success " ><ins>新用户注册</ins></a> <button type="submit" class="btn btn-primary float-right">登录</button> </div> </form> </div> </div> <!-- /container --> <!-- Optional JavaScript --> <!-- jQuery first, then Popper.js, then Bootstrap JS --> {# 以下三者的引用顺序是固定的#} <script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.js"></script> <script src="https://cdn.bootcss.com/popper.js/1.15.0/umd/popper.js"></script> <script src="https://cdn.bootcss.com/twitter-bootstrap/4.3.1/js/bootstrap.min.js"></script> </body> </html>
上面贴了一个完整版的代码,方便大家对比参考。
说明:
{{ login_form }}
就直接完成了表单内容的生成工作!login_form
这个名称来自你在视图函数中生成的form实例的变量名!<form>...</form>
标签,这个要自己写;{% csrf_token %}
标签,用于处理csrf安全机制;我们到浏览器中,看下实际生成的html源码是什么:
<form class="form-login" action="/login/" method="post"> <input type="hidden" name="csrfmiddlewaretoken" value="5oJMX0z8PkUXY7RPDPGjaD2Q28CndXKeKWlftJD6s0XM1NIUEi7a0iET1NCYikUw"> <h3 class="text-center">欢迎登录</h3> <tr><th><label for="id_username">用户名:</label></th><td><input type="text" name="username" maxlength="128" required id="id_username"></td></tr> <tr><th><label for="id_password">密码:</label></th><td><input type="password" name="password" maxlength="256" required id="id_password"></td></tr> <div> <a href="[/register/](http://127.0.0.1:8000/register/)" class="text-success " ><ins>新用户注册</ins></a> <button type="submit" class="btn btn-primary float-right">登录</button> </div> </form>
也就是说,Django的form表单功能,帮你自动生成了下面部分的代码:
<tr><th><label for="id_username">用户名:</label></th><td><input type="text" name="username" maxlength="128" required id="id_username"></td></tr> <tr><th><label for="id_password">密码:</label></th><td><input type="password" name="password" maxlength="256" required id="id_password"></td></tr>
这看起来好像一个<table>
标签啊?没错,就是<table>
标签,而且是不带<table></table>
的,捂脸!
实际上除了通过{{ login_form }}
简单地将表单渲染到HTML页面中了,还有下面几种方式:
{{ login_form.as_table }}
将表单渲染成一个表格元素,每个输入框作为一个<tr>
标签{{ login_form.as_p }}
将表单的每个输入框包裹在一个<p>
标签内{{ login_form.as_ul }}
将表单渲染成一个列表元素,每个输入框作为一个<li>
标签注意:上面的渲染方法中都要自己手动编写<table>
或者<ul>
标签。
重新启动服务器,刷新页面,如下图所示:
直接{{ login_form }}
虽然好,啥都不用操心,但是界面真的很丑,并且我们先前使用的Bootstraps4都没了。因为这些都需要对表单内的input元素进行额外控制,那怎么办呢?手动渲染字段就可以了!
可以通过{{ login_form.name_of_field }}
获取每一个字段,然后分别渲染,如下例所示:
<div class="form-group"> {{ login_form.username.label_tag }} {{ login_form.username}} </div> <div class="form-group"> {{ login_form.password.label_tag }} {{ login_form.password }} </div>
其中的label标签可以用label_tag
方法来生成。这样子更加灵活了,但是灵活的代价就是我们要写更多的代码,又偏向原生的HTML代码多了一点。
但是问题又...又...又来了!刷新登录页面,却是下图的样子:
好像Bootstrap4没有生效呀!仔细查看最终生成的页面源码,你会发现,input元素里少了form-control
的class,以及placeholder和autofocus,这可咋办?
在form类里添加attr属性即可,如下所示修改login/forms.py
from django import forms class UserForm(forms.Form): username = forms.CharField(label="用户名", max_length=128, widget=forms.TextInput(attrs={'class': 'form-control', 'placeholder': "Username",'autofocus': ''})) password = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput(attrs={'class': 'form-control','placeholder': "Password"}))
再次刷新页面,我们熟悉的Bootstrap4框架UI又回来了!
实际上,Django针对{{ login_form }}
表单,提供了很多灵活的模板语法,可以循环,可以取值,可以针对可见和不可见的部分单独控制,详细内容可以参考教程前面的章节。
"Django自动为每个input元素设置了一个id名称,对应label的for参数" 这里是手误,应该是“对应label的form参数”?
有个小坑,这节如果使用ModelForm关联User类的话,在表单验证时用户名字段始终无法通过验证,估计是因为模型类name字段定义了unique=True,解决方法是直接使用POST字典获取字段并手动验证
请问,form可以修改input的类,但是要怎么修改label的类呢
label属于说明文字,一般不做太多自定义。
不太喜欢Django提供的form
我也不喜欢,笑哭...
我也是好讨厌这个
二、修改视图 使用了Django的表单后,就要在视图中进行相应的修改: # login/views.py from django.shortcuts import render from django.shortcuts import redirect from . import models from . import forms # Create your views here. def index(request): pass return render(request, 'login/index.html') def login(request): if request.method == 'POST': login_form = forms.UserForm(request.POST) message = '请检查填写的内容!' if login_form.is_valid(): ###这的is_valid是属性还是方法呀,有括号会报错的,去掉括号就正常。 username = login_form.cleaned_data.get('username') password = login_form.cleaned_data.get('password')
遇到报错“'login_form' object has no attribute 'cleaned_data'”,就去掉原文 if login_form.is_valid():的括号就好了。
刘老师,请问下,我首次今天login页面,form表单不显示,点击登录按钮后就显示了,
def login(request): login_form = forms.UserForm() # userform要先实例化, if request.method == "POST": login_form = forms.UserForm(request.POST)
两个label :用户名和密码没有显示,不知道为啥???
按照上面的代码修改了完后,不显示用户名,密码的输入框啊,应该是表单传输失败了,有解决了的吗?
我也是额 你解决了吗
是在 return中 少了一个 local()
请问你怎么解决的呀
步骤到这里,登录功能不行了,点击登录,就单纯刷新而已,页面上一直提示那个message消息:请检查填写的内容!。。。。没有跳转到首页,见了鬼了,到底是怎么回事 @csrf_exempt def login(request): if request.session.get('is_login', None): return redirect("/index/") if request.method == "POST": login_form = forms.UserForm(request.POST) message = "请检查填写的内容!" if login_form.is_valid(): username = login_form.cleaned_data['username'] password = login_form.cleaned_data['password'] try: user = User.objects.get(name=username) if user.password == password: request.session['is_login'] = True request.session['user_id'] = user.id request.session['user_name'] = user.name return redirect('/index/') else: message = "密码不正确!" except: message = "用户不存在!" return render(request, 'main/login.html', locals()) login_form = forms.UserForm() return render(request, 'main/login.html', locals()) ----------------------------------------- <form class='form-login' action="/login/" method="post"> {% if message %} <div class="alert alert-warning">{{ message }}</div> {% endif %} {% csrf_token %} <h2 class="text-center">欢迎登录</h2> {# <div class="form-group">#} {# <label for="id_username">用户名:</label>#} {# <input type="text" name='username' class="form-control" id="id_username" placeholder="Username" autofocus required>#} {# </div>#} {# <div class="form-group">#} {# <label for="id_password">密码:</label>#} {# <input type="password" name='password' class="form-control" id="id_password" placeholder="Password" required>#} {# </div>#} <div class="form-group"> {{ login_form.username.label_tag }} {{ login_form.username}} </div> <div class="form-group"> {{ login_form.password.label_tag }} {{ login_form.password }} </div> {# <div class="form-group">#} {# {{ login_form.captcha.errors }}#} {# {{ login_form.captcha.label_tag }}#} {# {{ login_form.captcha }}#} {# </div>#} <button type="reset" class="btn btn-default pull-left">重置</button> <button type="submit" class="btn btn-primary pull-right">提交</button> </form> </div> </div> <!-- /container -->
解决了吗 我也有这个问题
username = forms.CharField(label='用户名', max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'})); username = forms.CharField(label='用户名:', max_length=128, widget=forms.TextInput(attrs={'class': 'form-control'})); 如果写为label='用户名',页面不会显示label,就只有输入框,用户名后面接":",template就会有。password也是一样。 并且我的现实方式也和上一章的一样,是label和input是上下结构,不是这章的左右结构。
换成Django表单后焦点没了,光标不能直接定位到username上了
该问题在启动时没有报错。问题是数据没有通过验证就可以直接登陆,而且message中的提示信息也没有显示出来,推断是post的表单出了问题,但是一直没有找到到底是哪里出了问题,就算复制过来用也还是不行。
后面的程序写全就有了。
NameError at /login/ name 'forms' is not defined
class UserForm(forms.ModelForm): class Meta: model = User fields = ["name", "password",] labels = {"name": "用户名", "password": "密码"}
在 login.html 文件中,用{{ login_form }} 替换了原来的// <tr><th><label for="id_username">用户名:</label></th><td><input type="text" name="username" value="jack" maxlength="128" required id="id_username" /></td></tr> <tr><th><label for="id_password">密码:</label></th><td><input type="password" name="password" maxlength="256" required id="id_password" /></td></tr>// 这两行生成填写用户名跟密码的表单后,没有内容。 也没有报错 ?
[13/Jul/2018 15:59:48] "GET /login/ HTTP/1.1" 200 2927 [13/Jul/2018 15:59:48] "GET /static/css/login.css HTTP/1.1" 304 0 [13/Jul/2018 15:59:48] "GET /login/js/bootstrap.min.js HTTP/1.1" 200 2927
views文件中login函数返回少了一个locals() 方法。
不知道怎么办,求老师给看一下 错误: Traceback (most recent call last): File "C:/Users/abc/PycharmProjects/demo1/books/views.py", line 6, in <module> from books.models import User File "C:\Users\abc\PycharmProjects\demo1\books\models.py", line 13, in <module> class User(models.Model): File "C:\Users\abc\AppData\Local\Programs\Python\Python36\lib\site-packages\django\db\models\base.py", line 100, in __new__ app_config = apps.get_containing_app_config(module) File "C:\Users\abc\AppData\Local\Programs\Python\Python36\lib\site-packages\django\apps\registry.py", line 244, in get_containing_app_config self.check_apps_ready() File "C:\Users\abc\AppData\Local\Programs\Python\Python36\lib\site-packages\django\apps\registry.py", line 127, in check_apps_ready raise AppRegistryNotReady("Apps aren't loaded yet.") django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.
class UserForm(forms.Form): username = forms.CharField(label="用户名", max_length=128) password = forms.CharField(label="密码", max_length=256, widget=forms.PasswordInput) 没有 class 你的css 是以 class来加样式的
module 'django.forms' has no attribute 'UserForm',是怎么回事
module 'django.forms' has no attribute 'UserForm'
from login import forms
改过还是不行,能问一下怎么解决的吗
from . import models
我是forms.py文件名错了,错写成form.py,少了个字母s。把文件名改过来就好了。 就这个小错误,查了一下午时间也没查出来,都快崩溃了,第二天早上,仔细阅读原文,突然发现问题所在。是昨天下午太心急,没仔细阅读原文。
像文中代码<form action="/login/"... 这个/login/表示的是什么意思呢?是指URL?
是的
使用forms.ModelForm渲染表单元素,name字段没有被渲染。我加了其他字段,也只有password字段渲染出来,这是什么情况?
用{{login_form.user.lable.tag}}的时候,lable标签的class属性也丢失了,这样只能在外链样式表里修改吗?我想在forms表单模型里修改可以吗?
{{ login_form }} 贴了这个 提交和重置不好用了 不知道问题出在哪
找到问题了
在 froms 中加 calss 样式,