Django是用python语言写的开源web开发框架,并遵循MVC设计。Django的主要目的是简便、快速的开发数据库驱动的网站·它强调代码复用,多个组件可以很方便的以”插件”形式服务于整个框架,Django有许多功能强大的第三方插件,你甚至可以很方便的开发出自己的工具包·这使得Django具有很强的可扩展性。它还强调快速开发和DRY(DoNot Repeat YourselD)原则。
重量级框架:对比Flask框架,Django原生提供了众多的功能组件,让开发更简便快速。提供项目工程管理的自动化脚本工具数据库ORM支持( 对象关系映射),模板,表单,Admin管理站点,文件管理,认证权限,session机制,缓存
MVT格式(Model View Template):有一种程序设计模式叫MVC(Model View Controller),其核心思想是分工、解耦,让不同的代码块之间降低耦合,增强代码的可扩展性和可移植性,实现向后兼容。
环境配置:
安装django:pip install django
在一台电脑上想开发多个不同的项目,需要用到用一个包的不同版本,如果用pip install django安装,会将最新的覆盖之前的版本,解决该问题的方式是使用虚拟环境
虚拟环境:可以搭建独立的 python运行环境,使得单个项目的运行环境与其它项目互不影响
安装虚拟环境(Linux):
pip install virtualenv
和pip install virtualenvwrapper
创建虚拟环境:mkvirtualenv [-p python3] 虚拟环境名
(加-p指定使用python3,不指定默认使用python2)如:mkvirtualenv py_django
创建会进入虚拟环境,进入虚拟环境后执行指令workon
可以查看当前有哪些虚拟环境
切换虚拟环境:workon 虚拟环境名
删除虚拟环境(不能删除正在使用的虚拟环境):rmvirtualenv 虚拟环境名
退出虚拟环境:deactivate
在虚拟环境中可以使用pip安装库:pip install 库名
创建Django项目:django-admin startproject name
运行指令后多了如下文件,manage.py
是一个管理工具/脚本(如创建子应用时会用到),settings.py
设置相关,urls.py
路由相关,wsgi.py
程序入口
运行项目:python manage.py runserver [127.0.0.1:8001]
(中括号内可指定)
刚开始运行没反应,在file>settings中进行如下设置,然后重启pycharm运行指令即可
创建子应用:python manager.py startapp name
运行指令后多了如下几个文件
view.py视图相关,tests.py测试相关,models.py模型相关,migrations迁移相关,admin.py后台相关,apps.py当前子应用相关
然后在主应用中将该其与子应用关联起来,有两种方式(子应用名
或者子应用名.apps.子应用名Config
)
模型的迁移:Model用于和关系型数据库交互
模型:当前项目的开发,都是数据驱动的;以下为书籍信息管理的数据关系: 书籍和人物是 :一对多关系;要先分析出项目中所需要的数据,然后设计数据库表
书籍信息表
字段名 | 字段类型 | 字段说明 |
---|---|---|
id | AutoField | 主键 |
name | CharField | 书名 |
id | name |
---|---|
1 | 西游记 |
2 | 三国演义 |
人物信息表
字段名 | 字段类型 | 字段说明 |
---|---|---|
id | AutoField | 主键 |
name | CharField | 人名 |
gender | BooleanField | 性别 |
book | ForeignKey | 外键 |
id | name | gender | book |
---|---|---|---|
1 | 孙情空 | False | 1 |
2 | 白骨精 | True | 1 |
3 | 曹操 | False | 2 |
4 | 貂蝉 | True | 2 |
使用Django进行数据库开发的提示
MVT 设计模式中的 Model,专门负责和数据库交互。对应 (models.py)
由于 Model 中内了 ORM框架,所以不需要直接面向数据库编程
而是定义模型类,通过 模型类和对象 完成数据库表的 增制改查
ORM框架(如下图) 就是把数据库表的行与相应的对象建立联。互相转换,使得数据库的操作面向对象
使用Django进行数据库开发的步骤 :
1.定义模型类
2.模型迁移
3.操作数据库
表与表之间关联的时候,必须要写on_delete参数,否则会报异常models.ForeignKey(BookInfo, on_delete=models.CASCADE)
on_delete=None, # 删除关联表中的数据时,当前表与其关联的field的行为
on_delete=models.CASCADE, # 删除关联数据,与之关联也删除
on_delete=models.DO_NOTHING, # 删除关联数据,什么也不做
on_delete=models.PROTECT, # 删除关联数据,引发错误ProtectedError
# models.ForeignKey(‘关联表’, on_delete=models.SET_NULL, blank=True, null=True)
on_delete=models.SET_NULL, # 删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空,一对一同理)
# models.ForeignKey(‘关联表’, on_delete=models.SET_DEFAULT, default=‘默认值’)
on_delete=models.SET_DEFAULT, # 删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值,一对一同理)
on_delete=models.SET, # 删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
from django.db import models
# Create your models here.
'''
1.定义模型类
2.模型迁移
2.1 先生成迁移文件(不会在数据库中生成表,只会创建一个数据表和模型的对应关系)
python manage.py makemigrations
2.2 再迁移(会在数据库中生成表)
python manage.py migrate
3.操作数据库
在哪里定义模型
模型继承自谁就可以
ORM对应的关系
表-->类
字段-->属性
'''
class BookInfo(models.Model):
'''
1.主键 当前会自动生成
2.属性复制过来就可以
'''
name = models.CharField(max_length=10)
class PeopleInfo(models.Model):
name = models.CharField(max_length=10)
# 性别
gender = models.BooleanField()
# 外键
book = models.ForeignKey(BookInfo, on_delete=models.CASCADE)
编写之后,在pycharm下面的terminal中运行python manage.py makemigrations
如果上面运行失败,是因为没有注册子应用(关联子应用),需要在主应用的settings.py中加上如下内容
python manage.py makemigrations
运行成功后,会在迁移文件中(子应用)生成如下文件
内容如下
# Generated by Django 4.2 on 2023-04-22 14:16
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='BookInfo',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=10)),
],
),
migrations.CreateModel(
name='PeopleInfo',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=10)),
('gender', models.BooleanField()),
('book', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='login.bookinfo')),
],
),
]
然后再terminal中继续执行python manage.py migrate
,结果如下即表示迁移成功
在主应用的settings.py中可以查看使用的是什么数据库
由上面可以知道这个文件为保存的数据库
打开该数据库,显示没有内容时,需要按如下步骤下载对应数据库的驱动
下载之后,上面的指令执行完,该数据库里面会多两个表格(login_bookinfo,login_peopleinfo)
上面创建的项目中,自带了登入界面,即,运行python manage.py runserver
之后,浏览页面http://127.0.0.1:8000/admin
会自动跳到登入页面,页面如下
接着我们将上面的英文修改为中文,settings.py中将LANGUAGE_CODE = 'en-us'
改为LANGUAGE_CODE = 'zh-Hans'
,然后刷新页面,显示的内容就是中文格式
# LANGUAGE_CODE = 'en-us'
# 语言
LANGUAGE_CODE = 'zh-Hans'
还可以修改时区,将TIME_ZONE = 'UTC'
修改为TIME_ZONE = 'Asia/Shanghai'
,这里看不出效果
# LANGUAGE_CODE = 'en-us'
# 语言
LANGUAGE_CODE = 'zh-Hans'
# TIME_ZONE = 'UTC'
# 时区
TIME_ZONE = 'Asia/Shanghai'
接着查看auto_user数据库没有数据
接着使用指令创建一个后台管理员用户:python manage.py createsuperuser
,运行指令后输入用户名(admin),邮箱([email protected]),密码(abc123456),执行指令后,查看auto_user数据库中有一条数据
创建之后,从新运行项目,然后浏览http://127.0.0.1:8000/admin
页面,输入账户(admin)和密码(abc123456)可以登入
在子应用的admin.py中输入如下内容,然后刷新页面可以看到多了如下内容
from django.contrib import admin
from login.models import BookInfo
# Register your models here.
# 注册模型
# admin.site.register(模型类)
admin.site.register(BookInfo)
保存了两条数据,就有两条数据内容,数据库中也可以查看的到
这是不知道哪个是西游记,哪个是三国演义,需要在子应用的models.py中添加个def __str__(self):
函数返回self.name
,然后浏览页面就会显示该内容
from django.db import models
# Create your models here.
class BookInfo(models.Model):
'''
1.主键 当前会自动生成
2.属性复制过来就可以
'''
name = models.CharField(max_length=10)
def __str__(self):
return self.name
class PeopleInfo(models.Model):
name = models.CharField(max_length=10)
# 性别
gender = models.BooleanField()
# 外键
book = models.ForeignKey(BookInfo, on_delete=models.CASCADE)
用户在URL中请求的是视图,视图接收请求后进行处理,并将处理的结果返回给请求者
使用视图时需要进行两步操作:定义视图;配置URLconf
定义视图
视图就是一个 Python 函数,被定义在 应用的 views.py 中
视图的第一个参数是 HttpRequest 类型的对象 reqeust,包含了所有 请求信息
视图必须返回 HttpResponse对象,包含返回给请求者的 响应信息
需要导入 HttpResponse 模块: from django.http import HttpResponse
定义视图函数:响应字符串 OK! 给客户端
子应用的view.py内容如下
from django.shortcuts import render
from django.http import HttpRequest
# Create your views here.
'''
视图
就是python函数
函数的第一个参数就是请求,和请求相关的,是HttpRequest对象
必须要返回一个相应,相应是HttpResponse的实例对象/子类实例对象
'''
def index(request):
return HttpRequest('index')
在上面的基础上,我们没有定义URL,访问http://127.0.0.1:8000/index
得到的是这样的页面
做如下配置可以正常浏览
在主应用中的settings.py中的如下内容是url的配置,如果有个文件abc.py是URL的文档,可以改为ROOT_URLCONF = ‘abc.py’
# ”ROOT_URICONE 是我们工程的URL的配置的入口
# 默认是 工程名.urls
# 可以修改,默认不改
ROOT_URLCONF = 'book.urls'
在主应用的urls.py中内容添加一项path('', include('login.urls'))
from django.contrib import admin
from django.urls import path, include
'''
1.urlpatterns是固定写法,它的值是列表
2.浏览器中输入的路径会和urlpatterns中的每一项匹配
如果匹配成功则直接引导到相应的模块,不成功则直接返回404.
3.urlpatterns中的元素是url,url的第一个参数是正则
4.浏览器的路由http://ip:port/path/?key=value中,http://ip:port/和get post不参数正则匹配
5.如果和当前的某项匹配成功,则引导到子应用中继续匹配
如果匹配成功,则停止匹配返回响应的视图
'''
# 路由匹配
urlpatterns = [
# 参照:正则;函数或者include函数
path('admin/', admin.site.urls),
# 添加一项,上面没匹配,跳转到子应用的urls.py中匹配
# include导入其他路由文件中
path('', include('login.urls'))
]
在子应用中创建urls.py,内容如下
from django.urls import path
from login.views import index
# 路由匹配
urlpatterns = [
# 参照:正则;函数
path('index/', index),
]
子应用的views.py内容如下
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
'''
视图
就是python函数
函数的第一个参数就是请求,和请求相关的,是HttpRequest对象
必须要返回一个相应,相应是HttpResponse的实例对象/子类实例对象
'''
def index(request):
return HttpResponse('index')
经过上面修改,继续浏览http://127.0.0.1:8000/index/
可以得到如下页面
在项目上创建template
目录,在该目录中创建一个index.html
文件
index.html文件内容如下
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Django模板title>
head>
<body>
<a>Django模板a>
body>
html>
在主应用的settings.py中做以下设置
然后修改子应用的views.py文件内容,内容如下
from django.shortcuts import render
# from django.http import HttpResponse
# Create your views here.
'''
视图
就是python函数
函数的第一个参数就是请求,和请求相关的,是HttpRequest对象
必须要返回一个相应,相应是HttpResponse的实例对象/子类实例对象
'''
def index(request):
# request,template name,context=None
# 参数1: 当前的请求
# 参数2: 模板文件
return render(request, 'index.html')
# return HttpResponse('index')
然后浏览http://127.0.0.1:8000/index/
显示页面为index.html的内容,页面如下
如果要将参数传入该index.html中,可以进行如下操作
修改templatge中的index.html文件,接收views.py传入的数据(这里的name是下面传入字典的键,得到的就是下面传入的字典的值Python
)
子应用的views.py内容如下(return中添加context参数传入index.html
)
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
'''
视图
就是python函数
函数的第一个参数就是请求,和请求相关的,是HttpRequest对象
必须要返回一个相应,相应是HttpResponse的实例对象/子类实例对象
'''
def index(request):
# request,template name,context=None
# 参数1: 当前的请求
# 参数2: 模板文件
# 参数3:context传递参数
context = {
'name': 'Python'
}
# 将context传递给index.html
return render(request, 'index.html', context=context)
# return HttpResponse('index')
创建项目:django-admin startproject bookmanager
进入项目:cd bookmanager
添加子应用:python manage.py startapp book
在主项目的settings.py
中配置
在子应用的models.py
中定义模型
from django.db import models
# Create your models here.
class BookInfo(models.Model):
name = models.CharField(max_length=10)
运行会生成数据库python manage.py makemigrations
和python manage.py migrate
在子应用的views.py
中设置
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.=
def index(request):
return HttpResponse('index')
在子应用中创建urls.py
路由文件
from django.urls import path
from login.views import index
urlpatterns = [
# 参照:正则;函数
path('index/', index),
]
在主应用的urls.py
中添加引入该路由
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
# 参照:正则;函数
path('admin/', admin.site.urls),
# 添加一项
path('', include('login.urls'))
]
在项目上创建template
目录,在该目录中创建一个index.html
文件,内容如下
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Django模板title>
head>
<body>
<a>Django模板{{name}}a>
body>
html>
在主应用的settings.py
中做以下设置(BASE_DIR
在该文件上面有定义)
然后修改子应用的views.py
文件内容,内容如下(return中添加context参数传入index.html
)
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def index(request):
# request,template name,context=None
# 参数1: 当前的请求 参数2: 模板文件 参数3:context传递参数
context = {
'name': 'Python'
}
# 将context传递给index.html
return render(request, 'index.html', context=context)
# return HttpResponse('index')
如果需要后台账户(admin):python manage.py createsuperuser
,然后输入账号邮箱密码等信息
在子应用的admin.py中添加如下内容
from django.contrib import admin
from login.models import BookInfo
# Register your models here.
admin.site.register(BookInfo)
然后在127.0.0.1:8000/admin
登入之后可以管理数据库,添加数据
为了能够显示数据,在子应用的models.py中做如下修改即可
from django.db import models
# Create your models here.
class BookInfo(models.Model):
name = models.CharField(max_length=10)
def __str__(self):
return self.name
要将数据库中的数据写入到index.html中,需要做如下修改
template中index.html
内容如下(遍历读取数据
)
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Django模板title>
head>
<body>
<ul>
{% for book in books %}
<li>{{ book }}li>
{% endfor %}
ul>
body>
html>
修改views.py
,获取数据库数据,传入index.html中
from django.shortcuts import render
from login.models import BookInfo
def index(request):
books = BookInfo.objects.all()
context = {
'books':books
}
# 将context传递给index.html
return render(request, 'index.html', context=context)
当浏览URL错误的时候,会显示如下页面
是因为主应用的settings.py里面有这行代码,如果改成False就不会显示
# 开发者进行调试使用,当部署上线的时候需要改为False
# 为True时,浏览页面发生错误,会告诉你那里错了
DEBUG = True
静态文件:项目中的CSS、图片、js都是静态文件。一般会将静态文件放到一个单独的目录中,以方便管理。在html页面中调用时,也需要指定静态文件的路径,Diango中提供了一种解析的方式配置静态文件路径,静态文件可以放在项目根目录下,也可以放在应用的目录下,由于有些静态文件在项目中是通用的,所以推荐放在项目的根目录下,方便管理。
为了提供静态文件,否要配置两个参数 :
STATICFILES_DIRS:存放查找静态文件的目录
STATIC_URL:访问静态文件的URL前缀
在主应用的settings.py中进行如下配置
# Django通过STATIC_UR区分静态资源和动态资源
STATIC_URL = 'static/'
# 告诉系统静态文件在哪,BASE_DIR变量在该文件上面有定义
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
然后访问http://127.0.0.1:8000/static/1.jpeg
就会出现static中的图片
1.定义模型类
2.模型迁移
2.1 先生成迁移文件(不会在数据库中生成表,只会创建一个数据表和模型的对应关系)
python manage.py makemigrations
2.2 再迁移(会在数据库中生成表)
python manage.py migrate
3.操作数据库
1、ORM对应的关系
表–>类
字段–>属性
2.模型类需要继承自models.Model
3.模型类会自动为我们添加一个主键
4.属性名=属性类型(选项)
。charfield类型必须设置max_length
属性类型
类型 | 说明 |
---|---|
AutoField | 自动增长的IntegerField,通常不用指定,不指定时Django会自动创建属性名为id的自动增长属性 |
BooleanField | 布尔字段,值为True或False |
NullBooleanField | 支持Null·True、False三种值 |
CharField | 字符串,参数max_length表示最大字符个数,charfield类型必须设置max_length |
TextField | 大文本字段,一般超过4000个字符时使用 |
lntegerField | 整数 |
DecimalField | 十进制浮点数,参数max_digits表示总位数,参数decimal_places表示小数位数 |
FloatField | 浮点数 |
DateField | 日期,参数auto_now表示每次保存对象时,自动设置该字段为当前时间,用于”最后一次修改”的时间截,它总是使用当前日期,默认为False; 参数auto now add表示当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为False: 参数auto now add和auto now是相互排斥的,组合将会发生错误 |
TimeField | 时间,参数同DateField |
DateTimeField | 日期时间,参数同DateField |
FileField | 上传文件字段 |
lmageField | 继承于FileField,对上传的内容进行校验,确保是有效的图片 |
选项
选项 | 说明 |
---|---|
null | 是否为空 |
unique | 唯一 |
default | 设置默认值 |
varbose_name | 主要是admin后台显示名字 |
from django.db import models
# Create your models here.
class BookInfo(models.Model):
name = models.CharField(max_length=10, unique=True, verbose_name='名字') # 唯一
# 发布日期
pub_data = models.DateField(null=True)
# 阅读量
readcount = models.IntegerField(default=0)
# 评论量
commentcount = models.IntegerField(default=0)
# 是否逻辑删除
is_delete = models.BooleanField(default=False)
# def __str__(self):
# return self.name
在models.py里面可以修改表名
from django.db import models
class BookInfo(models.Model):
name = models.CharField(max_length=10, unique=True, verbose_name='名字')
class Meta:
# 改表名
db_table = 'bookinfo'
# 修改后台admin的显示信息的配置
verbose_name = 'admin'
def __str__(self):
return self.name
models.py可以定义外键
数据库的外键级联操作
在设置外键时,需要通过on_delete选项指明主表除数据时,对于外键引用表数据如何处理,在django.db.models中包含了可选常量 :
CASCADE
级联,删除主表数据时连通一起删除外键表中数据
PROTECT
保护,通过抛出ProtectedError异常,来阻止删除主表中被外键应用的数据
SET_NULL
设置为NULL,仅在该字段null=True允许为null时可用
SET_DEFAULT
设置为默认值,仅在该字段设置了默认值时可用
SET()
设置为特定值或者调用特定方法
DO_NOTHING
不做任何操作,如果数据库前置指明级联性,此选项会抛出IntegrityError异常
from django.db import models
class BookInfo(models.Model):
name = models.CharField(max_length=10, unique=True, verbose_name='名字') # 唯一
# 发布日期
pub_data = models.DateField(null=True)
# 阅读量
readcount = models.IntegerField(default=0)
# 评论量
commentcount = models.IntegerField(default=0)
# 是否逻辑删除
is_delete = models.BooleanField(default=False)
class Meta:
# 改表名
db_table = 'bookinfo'
# 修改后台admin的显示信息的配置
verbose_name = 'admin'
def __str__(self):
return self.name
class PeopleInfo(models.Model):
# 有序字典
GENDER_CHOICES = (
(0, 'male'),
(1, 'female')
)
name = models.CharField(max_length=10,verbose_name='名称')
# 性别
gender = models.BooleanField(choices=GENDER_CHOICES, default=0)
description = models.CharField(max_length=200, null=True)
# 外键
book = models.ForeignKey(BookInfo, on_delete=models.CASCADE)
is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
class Meta:
db_table = 'peopleinfo'
verbose_name = '人物信息'
def __str__(self):
return self.name
在主应用的__init__.py
中输入如下内容
import pymysql
pymysql.install_as_MySQLdb()
修改主应用的settings.py修改这段代码进行配置
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'book',
]
# 小型数据库:Sqlite
# 中型数据库:MySQL,Sqlserver
# 大型数据库:oracle,DB2
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST': '127.0.0.1', # 主机
'PORT': '3306', # 端口
'USER': 'root', # 用户名
'PASSWORD': 'mysql', # 密码
'NAME': 'book', # 指定数据库
}
}
写入models.py内容
from django.db import models
class BookInfo(models.Model):
name = models.CharField(max_length=10, unique=True, verbose_name='名字') # 唯一
# 发布日期
pub_data = models.DateField(null=True)
# 阅读量
readcount = models.IntegerField(default=0)
# 评论量
commentcount = models.IntegerField(default=0)
# 是否逻辑删除
is_delete = models.BooleanField(default=False)
class Meta:
# 改表名
db_table = 'bookinfo'
# 修改后台admin的显示信息的配置
verbose_name = 'admin'
def __str__(self):
return self.name
class PeopleInfo(models.Model):
# 有序字典
GENDER_CHOICES = (
(0, 'male'),
(1, 'female')
)
name = models.CharField(max_length=10,verbose_name='名称')
# 性别
gender = models.BooleanField(choices=GENDER_CHOICES, default=0)
description = models.CharField(max_length=200, null=True)
# 外键
book = models.ForeignKey(BookInfo, on_delete=models.CASCADE)
is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
class Meta:
db_table = 'peopleinfo'
verbose_name = '人物信息'
def __str__(self):
return self.name
然后运行python manage.py makemigrations
再执行python manage.py migrate
执行之后,数据库中就有该数据
cmd终端连接数据库之后插入数据
insert into bookinfo(name, pub_data, readcount, commentcount, is_delete) values
('射雕英雄传','1999-1-1','11','22',0),
('天龙八部','1999-11-11','22','33',0),
('笑傲江湖','1999-3-3','55','44',0);
insert into peopleinfo(name, gender, book_id, description, is_delete) values
('郭靖', 1, 1, '降龙十八掌',0),
('黄蓉', 0, 1, '打狗棍法',0),
('乔峰', 1, 2, '降龙十八掌',0),
('段誉', 1, 2, '六脉神剑',0),
('东方不败', 0, 3, '葵花宝典',0);
在子应用的views.py中编写如下内容
from django.shortcuts import render
from django.http import HttpResponse
from book.models import BookInfo
# Create your views here.
def index(request):
# 到数据库中查询数据
books = BookInfo.objects.all()
# 组织数据
context ={
'books': books
}
# 传递给模板
# render(request, '', context)
return HttpResponse('index')
类似于 ipython的东西
python manage.py shell
执行之后在执行下面代码
from book.models import BookInfo
BookInfo.objects.all()
然后就会显示数据库中查询到的内容
使用iPython新增数据
方式一
python manage.py shell
from book.models import BookInfo
book = BookInfo(
name='python',
pub_data='2000-01-01'
)
# 需手动调用save方法
book.save()
# 方式二:直接入库
# object模型的管理类
# 对模型的增删改查都找它
python manage.py shell
from book.models import BookInfo
BookInfo.objects.create(
name='java',
pub_data='2001-01-02'
)
修改更新数据
方式一
python manage.py shell
from book.models import BookInfo
# 先查询数据
# select * from bookinfo where id=1
book = BookInfo.objects.get(id=1)
# 再更新数据
book.readcount=20
# 调用save保存
book.save()
方式二
python manage.py shell
from book.models import BookInfo
BookInfo.objects.filter(id=1).update(
readcount = 100,
commentcount = 200
)
删除操作
方式一
python manage.py shell
from book.models import BookInfo
# 先获取数据
book = BookInfo.objects.get(id=5)
# 调用删除方法
book.delete()
方法二
python manage.py shell
from book.models import BookInfo
BookInfo.objects.filter(id=4).delete()
查询操作
get得到某一个数据
all获取所有的
count 个数
python manage.py shell
from book.models import BookInfo
# select * from bookinfo where id=1
book = BookInfo.objects.get(id=1)
book.name
book.readcount
# 不存在会抛出异常
try:
book = BookInfo.objects.get(id=100)
except Exception:
pass
# 获取所有数据
book = BookInfo.objects.all()
# count
BookInfo.objects.count()
filter,get,exclude查询
filter,get,exclude都是相当于where查询,select * from bookinfo where id=1
filter :筛选/过滤 返回 n个结果(n = 0/1/n)
get : 返回1个结果
exclude :排除掉符合条似剩下的结果,相当于not
语法新式,如filter(字段名__运算符=值)
python manage.py shell
from book.models import BookInfo
# 查询编号为1的图书
# exact精确的,准确的,就是等于
BookInfo.objects.get(id__exact=1) # 返回单一对象
BookInfo.objects.get(id=1) # 简写,同上
BookInfo.objects.filter(id__exact=1) # 返回列表
BookInfo.objects.filter(id__exact=1)[0]
# 查询书名含'笑'的图书
BookInfo.objects.filter(name__contains='笑')
# 查询书名以‘部’结尾的图书
BookInfo.objects.filter(name__endswith='部')
# 查询书名为空的图书
BookInfo.objects.filter(name__isnull=True)
# 查询编号为1或3或5的图书
BookInfo.objects.filter(id__in=[1, 3, 5])
# 查询编号大于3的图书
# gt 大于 gte 大于等于
# lt 小于 lte 小于等于
BookInfo.objects.filter(id__gt=3)
# 查询数据id不为3的图书
BookInfo.objects.exclude(id=3)
BookInfo.objects.exclude(id__exact=3)
# 查询1980年发表的图书
BookInfo.objects.filter(pub_data__year='1980')
# 查询1990年1月1日后发表的图书
BookInfo.objects.filter(pub_data__gt='1990-1-1')
F对象:用于属性与属性直接的比较,
F对象的语法形式
filter(字段名__运算符=F('字段名'))
如:查询阅读量大于等于评论星的图书。
python manage.py shell
from book.models import BookInfo
from django.db.models import F
# 查询阅读量大于等于评论量的图书
BookInfo.objects.filter(readcount__gt=F('commentcount'))
# 查询阅读量大于等于评论量两倍的图书
BookInfo.objects.filter(readcount__gt=F('commentcount')*2)
Q对象:多条件查询
,如:需要查询id大于2 并且阅读量大于20的书籍
Q对象语法形式
或
: Q()|Q()
并且
:Q()&Q()
not
:~Q()
python manage.py shell
from book.models import BookInfo
# 需要查询id大于2并且阅读量大于20的书籍
# 方式一
BookInfo.objects.filter(id__gt=2).filter(readcount__gt=20)
# 方式二
BookInfo.objects.filter(id__gt=2, readcount__gt=20)
# 方法三,使用Q对象
from django.db.models import Q
BookInfo.objects.filter(Q(id__gt=2)&Q(readcount__gt=20))
# 需要查询id大于2或者阅读量大于20的书籍,这时候需要用到Q对象
BookInfo.objects.filter(Q(id__gt=2)|Q(readcount__gt=20))
聚合函数需要使用 aggregate
语法形式是:aggregte(Xxx('字段'))
python manage.py shell
from book.models import BookInfo
from django.db.models import Sum,Avg,Max,Min,Count
# 当前数据的阅读总量
BookInfo.objects.aggregate(Sum('readcount'))
python manage.py shell
from book.models import BookInfo
# 按阅读量排序
# 升序
BookInfo.objects.all().order_by('readcount')
# 降序
BookInfo.objects.all().order_by('-readcount')
书籍和人物的关系是 l:n
书籍 中没有任何关于人物的字段
人物 中有关专书籍的字段 book 外键
语法形式
:
通过书籍查询人物:主表模型.关联模型类名小写_set.all()
通过人物查询书籍:从表模型(实例对象).外键
通过书籍查询人物(一对多):查询书籍为1的所有人物信息
python manage.py shell
from book.models import BookInfo
# 查询书籍
book = BookInfo.objects.get(id=1)
# 根据书籍关联人物信息
book.peopleinfo_set.all()
通过人物查询书籍(多对一):查询人物为1的书籍信息
python manage.py shell
from book.models import PeopleInfo
# 查询人物
person = PeopleInfo.objects.get(id=1)
# 根据人物查询书籍
person.book.name
书籍和人物的关系是 l:n
书籍 中没有任何关于人物的字段
人物 中有关专书籍的字段 book 外键
语法形式
:
一对多
需要的是图书信息,已知条件是人物信息filter(关联模型类名小写__字段__运算符=值)
需要的是主表数据,已知条件是从表信息filter(关联模型类名小写__字段__运算符=值)
多对一
需要的是人物信息,已知条件是书籍信息filter(外键__字段__运算符=值)
需要是是从表数据,已知条件是主表信息filter(外键__字段__运算符=值)
# 一对多
python manage.py shell
from book.models import BookInfo
# 查询图书,要求图书人物为郭靖
BookInfo.objects.filter(peopleinfo__name__exact='郭靖')
BookInfo.objects.filter(peopleinfo__name='郭靖')
# 查询图书,要求图书中人物的表述包含‘八’
BookInfo.objects.filter(peopleinfo__description__contains='八')
# 多对一
python manage.py shell
from book.models import PeopleInfo
# 查询书名为“天龙八部”的所有人物
PeopleInfo.objects.filter(book__name='天龙八部')
PeopleInfo.objects.filter(book__name_exact='天龙八部')
# 查询困书阅读量大于30的所有人物
PeopleInfo.objects.filter(book__readcount__gt=30)
查询集:也称查询结果集、QuerySet,表示从数据库中获取的对象集合当调用如下过滤器方法时,Django会返回查询集( 而不是简单的列表 ) :
all():返回所有数据
filter(): 返回满足条件的数据
exclude()返回满足条件之外的数据
order_by(): 对结果进行排序
对查询集可以再次调用过滤馨进行过滤,如:books = BookInfo.objects.filter(readcount__gt=30).order_by(‘pub_date’)
也就息味着查询集可以含有零个、一个或多个过滤器:过滤器基于所给的参数限制查询的结果
从SQL的角度讲,查询集与select语句等价,过滤器像where、 limitorder by子句。
查询集两大特性
惰性执行:创建查询集不会访问数据库,直到调用数据时,才会访问数据库,调用数据的情况包括送代、序列化、与i合用。例如,当执行
books = BookInfo.objects .all()
语句时,并未进行数据库查询,只是创建了一个查询集books,继续执行追历选代操作后,才真正的进行了数据库的查询for book in books : print(book.name)
缓存:使用同一个查询集,第一次使用时会发生数据库的查询,然后Django会把结果缓存下来,再次使用这个查询集时会使用缓存的数据,减少了数据库的查询次数。
# 该方法每次执行都没有缓存,每次执行都会读取数据库内容
[book.id for book in BookInfo.objects.all()]
[book.id for book in BookInfo.objects.all()]
# 优化后,有缓存,重复使用不会从新读取数据库,提升速率
books = BookInfo.objects.all()
[book.id for book in books]
[book.id for book in books]
限制查询集:可以对查询集进行取下标或切片操作。对查询集进行切片后返回一个新的查询集,不会立即执行查询。
# 不支持负数索引
BookInfo.objects.all()[0:2]
链接:https://docs.djangoproject.com/en/4.2/topics/pagination/
python manage.py shell
from book.models import BookInfo
from django.core.paginator import Paginator
books = BookInfo.objects.all()
# 将数据分页
# object_list 结果集/列表
# per_page 每页多少记录
p = Paginator(books, 2)
# 获取第几页数据
books_page = p.page(1)
books_page[0]
创建项目:django-admin startproject bookmanager
进入项目:cd bookmanager
添加子应用:python manage.py startapp book
在主项目的settings.py
中配置
在子应用的models.py
中定义模型
from django.db import models
class BookInfo(models.Model):
name = models.CharField(max_length=10, unique=True, verbose_name='名字') # 唯一
# 发布日期
pub_data = models.DateField(null=True)
# 阅读量
readcount = models.IntegerField(default=0)
# 评论量
commentcount = models.IntegerField(default=0)
# 是否逻辑删除
is_delete = models.BooleanField(default=False)
class Meta:
# 改表名
db_table = 'bookinfo'
# 修改后台admin的显示信息的配置
verbose_name = 'admin'
def __str__(self):
return self.name
class PeopleInfo(models.Model):
# 有序字典
GENDER_CHOICES = (
(0, 'male'),
(1, 'female')
)
name = models.CharField(max_length=10,verbose_name='名称')
# 性别
gender = models.BooleanField(choices=GENDER_CHOICES, default=0)
description = models.CharField(max_length=200, null=True)
# 外键
book = models.ForeignKey(BookInfo, on_delete=models.CASCADE)
is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
class Meta:
db_table = 'peopleinfo'
verbose_name = '人物信息'
def __str__(self):
return self.name
在主应用的settings.py
中进行数据库相关配置
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'HOST': '127.0.0.1', # 主机
'PORT': '3306', # 端口
'USER': 'root', # 用户名
'PASSWORD': '123456', # 密码
'NAME': 'msql', # 指定数据库
}
}
同时还要在主应用的__init__.py
中添加如下代码
import pymysql
pymysql.install_as_MySQLdb()
接着运行python manage.py makemigrations
和python manage.py migrate
会生成数据库
然后使用指令在cmd中连接数据库并插入数据
insert into bookinfo(name, pub_data, readcount, commentcount, is_delete) values
('射雕英雄传','1999-1-1','11','22',0),
('天龙八部','1999-11-11','22','33',0),
('笑傲江湖','1999-3-3','55','44',0);
insert into peopleinfo(name, gender, book_id, description, is_delete) values
('郭靖', 1, 1, '降龙十八掌',0),
('黄蓉', 0, 1, '打狗棍法',0),
('乔峰', 1, 2, '降龙十八掌',0),
('段誉', 1, 2, '六脉神剑',0),
('东方不败', 0, 3, '葵花宝典',0);
在子应用的views.py
中设置
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.=
def index(request):
return HttpResponse('index')
在子应用中创建urls.py
路由文件
from django.urls import path
from login.views import index
urlpatterns = [
# 参照:正则;函数
path('index/', index),
]
运行指令python manage.py runsetver
之后,在主应用的urls.py
中添加引入该路由
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
# 参照:正则;函数
path('admin/', admin.site.urls),
# 添加一项
path('', include('login.urls'))
]
浏览http://127.0.0.1:8000/
就会出现如下页面
当运行python manage.py runsetver 192.168.0.107:8000
指定IP,浏览器可能会出现如下情况
这时需要在主应用的settings.py
中修改如下内容即可正常访问
# 允许以哪个主机的形式访问后端
# 默认是127.0.0.1,如果改变了需要手动加入
# 如果改变允许方式,需要将运行的ip/域名添加进来
# 安全机制,只能以罗列的来访问
ALLOWED_HOSTS = ['192.168.0.107', '127.0.0.1']
在主应用的settings.py中有如下指定url的配置内容
# 指定URL配置
ROOT_URLCONF = 'bookmanager.urls'
在主应用的urls.py中有路由配置,可以引入到子应用的路由中
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
# 引入子应用
path('', include('book.urls')),
]
在子应用中创建了urls.py,写入了部分路由
from django.contrib import admin
from django.urls import path
from book.views import index
urlpatterns = [
path('index/', index),
]
reverse 就是通过 name 来动态获取路径(路由)
如果没有设置namespace 则可以通过name来获取 reverse(name)
如果有设置namespace 则可以通过namespace:name来获取 reverse(namespace:name)
在子应用的urls.py
内容如下
from django.contrib import admin
from django.urls import path
from book.views import index
urlpatterns = [
path('index/', index),
]
在子应用的views.py
中内容如下,有几个跳转页面
from django.shortcuts import render, redirect
from django.http import HttpResponse
from book.models import BookInfo
# Create your views here.
def index(request):
# 登陆成功之后需要跳转到
return redirect('/index/')
# 首页注册成功之后需要跳转到首页
return redirect('/index/')
像上面一样,如果需要修改settings.py
的index
路由,则views.py
中的index也要一起替换,这时可以用下面的方法
name参数:在路由后面添加个name
参数
from django.contrib import admin
from django.urls import path
from book.views import index
urlpatterns = [
# 可以通过name进行命名,可以通过name找到这个路由
path('index/', index, name='name1'),
]
views.py
中内容如下reverse
中的参数为上面name
的值
from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
from book.models import BookInfo
# Create your views here.
def index(request):
# 路由是动态获取的
path = reverse('name1')
# 登陆成功之后需要跳转到
return redirect(path)
# 首页注册成功之后需要跳转到首页
return redirect(path)
防止name重复命名
当上面的name参数有重复的时候,可以在主应用urls.py的include中添加一个参数namespace
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
# 引入子应用
# 在第二个参数添加namespace,这里的name就变成了namespace:name
# namespace习惯使用子应用名
path('', include('book.urls', namespace='book')),
]
上面设置了namespace,views.py中就需要如下
from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
from book.models import BookInfo
# Create your views here.
def index(request):
# 如果上面使用了namespace参数,这里需要使用namespace:name来获取路由
path = reverse('book:name1')
# 登陆成功之后需要跳转到
return redirect(path)
# 首页注册成功之后需要跳转到首页
return redirect(path)
下载链接:https://www.postman.com/downloads/
下载之后直接双击运行即可
将url的参数传入视图函数中
子应用的urls.py内容如下,需要使用<>
将需要的数据传入index
函数中,<>
中的内容一定要与index
函数里面的一样
from django.contrib import admin
from django.urls import path
from book.views import index
urlpatterns = [
# http://127.0.0.1:8000/分类id/书籍id/
# 分组来获取正则中的数据,前面括号括起来的两个参数会传入index函数中
path('//' , index),
]
子应用的views.py
中函数,
和
分别对应下面两个参数
def index(request, category_id, book_id):
return HttpResponse(category_id+'/'+book_id)
链接
?
号的前面是路由,?
后面表示以get方式传递的参数,称为查询字符串
在登陆的时候会输入用户名和密码,理论上用户名和密码都应该以POST方式进行传递
为了理解,以get请求传递用户名和密码
这里获取?
后面的参数访问http://127.0.0.1:8000/1/100/?username=itcast&password=123&username=python
时得到对应的输出结果,下面为子应用的views.py
内容
from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
from book.models import BookInfo
# 运行时访问http://127.0.0.1:8000/1/100/?username=itcast&password=123&username=python
def index(request, category_id, book_id):
query_params = request.GET
print(query_params) #
# 当一键多值得时候只能获取一个值,要获取一键多值得时使用getlist方法
print(query_params['username']) # python
print(query_params.get('username')) # 同上
# 获取一键多值
print(query_params.getlist('username')) # ['itcast', 'python']
return HttpResponse(category_id+'/'+book_id+' ' + str(query_params))
当带参数进行post请求的时候(postman应用
),会出现下面的问题
意思是禁止访问,这时需要在主应用的settings.py中注释掉一行代码即可正常浏览
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',
]
子应用的views.py
内容
from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
from book.models import BookInfo
# Create your views here.
def index(request, category_id, book_id):
data = request.POST
print(data) #
return HttpResponse(category_id+'/'+book_id)
子应用的views.py
内容
from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
from book.models import BookInfo
import json
# Create your views here.
def index(request, category_id, book_id):
data = request.body
print(data) # b'{\r\n "username":"itcast",\r\n "password":"123"\r\n}'
print(data.decode()) # 输出JSON形式的字符串
'''
{
"username":"itcast",
"password":"123"
}
'''
# json.dumps 将字典转化为JSON形式的字符串
# json.loads 将JSON形式的字符串转化为字典
data = json.loads(data)
print(data) # {'username': 'itcast', 'password': '123'}
return HttpResponse(category_id+'/'+book_id)
浏览器访问127.0.0.1:8000/1/100/
,然后会有输出
from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
from book.models import BookInfo
def index(request, category_id, book_id):
# 打印请求头信息
print(request.META)
print(request.META['CONTENT_TYPE'])
return HttpResponse(category_id+'/'+book_id)
method:一个字符串,表示请求使用的HTTP方法,常用值包括 :GET、POST
user :请求的用户对象。
path : 一个字符串,表示请求的页面的完整路径,不包含域名和参数部分。
encoding :一个字符串,表示提交的数据的编码方式。如果为None则表示使用浏览器的默认设置,一般为utf-8。这个属性是可写的,可以通过修改它来修改访问表单数据使用的编码,接下来对属性的任何访问将使用新的encoding值·
FILES:一个类似于字典的对象,包会所有的上传文件
def index(request, category_id, book_id):
print(request.method)
print(request.user)
print(request.path)
print(request.encoding)
return HttpResponse(category_id+'/'+book_id)
视图在接收请求并处理后,必须返回HttpResponse对象或子对象。HttpRequest对象由Django创建,HttpResponse对象由开发人员创建
可以使用django.http.HttpResponse来构造响应对象
HttpResponse(content-响应体,content_type-响应体数据类型,status-状态码)
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def index(request):
# 参数content:传递字符串
# 参数status:只能是系统规定的
# 参数content_type:是一个MIME类型
# 语法形式:大类/小类 如:text/html text/css text/javascript
# application/json image/png image/gif image/jpeg
return HttpResponse('data', statue=400)
# return HttpResponse('data', statue=400, content_type='')
返回字典类型数据
子应用views.py内容如下
from django.http import JsonResponse
def index(request):
data = {'name':'itcast'}
return JsonResponse(data)
子应用views.py内容如下,浏览之后会自动跳转到http://www.itcast.cn
from django.shortcuts import render, redirect
# Create your views here.
from django.http import JsonResponse
def index(request):
# 页面跳转到http://www.itcast.cn
return redirect('http://www.itcast.cn')
from django.shortcuts import render, redirect, reverse
# Create your views here.
from django.http import JsonResponse
def index(request):
# 页面跳转到http://www.itcast.cn
path = reverse('book:index')
return redirect(path)
浏览器请求服务器是无状态的。
无状态:指一次用户请求时,浏览器、服务器无法知道之前这个用户做过什么,每次请求都是一次新的请求·
无状态原因:浏览器与服务器是使用Socket套接字进行通信的,服务器将请求结果返回给浏览器之后会关闭当前的Socket连接,而且服务器也会在处理页面完毕之后销毁页面对象。
有时需要保持下来用户浏览的状态,比如用户是否登录过,浏览过哪些商品等
实现状态保持主要有两种方式 :
——在客户端存储信息使用 cookie
——在服务器端存储信息使用 Session
Cookie是保存在客户端
Cookie是基于域名(IP)的
Cookie流程
第一次请求过程
1.我们的浏览器第一次请求服务器的时候,不会携带任何cookie信息
2.服务器接收到请求之后,发现 请求中没有任何cookie信息
3.服务器设置一个cookie.这个cookie设置在响应中
4.我们的浏览器接收到这个相应之后,发现响应中有cookie信息,浏览器会将cookie信息保存起来
第二次及其之后的过程
5当我们的浏览器第二次及其之后的请求都会携带cookie信息
6.我们的服务器接收到请求之后,会发现请求中携带的cookie信息,这样的话就认识是谁的请求了
views.py
from django.http import HttpResponse
def set_cookie(request):
# 1.先判断有没有Cookie信息
# 先假设没有
# 2.获取用户名
username = request.GET.get('username')
# 3.因为我们假设没有cookie信息,我们服务器就要设置cookie信息
response = HttpResponse('set_cookie')
# key,value
response.set_cookie('username', username)
# 4.返回响应
return response
def get_cookie(request):
'''
第二次及其之后请求
'''
# 服务器接收cookie
cookies = request.COOKIES
# cookies 是一个字典
username = cookies.get('username')
# 得到用户信息可以继续其他业务
return HttpResponse('get_cookie')
urls.py
from django.contrib import admin
from django.urls import path
from book.views import index
urlpatterns = [
path('set_cookie/', set_cookie),
path('get_cookie/', get_cookie),
]
设置Cookie过期时间:使用参数max_age
,单位为秒
def set_cookie(request):
# 1.先判断有没有Cookie信息
# 先假设没有
# 2.获取用户名
username = request.GET.get('username')
# 3.因为我们假设没有cookie信息,我们服务器就要设置cookie信息
response = HttpResponse('set_cookie')
# key,value
# max_age为秒数,设置Cookie过期时间
response.set_cookie('username', username, max_age=600)
# 4.返回响应
return response
删除Cookie:response.delete_cookie(key)
或者response.set_cookie(key, value, max_age=0)
def set_cookie(request):
username = request.GET.get('username')
response = HttpResponse('set_cookie')
response.set_cookie('username', username, max_age=600)
# 删除cookie
response.delete_cookie('username')
# 或者
response.set_cookie('username', username, max_age=0)
# 4.返回响应
return response
Session保存在服务器
session需要依赖于cookie
如果浏览器禁用了cookie,则session不能实现
项目的数据库中会有个django_session表格保存session相关数据(session_key,session_data,expire_date)(设置session的时候将数据保存到数据库中
)
session流程
第一次请求:
1.我们第一次请求的时候可以携带一些信息(用户名/密码) cookie中没有任何信息
2.当我们的服务器接收到这个请求之后,进行用户名和密码的验证,验证没有问题可以设置session信息
3.在设置session信息的同时(session信息保存在服务器端),服务器会在响应头中设置一个session id的cookie信息(由服务器设置)
4.客户端(浏览器)在接收到响应之后,会将cookie信息保存起来(保存 sessionid的信息)
第二次及其之后的请求:
5.第二次及其之后的请求都会携带 session id信息
6.当服务器接收到这个请求之后,会获取到sessionid信息,然后进行验证,验证成功,则可以获取 session信息(session信息保存在服务器端)
第一次请求:
1.我们第一次请求的时候可以携带一些信息(用户名/密码) cookie中没有任何信息
2.当我们的服务器接收到这个请求之后,进行用户名和密码的验证,验证没有问题可以设置session信息
3.在设置session信息的同时(session信息保存在服务器端),服务器会在响应头中设置一个session id的cookie信息(由服务器设置)
4.客户端(浏览器)在接收到响应之后,会将cookie信息保存起来(保存 sessionid的信息)
第二次及其之后的请求:
5.第二次及其之后的请求都会携带 session id信息
6.当服务器接收到这个请求之后,会获取到sessionid信息,然后进行验证,验证成功,则可以获取 session信息(session信息保存在服务器端)
views.py
内容如下
def set_session(request):
'''
第一次请求:
1.我们第一次请求的时候可以携带一些信息(用户名/密码) cookie中没有任何信息
2.当我们的服务器接收到这个请求之后,进行用户名和密码的验证,验证没有问题可以设置session信息
3.在设置session信息的同时(session信息保存在服务器端),服务器会在响应头中设置一个session id的cookie信息
4.客户端(浏览器)在接收到响应之后,会将cookie信息保存起来(保存 sessionid的信息)
'''
# 1.cookie中没有任何信息
print(request.COOKIES)
# 2.对用户名和密码进行验证
# 假设认为用户名和密码正确
user_id = 6666
# 3.设置session信息(设置的时候将数据保存到数据库中,设置cookie信息是以sessionid为key)
# request.session 理解为字典
request.session['user_id'] = user_id
# 4.返回响应
return HttpResponse('set_session')
def get_session(request):
'''
第二次及其之后的请求**:
5.第二次及其之后的请求都会携带 session id信息
6.当服务器接收到这个请求之后,会获取到sessionid信息,然后进行验证,验证成功,则可以获取 session信息(session信息保存在服务器端)
'''
# 请求都会携带 session_id信息
print(request.COOKIJES)
# 2,会获取到sessionid信息,然后进行验证
# 验证成功,可以获取 session信息
# request.session 字典
user_id = request.session['user_id']
user_id = request.session.get('user_id')
# 3.返回响应
return HttpResponse('get_session')
将两个视图转化为一个视图
登录页面
GET请求是获取登录的页面
POST请求是验证登录(用户名和密码是否正确)
from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
# GET 请求获取登入页面
def show_login(request):
return render(request)
# POST 请求是验证登入(用户名和密码是否正确)
def veri_login(request):
return redirect('首页')
转化为一个视图如下
from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
# 由连个视图变为一个视图
def login(request):
# 区分业务逻辑
if request.method == 'GET':
# GET请求获取登入页面
return render(request)
else:
# POST 请求是验证登入(用户名和密码是否正确)
return redirect('首页')
类视图:面向对象
类视图是采用面向对象的思路
一、定义类视图
1、继承自view(from django.views import View)
2、不同的请求方式有不同的业务逻辑
类视图的方法就直接采用http的请求名字作为我们的函数名,例如:get,post,put,patch等(as_view()
中)
3、类视图的方法的第二个参数必须是请求实例对象
类视图的方法必须有返回值,返回值是HttpResponse及其子类
from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
from django.views import View
class LoginView(View):
def get(self,request):
return HttpResponse('get')
def post(self,request):
return HttpResponse('post')
类视图URL引导
from django.urls import path
from book.views import index,LoginView
# as_view函数会根据请求方式的不同调用上面不同的函数,如果是GET请求,则调用上面的get函数
urlpatterns = [
path('login/', LoginView.as_view()),
]
需求如下
个人中心页面 ————必须登入才能显示
GET方式展示个人中心
POST实现个人中心信息修改
定义类视图
views.py代码如下
from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
from django.views import View
class CenterView(View):
def get(self, request):
return HttpResponse('个人中心展示')
def post(self, request):
return HttpResponse('个人中心信息修改')
urls.py
from django.urls import path
from book.views import index,CenterView
urlpatterns = [
path('center/', CenterView.as_view()),
]
由于展示个人中心页面必须登入才能显示所以需要做如下修改,这里可以判断用户是否登录
,没有登录的情况下,系统框架会自动跳到登入的界面
from django.shortcuts import render, redirect, reverse
from django.http import HttpResponse
from django.views import View
from django.contrib.auth.mixins import LoginRequiredMixin
class CenterView(LoginRequiredMixin, View):
def get(self, request):
return HttpResponse('个人中心展示')
def post(self, request):
return HttpResponse('个人中心信息修改')
在加了如上代码之后,浏览http://127.0.0.1:8000/center/
之后,在没有登入的情况下,会自动跳转到登录界面(这里没有导入登录界面路由),结果如下
中间件的作用:每次请求和响应的时候都会调用
Django中的中间件是一个轻量级、底层的插件系统,可以介入Django的请求和响应处理对程,修改Django的输入或输出·中间件的设计为开发者提供了一种无侵入式的开发方式,增强了Django框架的健壮性:
我们可以使用中间件,在Diango处理视图的不同阶段对输入或输出进行干预·
settings.py中有这么一段
# 中间件
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',
]
中间件的定义:定义一个中间件工厂函数,然后返回一个可以被调用的中间件。中间件工厂函数需要接收一个可以调用的get_response对象·返回的中间件也是一个可以被调用的对象,并且像视图一样需要接收一个request对象参数,返回一个response对象·
在子应用中创建middleware.py文件,内容如下(中间件的定义
)
def simple_middleware(get_response):
def middleware(request):
print('before request')
# 响应前
response = get_response(request)
# 响应后
print('after request')
return response
return middleware
在settings.py中添加middleware文件
# 中间件
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',
'book.middleware.simple_middleware', # 添加这行
]
实例:可以判断每次请求是否携带了cookie中的某些信息,通常如果不使用中间件,要在浏览中判断是否有cookie,需要在每个函数中都添加判断的代码,如下
views.py
from django.views import View
from django.contrib.auth.mixins import LoginRequiredMixin
class CenterView(LoginRequiredMixin, View):
def get(self, request):
username = request.COOKIES.get('username')
if username is None: # 添加判断
print('username is None')
return HttpResponse('个人中心展示')
def post(self, request):
username = request.COOKIES.get('username')
if username is None: # 添加判断
print('username is None')
return HttpResponse('个人中心信息修改')
可以利用中间件,建立个中间件文件,在中间件中加入如下代码,则在上面的函数中就不需要添加判断
views.py
from django.views import View
from django.contrib.auth.mixins import LoginRequiredMixin
class CenterView(LoginRequiredMixin, View):
def get(self, request):
return HttpResponse('个人中心展示')
def post(self, request):
return HttpResponse('个人中心信息修改')
middleware.py
def simple_middleware(get_response):
def middleware(request):
# 添加判断
username = request.COOKIES.get('username')
if username is None:
print('username is None')
print('before request')
# 响应前
response = get_response(request)
# 响应后
print('after request')
return response
return middleware
在上面的实例中,如果没有登入,可以直接加个return返回某页面,代码如下
from django.http import HttpResponse
def simple_middleware(get_response):
def middleware(request):
username = request.COOKIES.get('username')
if username is None:
print('username is None')
# 没有登入直接返回
return HttpResponse('没有登入')
print('before request')
# 响应前
response = get_response(request)
# 响应后
print('after request')
return response
return middleware
多个中间件顺序:请求前的执行顺序是按照注册的顺序从上往下(settings.py中),响应后的顺序是从下往上(注册的反顺序)
在middleware.py中定义多个中间件
def simple_middleware1(get_response):
def middleware(request):
print('before request 11111')
# 响应前
response = get_response(request)
# 响应后
print('after request 11111')
return response
return middleware
def simple_middleware2(get_response):
def middleware(request):
print('before request 22222')
# 响应前
response = get_response(request)
# 响应后
print('after request 22222')
return response
return middleware
在settings.py中引入(注册顺序右上到下
)
# 中间件
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',
'book.middleware.simple_middleware1', # 添加这行
'book.middleware.simple_middleware2', # 添加这行
]
输出结果如下
before request 11111
before request 22222
after request 22222
after request 11111
Django使用自带模板,在工程中创建模板目录templates,在这个目录里面创建index.html文件,内容额如下,这里写的是固定的,算不撒上是模板,模板需要里面的内容可以灵活变动,如下面的itcast可以根据信息的不同,显示不同内容,需要特殊语法将数据库或程序中的内容导入其中
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<a href="">itcasta>
body>
html>
在settings.py中配置文件修改TEMPLATES配置项的DIRS值
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(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',
],
},
},
]
子应用views.py
from django.shortcuts import render
class HomeView(View):
def get(self, request):
return render(request, 'index.html')
子应用urls.py
from django.urls import path
from book.views import HomeView
urlpatterns = [
path('index/', HomeView.as_view()),
]
views.py
from django.shortcuts import render
from django.views import View
class HomeView(View):
def get(self,request):
# 1.获取数据
username = request.GET.get('username')
# 2.组织数据
context = {
'username': username
}
return render(request, 'index.html', context=context)
templates中的index.html内容如下(使用{{ username }}
获取上面传入的数据)
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<a href="">{{ username }}a>
body>
html>
浏览链接http://127.0.0.1:8000/index/?username=abcd
就会显示username后面的数据
下面列举了列表,遍历,判断,索引展示
注意:运算符两侧必须要有空格
views.py
from django.shortcuts import render
from django.views import View
# Create your views here.
class HomeView(View):
def get(self,request):
# 1.获取数据
username = request.GET.get('username')
# 2.组织数据
context = {
'username': username,
'age': 10,
'friend': ['toy', 'jack'],
'scores': {
'math': 100,
'english': 90,
}
}
return render(request, 'index.html', context=context)
index.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<a href="">{{ username }}a>
<hr>
年龄:{{ age }}
<hr>
朋友:{{ friend }}
<hr>
朋友:{{ friend.1 }}
<hr>
分数:{{ scores.math }}
<hr>
遍历索引朋友:{% for item in friend %}
<li>{{ forloop.counter }}{{ item }}li>
{% endfor %}
<hr>
{% if age > 10 %}
大于10岁
{% else %}
不大于10岁
{% endif %}
body>
html>
过滤器
过滤器语法形式:变量|过滤器:参数
过滤器有几个常用的
safe,禁用转义,告诉模板这个变量是安全的,可以解释执行
length,长度,返回字符丰包含字符的个数,或列表、元组、字典的元素个数
default,默认值,如果变量不存在时则返回默认值(date|default:'默认值'
)
date,日期,用于对日期类型的值进行字符串格式化(value|data:"Y年m月j日 H时i分s秒"
),常用的格式化字符如下 :
字符 | 含义 | 解释 |
---|---|---|
Y | 年 | 格式4位(y两位) |
m | 月 | 01,02,12 |
d | 日 | 01,02 |
j | 日 | 1,2 |
H | 时 | 24小时制(h12小时制) |
i | 分 | 0-59 |
s | 秒 | 0-59 |
views.py
from django.shortcuts import render
from django.views import View
import datetime
class HomeView(View):
def get(self,request):
username = request.GET.get('username')
context = {
'datetime': datetime.datetime.now(),
}
return render(request, 'index.html', context=context)
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
日期:{{ datetime|date:'Y m d' }}
body>
html>
转义字符如下,按如下方式书写,会有转义字符
views.py
class HomeView(View):
def get(self,request):
# 1.获取数据
username = request.GET.get('username')
# 2.组织数据
context = {
'desc': ''
}
return render(request, 'index.html', context=context)
index.html
<body>
转义字符:{{ desc }}
body>
按上面书写会是这种效果,没有弹窗,会将scrpipt转化为字符串显示
经过如下修改,就会弹出弹框
<body>
转义字符:{{ desc|safe }}
body>
无定义的量默认值及长度
在views.py中没有定义的变量
<body>
无定义变量:{{ abc|default:'默认值' }}
<hr>
长度:{{ friend|length }}
body>
class HomeView(View):
def get(self,request):
# 1.获取数据
username = request.GET.get('username')
# 2.组织数据
context = {
'friend': ['toy', 'jack'],
}
return render(request, 'index.html', context=context)
模板继承和类的继承含义是一样的,主要是为了提高代码重用,减轻开发人员的工作量
父标签
如果发现在多个模板中某些内容相同,那就应该把这段内容定义到父模板中。
标签block: 用于在父模板中预留区域,留给子模板填充差异性的内容,名字不能相同·为了更好的可读性,建议给endblock标签写上名字,这个名字与对应的block名字相同,父模板中也可以使用上下文中传递过来的数据。
字标签
标签extends: 继承,写在子模板文件的第一行
子模版不用填充父模版中的所有预留区域,如果子模版没有填充,则使用父模版定义的默认值
这个页面和另一个页面某个部分是一样的模板格式
在template目录中创建一个基础文件base.html
,代码如下
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>{% block title %}Title{% endblock title %}title>
head>
<body>
{% block header %}
<h1>顶部h1>
{% endblock header %}
{% block main %}
<h1>主要部分h1>
{% endblock main %}
{% block footer %}
<h1>底部h1>
{% endblock footer %}
body>
html>
创建detail.html如下
把继承的模板写在最上面
需要改哪里,直接实现block,进行替换,没写的就还是原先base.html的内容
不需要的部分直接用空白block进行替换
(这里替换了标题,导航栏主题,删除了底部,标题没有变动
)
{% extends 'base.html' %}
{% block title %}
替换标题
{% endblock title %}
{% block main %}
<a href="">替换主体a>
{% endblock main %}
{% block footer %}{% endblock %}
jinja2是 Python 下一个被广泛应用的模板引擎,是由Python实现的模板语言,他的设计思想来源于Django的模板引擎,并扩展了其语法和一系列强大的功能,尤其是Flask框架内置的模板语言
由于django默认模板引擎功能不齐全,速度慢,所以我们也可以在Django中使用jinja2, jinja2宣称比django默认模板引擎快10-20倍
Django主流的第三方APP基本上也都同时支持Diango默认模板及inja2,所以要用inia2也不会有多少障碍。
安装jinja2模板:pip install jinja2
jiaja2配置
在settings.py中配置
TEMPLATES = [
{
# 'BACKEND': 'django.template.backends.django.DjangoTemplates', # 默认引擎
'BACKEND': 'django.template.backends.jinja2.Jinja2', # 修改为Jinja2模板
'DIRS': [os.path.join(BASE_DIR, 'template')],
'APP_DIRS': True,
'OPTIONS': {
'environment': 'jinja2.Environment', # 添加这行 默认的,可加可不加
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},# 如果报错,原始的代码复制到下面
{
'BACKEND': 'django.template.backends.django.DjangoTemplates', # 默认引擎
'DIRS': [os.path.join(BASE_DIR, 'template')],
'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',
],
},
},
]
直接使用上面的index.html会出现错误
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<a href="">{{ username }}a>
<hr>
年龄:{{ age }}
<hr>
朋友:{{ friend }}
<hr>
朋友:{{ friend.1 }}
<hr>
分数:{{ scores.math }}
<hr>
遍历索引朋友:{% for item in friend %}
<li>{{ forloop.counter }}{{ item }}li>
{% endfor %}
<hr>
{% if age > 10 %}
大于10岁
{% else %}
不大于10岁
{% endif %}
<hr>
日期:{{ datetime|date:'Y m d' }}
转义字符:{{ desc|safe }}
无定义变量:{{ abc|default:'默认值' }}
<hr>
长度:{{ friend|length }}
body>
html>
这是因为jiaja2的语法和上面模板的语法有些区别
区别在于过滤器不同,上面的模板是forloop,jiaja2是loop
jiaja2过滤器
index.html
<body>
日期:{{ date(datetime) }}
body>
from django.views import View
import datetime
class HomeView(View):
def get(self,request):
context = {
'datetime': datetime.datetime.now(),
}
return render(request, 'index.html', context=context)
有上面的还不够,会显示date未定义,这时在子应用中创建jinja2_env.py
文件,内容如下
from django.template.defaultfilters import date
from jinja2 import Environment
def environment(**option):
# 1.创建Environment实例
env = Environment(**option)
# 2.指定jinja2的函数指向django的指定过滤器
env.globals.update({
'date': date
})
# 3.返回Environment实例
return env
然后修改配置settings.py
TEMPLATES = [
{
# 'BACKEND': 'django.template.backends.django.DjangoTemplates', # 默认引擎
'BACKEND': 'django.template.backends.jinja2.Jinja2', # 修改为Jinja2模板
'DIRS': [os.path.join(BASE_DIR, 'template')],
'APP_DIRS': True,
'OPTIONS': {
# 'environment': 'jinja2.Environment', # 添加这行 默认的,可加可不加
'environment': 'book.jinja2_env.environment', # 指定jinja2的环境
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},# 如果报错,原始的代码复制到下面
{
'BACKEND': 'django.template.backends.django.DjangoTemplates', # 默认引擎
'DIRS': [os.path.join(BASE_DIR, 'template')],
'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',
],
},
},
]
然后就会出现内容
要显示特定的格式,在后面和之前一样添加参数
<body>
日期:{{ date(datetime,'Y m d') }}
body>
自定义过滤器
from jinja2 import Environment
def environment(**option):
# 1.创建Environment实例
env = Environment(**option)
# 2.将自定义过滤器添加到环境中
env.filters['do_listreverse'] = do_listreverse
# 3.返回Environment实例
return env
# 自定义过滤器
def do_listreverse(li):
if li == 'B':
return "abcd"
CSRF 指攻击者盗用了你的身份,以你的名义发送恶意请求。包括:以你名义发送邮件,发消息山盗取你的账号,甚至于购买商品,虚拟货币转账…
造成的问题 :个人隐私泄露以及财产安全。
生成随机码
csrf_token放入cookie是同源策略的原因,黑客(钓鱼)网站是用于获取不到我们当前网站的cookie信息的
def get(self, request):
from django.middleware.csrf import get_token
# 生成随机码
csrf_token = get_token(request)
response = render(request, 'index.html', context={'csrf_token': csrf_token})
# 将验证码保存至cookie中
response.set_cookie('csrftoken', csrf_token)
return response
获取用户输入的随机码并验证
def index(self, request):
# 获取用户数据
user_token = request.POST.get('csrftoken')
to_account = request.POST.get('to_account')
money = request.POST.get("money")
# 获取系统生成的验证码
server_token = request.COOKIES.get('csrf_token')
# 输入的验证码和系统生成的验证
if user_token != server_token:
return HttpResponse('输入错误')
上面获取数据是通过name属性获取的
<body>
<input type="hidden" name="csrftoken">
<input type="text" name="to_account">
<input type="number" name="money">
body>
源:是协议、域名、端口号组成
同源策略:同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源。
session数据的保存、获取、删除(views.py)
# 增加数据
class SetSession(View):
def get(self, request):
request.session['name'] = 'icast'
return HttpResponse('abc')
# 获取数据
class GetSession(View):
def get(self, request):
name = request.session['name']
return HttpResponse('abc')
# 删除数据
class DelSession(View):
def get(self, request):
# 删除一条数据
del request.session['name']
# 删除session所有数据
# request.session.clear()
# 把数据库/redis中的key都删除了
# request.session.flush()
return HttpResponse('abc')
# session时间,到了时间自动删除
class DelSession(View):
def get(self, request):
# 设置session时间,默认两周
request.session.set_expiry(10)
return HttpResponse('abc')
session数据存储位置
在settings.py中,可以设置session数据的存储方式,可以保存在数据库、本地缓存等
保存在数据库中,如下设置可以写,也可以不写,这是默认方式
SESSION_ENGINE=‘django.contrib.sessions.backends.db’
如果保存在数据库中,需要在INSTALLED_APPS中安装Session应用
INSTALLED_APPS = [
…
‘django.contrib.sessions’,
…
]
本地缓存:存储在本机内存中,如果丢失则不能找回,比数据库的方式读写更快
SESSION_ENGINE=‘django.contrib.sessions.backends.cache’
混合缓存:优先从本机内存中存取,如果没有则从数据库中存取
SESSION_ENGINE=‘django.contrib.sessions.backends.cached_db’
Redis:在redis中保存session,需要引入第三方扩展,我们可以使用django-redis来解决。
django-redis链接:https://django-redis-chs.readthedocs.io/zh_CN/latest/
安装扩展:pip install django-redis
配置settings.py内容如下
# caches缓存
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
}
}
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
login.html
<body>
<from action="" method="post">
<input type="text" name="money">
<input type="submit" value="提交">
from>
body>
urls.py
from book.views import LoginView
urlpatterns = [
path('login/', LoginView.as_view()),
]
views.py
class LoginView(View):
def get(self, request):
return render(request, 'login.html')
def post(self, request):
return HttpResponse('post')
在上面的基础上,浏览链接输入内容后点击提交会报错,需要在login.html中加入如下代码
<body>
<from action="" method="post">
{% csrf_token %}
<input type="text" name="money">
<input type="submit" value="提交">
from>
body>
然后点击提交,会跳转到post请求中去