1. MVT模型

Django模型的基本概念:
M(Model)负责数据与规则,V(View)负责业务与流程,T(Template)负责展示。
View(视图):真正的大脑
在 Django 中,View 并不是页面,而是:
-
接收 HTTP 请求
-
调用 Model 处理数据
-
决定返回什么响应(HTML / JSON / 重定向)
如果用一句话概括:
View 是请求与响应之间的决策者。
Model(模型):世界的事实陈述者
Model 是你对现实世界的抽象说明书。
在 Django 里,Model 通常就是一堆 Python 类,用来描述:
-
表结构(字段、类型)
-
数据约束(唯一、非空、外键)
-
与数据库交互的规则
它不关心页面长什么样,也不关心请求从哪来,只做一件事:
定义数据是什么,以及数据之间的关系。
Template(模板):克制的表现主义者
Template 只负责一件事:
把数据渲染成用户能看的东西。
2. Django的安装和简单初体验
## django下载安装
pip install django==3.1.2
## 指令创建
django-admin startproject zhibang(项目名称)
## 创建应用
python manage.py startapp login(应用app名称)
在项目的settings.py文件中
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'login' ## 加上应用名称
]
在url.py文件中设置路径和函数的对应关系
from django.contrib import admin
from django.urls import path
from login import views
urlpatterns = [
path('admin/', admin.site.urls),
path('home/', views.home),
]
在views.py文件中写视图函数
from django.shortcuts import render
def home(request): ## 参数名称业内一般都写成request,
print(request.path)
current_name = 'zhibang'
ret = render(request,'home.html',{'username':current_name})
return ret
templates文件夹中创建一个home.html文件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>尊贵的{{username}}用户,欢迎您!</h1>
</body>
</html>
在settings.py文件中加上如下配置
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR,'templates'], ## 注意的配置哦
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
浏览器访问
3. Django框架的url 模块
在路由模块中需要注意的点
urlpatterns = [
path('admin/', admin.site.urls),
path('home/',views.login),
path('index/',views.index)
]
## 1. 路径的前置导航斜杠(对应根路径那个),不需要写,django自动加上
## 2. http://127.0.0.1:8000/index 当我们访问django的url路径时,如果请求路径最后没有写/,那么django会发一个重定向的响应,告诉浏览器,加上/再来访问我
## 3. 在settigns.py配置文件中修改:
# 默认为True, 当值为True时,django需要请求路径后面加上斜杠,如果请求没有加,那么响应301重定向,让浏览器街上斜杠重新请求
APPEND_SLASH = True
url分组
urlpatterns = [
path('admin/', admin.site.urls),
path('home/',views.login),
path('index/<int:year>/',views.index) ## django的版本默认使用了关键字传参了。抛弃了以前的正则表达式的书写方式
]
路由分发
django中可以使用路由分发来来为url统一的管理起来。每一个 HTTP 请求进来,它干的第一件事不是执行业务逻辑,而是先问一句:这条 URL,到底该谁来处理?然后去找到对应的应用下面处理业务逻辑问题
### 创建app01,app02,app03三个应用
## 在总项目下的url.py中只要写入app的应用就可以了
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/',include('app01.urls')),
path('app02/',include('app02.urls')),
path('app03/',include('app03.urls')),
]
# 在每个应用下创建urls.py文件,写每个应用自己的路径
# app01/urls.py
from app01 import views
from django.urls import path, include
from app01 import views
urlpatterns = [
path('index/',views.index,name='index')
]
# app01/view.py
from django.shortcuts import render,HttpResponse
# Create your views here.
def index(resquest):
return HttpResponse('app01 ok!')
# app02/urls.py
from django.urls import include,path
from app02 import views
urlpatterns = [
path('index/',views.index,name='index')
]
# app02/view.py
from django.shortcuts import render,HttpResponse
# Create your views here.
def index(resquest):
return HttpResponse('app02 ok!')
# app03/urls.py
from django.urls import path,include
from app03 import views
urlpatterns = [
path('index/',views.index,name='index')
]
# app03/view.py
from django.shortcuts import render,HttpResponse
# Create your views here.
def index(request):
return HttpResponse('app03 ok')
#当用户访问的路径为 /app01/index/那么会先找到总路由,总路由先匹配路径前缀,匹配好之后,就找到对应的应用下面的urls.py文件中的路径来匹配剩余部分路径,然后执行对应的视图函数.
名称空间
## 总路由写法
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('app01/',include('app01.urls')),
path('app02/',include('app02.urls')),
path('app03/',include('app03.urls')),
]
# 在子应用中需要用app_name 定义
app_name = "app01"
urlpatterns = [
path('index/',views.index,name='index')
]
# 在view.py的视图函数后需要定义
from django.shortcuts import render,HttpResponse,reverse
def index(resquest):
# 命名空间名称:别名名称
print(reverse('app02:index'))
return HttpResponse('app02 ok!')
4. Django框架的view 模块
request方法
from django.shortcuts import render,HttpResponse,redirect
## request 常用的属性
def login(request):
print(request) #<WSGIRequest: GET '/login/?a=1&b=2'> WSGIRequest类的实例化对象
print(request.method)
print(request.POST)
print(request.GET) # request.GET.get('a') == 1
print(request.path) #当前请求路径
print(request.get_full_path()) #当前请求路径包含查询参数
print(request.META) #所有请求头的信息 {''HTTP_USER_AGENT':'asdfasdfasdf',....}
# request.META 字典类型数据,所有的请求头的键都加上了一个HTTP_键名称
return HttpResponse('ok')
redirect 方法
## view
## 模拟一个用户登录功能,用户提交用户名,登录成功后显示首页
from django.shortcuts import render,HttpResponse,redirect
def login(request):
currnet_user = "志邦"
ret = render(request,'login.html',{'username':currnet_user})
if request.method == 'GET':
return render(request,'login.html')
else:
username = request.POST.get('username')
if username == 'zhibang':
return redirect('home')
#return render(request,'home.html')
def home(request):
book = '书本'
return render(request,'home.html',{'book': book})
//登录页
<form action="" method="post">
{% csrf_token %}
<input type="text" name="username">
<button>提交</button>
</form>
<h1>
这是一个首页{{ book }}
</h1>
urls
# 这里我写了两个路径,一个是index,另一个home,name的参数最好指定为home这样redirect后面才能找到具体的路径
urlpatterns = [
path('admin/', admin.site.urls),
path('login/',views.login),
path('home/',views.home,name='home')
]
CBV和FBV
FBV (Function-Based View,基于函数的视图)。一个请求进来,走进一个函数,返回一个响应。逻辑像水管一样直来直去。我上面写的例子都是基于FBV的方式来运行的
CBV(Class-Based View,基于类的视图) 。把一次请求,拆成多个可复用、可覆盖的步骤。
轻度体验一下:
urls.py 路由文件的写法
urlpatterns = [
...
## 除了路径别的都是固定写法
path('book/',views.Bookview.as_view()),
...
]
视图部分写法
from django.views import View
class BookView(View):
# 通过反射获取到请求方法对应的类中的方法来执行
def get(self,request):
return HttpResponse('ok')
# 需要处理什么请求方法,就写对应名称的方法
在 Django 的 CBV 里,所有请求都会先进入 dispatch()。它的职责不是干活,而是分发任务。可以把它想成前台接待:"你是 GET?那走这边。你是 POST?那走那边。"
def dispatch(self, request, *args, **kwargs):
## request.method 是 'GET'、'POST' 这种大写字符串。lower() 后变成 'get','post'。
## self.http_method_names 在 Django 的 View 里,默认是 ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
if request.method.lower() in self.http_method_names: #get
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
return handler(request, *args, **kwargs) # HttpResponse('ok')
5. Django框架的Templates 模块
模板渲染
{{变量}}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>{{ name }}</h2>
<h2>{{ age }}</h2>
<h2>{{ hobby.0 }}</h2> <!-- 万能的据点好 -->
<h2>{{ d1.a }}</h2>
<h2>{{ a.my_hobby }}</h2> <!-- 调用对象的方法时,不能加括号,所以不能调用有参数的方法 -->
</body>
</html>
过滤器
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h2>{{ hobby|length }}</h2>
<h2>{{ xx|default:'啥也不是' }}</h2> <!-- 当xx为空或者后台没有给这个数据时,会显示默认值 -->
<h3>{{ file_size|filesizeformat }}</h3> <!-- 显示文件大小 -->
<h3>{{ ss|slice:'0:5' }}</h3>
<h3>{{ now|date:'Y-m-d H:i:s' }}</h3>
<h2>{{ a_tag|safe }}</h2>
<h2>{{ aa|truncatechars:'8' }}</h2> <!-- 截断字符,注意8包含了三个点 -->
<h2>{{ aa|truncatewords:'2' }}</h2> <!-- 以空格做个截断符号,截断单词,2不包含那三个点 -->
</body>
</html>
6. Django框架的orm 模块
在没有 ORM 的世界里,你要亲手写 SQL:建表、查数据、拼字符串、防 SQL 注入、处理游标和连接释放。
Django ORM 的目标很明确:让 80% 的业务查询,不再需要你直接写 SQL。
它通过三个核心概念完成这件事:模型(Model)、查询集(QuerySet)、映射关系。
orm配置连接mysql
## 1. 安装pymysql库
pip install -y pymysql
## 2. 在项目的主目录下__init__.py文件中引入
import pymysql
pymysql.install_as_MySQLdb()
修改settings.py配置文件
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', ## backends后面修改为你要连接哪种类型的数库
'NAME': 'django', ## 这里指定你要连接的数据库名称
'HOST': '192.168.0.50', ## 数据库的地址
'PORT': 3306, ## 数据库的端口
'USER': 'root', ## 数据库的用户名
'PASSWORD': '123' ## 数据库的密码
}
}
在应用文件夹的models.py文件中创建数据,这里我只是在举例创建,Book生成的表名称为 应用名称_模型类名小写
from django.db import models
# Create your models here.
class Book(models.Model):
id = models.AutoField(primary_key=True) ## 这是主键自增,如果你不设置的话django也会自动帮你加上的
title = models.CharField(max_length=100)
price = models.DecimalField(max_digits=5, decimal_places=2)
pub_date = models.DateField()
publish = models.CharField(max_length=100)
数据库同步(迁移)指令
python manage.py makemigrations
python manage.py migrate
6.1 数据库记录的增删改查-单表
增加
from app01 import models
## 方式一,用save()的方法增加记录
def query(request):
new_book = models.Book(
title = "西游记",
price = 19.9,
pub_date = '2022-09-04',
publish = '清华大学出版社'
)
new_book.save()
return HttpResponse('ok')
## 方式二,create方法的返回值为 新添加的数据的模型类对象
def query(request):
new_book = models.Book.objects.create(
title = '三国演义',
price = '20.9',
pub_date= '2022-09-14',
publish='北京大学出版社'
)
print(new_book.title) ## 通过模型类对象,直接获取属性对应的值
print(new_book.price)
return HttpResponse('ok')
## 方式三 批量添加
def query(request):
obj_list = []
for i in range(5):
book_list = models.Book(
title = '水浒传{}'.format(i),
price = '35.{}'.format(i),
pub_date = '2024-08-08',
publish = '复旦大学出版社'
)
obj_list.append(book_list)
models.Book.objects.bulk_create(obj_list) ## bulk_create 批量添加
return HttpResponse('ok')
查询
def query(request):
book_obj = models.Book.objects.all() ## queryset 类似于列表
print(book_obj)
book_objs = models.Book.objects.filter(id=3) ## 条件查询 结果为queryset类型数据
print(book_objs)
book_objs2 = models.Book.objects.filter() ## filter没有加条件,和all一样的效果
print(book_objs2)
book_objs3 = models.Book.objects.get(id=2) ## 条件查找 结果为: 模型类对象,但是get方法的查询结果有要求,有且只能有一条
print(book_objs3.title)
return HttpResponse('ok')
# exclude排除
# 返回结果为queryset类型数据,通过objects控制器可以调用,queryset类型数据也能调用
# obj_list = models.Book.objects.exclude(id=2)
# obj_list = obj_list.filter(title='少年阿宾1')
# obj_list = obj_list.all()
# obj_list = models.Book.objects.exclude(id=2).filter(title__contains='少年').exclude(id=5)
# order_by 排序
# 返回结果为queryset类型数据,queryset类型数据可以调用这个方法
# obj_list = models.Book.objects.all().order_by('-id') #-id加个-号表示按照该字段降序排列, desc asc
# '''select * from app01_book order by id desc;'''
# obj_list = models.Book.objects.all().order_by('price', '-id') #按照价格升序排列,价格相同的按照id降序排列
# reverse()
# 翻转必须在排序数据的基础上
# 返回结果为queryset类型数据,queryset类型数据可以调用这个方法
# obj_list = models.Book.objects.all().order_by('-id').reverse()
# count
# queryset类型数据可以调用这个方法,返回值为数字
# obj_list = models.Book.objects.all().count()
# first\last
# queryset类型数据可以调用这个方法,返回值为模型类对象
# obj_list = models.Book.objects.all().first()
# obj_list = models.Book.objects.all()[0]
# obj_list = models.Book.objects.all().last()
# exists
# 判断查询结果是有数据
# queryset类型数据可以调用这个方法
# obj_list = models.Book.objects.all().exists() #判断是否有数据的效率高,只找一条记录 limit 1
# values
# 可以获取指定字段数据
# objects可以调用, queryset也能调用,返回结果还是queryset,内容为一个个字典数据
# obj_list = models.Book.objects.values('title', 'price')
# obj_list = models.Book.objects.filter(id=5).values('title', 'price')
# values_list
# 可以获取指定字段数据,返回结果还是queryset,内容为一个个元组数据
# obj_list = models.Book.objects.values_list('title', 'price')
# obj_list = models.Book.objects.filter(id=5).values_list('title', 'price')
# distinct 去重
# 一般配合values和values_list来使用
obj_list = models.Book.objects.values('price').distinct()
删除
models.Book.objects.filter(id=3).delete() queryset类型数据可以调用delete方法删除查询结果数据
models.Book.objects.get(id=4).delete() 模型类对象也可以调用delete方法删除数据
修改
def query(request):
## 方式一 通过queryset类型数据修改
models.Book.objects.filter(id=5).update(
price=20,
title='xxxx'
)
models.Book.objects.get(id=5).update( #报错:模型类对象不能调用update方法
price=30,
)
# 修改方式2 通过模型类对象来修改
ret = models.Book.objects.get(id=5)
ret.price = 30
ret.title = '阿甘正传'
ret.save()
return HttpResponse('ok')
基于双下划线的模糊查询
# 查询书名以少年开头的哪些书
def query(request):
# obj_list = models.Book.objects.filter(title__startswith='少年') #以什么开头
# obj_list = models.Book.objects.filter(title__endswith='梅') #以什么结尾
# obj_list = models.Book.objects.filter(title__startswith='p') #区分大小写
# obj_list = models.Book.objects.filter(title__istartswith='p') #不区分大小写
# obj_list = models.Book.objects.filter(title__contains='th') #包含
# obj_list = models.Book.objects.filter(title__icontains='th') #包含 不区分大小写
# obj_list = models.Book.objects.filter(price__gt=15) 大于
# obj_list = models.Book.objects.filter(price__gte=15) 大于等于
# obj_list = models.Book.objects.filter(price__lt=15) 小于
# obj_list = models.Book.objects.filter(price__lte=15) 小于等于
# obj_list = models.Book.objects.filter(price=15 or price=18 or price=30)
# obj_list = models.Book.objects.filter(price__in=[15,18,30]) 价格等于15或者等于18或者等于30的书籍
# obj_list = models.Book.objects.filter(price__range=[15, 20]) #价格大于等于15并且小于等于20, between and
# obj_list = models.Book.objects.filter(id=10, price=15) #逗号连接的查询条件就是and的关系
# obj_list = models.Book.objects.filter(pub_date__year='2020') #2020年的所有书籍
# obj_list = models.Book.objects.filter(pub_date__year='2020',pub_date__month='11') #2020年11月份的所有书籍
# obj_list = models.Book.objects.filter(pub_date__year='2020',pub_date__month='11',pub_date__day='25') #2020年11月5号的所有书籍
# obj_list = models.Book.objects.filter(pub_date='2020-11-25') #2020年11月5号的所有书籍
return HttpResponse('ok')
6.2 数据库记录的增删改查-多表
创建多表查询的数据库表字段
from django.db import models
# 作者表,和作者详情表是一对一的对应关系,OneToOneField 写在任意一边都可以
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
au = models.OneToOneField(to="AuthDetail", to_field='id', on_delete=models.CASCADE)
## 另一种写法
# au = models.OneToOneField("AuthDetail",db_constraint=False,on_delete=models.CASCADE)
## db_constraint=False取消foreign key的强制约束效果,还可以继续使用orm的提供的属性或者方法来操作关系记录
# 作者详情表
class AuthDetail(models.Model):
birthday = models.DateField()
telephone = models.BigIntegerField()
addr = models.CharField(max_length=64)
## 出版社表
class Publish(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=64)
## 书籍表,和出版社表是一对多的关系,ForeignKey 需要写在多的那张表上,和作者表是多对多的关系,写在任意一边即可.
class Book(models.Model):
title = models.CharField(max_length=100)
price = models.DecimalField(max_digits=5, decimal_places=2)
pub_date = models.DateField()
publishs = models.ForeignKey('Publish',on_delete=models.CASCADE)
authors = models.ManyToManyField(to='Author')
增加
## 一对一的关系添加
def query(request):
ret = models.AuthDetail.objects.create(
birthday='2000-12-12',
telephone='122',
addr='苏州'
)
models.Author.objects.create(
name='志邦',
age=18,
# au_id = ret.id 如果用的是属性名称_id,那么值为关联记录的id值
au=ret, #如果写属性名称来添加关系数据,那么值为关联记录的模型类对象
)
return HttpResponse('ok')
## 一对多的关系添加
## 第一种写法,
pub_obj = models.Publish.objects.get(id=2)
models.Book.objects.create(
title='春秋',
price=39.9,
pub_date='2022-10-20',
publishs=pub_obj #如果写属性名称来添加关系数据,那么值为关联记录的模型类对象
)
return HttpResponse('ok')
## 第二种写法
models.Book.objects.create(
title='三体',
price=39.9,
pub_date='2022-10-20',
publishs_id=2 ## 如果用的是属性名称_id,那么值为关联记录的id值
)
return HttpResponse('ok')
## 多对多的关系添加
## 第一种写法
book_boj = models.Book.objects.get(title='水浒传')
author01 = models.Author.objects.get(id=1)
author02 = models.Author.objects.get(id=2)
book_boj.authors.add(author01,author02)
## 第二种写法,你只要的id值了可以直接写id
book_boj = models.Book.objects.get(title='水浒传')
book_boj.authors.add(1,2)
return HttpResponse('ok')
删除
# 一对一删除
def query(request):
## 第一种方法
models.Author.objects.get(id=1).delete()
## 第二种方法 外键关联到这条作者记录的都会被删除掉(级联模式下)
models.AuthDetail.objects.get(id=2).delete()
return HttpResponse('查询成功')
## 一对多的删除,可以把多的关系那一方比喻成大腿,大腿没了那所有的就都没了,单个没了不会影响到大腿的那一方
models.Book.objects.get(id=2).delete()
models.Publish.objects.get(id=2).delete()
return HttpResponse('ok')
# 一对多删除
def query(request):
## 一对多的删除,可以把多的关系那一方比喻成大腿,大腿没了那所有的就都没了,单个没了不会影响到大腿的那一方
## 删除一的那一方
models.Book.objects.get(id=2).delete()
## 删除多的那一方
models.Publish.objects.get(id=2).delete()
return HttpResponse('ok')
# 多对多删除
def query(request):
book_obj = models.Book.objects.get(id=14)
## clear清空所有
book_obj.authors.clear()
## remove 只删除单个
book_obj.authors.remove(3) ## 删除第三章表的book_id=14 并且作者id=3的那条记录
return HttpResponse('ok')
查询-基于对象的跨表查询
## 一对一的
## 正向查询,关系属性在哪个表里面,通过这个表的数据去查询另外一张表的数据,就是正向查询
# 查询张三这个作者的手机号,先查出这个张三这个作者通过外键找到作者详情表然后取出手机号
def query(request):
obj = models.Author.objects.get(name='张三')
print(obj.au.telephone) ## 正向查询靠属性
return HttpResponse('查询成功')
## 一对多的
## 正向查询,这里的书籍和出版社是1:n的关系,通过外键publishs关联
def query(request):
obj = models.Book.objects.get(title='佛经1')
print(obj.publishs.name)
return HttpResponse('查询成功')
## 反向查询
def query(request):
obj = models.Publish.objects.get(name='清华出版社')
books = obj.book_set.filter() ## 反向查询在一对多的关系是,使用 表名小写_set
for book in books:
print(book.title)
return HttpResponse('查询成功')
## 多对多的
# 正向查询
def query(request):
obj = models.Book.objects.filter(title='佛经2').first()
objs = obj.authors.all() ## #类似objects控制器
for obj in objs:
print(obj.name)
return HttpResponse('查询成功')
# 反向查询
## 查询一下张三写了哪些书
def query(request):
obj = models.Author.objects.get(name='张三')
objs = obj.book_set.all()
for i in objs:
print(i.title)
基于双下划线的跨表查询
## 正向查询靠属性, 反向查询靠表名小写
def query(request):
## 一对一的
## 正向查询,查询一下志邦这个作者的手机号
ret = models.Author.objects.filter(name='志邦').values('au__telephone')
print(ret)
## <QuerySet [{'au__telephone': 122}]>
## 反向查询
ret = models.AuthDetail.objects.filter(author__name='志邦').values('telephone')
print(ret)
return HttpResponse('查询成功')
def query(request):
## 正向查询
ret = models.Book.objects.filter(title='三体').values('authors__name')
print(ret)
## 反向查询
ret = models.Author.objects.filter(book__title='三体').values('name')
print(ret)
return HttpResponse('查询成功')
聚合查询
def query(request):
## 查询所有书籍的平均价格
ret = models.Book.objects.all().aggregate(Avg('price'))
print(ret)
return HttpResponse('查询成功')