前戏:纯手撸web框架
第一次推导
# 不足之处
1、代码重复
2、手动处理http格式的数据 并且只能拿到url后缀,其他数据获取繁琐(数据格式一样处理代码其实也大致一样 重复写)
3、并发问题
# 可以将web框架理解成服务端
import socket
"""
b'GET /index HTTP/1.1\r\nHost: 127ec-F...
b'GET /favicon.ico HTTP/1.1\r\nHost: 127...
如何做到后缀不同返回不同的内容
1、fav的那行不看
2、可以通过打印data得知/index 为我们输入的后缀
"""
server = socket.socket() # tcp 三次握手四次回收
server.bind(('127.0.0.1',8080)) # ip协议 以太网协议 arp协议..
server.listen(5) # 池的概念复习
while True:
conn, addr = server.accept()
data = conn.recv(1024)
# print(data) # 二进制数据,需要解码
data = data.decode('utf-8') # 字符串
# 获取字符串中特定的内容,正则,如果字符串有规律也可以考虑用切割
conn.send(b'HTTP/1.1 200 OK\r\n\r\n')
current_path = data.split(' ')[1] # 拿到列表,而我们后缀在d
print(current_path) # 拿到后缀
if current_path == '/index':
# conn.send(b'index heiheihie')
with open(r'myheml.html','rb') as f:
conn.send(f.read())
elif current_path == '/login':
conn.send(b'login')
else:
conn.send(b'400')
conn.close()
第二次推导
"""
# 借助 wsgiref
1、帮助封装了socket代码
2、帮忙处理了http格式的数据(大字典)
web服务网关接口
1、请求来的时候帮助自动拆分http格式数据,并且封装成好处理的数据格式
2、响应走的时候将数据打包成符合http格式
"""
from wsgiref.simple_server import make_server
def run(env, response):
"""
:param env: 请求相关的所有数据
:param response: 响应相关的所有数据
:return: 返回浏览器的数据
"""
response('200 OK', []) # 响应首行 响应头
# print(env) # 是一个大字典 wsgiref模块帮忙处理好http格式的数据
current_path = env.get('PATH_INFO')
if current_path == '/index':
return [b'index']
elif current_path == '/login':
return [b'login']
return [b'404 error']
if __name__ == '__main__':
server = make_server('127.0.0.1', 8081, run)
"""
会实时监听上面的地址和端口,只要有客户端
来了都会交给run函数处理(加括号触发run函数)
"""
server.serve_forever() # 启动服务端
第三次推导
# 由于第二次推导添加功能太麻烦,所以经过优化后得到以下内容
from wsgiref.simple_server import make_server
# 将env传入函数内,函数就拿到更多数据,可以做更多操作
def index(env):
return 'index'
def login(env):
return 'login'
def error(env):
return 'error'
urls = [
('/index', index),
('/login', login)
]
def run(env, response):
"""
:param env: 请求相关的所有数据
:param response: 响应相关的所有数据
:return: 返回浏览器的数据
"""
response('200 OK', []) # 响应首行 响应头
# print(env) # 是一个大字典 wsgiref模块帮忙处理好http格式的数据
current_path = env.get('PATH_INFO')
# 定义一个变量,存储匹配到函数名,一旦匹配到将url对应的函数名赋值给func
func = None
for url in urls:
if current_path == url[0]:
func = url[1]
break # 匹配到一个之后,立刻结束for循环
# 判断func是否有值
if func:
res = func(env)
else:
res = error(env)
return [res.encode('utf-8')]
if __name__ == '__main__':
server = make_server('127.0.0.1', 8081, run)
"""
会实时监听上面的地址和端口,只要有客户端
来了都会交给run函数处理(加括号触发run函数)
"""
server.serve_forever() # 启动服务端
第四次推导
# 将第三次推导解耦合为三个文件,一个文件夹
# 更具不同的功能拆分成不同的py文件
"""
wsgiref 写启动程序
urls.py 路由于视图函数对应关系
views.py 视图函数(后端业务逻辑)
templates文件夹 专门用来存储html文件
# 按照功能不同拆分之后 后续添加功能只需要在urls.py书写对应关系,在views.py中写函数逻辑即可
"""
# 第一个文件wsgiref
from wsgiref.simple_server import make_server
from url import urls
from vleus import *
# 将env传入函数内,函数就拿到更多数据,可以做更多操作
def run(env, response):
"""
:param env: 请求相关的所有数据
:param response: 响应相关的所有数据
:return: 返回浏览器的数据
"""
response('200 OK', []) # 响应首行 响应头
# print(env) # 是一个大字典 wsgiref模块帮忙处理好http格式的数据
current_path = env.get('PATH_INFO')
# 定义一个变量,存储匹配到函数名,一旦匹配到将url对应的函数名赋值给func
func = None
for url in urls:
if current_path == url[0]:
func = url[1]
break # 匹配到一个之后,立刻结束for循环
# 判断func是否有值
if func:
res = func(env)
else:
res = error(env)
return [res.encode('utf-8')]
if __name__ == '__main__':
server = make_server('127.0.0.1', 8081, run)
"""
会实时监听上面的地址和端口,只要有客户端
来了都会交给run函数处理(加括号触发run函数)
"""
server.serve_forever() # 启动服务端
# 第二个文件 urls.py
from vleus import *
# url于函数的对应关系
urls = [
('/index', index),
('/login', login),
('/xxx',xxx)
]
# 第三个文件views.py
def index(env):
return 'index'
def login(env):
return 'login'
def error(env):
return 'error'
def xxx(env):
with open(r'templates/myhtml.html','r',encoding='utf-8') as f:
return f.read()
# 第四个目录templates文件夹
myhtml.html
动静态网页
静态网页
页面上的数据是直接写死的,万年不变
动态网页
数据是实时获取的
1、后端获取当前时间,展示到当前html页面上
2、将一个字典传递给html文件,并且可以在文件上方便快捷操作字典数据
3、利用wsgiref模块封装web框架,加上jinja2模板语法 结合前端后端数据库
时间案例
# 让html页面显示时间
import datetime
def get_time(env):
current_time = datetime.datetime.now().strftime('%Y-%m-%d %X')
# 如何将后端获取到的数据传递给html文件
with open('templates/myhtml.html','rt',encoding='utf-8') as f:
data = f.read()
# data就是一堆字符串
data = data.replace('asdasdasda',current_time)
return data
jinja2模块案例
# 将一个字典传递给html文件,并且可以在文件上方便快捷操作字典数据
# 在后端html文件起作用
pip3 install jinja2
# 模板语法
{{ user }}}
{{ user.get('username')}}
{{ user.get('age')}}
from jinja2 import Template
def get_dict(env):
user_dic = {'username':'jason','age':18}
with open(r'templates/myhtml.html','r',encoding='utf-8') as f:
data = f.read()
tmp = Template(data)
res = tmp.render(user = user_dic)
# 给get_dict,html传递了一个值,页面上通过变量名user能够拿到user_dict
return res
数据库案例(融汇贯通)
# 利用wsgiref模块封装web框架,加上jinja2模板语法 结合前端后端数据库
html文件
<div class="container">
<dic class="row">
<div class="col-md-8 col-md-offset-2">
<h1 class="text-center">用户数据</h1>
<table class="table table-hover table-striped"></table>
<thead>
<tr>
<th>ID</th>
<th>username</th>
<th>password</th>
<th>hobby</th>
</tr>
</thead>
<tbody>
{% for user_dic in user_list%}
<tr>
<td>{{user_dic.id}}</td>
<td>{{user_dic.username}}</td>
<td>{{user_dic.password}}</td>
<td>{{user_dic.hobby}}</td>
</tr>
{% endfor %}
</tbody>
</div>
</dic>
</div>
vleus文件
import pymysql
def get_user(env):
# 去数据库中获取数据,传递给html页面,借助于模板语法,发送给浏览器
conn = pymysql.connect(
host = '127.0.0.1',
port = 3306,
user = 'root',
password = 'admin123',
db = 'day59',
charset = 'utf8',
autocomit = True # 自动提交
)
cursor = conn.cursor(cursor = pymysql.cursors.DictCursor)
sql = 'select * from user'
affect_rows = cursor.execute(sql) # 影响的行数
data_list = cursor.fatchall() # 字典套数组的模式
# 将获取到的数据传递给html文件
with open(r'templates/myhtml.html','r',encoding='utf-8') as f:
data = f.read()
tmp = Template(data)
res = tmp.render(user_list = data_list)
# 给html文件传递一个值,可以通过变量user_list就能拿到data_list
python三大主流web框架
django
特点:大而全 自带的功能特别多,类似航母
不足:有时候过于笨重
flask
特点:小而精 自带的功能特别少,类似游骑兵
第三方的模块特别多,而且越来越小django
不足:比较依赖于第三方的开发者
tornado
特点:异步非阻塞 支持高并发
牛逼到甚至可以开发游戏服务器
不足:暂时不会
一个框架大致分为三部分
A:socket部分
B:路由与视图函数对应关系
C:模板语法
django
A用的是别人的 wsgiref模块
B用自己的
C用自己的(没有jinja2好用,但是也方便)
flask
A用的是别人的 werkzeug(内部还是wsgiref模块)
B用自己的
C用别人的(jinja2)
tornado
ABC都是自己写的
django框架
前言
注意事项:
正常启动django项目要求
1、计算机名称不能有中文
2、pycharm窗口只开一个项目
3、项目里面所有文件也是不要出现中文
4、python解释器尽量使用3.4到3.6之间的版本
如果项目报错,可以点击最后一个报错信息
去源码中,把逗号删掉
django版本问题
市面上有三个版本
1.x 2.x 3.x(直接忽略)
1.x 和 2.x 本身差距也不大,学习主要以1.11为例,会讲解2.x区别
django安装
pip3 install django == 1.11.11
验证是否成功
在终端输入django-admin看看有没有反应
django必会命令
linux创建django项目 (重点)
命令行操作
1、创建django项目
可以先切换到对应的盘,然后再创建
django-admin startproject 项目名
项目名文件夹
manage.py
mysite文件夹
__init__.py
settings.py
urls.py
wsgi.py
2、启动django项目(先切换到mysite文件夹下)
"""
一定要先切换到项目目录下
"""
python3 manage.py runserver
htpp://127.0.0.1:8000
3、创建应用
python manage.py startapp app01
名字应该见名知意
注意事项:
# 命令行和pycharm创建的区别
# pycharm创建
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'] # 拼接templates路径
]
# linux创建
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': []
]
"""
意味着在用命令行创建django项目的时候还需要去配置文件中配置路径
"""
# 创建出来的应用第一步先去配置文件中注册
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app1.apps.App1Config',
]
主要文件介绍
-mysite项目文件夹
--mysite文件夹
---settings.py 配置文件
---urls.py 路由于视图函数对应的关系
---wsgi.py wsgiref模块(不考虑)
--manage.py django的入口文件
--db.sqlite3 django自带的sqlite3数据库(小型数据库,功能不多有bug)
--app01文件夹
---admin.py django后台管理
---apps.py 注册使用
---migrations文件夹 数据库迁移记录
---models.py 数据库相关的 模型类(orm)
---tests.py 测试文件
---views.py 视图函数(视图层)
django三板斧
HttpRespose
返回字符串类型的数据
return HttpResponse('你好啊,我是django妹质')
render
返回html文件的
return render(request,'myfilst.html')
# 方式一:将user_dict传给html文件,html文件通过data取值
return render(request,'myfilst.html',{'data':user_dic,'data',123})
# 方式二:locals会将所在名称空间中所有名字传递给html页面
return render(request,'myfilst.html',locals())
redirect
重定向
return redirect('http://47.96.177.8:82/')
return redirect('/home/') # 还可以重定向到自己的目录
===============案例===============
from django.shortcuts import render,HttpResponse,redirect
def index(request):
"""
:param request: 请求相关的所有数据,比之前的env更加牛逼
:return:
"""
# return HttpResponse('你好啊,我是django妹质')
# return render(request,'myfilst.html') # 自动去tempaltes文件夹下查找文件
# return redirect('http://47.96.177.8:82/')
return redirect('/home/')
静态文件配置
"""
我们将html文件默认放在templates文件夹下
我们将网站所使用的静态文件夹默认都放在static文件夹下
静态文件
前端已经写好了的 能够直接调用使用的文件
网站写好的js文件
网站写好的css文件
网站用到的图片文件
第三方前端框架
...
拿来直接可以使用
"""
# django不会自动创建static文件夹,需要手动创建
一般情况下我们在static文件夹下还会进一步划分
-static
--js
--css
--img
其他第三方文件
"""
在浏览器中输入url能够看到对应的资源
是应为后端提前开设了该资源的接口
如果访问不到资源,说明后端没有开设该资源的接口
向html文件就会应为以上原因使用不了bootstrap
file:///D:/pycharm/pythonProject/djangoProject/djangoProject/bootstrap-3.4.1-dist/css/bootstrap.min.css
"""
# 静态文件配置
"""
STATIC_URL = '/static/' # 类似与访问静态文件的令牌
# 访问页面的时候导入的bootstrap路径只有
# 令牌对了才能拿着后缀去下面列表中查找
STATICFILES_DIRS = [
os.path.join(BASE_DIR,'static'),
os.path.join(BASE_DIR,'static1')
]
# 静态文件动态解析
{% load static %}
<link href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}" rel="stylesheet">
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
这样一来,无论STATIC_URL如何变都能动态获取令牌
***************************************
当在写django项目的时候,可能出现后端代码修改了,但是前端页面没有变化的情况
1、在同一个端口开了好几个django项目
而你一直跑的是第一个django项目
2、浏览器缓存问题
settings
network
disable cache勾上
***************************************
request对象方法初识
request.method # 返回请求方式,并且是全大写的字符串形式
request.POST # 获取用户post请求提交的普通数据(不包含文件)
request.POST.get() # 只获取列表最后一个元素,str类型
request.POST.getlist() # 直接将列表取出
request.GET # 获取用户提交的get请求数据(url后面的数据)
request.GET.get() # 只获取列表最后一个元素
request.GET.getlist() # 直接将列表取出
"""
get请求携带的数据是有大小限制的,大概4kb左右
post请求则没有限制
"""
# form表单默认是get请求数据
# 不论是get请求还是post请求都会触发login函数体代码
http://127.0.0.1:8000/login/?username=&password=
form表单action参数
1、不写 默认朝当前所在的url提交数据
2、全写,指名道姓
3、只写后缀/ab_render/
# 在前期我们使用django提交post请求的时候,需要去配置文件中注释掉一行代码
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',
]
def login(request):
"""
get请求和post请求应该有不同的处理机制
:param request: 请求相关的数据对象,里面有很多的建议方法
:return:
"""
# print(request.method) # 返回请求方式,并且是全大写的字符串
# if request.method == 'GET':
# print('来了老弟')
# return render(request,'login.html')
# elif request.method == 'POST':
# return HttpResponse('收到了,宝贝')
if request.method == 'POST':
# 获取用户数据
print(request.POST) # 获取用户提交的post请求数(不包含文件)
# < QueryDict: {'username': [''], 'password': ['']} >
# username = request.POST.get('username')
# asd = request.POST.getlist('username')
# print(username, type(username))
# print(asd, type(asd))
# # get只会获取列表最后一个元素
return HttpResponse('收到了,宝贝')
# 获取url后面携带的参数
print(request.GET)
print(request.GET.get('username'))
print(request.GET.getlist('username'))
return render(request, 'login.html')
pycharm连接数据库
# 找到pycharm databases选项
# 选择对应数据库,下载对应驱动
django连接数据库(mysql)
# 默认用的是sqkite3
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db.sqlite3',
}
}
# django连接mysql
1、第一步配置文件中配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'bianji',
'USER':'root',
'PASSWORD':'Huawei12#$',
'HOST':'127.0.0.1',
'PORT':3306,
'CHARSET':'utf8'
}
}
2、代码声明 init文件指定
django默认用的是mysqldb模块连接mysql
但是该模块兼容性不好,需要手动改为pymysql连接
所以需要告诉django使用pymysql
# 在项目名下的init或者任意应用下的init文件中书写代码
import pymysql
pymysql.install_as_MySQLdb()
django ORM
"""
orm不会帮你创建库,只能创建表的层面
需要提前创建号库
"""
ORM. 对象关系映射
作用:能够让一个不会sql语句的小白也能够用python代码操作数据库
不足:封装程度太高,有时候sql语句的效率低 需要自己写sql语句
类 映射 表
对象 记录
对象属性 记录某个字段对应的值
# 1、先去models.py书写一个类
# 这个类里面需要继承models.Model
class User(models.Model):
# id int primary_key auto_increment
id = models.AutoField(primary_key=True)
# username varchar(32)
username = models.CharField(max_length=32,verbose_name='对字段的解释')
"""
CharField必须要指定max_length参数,不写会报错
verbose_name:该参数是所有字段都有的,对字段的解释
"""
# password int
password = models.IntegerField(verbose_name='密码')
class Author(models.Model):
# 由于一张表中必须要有一个主键字段,并且一般情况下都叫id字段
# 所以orm当你不自定义主键字段的时候,orm会自动创建一个名为id主键字段
# 也就意味着后续我们在创建模型表的时候如果主键字段名没有额外的叫法,那么主键字段可以省略不写
# username varchar(32)
username = models.CharField(max_length=32)
# password int
password = models.IntegerField()
****************数据库迁移命令*******************
# 2、数据库迁移命令
python manage.py makemigrations
# 将操作记录记录到mirgrations内,不会操作数据库
python manage.py migrate
# 将操作真正的同步到数据库
# 只要修改了models.py中更数据库相关的代码,就必须重写执行上述的两条命令
# pycharm简单快捷输入
tools
run manage.py task
自动提示
利用ORM实现数据的增删改查操作
字段的增删改
# 字段的添加
1、当表中已经有数据 可以在终端内直接给出默认值
2、该字段可以为空
info = models.CharField(max_length=32,verbose_name='个人简介',null=True)
3、直接给字段设置默认值
hobby = models.CharField(max_length=32,verbose_name='兴趣爱好',default='dbj
# 字段的修改
直接修改代码,然后执行数据库迁移的两条命令即可
# 字段的删除
直接注释对应的字段,然后执行数据库迁移的两条命令即可
执行完毕之后字段对应的数据也没有了
"""
在操作models.py的时候一定要细心
千万不要注释一些字段
执行迁移命令之前,先检查以下自己写的代码
"""
数据的增删改
from app1 import models
# 查
res = models.User.objects.filter(username=username)
# select * from user where username='jason';
# 返回值可以先看成列表套数据的对象格式
# 也支持索引取值,切片操作,但是不支持负数索引
# 他也不推荐使用索引方式取值,推荐使用.first():
user_obj = models.User.objects.filter(username=username).first()
# filter括号内可以携带多个参数,参数与参数之间是and关系
# 增
# 第一种增加,create方法
res = models.User.objects.create(username=username,password=password)
返回的是当前被创建对象本身
print(res,res.username,res.password)
# 第二种增加,save方法
user_obj = models.User(username=username,password=password)
先生成一个类对象,对象调用类方法
user_obj.save() # 保存数据
# 删
# 批量删除(通过修改条件可以实现删除对应数据)
models.user.objects.filter(id=delete_id).delete()
# 改
# 修改数据方式1(批量更新)
# models.user.objects.filter(id=edit_id).update(username=username,password=password)
"""
将filter查询出来的所有对象全部更新,批量操作,通过筛选主键可以实现某一条数据更新
只修改被修改的字段
"""
# 修改数据方式2(单独更新)
edit_obj.username = username
edit_obj.password = password
edit_obj.save()
# 编辑数据
1、先获取用户想要编辑的主键值
url?edit_id=1
url/1/
2、后端查询出对应的数据展示到前端
利用input标签的value属性
3、提交post请求修改数据
前期提交post请求需要去配置文件中注释一行
案例:实现登录,注册,编辑用户
urls.py文件
*****************urls.py文件*******************
from django.contrib import admin
from django.urls import path
from app02 import views
urlpatterns = [
path('admin/', admin.site.urls),
# 登录功能
path('login/', views.login),
# 注册功能
path('register/', views.register),
# 展示功能
path('userlist/', views.user_list),
# 编辑功能
path('edit_user/', views.edit_user),
# 删除用户
path('delete_user/', views.delete_user),
]
views.py文件
*****************views.py文件*******************
from django.shortcuts import render, HttpResponse, redirect
from app02 import models
# Create your views here.
# 登录功能
def login(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
from app02 import models
user_obj = models.user.objects.filter(username=username).first()
if user_obj:
if password == user_obj.password:
return HttpResponse('登录成功')
else:
return HttpResponse('密码错误')
return HttpResponse('账号错误')
return render(request, 'login.html')
# 注册功能
def register(request):
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
from app02 import models
# 方式一
# res = models.user.objects.create(username=username,password=password)
# 方式二
user_obj = models.user(username=username, password=password)
user_obj.save()
return render(request, 'register.html')
# 展示功能
def user_list(request):
from app02 import models
# 查询表中的数据,方式一
# data = models.user.objects.filter()
# print(data)
user_queryset = models.user.objects.all()
print(user_queryset)
# 将函数内的变量都传给html文件
return render(request,'userlist.html',locals())
# 编辑功能
def edit_user(request):
# 获取url问号后面的主键值
edit_id = request.GET.get('user_id')
# 后端查询出用户想要编辑的数据对象,展示到前端页面给用户查看和编辑
edit_obj = models.user.objects.filter(id=edit_id).first()
if request.method == 'POST':
username = request.POST.get('username')
password = request.POST.get('password')
# 去数据库中修改对应的数据内容
# 修改数据方式1(批量更新)
# models.user.objects.filter(id=edit_id).update(username=username,password=password)
"""
将filter查询出来的所有对象全部更新,批量操作,通过筛选主键可以实现某一条数据更新
只修改被修改的字段
"""
# 修改数据方式2(单独更新)
edit_obj.username = username
edit_obj.password = password
edit_obj.save()
"""
上述方法当字段特别多的时候效率会很低
从头到尾将所有字段更新一边,无论字段是否被修改
"""
# 编辑完跳转到数据的展示页面
return redirect('/userlist/')
# 将数据对象展示到页面上
return render(request,'edit_user.html',locals())
# 删除功能
"""
和编辑功能类似
"""
def delete_user(request):
# 获取用户想要删除的数据id主键值
delete_id = request.GET.get('user_id')
# 直接去数据库中找到对应的数据删除即可
models.user.objects.filter(id=delete_id).delete()
"""
批量删除
"""
# 跳转到展示页面
return redirect('/userlist/')
models.py文件
*****************models.py文件*******************
from django.db import models
# Create your models here.
class user(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
def __str__(self):
return '%s'%self.username
*****************html文件*******************
*****************edit_user.thml文件*******************
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
{% load static %}
<link href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}" rel="stylesheet">
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<h1 class="text-center">编辑页面</h1>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<form action="" method="post">
<p>username:<input type="text" name="username" class="form-control" value="{{ edit_obj.username}}"></p>
<p>password:<input type="text" name="password" class="form-control" value="{{ edit_obj.password }}"></p>
<input type="submit" class="btn-block btn btn-info" value="编辑">
</form>
</div>
</div>
</div>
</body>
</html>
login.html文件
*****************login.html文件*******************
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
{% load static %}
<link href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}" rel="stylesheet">
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<h1 class="text-center">登录</h1>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<form action="" method="post">
<p>username:<input type="text" name="username" class="form-control"></p>
<p>password:<input type="text" name="password" class="form-control"></p>
<input type="submit" class="btn-block btn btn-danger">
</form>
</div>
</div>
</div>
</body>
</html>
register.html
*****************register.html文件*******************
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
{% load static %}
<link href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}" rel="stylesheet">
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<h1 class="text-center">注册</h1>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<form action="" method="post">
<p>username:<input type="text" name="username" class="form-control"></p>
<p>password:<input type="text" name="password" class="form-control"></p>
<input type="submit" class="btn-block btn btn-danger">
</form>
</div>
</div>
</div>
</body>
</html>
userlist.html文件
*****************userlist.html文件*******************
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
{% load static %}
<link href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}" rel="stylesheet">
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<h1 class="text-center">页面展示</h1>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>username</th>
<th>password</th>
</tr>
</thead>
<tbody>
{% for user_obj in user_queryset %}
<tr>
<td>{{ user_obj.id }}</td>
<td>{{ user_obj.username }}</td>
<td>{{ user_obj.password }}</td>
<td>
<a href="/edit_user/?user_id={{ user_obj.id }}" class="btn btn-primary btn-xs">编辑</a>
<a href="/delete_user/?user_id={{ user_obj.id }}" class="btn btn-danger btn-xs">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
orm如何创建表关系
表与表主键的关系
一对一 外键创在任意一方即可
一对多 外键创在多的一方
多对多 额外创一个表,另外两张表外键指向创的表
"""
orm中如何定义三种关系
# 一对多
publish_wj = models.ForeignKey('Publish',on_delete=models.CASCADE)
# 多对多
authors_wj = models.ManyToManyField('Author')
# 一对一
author_detall = models.OneToOneField('Author', on_delete=models.CASCADE)
ps:on_delete=models.CASCADE级联操作,但是多对多貌似不用加这个参数
"""
"""
图书表 图书表和出版社多对一,和作者表多对多
出版社
作者表 作者表和作者详情表一对一
作者详情表
"""
*********************案例代码*************************
# 图书表
class Book(models.Model):
title = models.CharField(max_length=32,verbose_name='头')
price = models.DecimalField(max_digits=8, decimal_places=2)
# 总共八位,小数点后面占两位
"""
图书和出版社是一对多,并且书是多的一方,所以外键字段放在书表里面
"""
publish_wj = models.ForeignKey('Publish',on_delete=models.CASCADE)
# to_field参数可以不写,默认就是与出版社表的主键字段做外键关联
"""
图书和作者是多对多的关系,ORM外键字段建在任意一方均可
"""
authors_wj = models.ManyToManyField('Author')
# author是一个虚拟字段,主要是告诉orm,书籍表和作者表是多对多
# 让orm自动创建第三张关系表
# 这里貌似不用加on_delete=models.CASCADE 级联删除
# 出版社
class Publish(models.Model):
name = models.CharField(max_length=32)
# 地址
addr = models.CharField(max_length=32)
# 作者表
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
"""
作者与作者详情是一对一的府岸西,外键在任意一方都行
"""
author_detall = models.OneToOneField('Author', on_delete=models.CASCADE)
# 作者详情表
class AuthorDetail(models.Model):
phone = models.BigIntegerField() # 或者直接字符类型
addr = models.CharField(max_length=32)
django生命周期流程图
django路由层
urls.py
# 路由匹配
url(r'test',views.text),
url(r'testadd',views.testadd)
"""
url方法第一个参数就是正则表达式
只要第一个参数正则表达式能够匹配到内容,那么就会停止往下匹配
直接指向当前匹配到的对应视图函数
在浏览器输入url的时候会默认加斜杠
django内部做重定向
第一次匹配不行
会自动匹配第二次,这一次会加斜杠再来一次
# 取消自动加斜杠
APPEND_SLASH = False/True # 默认是自动加斜杠的
urlpatterns = [
path(r'^admin/', admin.site.urls),
# 首页
path(r'^$', views.login),
# 路由匹配
path(r'^test/$', views.register),
path(r'^userlist/$', views.user_list),
# 尾页404
path(r'', views.delete_user),
]
如果是test则输入testasdasd也能匹配到
如果是test/则输入asdasdtest/也能匹配到
如果是^test/则输入test/asdad也可以匹配到
如果是^test/$这样才能精确匹配
如果正则里啥都不写r'',则表示全部匹配这种一般丢到最下面
正则里是r'^$',这种一般表示首页,直接记不要问为啥
ps:
^ 匹配字符串的开头
$ 匹配字符串的末尾。
无名分组,有名分组
# 1、无名分组
"""
分组:就是将某一个正则加小括号括起来
"""
path(r'^test/(\d+)/', views.register)
def register(request,xx)
print(xx)
pass
# 无名分组就是将括号内正则表达式匹配到的内容当作位置参数传递给视图函数
ps:
\d 匹配任意数字,等价于 [0-9]
# 2、有名分组
"""
可以给正则表达式起一个别名
"""
path(r'^test/(?p<year>\d+)/(?p<year>\d+)', views.register)
def register(request,year)
print(year)
pass
# 有名分组就是将括号内正则表达式匹配到的内容当作关键字参数传递给视图函数
# 3、有名无名不能混用,但是同一个分组可以使用n次
# 4、单个分组可以重复使用
path(r'^test/(\d+)/(\d+)/', views.register)
def register(request,*args,**kwargs)
print(args)
print(kwargs)
path(r'^test/(?p<year>\d+)/(?p<age>\d+)/', views.register)
def register(request,*args,**kwargs)
print(args)
print(kwargs)
反向解析
# 通过一些方法得到一个结果,改结果可以直接访问对应的url触发视图函数
# 1、反向解析
# 第一步,先给路由与视图函数起一个别名
path(r'^test/', views.register,name='ooo')
# 第二步反向解析
后端方向解析:可以拿到数据的主键值
from django.shortcuts import reverse
print(reverse('ooo'))
这样后端可以拿到解析的url /test/user_obj.id
前端反向解析:
<a href="{% url 'ooo' user_obj.id %}">编辑</a>
# 该标签访问的是 /ooo/user_obj.id
# 2、无名分组反向解析
url(r'^index/(\d+)/',views.index,name='xxx')
# 前端
{% url 'xxx' 123 %}
# 后端
reverse('xxx',args=(1,))
# 3、有名分组反向解析
url(r'^index/(?p<year>\d+)/',views.index,name='ooo')
# 前端
<a href="{% url 'ooo' year=123 %}">111</a> 了解
<a href="{% url 'ooo' 123 %}">222</a>
# 后端
reverse('ooo',kwargs=('year',123)) 了解
reverse('ooo',args=(1,))
# 4、案例
"""
这个参数写代码的时候应该放什么?
数字一般放的是数据的主键值 数据的编辑和删除url(r'^index/(\d+)/',views.index,name='xxx')
def edit(request,edit_id):
pass
{%for user_obj in user_queryset%}
<a href="{% url 'xxx' user_obj.id %}">编辑</a>
{%endfor%}
案例:通过无名有名,反向解析,完成数据的增删改
"""
无名反向解析案例
"""
通过无名反向解析,完成数据的编辑
"""
urls:
from django.contrib import admin
from django.urls import re_path
from app01 import views
urlpatterns = [
re_path('admin/', admin.site.urls),
re_path(r'^bianji/', views.bj_user),
re_path(r'del_user/(\d+)/', views.del_user, name='ooo'),
re_path(r'edit_user/(\d+)/', views.edit_user, name='eee'),
]
========================================
views:
from django.shortcuts import render, HttpResponse, redirect
import app01.models
from app01 import models
from django.shortcuts import reverse
# Create your views here.
# 编辑页面
def bj_user(request, ):
user_queryset = models.User.objects.all()
# print(reverse('xxx', args=(1,)))
return render(request, 'bj_user.html', locals())
# 编辑数据
def edit_user(request, edit_id):
edit_obj = models.User.objects.filter(id=edit_id).filter()
print(edit_obj)
if request.method == 'POST':
name = request.POST.get('username')
describe = request.POST.get('password')
models.User.objects.filter(id=edit_id).update(name=name, describe=describe)
return redirect('/bianji/')
return render(request, 'edit_user.html', locals())
# 删除数据
def del_user(request, edit_id):
models.User.objects.filter(id=edit_id).delete()
return redirect('/bianji/')
=============================================
edit_user.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
<link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/3.4.1/js/bootstrap.min.js"></script>
</head>
<body>
<h1 class="text-center">编辑页面</h1>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<form action="" method="post">
{% for foo in edit_obj %}
<p>username:<input type="text" name="username" class="form-control" value="{{ foo.name }}"></p>
<p>password:<input type="text" name="password" class="form-control" value="{{ foo.describe }}"></p>
<input type="submit" class="btn-block btn btn-danger">
{% endfor %}
</form>
</div>
</div>
</div>
</body>
</html>
==============================================
bj_user.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.4.0/jquery.min.js"></script>
{% load static %}
<link href="{% static 'bootstrap-3.4.1-dist/css/bootstrap.min.css' %}" rel="stylesheet">
<script src="{% static 'bootstrap-3.4.1-dist/js/bootstrap.min.js' %}"></script>
</head>
<body>
<h1 class="text-center">展示页面</h1>
<div class="container">
<div class="row">
<div class="col-md-8 col-md-offset-2">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>username</th>
<th>describe</th>
</tr>
</thead>
<tbody>
{% for user_obj in user_queryset %}
<tr>
<td>{{ user_obj.id }}</td>
<td>{{ user_obj.name }}</td>
<td>{{ user_obj.describe }}</td>
<td>
{# <a href="/edit_user/?user_id={{ user_obj.id }}" class="btn btn-primary btn-xs">编辑</a>#}
<a href="{% url 'eee' user_obj.id %}" class="btn btn-primary btn-xs">编辑</a>
<a href="{% url 'ooo' user_obj.id %}" class="btn btn-danger btn-xs">删除</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
路由分发
"""
每一个应用都可以有自己的templates文件夹,urls.py static文件夹
正是基于上述的特点,django能够做到分组开发(每个人只写自己的app)
二作为组长,只需要将手下书写的app全部cp到一个新的django项目中 然后配置文件里注册所有的app,在利用路由分发的特点将所有的app整合起来
当一个django项目中url特别多的时候,总路由urls.py代码非常冗余不好维护
这个时候也可以利用路由分发来减轻总路由的压力
利用路由分发之后,总路由不在干路由与视图的直接对应关系
而是做一个分发处理
识别当前url属于那个app的,然后进行转发
"""
# 1、总路由
from app01 import urls as app01_urls
from app02 import urls as app02_urls
urlpatterns = {
url(r'^admin/',admin.site.urls),
# 路由分发
# 方法一:
url(r'^app01/',include(app01_urls)), # 只要url前缀是app01开头的全部交给app01处理
url(r'^app02/',include(app02_urls)),
# 方法二(常用):
url(r'^app01/',include(app01.urls)),
url(r'^app02/',include(app02.urls)),
}
# 2、子路由
# app01 urls.py
from django.conf.urls import url
from app01 import views
urlpatterns = {
url(r'^reg/',views.reg)
}
# app02 urls.py
from django.conf.urls import url
from app02 import views
urlpatterns = {
url(r'^reg/',views.reg)
}
命名空间(了解)
当我们多人开发app的时候,大家设置的别名一样时,反向解析则不能自动识别
这个时候可以用过命名空间来区分
# 名称空间
# 总路由
url(r'^app01/',include('app01_urls',namespace='app01')),
url(r'^app02/',include('app02_urls',namespace='app02'))
# 解析的时候
# app01
urlpatterns = {
url(r'^reg/',views.reg,name='reg')
}
# app02
urlpatterns = {
url(r'^reg/',views.reg,name='reg')
}
# 后端解析
reverse('app01:reg')
reverse('app02:reg')
# 前端解析
{% url 'app01:reg'%}
{% url 'app02:reg'%}
伪静态(了解)
"""
静态网页
数据是写死的,万年不变
伪静态
将一个动态网页伪装成静态网页
"""
# 实现,直接加个.html
urlpatterns = {
url(r'^reg.html/',views.reg,name='reg')
}
django版本区别(了解)
"""
1、django1.x路由层使用的是url方法
而在2或者3本本中路由层使用的是path方法
url()第一个参数支持正则
path()第一个参数不支持正则,写什么就匹配什么
2、虽然path不支持正则,但是它的内部支持五种转换器
str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
int,匹配正整数,包含0。
slug,匹配字母、数字以及横杠、下划线组成的字符串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
3、例子
path('articles/<int:year>/<int:month>/<slug:other>/', views.article_detail)
# 针对路径http://127.0.0.1:8000/articles/2009/123/hello/,path会匹配出参数year=2009,month=123,other='hello'传递给函数article_detai
"""
django视图层
三板斧
HttpRespose
返回字符串类型的数据
return HttpResponse('你好啊,我是django妹质')
render
返回html文件的
return render(request,'myfilst.html')
# 方式一:将user_dict传给html文件,html文件通过data取值
return render(request,'myfilst.html',{'data':user_dic,'data',123})
# 方式二:locals会将所在名称空间中所有名字传递给html页面
return render(request,'myfilst.html',locals())
redirect
重定向
return redirect('http://47.96.177.8:82/')
return redirect('/home/') # 还可以重定向到自己的目录
===============案例===============
from django.shortcuts import render,HttpResponse,redirect
def index(request):
"""
:param request: 请求相关的所有数据,比之前的env更加牛逼
:return:
"""
# return HttpResponse('你好啊,我是django妹质')
# return render(request,'myfilst.html') # 自动去tempaltes文件夹下查找文件
# return redirect('http://47.96.177.8:82/')
return redirect('/home/')
jsonResponse
"""
json格式的数据有什么用?
前后端交互需要使用json作为过渡,实现跨语言传输数据
"""
import json
from django.http import JsonResponse
def ab_json(request):
user_dict = {'username':'khw阿斯顿'}
1 = [1,2,3]
# 先转成json格式字符串
json_str = json.dumps(user_dict,ensure_ascii=False) # 表示不动冒号里的数据,也就是不将起转化为ASCII码
# 将该字符串返回
# return HttpResponse(json_str)
# return JsonResponse(l,safe=False)
# 默认只能序列化字典,序列化其他需要加safe参数
form表单上传文件
"""
form表单上传文件夹类型的数据
1、method必须指定成posts
2、enctype必须换成formdata
"""
def ab_file(request):
if request.method == "POST":
# print(request.POST) 只能获取普通的键值对数据,文件不行
print(request.FILES) # 获取文件数据
file_obj = request.FILES.get('file') # 文件对象
print(file_obj.name)
with open('file','wb') as f:
for line in file_obj.chunks(): # 推荐加上chunks方法,其实不加也是一行一行读取数据
f.write(line)
return render(requet,'form.html')
# request对象方法
request.method # 返回请求方式,并且是全大写的字符串形式
request.POST # 获取用户post请求提交的普通数据(不包含文件)
request.GET
request.FILES # 获取文件
request.body # 原生浏览器发送过来的二进制数据
request.path # 能获取url参数
request.get_full_path() # 能获取完整的url及问号后面的参数
FBV与CBV
# 视图函数既可以是函数也可以是类
# FBV函数
def index(request):
return HttpResponse('index')
# CBV类
# 能够更具请求方式不同自动匹配方法的执行
from django.views import View
# CBV路由
url(r'^login/',views.Mylogin.as_view())
""" 只要是处理业务逻辑的视图函数,形参里面肯定要有request """
class Mylogin(view):
def get(self,request):
return render(request,'form.html')
def post(self,request):
return HttpResponse('post方法')
CBV源码剖析
re_path(r'^login/', views.MyLogin.as_view())
# 上述代码在启动django的时候会立即执行as_view方法
# 通过源码得知该行等价于 re_path(r'^login/', views.view)
# CBV与FBV在路由匹配上本质是一样的,都是路由对应函数的内存地址
"""
函数名/方法名 加括号执行优先级最高
猜测
as_view
要么是被@staicmethod修饰的静态方法
要么是被@classmethod修饰的类方法 正确
"""
def as_view(cls, **initkwargs):
"""
cls 就是我们自己写的类 MyLogin
"""
def view(request, *args, **kwargs):
self = cls(**initkwargs) # cls我们自己写的类
# self = Mylogin(**initkwargs) # 产生一个自己写的类的对象
return self.dispatch(request, *args, **kwargs)
"""
ps:一定要知道self.dispatch这个对象属性的查找顺序
先从对象自己找
再去产生对象的类里面找
之后再去父类里找
总结:看源码只要看到self点了一个东西,一定要问自己当前这个self到底是谁
"""
return view
# CBV精髓
def dispatch(self, request, *args, **kwargs):
# 获取当前请求的小写格式,然后对比当前请求方式是否合法
# get请求为例
# post请求
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
"""
反射:通过字符串来操作对象属性或方法
handler = getattr(自己写的类产生的对象, 'get', 当找不到get属性或者方法会用到这个参数)
handler = 我们自己写的类里面的get方法
"""
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs)
"""
自动调用get方法
"""
settings源码剖析
django模板层
模板语法传值
{{}}:变量相关
{%%}:逻辑相关
def index(request):
# 模板语法传递的后端python数据类型
n = 123
f = 11.11
s = '字符串'
b = True
l = ['地', '瓜']
t = (111, 222, 333)
d = {'username': 'dig', 'age': 18, 'info': '这个人有点意思','hobby':[111,222,{'info':'NB'}]}
se = {'撒旦', '地方', '速度'}
def func():
print('我被执行了')
return '我在等你'
class MyLogin(object):
def get_self(self):
return 'self'
@staticmethod
def get_func():
return 'func'
@classmethod
def get_class(cls):
return 'cls'
# 对象被展示到html页面上就类似于执行了打印操作,也会触发__str__方法
def __str__(self):
return '会不会玩'
obj = MyLogin()
return render(request, 'index.html', locals())
# 模板传值
<p>{{ n }}</p>
<p>{{ f }}</p>
<p>{{ s }}</p>
<p>{{ b }}</p>
<p>{{ l }}</p>
<p>{{ d }}</p>
<p>{{ t }}</p>
<p>{{ se }}</p>
<p>传递函数名会自动加括号调用,但是模板语法不支持给函数传额外的参数:{{ func }}</p>
<p>传类名的时候也可以自动加括号调用(实例化){{ MyLogin }}</p>
<p>内部能够自动判断出当前的变量名是否可以加括号调用,如果可以就会自动执行,这个针对的是函数和类名</p>
<p>{{ obj }}</p>
<p>{{ obj.get_class }}</p>
<p>{{ obj.get_func }}</p>
<p>{{ obj.get_self }}</p>
# 模板取值
<p>{{ d.username }}</p>
<p>{{ l.0 }}</p>
<p>{{ d.hobby.2.info }}</p>
# 通过点来取值
模板语法之过滤器(最多两个参数)
def index(request):
# 模板语法传递的后端python数据类型
n = 123
f = 11.11
s = '字符串'
b = True
l = ['地', '瓜','3','4']
t = (111, 222, 333)
d = {'username': 'dig', 'age': 18, 'info': '这个人有点意思','hobby':[111,222,{'info':'NB'}]}
se = {'撒旦', '地方', '速度'}
def func():
print('我被执行了')
return '我在等你'
class MyLogin(object):
def get_self(self):
return 'self'
@staticmethod
def get_func():
return 'func'
@classmethod
def get_class(cls):
return 'cls'
# 对象被展示到html页面上就类似于执行了打印操作,也会触发__str__方法
def __str__(self):
return '会不会玩'
obj = MyLogin()
file_size = 10240000
import datetime
current_time = datetime.datetime.now()
info = '# 商业转载请联系作者获得授权,非商业转载请注明出处。'
egl = 'my name is khw age 18'
msg = 'i love you and you?'
hhh = '<h1>地瓜<h1>'
from django.utils.safestring import mark_safe
res = mark_safe('<h1>木可<h1>')
return render(request, 'index.html', locals())
<p>默认长度:{{ s|length }}</p>
<p>默认值(第一个参数布尔值是true就展示第一个参数的值,否则展示默认值):{{ b|default:"啥也不是" }}</p>
<p>文件大小:{{ file_size|filesizeformat }}</p>
<p>日期格式化:{{ current_time|date:'Y-m-d H:i:s' }}</p>
<p>切片操作(0到4步长为2):{{ l|slice:'0:4:2' }}</p>
<p>切取字符(摘要,后面数字表示截取多少个字符,包含三个点):{{ info|truncatechars:9 }}</p>
<p>切取单词(按照空格切,中文也是一样):{{ egl|truncatewords:4 }}</p>
<p>移除特定的字符(该例将空格切除):{{ msg|cut:' ' }}</p>
<p>拼接操作:{{ l|join:"$" }}</p>
<p>拼接操作(加法):{{ n|add:10 }}</p>
<p>拼接操作(还能拼接字符串):{{ s|add:msg }}</p>
<p>转义:{{ hhh|safe }}</p>
# 转义
# 前端操作
<p>转义:{{ hhh|safe }}</p>
# 后端操作
from django.utils.safestring import mark_safe
res = mark_safe('<h1>木可<h1>')
return render(request, 'index.html', locals())
模板语法之标签
# for循环
{% for foo in l %}
<p>{{ forloop }}</p>
<p>{{ foo }}</p>
{% empty %}
<p>for循环可迭代对象是空,根本没法循环</p>
{% endfor %}
{'parentloop': {}, 'counter0': 2, 'counter': 3, 'revcounter': 2, 'revcounter0': 1, 'first': False, 'last': False}
{'parentloop': {}, 'counter0': 3, 'counter': 4, 'revcounter': 1, 'revcounter0': 0, 'first': False, 'last': True}
first:表示该for循环是否为第一次
last:表示该for循环是否为最后一次
counter0:表示计数 2 表示从2开始
counter:表示数数,3表示从3开始
# if判断
{% if b %}
<p>贝比</p>
{% elif s%}
<p>都来把</p>
{% else %}
<p>老贝比</p>
{% endif %}
# for与if混合使用
{% for foo in l %}
{% if forloop.first %}
<p>这是我的第一次</p>
{% elif forloop.last %}
<p>这是最后一次</p>
{% else %}
<p>老贝比</p>
{% endif %}
{% empty %}
<p>for循环可迭代对象是空,根本没法循环</p>
{% endfor %}
# 处理字典的其他方法
{% for foo in d.keys %}
<p>{{ foo }}</p>
{% endfor %}
{% for value in d.values %}
<p>{{ value }}</p>
{% endfor %}
{% for item in d.items %}
<p>{{ item }}</p>
{% endfor %}
# with起别名
{% with d.hobby.2.info as nb %}
<p>{{ nb }}</p>
<p>在whit语法内可以通过as后面的别名使用到前面非常复杂的获取数据方式</p>
{% endwith %}
自定义过滤器、标签、inclusion_tag
"""
先三步走
1、在应用下面创建一个名字“必须”叫templatetages文件夹
2、在该文件夹内创建“任意”名称的py文件 eg:mytag.py
3、在该py文件内“必须”先写下面两句话
from django import template
register = template.Library()
"""
# 1、自定义过滤器(参数最多两个)
@register.filter(name='baby')
def my_sum(v1, v2):
return v1 + v2
自定义的使用(过滤器最多只能有两个参数)
{% load mytag %}
<p>{{ n|baby:666 }}</p>
# 2、自定义标签(参数可以多个)
@register.simple_tag(name='plus')
def index(a,b,c,d):
return '%s-%s-%s-%s'%(a,b,c,d)
标签多个参数彼此之间空格隔开
{% load mytag %}
<p>{% plus 'jason' 123 123 123 %}</p>
# 3、自定义inclusion_tag
"""
内部原理
先定义一个方法
在一面上调用方法,并可以传值
该方法会生成一些数据然后传递给一个html页面
之后将渲染好的结果放到调用位置
"""
# 自定义inclusion_tag
@register.inclusion_tag('left_menu.html')
def left(n):
data = ['第{}项'.format(i) for i in range(n)]
# return {'data':data}
return locals() # 将data传递给left_menu.html
使用:
{% left 10 %}
# 总结:当html页面某一个地方的页面需要传参数才能动态渲染出来,并且在多个页面上都需要使用该局部,那么就考虑该局部页面做成inclusion_tag形式
(在讲bbs的时候使用到)
模板的继承
模板继承,选选好一个想要继承的模板页面
# 模板的继承
{% extends 'bj_user.html' %}
# 继承之后子页面和模板页面长得一摸一样,需要在模板页面上提前划定可以被修改的区域
{% block content %}
模板的内容
{% endblock %}
# 子页面就可以声明想要修改那块决定的区域
{% block content %}
子页面的内容
{% endblock %}
# 一般情况下模板页面上至少有三块可以修改的区域
1、css区域
2、html区域
3、js区域
# 这样操作后,每个页面就可以有自己独有的css,html,js代码
"""
一般情况下,模板的页面上划定的区域越多,扩展性越高
如果太多,还不如自己写
"""
模板的导入
"""
将页面的某一个局部当成一个模块的形式
那个地方需要直接导入即可
"""
<p>模板的导入</p>
{% include 'bj_user.html' %}
django模型层
图书管理系统
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8, decimal_places=2)
publish_data = models.DateField(auto_now_add=True)
# 书和出版社一对多
publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
# 和作者多对多
author = models.ManyToManyField(to='Author')
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
email = models.EmailField() # 本质还是varchar(254),该字段是给校验性组件看的
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
# 作者和作者详情一对一
author_detail = models.OneToOneField(to='AuthorDetail',on_delete=models.CASCADE)
class AuthorDetail(models.Model):
phone = models.BigIntegerField() # 电话号码用BigIntegerField或者CharField
addr = models.CharField(max_length=32)
单表操作
# django自带的sqllite3数据库对日期格式不是很敏感 处理的时候容易出错
# 增
# res = models.user.objects.create(name='digua', age=18, register_time='2021-11-28')
# print(res)
# import datetime
# ctime = datetime.datetime.now()
# user_obj = models.user(name='egon', age=84, register_time=ctime)
# user_obj.save()
# 删
# res = models.user.objects.filter(pk=2).delete()
# print(res)
# user_obj = models.user.objects.filter(pk=1).first()
# user_obj.delete()
"""
pk会自动查找到当前表的主键字段,指代的就是当前表的主键
用了pk之后就不需要知道当前表的字段到底叫什么
"""
# 修改
# models.user.objects.filter(pk=4).update(name='hongweidsb')
# user_obj = models.user.objects.get(pk=4)
# user_obj.name = 'egonPPP'
# user_obj.save()
# print(user_obj)
"""
get方法直接返回的就是当前数据对象
该方法不推荐
一旦数据不存在,该方法会直接报错而filter则不会,所以还是用filter
"""
测试脚本
"""
当你只是想测试django中某一个py文件内容,那么你可以不用书写前后端交互模式,可以直接写一个测试脚本
"""
from django.test import TestCase
# Create your tests here.
import os
if __name__ == '__main__':
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'djangoProject.settings')
import django
django.setup()
from app01 import models
models.User.objects.all()
django终端答应sql语句
# 方式1
res3 = models.user.objects.values_list('name')
print(res3)
print(res3.query)
"""
# 查看内部sql语句的方式,queryset对象才能狗点query查看内部sql语句
"""
# 方式2:所有sql语句都能查看
取配置文件中配置以下即可
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}
必知必会13条
必知必会n多条
# 1、all() 查询所有数据
# 2、filter() 带有过滤条件
# 3、get() 直接拿数据对象,但不存在会报错
# 4、first() 拿queryset里面的第一个元素
res = models.user.objects.all().first()
print(res)
# 5、last() 拿queryset对象里最后一个元素
res1 = models.user.objects.all().last()
print(res1)
# 6、values() 筛选取值
res2 = models.user.objects.values('name')
print(res2)
# 里面是列表套字典
# 7、values_list() 筛选取值
res3 = models.user.objects.values_list('name')
print(res3)
# 8、distinct() 去重
res = models.user.objects.all().distinct()
print(res)
需要将主键筛选走才能去重
res = models.user.objects.values('name', 'age').distinct()
print(res)
# 9、order_by() 排序
res = models.user.objects.order_by('age') # 默认升序
res = models.user.objects.order_by('-age') # 降序
print(res)
# 10、reverse() 反转,反转的前提是数据已经排序
res = models.user.objects.order_by('age')
res1 = models.user.objects.order_by('age').reverse()
print(res1)
# 11、count() 统计当前数据的个数
res = models.user.objects.count()
print(res)
# 12、exclude() 排除在外
res = models.user.objects.exclude(name='json')
print(res)
# 13、exists() 判断某个东西是否存在
res = models.user.objects.filter(pk=9).exists()
print(res)
神奇的双下划线查询
"""
# 神奇的双下划线查询
1、 年龄大于35岁的数据
res = models.user.objects.filter(age__gt=35)
print(res)
2、 年龄小于35
res = models.user.objects.filter(age__lt=35)
print(res)
3、 大于等于,小于等于
res = models.user.objects.filter(age__gte=32)
res = models.user.objects.filter(age__lte=32)
4、 年龄是18 或者32 或者40
res = models.user.objects.filter(age__in=[18,32,40])
print(res)
5、 年龄在18到40岁之间的
res = models.user.objects.filter(age__range=[18,40])
首尾都会取
6、 模糊查询,查询出名字含有n的数据 模糊查询,不忽略大小写
res = models.user.objects.filter(name__contains='n')
7、 忽略大小写
res = models.user.objects.filter(name__icontains='D')
print(res)
8、以什么开头,什么结尾的
models.user.objects.filter(name__startswith='j')
models.user.objects.filter(name__endswith='j')
9、查询出 注册时间是2021年6月的数据,22年的数据
res = models.user.objects.filter(register_time__month='6')
res = models.user.objects.filter(register_time__year='2021')
...
"""
外键字段的增删改
"""
一对多的外键增删改查
1、增
1)直接写实际字段id
models.Book.objects.create(title='三国演绎', price=123.23, publish_id=1)
2)虚拟字段,给数据对象
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.create(title='剑来', price=231.23, publish=publish_obj)
2、删
级联删除
models.Publish.objects.filter(pk=1).delete()
3、修改
1)直接写id
models.Book.objects.filter(pk=1).update(publish_id=2)
2)传对象
obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.filter(pk=1).update(publish=obj)
多对多外键增删改查(就是第三张关系表的增删改)
1、如何给书籍绑定作者?(增加)
1)放id
# 先拿到一个书籍对象,里有个authors外键属性,点了authors之后就已经到了第三方关系表了
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.author.add(1,2) # 给书籍id为1的书籍绑定一个主键为1和2的作者
2)放对象
book_obj = models.Book.objects.filter(pk=1).first()
author_obj = models.Author.objects.filter(pk=3).first()
book_obj.author.add(author_obj)
2、删除作者字段
1)放id
book_obj = models.Book.objects.filter(pk=1).first()
book_obj.author.remove(3,2,1)
# 删除书主键为1,作者主键为3,2,1的这三条数据
2)放对象
book_obj = models.Book.objects.filter(pk=5).first()
author_obj = models.Author.objects.filter(pk=3).first()
book_obj.author.remove(author_obj)
# 删除主键为5的书的,作者主键为3的这条数据
3、修改字段 set
1)放id
book_obj = models.Book.objects.filter(pk=2).first()
book_obj.author.set([1,2]) # 括号内必须给一个可迭代对象
# 将书id为2的数据删除,然后创建将书id为2的作者id改为1和2的两条数据
book_obj.author.set([3])
# 将书id为2的数据删除,然后创建书为2作者id为3的数据
2)放对象
book_obj = models.Book.objects.filter(pk=6).first()
author_obj = models.Author.objects.filter(pk=3).first()
book_obj.author.set([author_obj])
# 将书id为6的这条数据中的作者id改为3
3、清空数据
比如有本书下架了,我想将书籍和作者的绑定关系给清空
book_obj = models.Book.objects.filter(pk=6).first()
book_obj.author.clear()
# 将主键为6的这本书和作者的所有关系清空掉,括号内不加参数
"""
正反向概念
"""
一对一和多对多的正反向判断:
正向
看外键字段在哪一方
书查出版社则外键在书手上,这就是正向
反向
出版社查书,外键在书上,则这种为反向
"""
"""
正向查询按字段
反向查询按表名小写
"""
多表查询
子查询就是分步查询
"""
基于对象的跨表查询
1、查询书籍主键为1的出版社 正向按字段
book_obj = models.Book.objects.filter(pk=1).first()
# 书查出版社 正向
res = book_obj.publish
# 书籍主键为1的书对应的出版对应东方出版社
print(res)
print(res.name)
print(res.addr)
2、查询书籍主键为1的作者 书查作者外键在书,为正向,按字段
book_obj = models.Book.objects.filter(pk=2).first()
res = book_obj.author # app01.Author.None
res = book_obj.author.all()
print(res)
ps:如果结果为app01.Author.None,则需要.all()
3、查询作者hongwei的电话号码 作者查作者详情,正向
author_obj = models.Author.objects.filter(name='hongwei').first()
res = author_obj.author_detail (当结果为一个对象的时候不需要.all)
print(res)
print(res.phone)
print(res.addr)
ps:正向什么需要加.all
当结果可能有多个的时候有.all
如果只有一个则不需要.all
4、查询出版社是东方出版社出版的书 出版社查书,反向,表名小写
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
res = publish_obj.book_set.all()
print(res)
5、查询作者是hongwei写过的书 作者查书,反向,表名小写
author_obj = models.Author.objects.filter(name='hongwei').first()
res = author_obj.book_set.all()
print(res)
6、查询手机号为110的作者姓名 作者详情查作者,反向,表名小写
author_detail_obj = models.AuthorDetail.objects.filter(phone=110).first()
res = author_detail_obj.author
print(res.name)
ps:反向什么时候需要加_set.all()
反向查询的时候
当你的查询结果可以有多个的时候,就必须加_set.all()
当你的结果只有一个的时候,不需要加_set.all()
"""
"""
基于双下划线的跨表查询
1、查询hongwei的手机号和姓名
res = models.Author.objects.filter(name='hongwei').values('author_detail__phone', 'name)
print(res)
反向,不允许点author
# 拿作者是hongwei的作者详情
res = models.AuthorDetail.objects.filter(author__name='hongwei').values('phone','author__name')
print(res)
2、查询书籍主键为1的出版社名称和书的名称
res = models.Book.objects.filter(pk=1).values('title', 'publish__name')
print(res)
反向:
res = models.Publish.objects.filter(book__id=1).values('name','book__title')
print(res)
3、查询书籍主键为1的作者姓名
res = models.Book.objects.filter(pk=2).values('title','author__name')
print(res)
反向:
res = models.Author.objects.filter(book__id=1).values('name', 'book__title')
print(res)
4、查询书籍主键是1的作者的手机号
res = models.Author.objects.filter(book__id=2).values('name','author_detail__phone')
print(res)
res = models.Book.objects.filter(pk=2).values('author__author_detail__phone')
print(res)
只要掌握了正反向概念以及双下划线
就能无限跨表
"""
聚合查询(聚合函数)
分组查询(group by)annotate
F与Q查询
django事务
orm中常用字段及参数
数据库查询优化
图书管理系统案例