Web框架开发-Django-form组件
django 之知识点总结以及Form组件
一、model常用操作
1、13个API查询:all,filter,get ,values,values_list,distinct,order_by ,reverse , exclude(排除),count,first,last,esits(判断是否存在)
需要掌握的all、values、values_list
- all:打印的是一个QuerySet集合,一个列表里面放的对象
- values:是一个字典形式
- values_list:是一个元组形式
all的性能是最低的
2、only和defer
datalist = models.Userinfo.objects.all().only("name","email") #拿到的还是一个QuerySet集合,仅仅取name和email for item in datalist: print(item.id) print(item.name) print(item.pwd) #只要表里有这个字段,一样会取到值,额外的会再发一次请求 datalist = models.Userinfo.objects.all().defer("name","email") #阻止,不取name和email for item in datalist: print(item.id) print(item.pwd)
注意:用only的话就去取only里面的字段,取其它的字段效率太低了,尽可能的少的连接数据库
3、路由系统
反向生成URL:
有两种方式:{% url “a1” %}
reverse(“a1”)
用reverse需要导入: from django.core.urlresolvers import reverse
/index/ func name=a1 {% url "a1"} reverse('a1') /index/(\d+)/ func name=a2 {% url "a2" 11 %} reverse('a2',args=(11,)) /index/(?P<nid>\d+)/ func name=a3 {% url "a2" nid=11 %} reverse('a3',kwargs={'nid':11})
def index(request): if request.method == "POST": """ 两个return是一样的,用url反向解析就相当于下面的路径在urls里面 协商别名name="index",但在模板中还是要用{%url"index"%} """ # return redirect(reverse(index)) return redirect("index.html") # 跳转到个人主页 return render(request, "hh.html")
4、Django的生命周期
web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)
- 首先走wsgi模块,这个模块也是一个协议,包括wsgiref和uwsgi
- 然后路由分配---------views视图
- 从数据库取数据--------渲染到HTML
二、form组件
一、Form组件介绍
Form组件可以做的几件事情:
1、用户请求数据验证
2、自动生成错误信息
3、打包用户提交的正确信息
4、如果其中有一个错误了,其他的正确这,保留上次输入的内容
4、自动创建input标签并可以设置样式
二、Form组件的使用
1、创建规则
class Foo(Form) # 必须继承 username = XXX password = XXX email = XXX 注意这里的字段必须和input字段一致
2、数据和规则进行匹配
先导入view.py
from django.forms import Form from django.forms import fields from django.forms import widgets
# 1、创建规则 class TeacherForm(Form): # 必须继承form # 创建字段,本质上是正则表达式 username = fields.CharField( required=True, # 必填字段 error_messages={"required": "用户名不能为空!"}, widget=widgets.TextInput(attrs={"placeholder": "用户名","class": "form-control"}) # 自动生成input框 ) password = fields.CharField( required=True, error_messages={'required': '密码不能为空!'}, widget=widgets.TextInput(attrs={'placeholder': '密码', 'class': 'form-control'}) ) email = fields.EmailField( required=True, error_messages={"requeired": "邮箱不能为空!", "invalid": "无效的邮箱格式"}, widget=widgets.EmailInput(attrs={"placeholder":"邮箱", "class": "form-control"}) ) # 2、使用规则:将数据和规则进行匹配 def teacherindex(request): teacher_obj = models.UserInfo.objects.all() print(teacher_obj) return render(request, "teacherindex.html", {"teacher_obj": teacher_obj}) def add(request): if request.method == "GET": form = TeacherForm() # 只是显示一个input框 return render(request, "add.html", {"form": form}) else: form = TeacherForm(data=request.POST) if form.is_valid(): # 开始验证 form.cleaned_data['ut_id'] = 1 # 要分的清是班主任还是讲师 models.UserInfo.objects.all().create(**form.cleaned_data) return redirect("/teacherindex/") else: return render(request, "add.html", {"form": form})
add.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加老师信息</title> </head> <body> <form method="post" novalidate> {% csrf_token %} <p>姓名:{{ form.username }}</p>{{ form.errors.username.0 }} <p>密码:{{ form.password }}</p>{{ form.errors.password.0 }} <p>邮箱:{{ form.email }}</p>{{ form.errors.email.0 }} <p><input type="submit" value="提交"></p> </form> </body> </html>
teacherindex.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div class="container"> <div class="row"> <div class="col-md-8"> <table class="table-bordered table-striped"> <thead> <tr> <th>编号</th> <th>姓名</th> <th>邮箱</th> </tr> </thead> <tbody> {% for teacher in teacher_obj %} <tr> <td>{{ teacher.id }}</td> <td>{{ teacher.username }}</td> <td>{{ teacher.email }}</td> </tr> {% endfor %} </tbody> </table> </div> </div> </div> </body> </html>
如果访问视图的是一个GET
请求,它将创建一个空的表单实例并将它放置到要渲染的模板的上下文中。这是我们在第一个访问该URL 时预期发生的情况。
如果表单的提交使用POST
请求,那么视图将再次创建一个表单实例并使用请求中的数据填充它:form = NameForm(request.POST)
。这叫做”绑定数据至表单“(它现在是一个绑定的表单)。
我们调用表单的is_valid()
方法;如果它不为True
,我们将带着这个表单返回到模板。这时表单不再为空(未绑定),所以HTML 表单将用之前提交的数据填充,然后可以根据要求编辑并改正它。
如果is_valid()
为True
,我们将能够在cleaned_data
属性中找到所有合法的表单数据。在发送HTTP 重定向给浏览器告诉它下一步的去向之前,我们可以用这个数据来更新数据库或者做其它处理。
备注: form = TeacherForm() #没有参数,只是一个input框
form = TeacherForm(data=request.POST) # 数据和规则放置一起 (添加的时候用)
form = TeacherForm(initial={'username':obj.username,'password':obj.password,'email':obj.email}) # 显示input,并且将数据库中的默认值填写到input框中 (编辑的时候用)
Widgets
每个表单字段都有一个对应的Widget
类,它对应一个HTML 表单Widget
,例如<input type="text">
。
在大部分情况下,字段都具有一个合理的默认Widget。例如,默认情况下,CharField
具有一个TextInput Widget
,它在HTML 中生成一个<input type="text">
。
字段的数据
不管表单提交的是什么数据,一旦通过调用is_valid()
成功验证(is_valid()
返回True
),验证后的表单数据将位于form.cleaned_data
字典中。这些数据已经为你转换好为Python 的类型。
注:此时,你依然可以从request.POST
中直接访问到未验证的数据,但是访问验证后的数据更好一些。
在上面的联系表单示例中,is_married将是一个布尔值。类似地,IntegerField
和FloatField
字段分别将值转换为Python 的int
和float
。
三、数据库表设计
设计表时注意的几点:
1、 nid = models.AutoField(primary_key=True) #如果不指定django会默认加上id的
nid = models.BigAutoField(primary_key=True) #但那些整型满足不了你的时候,就用BigAutoField
2、对于类的注释一般加在类里面
3、verbose_name=“标题” 字段的中文提示
4、ForeignKey(to = "表名",tofield= "字段") #这两个to可以不用写,但是关联的表名一定要写
5、releated_name = "uuu" 反向查询。如果一个表中有多个ManyTwoMany()或者ForeignKey()必须加上releated_name
6、字段经常变动的适合连表
字段变化小,不怎么变的适合在一个表中,不进行连表:就用choices
吧班主任和老师可以放到一个表中、因为他们有相同的属性,如果属性全是一样的,可以放在一个表里(推荐)
也可以分开放,老师表是老师表,班主任表是班主任表。这样就会进行连表操作,连表有性能消耗。
举例:文章和文章类型
分析:一个文章有一个类型,一个类型可以对应多个文章(所以文章和文章类型是一对多的关系,关联字段要放在多的一方)
一:连表设计:
class News(models.Model): title = models.CharField(max_length=32) summary = models.CharField(max_length=255) news_type = models.ForeignKey(to="NewsType") class NewsType(models.Model): type_title = models.CharField(max_length=32) News: id title summary news_type_id t.... 科技... 2 t.... 科技... 1 t.... 科技... 2 NewsType: id title 图片 挨踢1024 段子 # 查看所有新闻 new_list = models.News.objects.all() for row in new_list: print(row.title,row.summary,row.news_type.title)
二 :放在一个表中的操作:choices
class News2(models.Model): title = models.CharField(max_length=32) summary = models.CharField(max_length=255) news_type_chices = ( (1, '图片'), (4, '挨踢1024'), (3, '段子'), ) news_type = models.IntegerField(choices=news_type_chices) # 查看所有新闻 new_list = News.objects.all() for row in new_list: print(row.title,row.summary, row.get_news_type_display() )
举例二:用户和用户类型
一:连表设计
class UserType(models.Model): """ 用户类型表,个数经常变动 """ title = models.CharField(max_length=32) class UserInfo(models.Model): """ 用户表:讲师和班主任 """ username = models.CharField(max_length=32) password = models.CharField(max_length=64) email = models.CharField(max_length=32) ut = models.ForeignKey(to="UserType")
二:不连表设计:choices
class UserInfo(models.Model): # """ # 用户表 # """ username = models.CharField(max_length=32) password = models.CharField(max_length=64) email = models.CharField(max_length=32,verbose_name="邮箱") user_type_choices = ( (1, '班主任'), (2, '讲师'), ) user_type_id = models.IntegerField(choices=user_type_choices)
四、登录
可设置一个装饰器
def auth(func): def inner(request,*args,**kwargs): is_login = request.session.get("is_login", None) if not is_login: return redirect("/login/") ret = func(*args,**kwargs) return ret return inner
需要注意的:
1、action不写路径,默认提交到当前
2、向后台提交数据用post,获取数据用get
3、submit一般加上value,有些浏览器可能会不识别
4、一般配置文件的键都是大写的
五、Form基本使用
类 字段 is_valid() cleaned_data errors 字段参数: max_length min_length validators = [RegexValidators("XXX")] 钩子函数: clean_字段名 注意: 必须有返回值 只能拿自己当前字段值 raise ValidationError("XXX") 下拉框数据源时时更新: 1、重写init方法 先执行父类构造方法 self.fields["xx"].choices = XXXX 2、ModelChoiceField
用户登录
- form的字段可以定义正则表达式 password = fields.CharField( required=True, min_length=3, max_length=18, error_messages={ 'required': '密码不能为空', 'min_length': '密码长度不能小于3', 'max_length': '密码长度不能大于18', 'invalid': '密码格式错误', }, validators=[RegexValidator('\d+','只能是数字') ] ) 注意:error_messages的优先级比validators高
class LoginForm(Form): username = fields.CharField( required=True, #必填字段 min_length=3, max_length=16, error_messages={ "required":"用户名不能为空", "min_length":"长度不能小于3", "max_length":"长度不能大于16" }, widget=widgets.TextInput({"placeholder":"username","class":"form-control"}) ) password = fields.CharField( required=True, min_length=3, max_length=16, error_messages={ "required": "密码不能为空", "min_length": "密码长度不能小于3", "max_length": "密码长度不能大于16", # "invalid":"密码格式错误" # error_messages的优先级高,如果写上"invalid":"密码格式错误"这个就会优先显示这个错误 }, widget=widgets.PasswordInput({"placeholder":"password","class":"form-control"}), validators=[RegexValidator("\d+","密码只能是数字")] #可以进行正则匹配提示错误 ) def clean_username(self): user = self.cleaned_data["username"] is_exits = models.UserInfo.objects.filter(username=user).count() if not is_exits: raise ValidationError("用户名和密码错误") return user #必须有return
- 主动向form中添加错误信息
# form.add_error('password','用户名或密码错误')
form.add_error('password',ValidationError('用户名或密码错误'))
这两个都可以,建议用第二个
Form扩展(钩子函数)
如果对username做扩展
#先做正则表达式判断
#然后自定义方法验证:也就是clean_xx,称为钩子函数
def clean_username(self): #可以写自己的验证提示 不像validators只写正则表达式。在这里可以随意写 user=self.clean_data["username"] is_esits = models.UserInfo.objects.filter(username=user).count() if not is_esits: raise validationError("用户名不存在") return user #必须有返回值 如果 def clean_username(self): 只能取password字段的值 如果 def clean_username(self): 只能取username字段的值 注意:在自己写钩子函数的时候,只能拿自己的字段不能拿别人的 每一种字段就可以用 正则+自定义正则+自定义钩子函数