第六章Django
web应用程序
server端建立socket,不断地accept,当收到客户端连接信号之后,服务端向客户端发送数据,将html网页打开,read出来,并发送至客户端,这样客户端就可以浏览到网页的内容
http协议:
包头里面的数据用 一个“\r\n”来区分,最后使用两个“\r\n”来区分数据
GET协议把数据放在了包头里面,POST协议把数据放在包头的最后面
GET提交的数据以“?”区分,参数之间以“&”进行连接
相应格式:
协议版本 状态码 状态码的原因短语 相应首部字段 主体
web框架 yuan功能总结
main.py: 启动文件,封装了socket
1 urls.py: 路径与视图函数映射关系 ---- url控制器
2 views.py 视图函数,固定有一个形式参数:environ -----视图函数,
3 templates文件夹: html文件 -----模板
4 models: 在项目启动前,在数据库中创建表结构 ----- 与数据库相关
Django的安装与启动
创建应用:python manage.py startapp blog
urlpatterns = [
re_path(r'^articles/2003/$', views.special_case_2003),
re_path(r'^articles/([0-9]{4})/$', views.year_archive),
re_path(r'^articles/([0-9]{4})/([0-9]{2})/$', views.month_archive),
re_path(r'^articles/([0-9]{4})/([0-9]{2})/([0-9]+)/$', views.article_detail),
]
有名分组:在组前面加上?P<组名> 即可,在传参时的形参必须是“组名”,否则会出错
命名空间:namespace
app01.urls:
需要加上
app_name = "app01"
urlpatterns=[
re_path(r'^index/',views.index,name="index"),
]
同理在ap02.urls加上相同的配置
views.py:
def index(request):
return HttpResponse(reverse("app01:index"))
需要加上反向解析
注册自定义转换器
先建一个类
class FourDigitYearConverter:
regex = '[0-9]{2}' #正则表达式的公式
def to_python(self,value):
return int(value)
def to_url(self,value):
return '%04d' % value
在项目中注册转换器
# 注册定义的url转换器
register_converter(FourDigitYearConverter,'mm') #FourDigitYearConverter : 为以上类名
之后在urls里可以直接path使用
path('articles/<mm:month>/',views.month_archive)
模板语法 只有两个
1.{{}}
可以在
return render(request,'index.html',locals()) 这样就把该函数里的全部全局变量都传过去了
同时在index.html可以通过 . 来过去数据 如一个数组l=【‘123’,‘456’,‘789’】 ,在html可以通过l.2 获取到数据456
2{%%}
{%if%}
{%endif%}
自定义标签与过滤器
1.在settings的INSTALLED_APPS加上当前app的名称,不然django无法找到自定义的simple_tag.
2.在app文件夹里面添加templatetags模块(模块名只能是templatetags)
3.在templatetags里面创建任意py文件
4.在该py文件里面引入
from django import template
register = template.Library() # 固定名字
@register.filter #加上装饰器,这个为模板过滤器
def filter_multi(x,y):
return x*y
@register.simple_tag #标签过滤器
def filter_tag(x,y):
return x*y
5.在html文件里面读取该过滤器
{%load py的文件名%}
6.使用
{{ 2 | filter_multi:5 }}
{% filter_tag 5 6 %}
继承:extends
在一个base.html 里面把模板写好
在另一个html里面引入base.html
{% extends "base.html" %}
这时候可以在下面继续添加block里面的内容
{{% block title %}
<title>第一个</title>
{% endblock title%}
模型层ORM
ORM 对象-关系-映射
通过编写ORM层,控制pysql转换成db语句,再去控制db数据库。py文件里面通过创建一个类
生成表模型
1.创建模型
from django.db import models # Create your models here. class Book(models.Model): id = models.AutoField(primary_key=True) title=models.CharField(max_length=32) state=models.BooleanField() pub_date=models.DateField() price=models.DecimalField(max_digits=8,decimal_places=2) publish=models.CharField(max_length=32)
创建一个app,然后在app项目文件下的models下创建类
2.配置链接数据库settings
在项目文件下的settings文件设置下,设置数据库信息
DATABASES = { 'default':{ 'ENGINE':'django.db.backends.mysql', 'NAME':'bms', #创建数据库名字 'USER':'root', #链接数据库用户名 'PASSWORD':'', #链接数据库密码 "HOST":'127.0.0.1', #目标数据库地址 'PORT':3306 #目标数据库端口 } }
3.确认信息
①在项目app文件下的__init__插入代码:
import pymysql pymysql.install_as_MySQLdb()
②确保配置文件中的INSTALLED_APPS中写入我们创建的app名称
如在installed_apps 下添加app文件名
最后,可以进行数据迁移
python manage.py makemigrations python manage.py migrate
数据库的建立create:
方式一:
book_obj = Book.objects.create(title='第一个数据库',state=True,price=500,pub_date="2012-12-12",publish='人民出版社') #里面的数据格式需要与建立时的数据格式一致
方式二:
book_obj = Book(title='方式二',publish='机械出版社',state=True,pub_date="2010-12-11",price=10.12)
book_obj.save() #需要多一个save
查询表:
Django 自己生成的一个数据对象:QuerySet: [obj1,obj2,obj3....]
可以通过语句Book.object ---> 生成QuerySet数据
Book.object.all() 可以读取所有的数据
values方法: 返回和调用都是QuerySet
book_obj = Book.objects.values('price')
相当于
list = []
for obj in QuerySet:
list.append{'price':obj['price']}
.... #如果在values后面加值,就继续append
values_list方法: 返回和调用都是QuerySet
book_obj = Book.objects.values_list('price')
distinct:去重 从返回结果中剔除重复纪录
表的查询
一对多的查询:
A---B
如果关联表在A表中:
A----->B :正向查询
反过来通过B去查A 则是反向查询,主要看关联表在哪张表里面
正向查询:看字段
反向查询:看表名:表名小写__set.all()
from django.db import models # Create your models here. class Author(models.Model): nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) age=models.IntegerField() # 与AuthorDetail建立一对一的关系, on_delete must add after Django 2.0 when use foreign Key authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE) class AuthorDetail(models.Model): nid = models.AutoField(primary_key=True) birthday=models.DateField() telephone=models.BigIntegerField() addr=models.CharField( max_length=64) class Publish(models.Model): nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) city=models.CharField( max_length=32) email=models.EmailField() class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField( max_length=32) publishDate=models.DateField() price=models.DecimalField(max_digits=5,decimal_places=2) # 与Publish建立一对多的关系,外键字段建立在多的一方 publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE) # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表 authors=models.ManyToManyField(to='Author',)
聚合与分组查询
关键字:annotate
总结 跨表的分组查询的模型:
# 每一个后表模型.objects.values("pk").annotate(聚合函数(关联表__统计字段))
AJAX
特点:
1.使用JavaScript向服务器发送异步请求
2.局部刷新
$(".Ajax").click(function () {
$.ajax({
url:"/test_ajax/", //请求地址
type:"get", //请求的类型poet或者是get
success:function (data) {
$(".haha").html(data)
console.log(data)
}
})
})
数据之间的传输 通过使用json序列化
在py里面使用json.dump序列化
在Ajax里面使用JSON.parse 反序列化进行解析
利用Form表单进行文件上传
def file_put(request): if request.method=="POST": print('123') print(request.POST) print(request.FILES) file_obj = request.FILES.get("tou") with open(file_obj.name,'wb') as f: for i in file_obj: f.write(i) return HttpResponse('OK') return render(request, "file_put.html")
批量导入数据库的数据:
''' 批量导入数据: Booklist=[] for i in range(100): book = Book(user="book_%s"%i,pwd=i*i) Booklist.append(book) Book.objects.bulk_create(Booklist) '''
分页器:
book_list = Book.objects.all() paginator = Paginator(book_list,10) print("count:", paginator.count) # 数据总数 print("num_pages", paginator.num_pages) # 总页数 print("page_range", paginator.page_range) # 页码的列表 page1=paginator.page(4) #第4页的page对象数据 for i in page1: #遍历 print(i) print(page1.object_list) #直接拿出该page的所有对象
#捕捉EmptyPage错误信息时做的处理
try:
current_page = int(request.GET.get("page",1))
page1 = paginator.page(current_page)
except EmptyPage as e:
page1 = paginator.page(1)
print(page2.has_next()) #是否有下一页 print(page2.next_page_number()) #下一页的页码 print(page2.has_previous()) #是否有上一页 print(page2.previous_page_number()) #上一页的页码
form组件
form.is_valid() #:返回布尔值 form.cleaned_data #:{"name":"yuan","email":"123@qq.com"} 返回匹配成功的字典 form.errors #返回对应类字段的错误信息 :{"name":[".........."]}
from django.forms import widgets
class UserForm(forms.Form): #用于认证用 name = forms.CharField(max_length=32,label="用户名", widget=widgets.TextInput(attrs={"class":"form-control"}),error_messages={“required”:“该字段不能为空”}) #attr即为修改样式,error_message修改默认的错误信息,必须为required这个键名 pwd = forms.CharField(min_length=4) r_pwd = forms.CharField(min_length=4) email = forms.CharField() tel = forms.CharField(min_length=4,max_length=32) def form_test(request): if request.method == 'POST': form = UserForm(request.POST) if form.is_valid(): #校验是否验证通过,同时会把错误、正确的信息放在两个字典里面 print(form.cleaned_data) else: print(form.cleaned_data) print(form.errors) # ErrorDict : {"校验错误的字段":["错误信息",]} print(form.errors.get("name")) # ErrorList ["错误信息",] return HttpResponse("OK") form = UserForm() return render(request, "form_test.html", locals()) form = UserForm() return render(request,"form_test.html")
在html里面想打印错误信息,可以{{form.name.errors}} 这样可以获取name里的错误信息
利用form表单组件 进行文件上传,使用FormData
$(".login_btn").click(function () { var form_data = new FormData(); //首先实例化一个FormData form_data.append("user",$("#id_user").val()) //再往里面添加数据 form_data.append("pwd",$("#id_pwd").val()) form_data.append("r_pwd",$("#id_r_pwd").val()) form_data.append("email",$("#id_email").val()) form_data.append("img",($("#avater")[0].files[0])) //该为文件,主要是传它才使用FormData,不然不用这么麻烦 form_data.append("csrfmiddlewaretoken",$("[name='csrfmiddlewaretoken']").val()) $.ajax({ url:"", type:"post", contentType:false, //必须设置这两个值 processData:false, data:form_data, //最后把这个对象传给data success:function (data) { console.log(data) } }) })
局部钩子
在规则里面class 添加一个方法"clean_%s"name
def clean_tel(self): val = self.cleaned_data.get('tel') if len(val) == 11: return val else: raise ValidationError("手机号格式错误")
form.is_valid--》self.errors-->self.full_clean--》self._clean_fields()
依次校验每个字段,之后再走钩子去判断里面有没有clean_%s 用户自定义的方法
if hasattr(self, 'clean_%s' % name): value = getattr(self, 'clean_%s' % name)() self.cleaned_data[name] = value
全局钩子
def clean(self): pwd = self.cleaned_data.get('pwd') r_pwd = self.cleaned_data.get('r_pwd') if pwd and r_pwd: if pwd == r_pwd: return self.cleaned_data else: raise ValidationError('两次密码不一致!') else: return self.cleaned_data
视图函数 form_test.html
<div class="container"> <form action="" method="post"> {% csrf_token %} <div class="row"> <div> <p> {{ form.name.label }}{{ form.name }}<span>{{ form.name.errors }}</span><span class="has-error pull-right">{{ errors.0 }}</span> #errors类 自己写 </p> </div> <div> <p> {{ form.pwd.label }}{{ form.pwd }}<span>{{ form.pwd.errors }}</span> </p> </div> <div> <p> {{ form.r_pwd.label }}{{ form.r_pwd }}<span>{{ form.r_pwd.errors }}</span><span class="pull-right error">{{ errors.0 }}</span> </p> </div> <div> <p> {{ form.email.label }}{{ form.email }}<span>{{ form.email.errors }}</span> </p> </div> <div> <p> {{ form.tel.label }}{{ form.tel }}<span>{{ form.tel.errors }}</span> </p> </div> <input type="submit" value="提交">
cookie:具体一个浏览器针对一个服务器存储key-value({})
获取cookie:
cookie 信息储存在request.COOKIE