Django命令
app的创建
创建的应用要去配置文件中注册
创建出来的应用第一步先去配置文件中注册
配置文件(settings.py)中的DEBUG=True 在上线后改为false
Django基础操作
HttpResponse:返回字符串
render :返回html页面
redirect : 重定向
django 文件
urls.py 路由与函数对应关系
views.py 后端业务逻辑 存放视图函数
templates文件夹 存储html文件
静态文件配置(手动创建)
我们将html文件默认都放在templates文件下
我们将网站所用的静态文件默认都放在static文件下(js文件,
css文件,图片文件,第三方前端框架)
settings:
STATICFILRES_DIRS=[
os.path.join(BASE_DIR,“static”)
]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aCTdljuy-1621083204563)(/Users/weita/Library/Application Support/typora-user-images/image-20210505162356835.png)]
form表单默认是get请求提交
form表单action参数
-
不写,默认朝当前所在路由提交数据
-
全写/只写后缀
request对象
获取当前请求方式:request.method 返回全大写的字符串
获取用户post请求的数据(不包括文件) request.POST
get只能获取列表最后一个元素,
getlist可以获取全部元素
pycharm链接数据库
django连接数据库
Django默认使用的数据库是sqlite3
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
#Django链接数据库
1.配置数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'db_test', # 数据库名
'USER': 'root', # 用户名
'PASSWORD': 'rootroot', # 密码
'HOST': '127.0.0.1', # 主机
'PORT': '3306', # 端口号
'CHARSET': 'utf8' # 字符集
}
}
2.代码申明
用pymysql连接#在项目名下的__init__.py文件中书写
import pymysql
pymysql.install_as_MySQLdb()
Django ORM
'''
ORM 对象关系映射
作用:能够让使用者通过python面线对象的代码简单快捷操作数据库
不足:封装程度太高,有时候sql语句的效率偏低,需要自己写sql语句
类 =========》表
对象========》记录
对象属性=====》记录某个字段对应的值
应用下的models.py
'''
1.先在app目录下models.py文件中书写类
class User(models.Model):
# id int primary_key auto_increment
id = models.AutoField(primary_key=True)
# username varchar(32)
username = models.CharField(max_length=16) #CharField必须指定最大长度,否则会报错
#verbose_name:该参数时所有字段都有的,用来对字段的解释
# password int(16)
password =models.IntegerField(max_length=16)
# 魔术方法现实username,实际仍然是一个对象
def __str__(self):
return '{}'.format(self.username)
2.数据库迁移命令
终端中输入:
1.python3 manage.py makemigrations#将操作记录记录到migrations中
2.python3 manage.py migrate#将操作同步到数据库中
#一旦修改了models.py 中关于数据库的命令,就必须执行上述操作
#由于一张表中必须要有一个主键字段,并且一般情况下都是id字段,所有orm会在当你不定义主键字段时,自动生成一个名为id的主键字段
数据的增删改查
#查:
def login(request):
if request.method =='POST':
# 获取用户的用户名和密码,利用ORM操作数据,校验数据是否正确
username = request.POST.get('name')
password = request.POST.get('pwd')
# 去数据库中查询数据
user_obj = models.User.objects.filter(username=username).first()#filter括号内可以写多个参数,查询时默认是and关系
if user_obj:
if password == user_obj.password:
return HttpResponse('登陆成功')
return HttpResponse('密码错误')
return HttpResponse('用户不存在')
# return HttpResponse("dl")
return render(request, "login.html")
#将数据展示到页面,render携带参数,locals将全部参数传入
return render(request, 'userlist.html', locals())
#以对象字典形式传出数据
user_queryset = models.User.objects.all()
return render(request, 'userlist.html', {'user_queryset': user_queryset})
#增:
#获取数据存入数据库
第一种:
res = models.User.objects.creat(username = username, password = password)
#返回被创建的对象本身
第二种:
user_obj = models.User(username = username, password = password)
user_obj.save()
#改
models.User.objects.filter(id=edit_id).updata(username=username, password=password)
#将filter查询出来的列表中的所有对象全部更新
#update是批量更新操作
#删
models.User.objects.filter(id=delete_id).delete()
ORM 建立表关系
'''一对多'''
外键字段建在多的那方
'''多对多'''
需要建立第三方表来专门存储
'''一对一'''
建在哪方都可以
'DIRS': [BASE_DIR, 'templates']
import pymysql
pymysql.install_as_MySQLdb()
from django.db import models
# Create your models here.
class Book(models.Model):
title = models.CharField(max_length=32)
# 整数部分最大8位,小数部分两位
price = models.DecimalField(max_digits=8, decimal_places=2)
# 建立外键
# 如果字段对应的是ForeignKey,ORM会在字段后加上 _id
publish = models.ForeignKey(to='Publish') # to_field不写默认与Publish主键关联
# 图书与作者是多对多的关系,在mysql中需要创建第三方表,而ORM中会自动生成
# 外键建在哪一方都可以,建议建在查询频率高的一方
authors = models.ManyToManyField(to='Author')
"""
authors是一个虚拟字段,用来告知ORM图书表和作者表是多对多的关系
让ORM自动创建第三张表
"""
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
class Author(models.Model):
name = models.CharField(max_length=16)
age = models.IntegerField()
# 作者表与作者信息表是一对一关系
# OneToOneField 也会自动家_id后缀
authorInfo = models.OneToOneField(to='AuthorInfo')
class AuthorInfo(models.Model):
phone = models.BigIntegerField() # 也可以用字符型
addr = models.CharField(max_length=64)
#在Django1.x版本中外键默认都是级联更新删除的
Django请求生命周期流程图
web服务网关接口:
Django再带的是wsgiref:1. 请求来的时候解析封装 2. 相应走的时候打包处理
Django自带的wsgiref模块本身支持的并发量很小,最多1000左右,上线后会换成uswgi
WSGI跟wsgiref和uwsgi是什么关系?
WSGI(python web server gateway interface),web服务网关接口,是一种协议,wsgiref和uwsgi是实现改协议的功能模块
缓存数据库:提前已经想要的数据准备好了,提高响应效率
路由层
路由匹配:
'''url方法第一个参数是匹配正则'''
url(r'test',views.test)
url (r'testadd', views.testadd)
'''只要第一个参数正则能匹配到内容,不会往下执行,而是和直接执行视图函数'''
#你在输入url的时候会默认加/
#是因为Django内部在帮你做重定向(一次匹配不行,url后面加/再来一次,在settings中可以取消)
APPEND_SLASH = False#一般情况不要改,默认是True
'''
在路由前加^,在结束后加$是最完整的格式,最为严谨
'''
url(r'^test/$',views.test)
'''首页匹配'''
url(r'^$',views.test)#如果什么都不写的话输入什么url都可以匹配
# 匹配四个0到9的数字
url(r'^test/[0-9]{4}/$',views.test)
# 匹配多个数字
url(r'^test/\d+/',views.test)
无名分组
'''
分组:就是给某一段正则表达式用小括号括起来
'''
url(r'^test/(\ d+)'/,views.test)
def test(request,xxx):
print(xxx)
return HttpResponse('test')
# 无名分组就是将括号内正则表达式匹配到的内容当作位置参数传递给后面的视图函数
有名分组
'''
可以给正则表达式去一个别名
'''
url(r'^test/(?P<year>\d+)'/,views.test)
def text(request,year):
print(year)
return HttpResponse('test')
# 有名分组就是将括号里的正则表达式匹配到的内容当作关键字参数传递给视图函数
无名有名不能混用!!!但是单个分组可以多次使用
反向解析
#通过一些方法得到一个结果 该结果可以直接访问对应的url触发视图函数
1. 先给路由与视图函数起一个别名
2. 反向解析
#后端反向解析
from django.shortcuts import render, HttpResponse, reverse
reverse('aaa')
#前端反向解析
<a href="{% url 'aaa' %}">
# 给url起一个别名
url(r'^indexxxxx/', views.index, name='aaa')
无名函数反向解析
'''无名分组反向解析'''
#路由书写
url(r'^index/(\d+)', views.index, name='aaa')
#前端
<a href="{% url 'aaa' 1 %}">
#后端
def home(request):
reverse('aaa', args=(1,))
return render(request, 'index.html')
def index(request, args):
return HttpResponse('index')
'''
这个数字写代码的时候,一般情况下放的是数据的主键值
用来数据的编辑和删除操作
'''
有名分组反向解析
# 前端
<a href="{% url 'aaa' year=1 %}">#了解
<a href="{% url 'aaa' 1 %}">
# 后端
# 有名分组反向解析(了解)
reverse('aaa', kwargs={'year': 123})
# 简便写法
reverse('aaa', args=(1,))
路由分发
'''
Django的每一个应用都可以有自己的tmplates文件夹 urls.py static文件夹
正是基于上述的特点 Django能够非常好的做到分组开发
只需要将所有的app拷贝到一个新的Django项目中 然后在配置文件中注册所有的app在利用路由分发的特点将所有的app整合
当一个Django中的url特别多时:
总路由urls.py代码非常冗余不好维护这个时候也可以利用路由分发来减轻总路由的压力
利用路由分发之后,总路由不再干预路由与视图函数的直接对应关系,而是做一个分发处理,识别当前url时属于哪个应用下的,直接发给对应的应用去处理
'''
'''========基础写法========='''
import include
from app01 import urls as app01_urls
from app02 import urls as app02_urls
uelpatterns = [
url(r'^admin/',admin.site.urls),
#只要url前缀是app01,全部交给app01中的urls处理
url(r'^app01/',include(app01_urls)),
url(r'^app02/',include(app02_urls))
]
'''===========进阶写法===========''''
import include
url(r'^app01/',include(app01_urls)),
url(r'^app02/',include(app02_urls))
#总路由不能加$结尾
名称空间
'''
一般情况下,反向解析是没有办法是没有办法识别前缀的
'''
#名称空间
其实只要保证名字不冲突就行,有多个app时,其别名加上app前缀
伪静态
将一个动态网页伪装成静态网页
伪装的目的在于增大本网站的seo查询力度,并且增加搜索引擎收藏本网站的概率
搜索引擎本质上就是一个巨大的爬虫程序
虚拟环境
在正常开发中,我们会给每一个项目配备一个该项目独有的解释器环境,该环境内只有该项目用到的模块,用不到一概不装
虚拟环境
没创建一个虚拟环境就类似于重新下载了一个纯净的python解释器,但是虚拟环境不要创建太多,是需要消耗硬盘空间的
Django版本区别
'''
区别一:
1.x版本路由层使用的是url方法(支持正则)
2.x,3.x版本使用的是path方法(不支持正则,精准匹配)但是re_path是支持正则的,需要导入re_path模块
区别二:
虽然path不支持正则,但是他内部有五种转换器
str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
int,匹配正整数,包含0。
slug,匹配字母、数字以及横杠、下划线组成的字符串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
'''
视图层
视图层必须返回一个HttpResponse对象
JsonResponse对象
json格式的数据有什么用?
前后端数据交互需要使用到json作为过渡,是想跨语言传输数据
import json
from django.http import JsonResponse
def ajson(request):
# 将数据转成json格式,再将数据返回
user_dic = {'name':'smart好帅', 'age': '20', 'hobby': 'girl'}
# user_json=json.dumps(user_dic)
# return HttpResponse(user_json)
# 看源码
return JsonResponse(user_dic, json_dumps_params={'ensure_ascii':False})
#JsonResponse默认只能序列化字典,序列化列表需要加参数
safe=False
form表单上传文件
form表单上传文件类型的数据
- method必须制定成post
- enctype必须换成formdata
def file(request):
if request.method =='POST':
print(request.POST) # 拿不到文件数据,只能拿到普通键值对
# <MultiValueDict: {'file': [<InMemoryUploadedFile: (通用激活码)2018.1之前的版本用这个.txt (text/plain)>]}>
print(request.FILES)
f_obj = request.FILES.get('file')
with open(f_obj.name, 'w') as f:
for line in f_obj.chunks(): # 推荐加上chunks方法
f.write(line)
return render(request, 'afile.html')
requset对象方法
'''
request.method
request.POST
request.GET
request.FIlES
request.path
#只能拿到路由
request.get_full_path()
#能过获取完整的路由和?后的参数
request.body
#原生的浏览器发来的二进制数据
'''
FBV与CBV
CBV源码剖析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9nOx0Pyi-1621083204568)(/Users/weita/Library/Application Support/typora-user-images/image-20210506222944204.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tmQFplTi-1621083204572)(/Users/weita/Library/Application Support/typora-user-images/image-20210506223148351.png)]
模版层
模版语法传值
{{}}:变量相关
{%%}:逻辑相关
# 模版语法可以传递的后端python数据类型
#内部能够自动判断出当前的变量名是否能够加括号调用,如果可以就会自动执行,针对的是函数名和类名
1. 基本数据类型
2. 函数# 传递函数名会自动加括号调用,但是不能穿参
3. 类# 传类的时候也会加括号实例化
#Django模版语法取值只能使用句点符'.'
.键
.索引
过滤器
# 过滤器就类似于模版语法内置的内置方法
# 过滤器最多两个参数(前面一个后面一个)
#基本语法:{{数据|过滤器:参数}}
1. length#统计长度
2. default#默认值,第一个参数就展示第一个,否则就第二个
3. filesizeformat#文件大小
4. data:"Y-m-d X"#日期格式化
5. slice:"0:4:2"#切片操作,支持步长
6. truncatechars:9#切取字符(文章摘要)
7. truncatewords:9#切取单词
8. cut:"a"#移除特定的字符
9. join:" "#拼接操作
10. add:10#加法/拼接
11.safe#转义(前端)
#后端转义
from django.utils.safestring import mark_safe
res = mark_safe(<h1>aaa</h1>)
#以后在全栈项目的时候,前端的代码不一定要在前端写,后端也可以写
标签
#for循环
{%for foo in dic%}
{%end for%}
#if判断
{%if a%}
{%elif%}
{%end if%}
#混用
自定义过滤器、标签、inclusion_tag
-
在应用下创建一个templatetags文件夹
-
在该文件夹内创建任意名称端py文件
-
在该py文件内必须有:
from django import template register = template.Library() #自定义过滤器(在前端中{% load 文件名 %}) @register.filter(name='aaa') def mysum(aq,aw): return aq+aw #自定义标签 (在前端中{% load 文件名 %}) @register.simple_tag(name='bb') def index(a,b,c,d): return '{}-{}-{}-{}'.format(a,b,c,d)
模版的继承
{% entends 'home.html' %} #模版的继承 #继承了之后子页面和模版页面一样,你需要在模版页面上提前划定可以被修改的区域 {% block content %} 模版内容 {% endblock %} #子页面就可以声明想要修改那块区域了 {% block content %} 子页面内容 {% endblock %} #一般情况下,一般有三块可以被修改的地方 1. css区域 2. html区域 3. js区域
模版的导入
''' 将页面的某个局部当成模块的形式 哪个地方需要就可以直接导入 ''' {% include 'aaa.html'%}
模型层
单表操作
#django自带的sqlite3数据库对日期不敏感
必知13条
1.all()#查询所有数据
2.filter()#有过滤条件的查询
3.get()#直接拿数据对象,但是条件不存在直接报错
4.first()#拿queryset里的第一个元素
5.last()# 拿queryset里的最后一个元素
6.values()#可以指定获取的数据字段res=models.User.objects.values('name')返回结果是<QuerySet[{'name':'aa'},{'name':'bb'}]>
7.values_list()#可以指定获取的字段返回结果是<QuerySet[(a,13),(r,2)]>
8.distinct()#去重,因为有主键的存在,所以要先获取指定字段在进行去重,去重必须是一摸一样的数据res=models.User.objects.values('name','age').distinct()
9.order_by()#排序,默认升序,res=models.User.object.oder_by('age')
#res=models.User.object.oder_by('-age')降序
10.reverse()#反转,反转的前提是数据已经排序过
11.count()#统计当前数据的个数
12.exclude()#排除在外
13.exists()#存在
测试脚本
当你只是想测试dango中的某个py文件内容 你们你可以不用书写前后端交互的形式,而是直接写一个测试脚本即可
#测试环境的准备
#取manage.py中拷贝前四行代码,然后自己写两行
import os
if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_ORM.settings")
import django
django.setup()
#然后在该文件就可以执行了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AdCLCeS6-1621083204578)(/Users/weita/Library/Application Support/typora-user-images/image-20210507153953653.png)]
双下划线查询
#年龄大于35
res=models.User.objects.filter(age__gt=35)
#年龄小于35
res=models.User.objects.filter(age__lt=35)
#大于等于
res=models.User.objects.filter(age__gte=35)
#小于等于
res=models.User.objects.filter(age__lte=35)
#年龄是18,32的
res=models.User.objects.filter(age__in=[18,32])
#年龄在18到30之间的,首尾都要
res=models.User.objects.filter(age__range=[18,30])
#名字里有n的 模糊查询,默认区分大小写
res=models.User.objects.filter(name__contains='a')
#不区分大小写
res=models.User.objects.filter(name__icontains='a')
#以什么开头
__startswith='a'
#以什么结尾
__endswith='a'
#月/年份查询
__month,__year
多表查询
一对多外键增删改查
#增
#1.直接写实际字段id
models.Book.objects.create(title='aaa',price=123,pubulic_id=1)
#2.虚拟字段,对象
pubulic_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='bbb',price:333,public=public_obj)
#删
models.Pubulic.object.filter(pk=1).delect()
#修改
#1.
models.Book.objects.filter(pk=1).update(public_id=2)
#2.
pubulic_obj=models.Pubulic.objects.filter(pk=1).first()
models.Book.object.objects.filter(pk=1).pudate(publich=pubulic_obj)
多对多外键增删改查
#多对对的增删改查就是操作第三张表
#增
book_obj=models.Book.objects.filter(pk=1).first()
book_obj.authors.add(1,2,3)#给书籍id为1的书籍绑定一个主键为1\2\3的作者
#修改
set([1,2])
#删除
remove(1,2)
正反向
#正向
外键字段在书籍表,书籍表查出版社就是正向
#反向
出版社表查书表就是反向
'''
正向查询看字段
反向对象.表面小写
'''
多表查询
基于对象的跨表查询(子查询)
#正向
book_obj=models.Book.objects.filter(pk=1).first
res=book_obj.publish#拿到出版社对象
res.name
res,addr
================================================
book_obj=models.Book.objects.filter(pk=1).first
res=book_obj.authors#app.Author.None
res=book-obj.authors.all()#当结果可能有多个的时候需要加all(),只有一个不要加
#反向
publish_obj=models.Pubish.objects.filter(name='111').first
res=publish_obj.book_set.all()
res=author_obj.book_set.all()
'''
当查询结果有多个时候必须加_set.all()
只有一个是不需要
'''
基于双下划线的跨表查询(联表查询)
'''正向'''
res=models.Author.objects.filter(name='aa').values(author_datail__phone)
res = models.Book.objects.filter(pk=1).values('title', 'publish__name')
print(res) # <QuerySet [{'title': '论语', 'publish__name': '南方出版社'}]>
'''反向'''
res = models.Publish.objects.filter(book__title='论语').values('addr', 'book__price')
print(res)
聚合查询
'''
五个聚合函数的使用:Max,Min,Sum,Count,Avg
只要跟数据库相关的模块基本上都在,django.db.models里
或者django.db里
'''
from django.db.models import Max,Min,Sum,Count,Avg
1.Max
2.Min
3.Sum
4.Count
5.Avg #res = models.Book.objects.aggregate(Avg('price'))
分组查询(annotate)
res=models.Book.objects.annotate()#models后面.什么就是按什么分组
res=models.Book.objects.annotate(author_num=Cont('authors')).valuse('anthor_num')
#如果分组查询报错
#需要修改数据库严格模式
F于Q查询
#F查询
from django.db.models import F,Q
res = models.Book.objects.filter(maichu__gt=F('kucun'))
models.Book.objects.filter(price=F('price')+1100)
#在操作字符类型的数据的时候F不能直接字符拼接
from django.db.models.functions import Concat
fron django.db.models import Value
models.Book.objects.update(title=Concat(F('title'),Value('aaa')))
#Q查询
from django.db.models import F,Q
res= models.Book.objects.filter(maichu__gt=100,price__lt=600)#fliter括号里的多个参数是and关系
#用Q包裹里面还是and关系,但是可以加|~
models.Book.objects.filter(Q(maichu__gt=100)|Q(maichu__gt=100)#|就是or,~就是非
#Q的高阶用法能够将查询条件的左边变成字符串形式
q=Q()
q.connector='or'#要加上or条件
q.children.append(('maichu__gt',100))
q.children.append(('maichu__gt',100))
res=models.Book.objects.filter(q)#默认还是and关系
Django中开启事务
'''
事务
ABID
原子性:不可分割
一致性:与原子性相辅相成
隔离型:事务间互不干扰
持久性:一旦确认永久生效
事务的回滚:
rollback
事务的确认:
commit
'''
#Django中开启事务
from django.db import transaction
with transaction.atomic():
#sql语句
其他操作
ORM中常用字段及参数
AutoField
主键字段 primary_key=True
CharField
verbose_name 字段的注释
max_length 长度
IntegerField
DecimalField
max_digits=8#整数部分8位
decimal_places=2#小数部分2位
EmailField
DataField#data
DataTimeField#datatime
auto_now#每次修改数据都会自动更新当前时间
auto_now_add#只有创建数据时会修改时间
BooleanField
TextField#文本类型,没有字数限制
FileField#文件字段
upload_to='/data'#给该字段传一个文件对象,会自动将文件保存到/data目录下,然后将文件路径保存到数据库中
数据库查询优化
only与defer:
查询only括号内的字段不会走数据库,没有的字段会重新走数据库
defer括号内放的字段不在查询出来的对象里面
查询defer括号内的字段会走数据库,没有的字段不会走数据库
select_related与prefetch_related
#select_related会将book和publish连起来,然后一次性将大表里的数据封装给查询对象,这时候不管查询什么都不需要走数据库
res = models.Book.objects.select_related('publish')#只能是一对多,一对一关系
for i in res:
print(i.publish.addr)#联表查询
res = models.Book.objects.prefetch_related('publich')
#prefetch_related方法内部其实是子查询,将子查询的所有结果也封装到对象里面
choicse参数(数据库字段设计常见)
只要某个字段的可能性是可以列举完全的,你们一般情况下都会采用choices参数
gender_choices=((1,'男'),(2,'女'))
gender=models.IntergerField(choices=gender_choices)
#该gender字段存的还是数字,单数如果存的数字在gender_choices元组列举的范围之内,那么可以非常轻松的获取到数字对应的真正内容
#1.gender字段存的数字不在上述元组列举的范围
存的时候没有列举出来的数字也是能存的,范围还是由IntergerField决定
#2.gender字段在字段内,专门获取对应信息
只要是choices参数的字段,如果想要获取对应信息,固定写法 get_字段名_display()
user_object = models.User.objects.filter(pk=1).first
print(user.object.get_gender_display())
#如果没有对应关系,输入什么展示什么
choices参数使用场景非常广泛
MTV与MVC模型
M:models
T:templates
V:views
dango是MTV模型
M:models
V:views
C:controller
dango本质也是MVC模型
多对多三种创建方式
#全自动
通过ORM自动创建第三张表
'''
优点:代码不需要自己写,还支持orm提供 的第三张关系表的方法
不足:第三张关系表的拓展性差(没有办法额外添加字段)
'''
#存手动
自己写第三张关系表,自己添加外键
"""
优点:拓展性强
缺点:需要写代码,不能使用orm提供的简单的办法(不推荐使用)
"""
#半自动
class Book(mmodels.Model):
name = models.CharField(max_length=32)
author=models,ManyToManyFied(to='Author', through='Book_Author',through_field=('book','author'))
#through_fields字段的先后顺序,当前表是谁就把谁放前面
class Author(models.Model):
name=models.CharField(max_length=32)
class Book_Author(models.Model):
book=models.ForeignKey(to='Book')
author=models.ForeignKey(to='Author')
#半自动可以orm可以使用正反向查询,但是不能使用add,set,remove,clear
"""
一般情况都会使用半自动,因为第三章表是自己建的,add,set,remove,clear都无所谓
"""
Ajax
"""
异步提交
局部刷新
AJAX常见应用情景
搜索引擎根据用户输入的关键字,自动提示检索关键字。
还有一个很重要的应用场景就是注册时候的用户名的查重。
其实这里就使用了AJAX技术!当文件框发生了输入变化时,使用AJAX技术向服务器发送一个请求,然后服务器会把查询到的结果响应给浏览器,最后再把后端返回的结果展示出来。
整个过程中页面没有刷新,只是刷新页面中的局部位置而已!
当请求发出后,浏览器还可以进行其他操作,无需等待服务器的响应!
发送请求的方式
1. 浏览器地址栏之际输入url GET请求
2. a标签href属性 GET请求
3. form表单 GET/POST
4. ajax GET/POST
"""
AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步的Javascript和XML”。即使用Javascript语言与服务器进行异步交互,传输的数据为XML(当然,传输的数据不只是XML)。
AJAX 不是新的编程语言,而是一种使用现有标准的新方法。
AJAX 最大的优点是在不重新加载整个页面的情况下,可以与服务器交换数据并更新部分网页内容。(这一特点给用户的感受是在不知不觉中完成请求和响应过程)
AJAX 不需要任何浏览器插件,但需要用户允许JavaScript在浏览器上执行。
- 同步交互:客户端发出一个请求后,需要等待服务器响应结束后,才能发出第二个请求;
- 异步交互:客户端发出一个请求后,无需等待服务器响应结束,就可以发出第二个请求。
在前端页面使用ajax的时候需要导入jQuery(并不只有jQuery能够实现ajax,其他的框架也可以,但是原理是一样的)
Ajax基本语法
<script>
$('#btn').on('click',function (){
$.ajax({
//1.指定往哪个后端发送请求
url:'',
//2.请求方式
type:'post',
//3.数据
data:{
'd1':$('#d1').val(),
'd2':$('#d2').val(),
},
//4.回调函数,后端返回结果是触发,args接收返回结果
success:function (args){
$('#d3').val(args.msg)
}
})
})
</script>
def my_ajax(request):
if request.method == 'POST':
print(request.POST)
d1 = request.POST.get('d1')
d2 = request.POST.get('d2')
d3 = int(d1)+int(d2)
d4 = {'code': 100, 'msg': d3}
return JsonResponse(d4) # 用JsonResponse前端回调函数会自动反序列化
return render(request, 'index.html')
前后端传输数据的编码格式
'''
get请求的数据就是放在url后的
url?username=aaa&password=123
'''
#可以向后端发送POST请求的方式
1.form表单
2.ajax请求
'''
前后端传输数据的格式编码格式
urlencoded
formdata
json
'''
#form表单
默认的数据编码格式是urlencoded
数据格式:username=aaa&password=123
Django后端针对符合urlencoded编码格式的数据都会自动帮你解析封装发哦request.POST中
如果编码格式改成formdata,那么针对普通的键值对还是解析到request.POST中,而将文件解析道requset.FILES中
form表单是没有办法发送json格式的
#ajax
默认的编码格式也是urlencoded
数据格式username=aaa&password=123
Django后端针对符合urlencoded编码格式的数据都会自动帮你解析封装发哦request.POST中
#ajax发送json格式数据
'''
前后端传输数据的时候一定要确保编码格式跟数据真正的格式一致
'''
$('#btn').on('click',function (){
$.ajax({
url:'',
type:'post', data:JSON.stringify({'d1':$('#d1').val(),'d2':$('#d2').val(),}),#将数据转成json格式
contentType:'application/json',#将数据传输编码格式改为json
json格式{"name":"aaa","age":21}
Django针对json格式的数据不做任何处理,需要手动处理,json格式的数据会以二进制形式放在requset.body中
if request.is_ajax():
json_bytes = request.body
# json_str = json_bytes.decode('utf8')
json_dict = json.loads(json_bytes)
# json.loads内部如果传入的是一个二进制数据会自动解码+反序列化
'''
request对象方法补充:
request.is_ajax()
判断当前请求是否是ajax请求,返回boolean
'''
#注意点
1.通过contentType参数指定application/json
2.数据必须是真正的json格式
3.Django不会帮你处理json格式数据需要自己去request.body中获取并处理
ajax发送文件
$('#d3').on('click',function (){
//先利用FormData内置对象
let formDataObj = new FormData
formDataObj.append('name',$('#d1').val()); // 普通键值对
formDataObj.append('file',$('#d2')[0].files[0]); //文件对象
$.ajax({
url:'',
type:'post',
data:formDataObj,
//ajax发送文件对象的两个必要参数
contentType:false, //不要使用任何编码,Django会识别FormData对象
processData: false, //告诉浏览器不要对数据做任何处理
success:function (args){
}
if request.is_ajax():
if request.method == 'POST':
print(request.POST)
print(request.FILES)
return render(request, 'my_file.html')
'''
总结:
1.需要用到js内置对象FormData
2.FormData可以添加普通键值对和文件对象
3.需要指定两个必要关键参数
1.contentTpye
2.processData
4.Django后端能够直接识别道FormData对象并且能够将内部的普通键值对解析封装发哦request.POST中,将文件数据自动解析封装道request.FILES中
'''
Django自带的序列化组件
from myapp import models
from django.core import serializers
def index(request):
user_list = models.NewUser.objects.all()
# 序列化
res = serializers.serialize('json', user_list)
# 会自动将数据变成json格式
# 后端写接口就是利用序列化组件渲染数据然后写一个接口文档
return HttpResponse(res)
批量插入
当你想要批量插入数据的时候,使用orm给你提供的bulk_create能大大减少操作事件
models.Book.object.bulk_creat(book_list)
forms组件
能渲染html代码,校验数据,展示显示提示
数据校验可有可无,后端必须要有,因为前端端校验是弱不禁风的,你可以直接修改,或者利用爬虫程序绕过前端页面直接朝后端提交数据
#渲染html页面
#forms组件只会自动渲染获取用户输入的标签(input select radio checkbox)
#不会渲染按钮
#后端先产生一个空对象
from django import forms
class MyForm(forms.Form):
username = forms.CharField(max_length=8, min_length=3)
password = forms.CharField(max_length=8, min_length=3)
email = forms.EmailField
def my_form(request):
form_obj = MyForm()
return render(request, 'myForm.html', form_obj)
#第一种,书写代码极少,封装程度太高,不便于后续扩展
{{form_obj.as_p}}
{{form_obj.as_ul}}
{{form_obj.as_table}}
#第二种,可扩展性很强,但是书写代码太多,不用
{{form_obj.username.label}}:{{form_obj.username}}
{{form_obj.password.label}}:{{form_obj.password}}
{{form_obj.email.label}}:{{form_obj.email}}
#第三种(推荐)
{% for form in form_obj %}
{{form.label}}:{{form}}
{% endfor %}
'''
label属性默认展示的是类中定义字段首字母大写的形式
'''
forms组件显示错误信息
<form action="" method="post" novalidate>
{% for form in form_obj %}
<p>{{ form.label }}:{{ form }}</p>
<span style="color: red">{{ form.errors.0 }}</span>
{% endfor %}
<input type="submit" value="提交" class="btn btn-info">
</form>
from django import forms
class MyForm(forms.Form):
username = forms.CharField(max_length=8, min_length=3, label='账号', error_messages={
'max_length': '账号最大八位',
'min_length': '账号最小八位',
'required': '账号不能为空'
})
password = forms.CharField(max_length=8, min_length=3, label='密码', error_messages={
'max_length': '密码最大八位',
'min_length': '密码最小八位',
'required': '密码不能为空'
})
email = forms.EmailField(label='邮箱', error_messages={
'invalid': '邮箱无效',
'required': '邮箱不能为空'
})
def my_form(request):
form_obj = MyForm()
if request.method == 'POST':
# 校验信息
form_obj = MyForm(request.POST)
if form_obj.is_valid():
pass
return render(request, 'myForm.html', locals())
钩子函数(HOOK)
'''
在特定的节点自动触发完成响应操作
在forms组件中有两类钩子
1.局部钩子
当你需要给某个字段增加校验规则的时候可以使用
2.全局钩子
当你需要给多个字段增加校验规则时使用
'''
def clean_username(self):
username = self.cleaned_data.get('username')
if '666' in username:
self.add_error('username', '光喊666是没有用的')
return username
# 全局钩子
def clean(self):
password = self.cleaned_data.get('password')
confirm_password = self.cleaned_data.get('confirm_password')
if password==confirm_password:
pass
self.add_error('confirm_password', '两次密码不一致')
return self.cleaned_data
form参数
label 字段名
error_messages 自定义报错信息
initial 默认值
required 控制字段是否必填
widget=forms.widgets.TextInput(attrs={'class':'form_control'},{'':''})
#修改样式和类型,多个属性空格隔开就行
widget=forms.widgets.PasswordInput()
#还支持正则校验
validators=[
RegexValidator(r'^[0-9]+$','请输入数字')
RegexValidator(r'^159[0-9]+$','必须以159开头')
]
cookie与session
cookie 服务端保存在客户端浏览器上的信息都可以称之为cookie,它的表现形式一般都是键值对(可以有多个)
session 数据是保存服务端的,表现形式一般也是键值对
token session虽然数据是保存在服务端的,但是禁不住数据量大
服务端不在保存数据,登陆成功之后,将一段信息进行加密处理(加密算法是私密的),将加密之后的结果拼接在信息后面,整体返回给浏览器保存,浏览器下次访问的时候带着该信息,服务端自动切取前面的一段信息再次使用自己的加密算法跟浏览器尾部的秘闻进行对比
jwt认证
session是基于cookie工作的
Django操作cookie
#设置cookie
obj.set_cookie(key,value)
#获取cookie
request.COOKIES.get(key)
#装饰器
def code(func):
def inner(request, *args, **kwargs):
target_url = request.get_full_path()
if request.COOKIES.get("code") == 'laowangairenqi':
return func(request, *args, **kwargs)
else:
return redirect('/?next={}'.format(target_url))
return inner
session操作
1.在默认情况向操作session的时候需要Django默认的一张django_session表
#Django默认的session的过期时间是14天,可以手动设置过期时间(request.session.set_expiry(),括号内可以放1.整数 多少秒,2.日期对象 到具体时间,3.0 关闭浏览器失效,4.不写 默认14天)
#django_session中的数据条数是取决于浏览器的,同一个计算机同一个浏览器是只会生成一条,除非session过期了
#设置session
request.session['key']=value
#获取
request.session.get('key')
#清除session
request.session.delete()#只删服务器
request.session.flush()#清空浏览器和服务端
Django中间件
#Django自带七个中间件,每个中间件都有各自对应的功能,并且Django还支持程序员自定义中间件,并且暴露给程序员五个可以自定义的方法
必须掌握:
process_request:
process_response
了解
process_view
process_template_response
process_exception
1.请求来的时候需要先经过中间件才能达到真正的Django后端
2.响应走的时候最后也需要经过中间件才能发送出去
#Django中间件
MIDDLEWARE = [
# 字符串形式的导入路径
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
如何自定义中间件
'''
1.在项目名或者应用名下创建一个任意文件夹
2.在文件夹里创建任意一个py文件
3.在文件内书写类(必须继承MiddlewareMixin),然后在类里自定义方法(用几个写几个)
4.需要将类的路径以字符串的形式注册到配置文件中才能生效(settings中的MiddleWare)
'''
必须掌握:
process_request:
1.请求来的时候需要经过每一个中间件里面的process_request方法,结果的顺序是按照配置文件中的中间件从上往下的顺序一次执行
2.太高中间件里面没有定义该方法,那么直接跳过执行下一个中间件
3.如果该方法返回HttpResponse对象,那么请求将不在继续往后执行,而是直接原路返回(校验失败不允许访问)
#process_request方法就是用来做全局相关的所有限制功能
process_response:
1.响应走的时候需要经过每一个中间件里面的process_response方法该方法有两个额外的参数request,response
2.改方法必须返回一个HttpResponse对象
2.1默认放回的就是形参response
2.2可以返回自定义的HttpRespnse对象
3.顺序是按照配置文件中注册了的中间件从下往上一次经过,如果没有定义直接跳过执行下一个
#一旦在process_request方法就已经返回了HttpResponse对象,那么响应走的时候会直接走同级别的process_response返回
from django.utils.deprecation import MiddlewareMixin
class MyMiddleware(MiddlewareMixin):
def process_request(self, request):
print('自定义中间件')
def process_response(self,request,response):
'''
:param request:
:param response: 就是django后端返回给浏览器的内容
:return:
'''
print('自定义中间件process_response方法')
return response
了解
process_view:在路由匹配之后执行视图函数之前,会自动执行中间件里面的process_view方法结果的顺序是按照配置文件中的中间件从上往下的顺序一次执行
process_template_response:返回的HttpRespnse对象有render属性的时候才会触发
process_exception:当视图函数中出现异常时触发
csrf
#csrf跨站请求伪造校验
网站再给用户返回一个具有提交数据功能页面的时候回个这个页面加一个唯一标识,当这个页面超后端发送post请求的时候,后端会先校验唯一标识,如果唯一标识不对直接拒绝访问(403),如果成功则正常执行
#form如何符合校验
{% csrf_token %}
#ajax如何符合校验
1.利用标签查找
data:{'csrfmiddlewaretoken':$('[name=csrfmiddlewaretoken]').val()}
2.快捷书写
data:{'csrfmiddlewaretoken':'{{csrf-token}}'}
3.通用
直接拷贝js代码,并应用到html中
#csfr装饰器
from django.views.decorators.csrf import csrf_exempt, csrf_protect
csrf_exempt:不校验
csrf_protect:需要校验
补充
#模块;
importlib#可以通过字符串形式导入模块,但是最小只能到py文件名
Auth模块
'''
在执行数据库迁移命令的时候会自动生成很多张表,django_session,auth_user
Django在启动之后就会直接访问admin路由,需要输入用户名和密码,数据参考的就是auth_user表,并且还必须是管理员用户才能进入
'''
创建超级用户
python3 manage.py createsuperuser
依赖
from django.contrib import auth
auth.authenticate(request,username=username,password=password)
#自动查找auth_user标签
#自动给密码加密在比对
#需要传入用户名和密码(一步筛选出)
#这个方法的返回值是 用户对象,数据不符合返回none
auth.login(request,user_obj)
#类似于request.session[key]=user_obj
#执行了该方法可以在任何地方通过request.user获取到当前登陆的用户对象
request.user.is_authenticated()#判断是否登陆
from django.contrib.auth.decorators import login_required
@login_required
def home(request):
#添加装饰器,用户登陆之后才能访问
@login_required(login_url='/login/')
#没有登陆跳转到指定页面,局部配置
#全局配置,在settings中设置 LOGIN_URL='/login/'
request.user.check_password(old_password)#校验密码
request.user.set_password(new_password)#修改密码
request.user.sava()#保存到数据库
auth.logout(request)#注销用户
User.object.create_user(usename=username,password=paaword)#创建普通用户
如何扩展auth_user表
面向对象的继承
from django.contrib.auth.models import User,AbstractUser
class UserInfo(AbstractUser)
#如果继承了AbstractUser表,那么在执行数据库迁移命令的时候auth_user表就不会在创建出来了,而User_Info表会出现auth_user表所有字段外加自己扩展的字段
#前提1 在继承之前没有执行过数据库迁移命令
#前提2 继承类里不要覆盖字段名
#前提3 settings配置(AUTH_USER_MODEL = 'app01.UserInfo')应用名.表名
模块补充
pillow模块,用来处理图片相关
from PIL import Image, ImageDraw, ImageFont
Image:生成图片
ImageDraw:在图片上操作
ImageFont:控制字体样式
from io import BytesIO, StringIO
内存管理器模块
BytesIO:临时存储数据,返回时数据是二进制格式
StringIO:临时存储数据,返回时数据是字符串形式
def get_random():
return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)
def get_code(request):
# 利用PIL模块动态生成图片
img_obj = Image.new(mode='RGB', size=(290, 35), color=get_random())
img_draw = ImageDraw.Draw(img_obj) # 生成一个画笔对象
img_font = ImageFont.truetype('static/font/1.ttf', 30) # 字体样式和大小
# 产生随机验证码
auth_code = ''
for i in range(4):
random_int = str(random.randint(0, 9))
# 将随机产生到数字写到图片上
img_draw.text((30+i*60, -2), random_int, get_random(), img_font)
auth_code = auth_code+random_int
request.session['auth_code'] = auth_code
io_obj = BytesIO() # 生成一个内存管理器
img_obj.save(io_obj, 'png')
return HttpResponse(io_obj.getvalue()) # 从内存管理器中读取图片
admin后台管理
'''
Django提供了一个可视化的界面用来让你方便的对你的模型表进行数据的增删改查
如果想要使用admin后台管理操作模型表,需要先注册你的模型表,告诉admin你需要操作那些表
去admin.py中注册
admin.site.register(models.UserInfo)
admin.site.register(models.Blog)
admin.site.register(models.Tag)
admin.site.register(models.Article)
admin.site.register(models.Up_down)
admin.site.register(models.Category)
admin.site.register(models.Comment)
admin.site.register(models.Article_tag)
'''
class Meta:
verbose_name_plural = '用户表'
'''
用来修改admin后台管理的模型表的显示
'''
null =True,告诉数据库可以空
blank=True告诉admin后台可以为空
media配置
该配置可以让用户上传所有的文件都固定存放到某一个指定的文件夹下
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4cSmvcwV-1621083204581)(/Users/weita/Library/Application Support/typora-user-images/image-20210513201039181.png)]
图片防盗链
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5MpnEfZY-1621083204582)(/Users/weita/Library/Application Support/typora-user-images/image-20210513214744302.png)]
富文本编辑器
kindeditor
beautisoup模块
bs4
专门用来帮你处理html页面
改模块主要用于爬虫程序
from bs4 import BeautifulSoup
soup = BeautifulSoup(content, 'html.parser')
tags = soup.find_all()
for tag in tags:
if tag == 'script':
tag.decompose()