文章目录
- HTTP协议
- socket实现一个web服务器
- WSGI实现一个web服务器
- WSGI实现支持多URL的web服务器
- WSGI实现图片显示的web服务器
- MVC && MTV
- 1.MVC
- 2.MTV
- 3.总结
- 一、创建Django项目
- 1.创建项目
- 2.创建app
- 3.第一次django 请求
- 二、模板
- 1.配置settings.py
- 2.模板语法
- 3.继承模板
- 三、models模型
- 1.常用字段&属性
- 四、设置数据库
- 1.设置mysql库
- 2.mysqldb替换mysql
- 3.数据迁移
- 五、URL路由匹配
- 1.正则匹配
- 2.路由匹配&传参
- 3.管理多个URL文件
- 1.去除重复url路径
- 2.视图传参数
- 六、View视图
- 1.HttpRequest对象
- 2.HttpResponse对象
- 3.CBV-类方法
- 4.通过HttpResponse下载文件
- 5.分页功能
- 七、ORM的增删改查语句
- 1.创建语句
- 2.查询语句
- 3.修改语句
- 4.删除语句
- 5.Filter过滤条件
- 6.多模块关联关系
- 7.Queryset对象方法
- 8.ORM对象操作
- 1.单表对象操作
- 2.外键关联操作
- 3.外键反向关联操作
- 4.多对多操作
- 5.多对多反向操作
- 八、Django会话技术
- 1、cookie工作原理
- 2.Cookie
- 2、CSRF
- 防止CSRF
- 九、ORM-admin后台管理
- 1.管理app下的表(模型)
- 2.自定制admin(管理界面)
- 返回上级目录
HTTP协议
HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传送协议。它可以使浏览器更加高效,使网络传输减少。它不仅保证计算机正确快速地传输超文本文档,还确定传输文档中的哪“部分,以及哪部分内容首先显示(如文本先于图形)等。
socket实现一个web服务器
# -*- coding:utf-8 -*-
# created by Alex Li - 路飞学城import socketdef main():sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)sock.bind(('localhost', 8000))sock.listen(5)while True:# 等待浏览器访问conn, addr = sock.accept()# 接收浏览器发送来的请求内容data = conn.recv(1024)print(data)# 给浏览器返回内容conn.send(b"HTTP/1.1 200 OK\r\nContent-Type:text/html; charset=utf-8\r\n\r\n")conn.send("<h1 style='color:blue'>电脑前的你长的真好看!</h1>".encode("utf-8"))# 关闭和浏览器创建的socket连接conn.close()if __name__ == "__main__":main()
WSGI实现一个web服务器
- WSGI (Web Server Gateway lnterface) 是一种规范,它定义了使用python编的web app (应用程序) 与web server (socket服务端)之间接口格式,实现web app与web server间的解耦
- 通俗的说:当规范建立后,程序就不再重复编写web server (socket服务端),而是直接使用现成的实现WSGI的模块 (例如:wsgiref、uwsgiwerkzeug),从而让程序员更加专注与业务代码与其重复造轮子,不如直接用现成的。
from wsgiref.simple_server import make_serverdef run_server(environ,start_response):""":param environ: 请求相关内容,比如浏览器类型、版本、来源地址、url等:param start_response: 响应相关:return:"""start_response("200 OK",[('Content-Type','text/html;charset=utf-8')])return [bytes('<h1>Hello world!</h1>',encoding='utf-8')]if __name__ == '__main__':httpd = make_server('localhost',8000,run_server)httpd.serve_forever() # 死循环
WSGI实现支持多URL的web服务器
from wsgiref.simple_server import make_serverdef book(environ,start_response):start_response("200 OK", [('Content-Type', 'text/html;charset=utf-8')])return [bytes('<h1 style="color:pink">book page!</h1>', encoding='utf-8')]def cloth(environ,start_response):start_response("200 OK", [('Content-Type', 'text/html;charset=utf-8')])return [bytes('<h1 style="color:yellow">cloth page!</h1>', encoding='utf-8')]passdef url_dispacher():urls = {'/book':book,'/cloth':cloth,}return urlsdef run_server(environ,start_response):""":param environ: 请求相关内容,比如浏览器类型、版本、来源地址、url等:param start_response: 响应相关:return:"""request_url = environ.get("PATH_INFO") # 获取用户请求的urlurl_list = url_dispacher() # 拿到所有urlif request_url in url_list:func_data = url_list[request_url](environ,start_response)return func_data # 真正返回数据给用户else:start_response("404 ",[('Content-Type','text/html;charset=utf-8')])return [bytes('<h1 style="font-size:50px">404,Page not found!</h1>',encoding='utf-8')]if __name__ == '__main__':httpd = make_server('localhost',8000,run_server)httpd.serve_forever() # 死循环
WSGI实现图片显示的web服务器
from wsgiref.simple_server import make_server
import re
import osBASE_DIR = os.path.dirname(os.path.abspath(__file__))def book(environ,start_response):start_response("200 OK", [('Content-Type', 'text/html;charset=utf-8')])data = """<h1>欢迎来到日本人专区</h1><img src='/static/imgs/testimg.gif' /><p>上路飞学城,看尽天下小片</p>"""return [bytes(data, encoding='utf-8')]def cloth(environ,start_response):start_response("200 OK", [('Content-Type', 'text/html;charset=utf-8')])return [bytes('<h1 style="color:yellow">cloth page!</h1>', encoding='utf-8')]passdef url_dispacher():urls = {'/book':book,'/cloth':cloth,}return urlsdef img_handler(request_url):""":param request_url: static/imgs/testimg.gif:return:"""img_path = re.sub('/static','/static_data',request_url)img_abs_path = "%s%s"% (BASE_DIR,img_path)if os.path.isfile(img_abs_path):f = open(img_abs_path,"rb")data = f.read() # 读取文件内容数据return [data,0] # 0:成功读取,1:没有读取return [None,1]def run_server(environ,start_response):""":param environ: 请求相关内容,比如浏览器类型、版本、来源地址、url等:param start_response: 响应相关:return:"""request_url = environ.get("PATH_INFO") # 获取用户请求的urlurl_list = url_dispacher() # 拿到所有urlif request_url in url_list:func_data = url_list[request_url] (environ,start_response) # 调用相应的url函数return func_data # 真正返回数据给用户elif request_url.startswith("/static/"): # 表示图片img_data,img_status = img_handler(request_url)if img_status == 0: # 读取到图片数据start_response("200 OK", [('Content-Type', 'text/html;charset=utf-8')])return [img_data]else:start_response("404 ",[('Content-Type','text/html;charset=utf-8')])return [bytes('<h1 style="font-size:50px">404,Page not found!</h1>',encoding='utf-8')]if __name__ == '__main__':httpd = make_server('localhost',8001,run_server)httpd.serve_forever() # 死循环
MVC && MTV
1.MVC
MVC 是一种使用 MVC (Model View Controller 模型-视图-控制器)设计创建 Web 应用程序的模式
- Model(模型)一般对应数据库操作、纪录的存取
- View (视图)决定着如何展示数据
- Controller (控制器) 负现处理用户交互的部分·控制器负责从视图读取数据,控制用户输入,并向模型发送数据。(urls)
2.MTV
Django是一个MTV框架,其架构模板看上去与传统的MVC架构并没有太大的区别。
Django将MVC中的视图进一步分解为 Django视图(views.py) 和 Django模板(html文件)两个部分,分别决定“展现哪些数据”和“如何展现”,使得Django的模板可以根据需要随时替换,而不仅仅限制于内置的模板,至于MVC控制器部分,由Django框架的URLconf(urls.py)来实现。
3.总结
MVC VIEWS 负责 业务逻辑处理+数据展示
MTV Views 业务逻辑处理
Templates 数据展示
一、创建Django项目
- 要在终端中使用Django创建一个项目,可以按照以下步骤进行操作:
-
打开终端(命令行界面)
-
确保你已经安装了Django。可以通过运行以下命令来检查Django是否已安装:
django-admin --version
如果没有安装Django,可以使用以下命令安装:
pip install django
-
创建项目。在终端中使用以下命令创建一个Django项目:
django-admin startproject project_name
把 “project_name” 替换为你想要的项目名称。这将在当前目录下创建一个名为 “project_name” 的文件夹,其中包含用于构建Django项目所需的文件和目录。
创建应用。在项目目录中使用以下命令创建一个Django应用:
django-admin startapp app_name
把 “app_name” 替换为你想要的应用名称。这将在项目目录下创建一个名为 “app_name” 的文件夹,其中包含用于构建Django应用的文件和目录。
-
进入项目目录。使用以下命令进入刚创建的项目目录:
cd project_name
这会将终端的当前工作目录切换到你的项目目录中。
-
运行开发服务器。在项目目录中,运行以下命令来启动Django的开发服务器:
python manage.py runserver 0.0.0.0:8000
这将运行开发服务器,默认在本地的8000端口上。
-
现在,你可以在浏览器中访问
http://localhost:8000/
来查看新创建的Django项目的初始页面。
这样就完成了在终端中创建一个Django项目的步骤。你可以根据需要在项目中添加应用、模型、视图等。
1.创建项目
django-admin startproject my_site
项目目录结构
mysite
├── manage.py # 管理程序的文件,启动和结束等。
└── my_site├── __init__.py├── settings.py # 程序的配置文件├── urls.py # 程序的路由系统,即:url和处理其函数的对应的关系└── wsgi.py # 指定框架的wsgi
2.创建app
django-admin startapp app01
├── __init__.py 包
├── admin.py 数据库后台
├── apps.py #django 把项目和app 关联起来的一个文件
├── migrations #数据库相关
│ └── __init__.py
├── models.py # 数据库操作地方
├── tests.py # 单元测试
└── views.py # 业务逻辑代码
3.第一次django 请求
- 匹配路由, 路由分发器查找用户请求的url 对应关系,
- 找到了业务函数,就调用
- 找不到就报404
- 业务函数,执行业务逻辑
- 返回数据给浏览器
1。 urls.py 编写路由
2. 在views.py 写一个业务函数2.1 编写业务 代码2.2 通过HttpResponse 方法返回数据给前端3. python manage.py runserver 0.0.0.0:8000
二、模板
1.配置settings.py
- TEMPLATES 配置Html文件,即配置该文件的路径,以便运行程序查找
- 配置jinja引擎,需在该项目下新建一个文件:jinja2_env.py,并添加代码:
# 安装模块:pip install jinja2from django.templatetags.static import static
from django.urls import reverse
from jinja2 import Environmentdef environment(**options):env = Environment(**options)env.globals.update({'static': static,'url': reverse,})return env
# 项目默认配置django引擎
# 若需配置jinja引擎,需添加
'BACKEND': 'django.template.backends.jinja2.Jinja2',
'environment':'django4.jinja2_env.environment', # django4-项目名称,jinja2_env-该项目下新建的jinja2_env.pyTEMPLATES = [ # 模版,html文件# jinja2模板引擎 (在html文件中可以引用函数){'BACKEND': 'django.template.backends.jinja2.Jinja2','DIRS': [BASE_DIR/'templates'],'APP_DIRS': True,'OPTIONS': {# 添加environment,并指定到jinja2_env文件中的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',],},},# django模板引擎{'BACKEND': 'django.template.backends.django.DjangoTemplates','DIRS': [BASE_DIR/"templates"], # html 文件夹的位置'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',],},},
]
2.模板语法
- 在templates目录下的html文件
"""注释"""
{# 单行注释 #}
{% comment %}多行注释
{% endcomment %}"""变量"""
<p>{{ name }}</p>
<p>{{ boddy.2 }}</p> # 列表:索引号取值
<p>{{ addr.city }}</p> # 字典"""if语句"""
{% if age < 18 %}...
{% elif age > 60 %}...
{% else %}...
{% endif %} """结合运算符"""
{% if age >= 18 and age < 60%}
{% if age >= 18 or age < 60%}"""in"""
{% if 'movie' in boddy %}...
{% endif %}"""for循环语句"""
{% for b in boddy %}...
{% endfor %}
"""empty用法"""
{% for b in boddy2 %}...
{% empty %} <p>boddy2为空或不存在</p>
{% endfor %}"""下标"""
{% for foo in boddy %}<p>counter_{{ forloop.counter }} # 下标从1开始counter0_{{ forloop.counter0 }} # 下标从0开始revcounter_{{ forloop.revcounter }} # 下标颠倒从大到小revcounter0_{{ forloop.revcounter0 }} # 下标颠倒从大到小(0)</p>
{% endfor %}"""嵌套语句"""
<table border="2" width="400">{% for star in stars %}{% for s in star %}{{ s }}-{{ forloop.parentloop.counter }}- # 父循环的下标{{ forloop.counter }} # 本次循环下标{% endfor %}{% endfor %}
</table>"""过滤器"""
<p>{{ age }}</p>
<p>{{ age|add:5 }}</p> # add:变量+5
<p>{{ age|add:-5 }}</p> # add:变量-5<p>{{ name|first|upper }}</p> # 将变量name第一个字符串大写
<p>{{ name|last|lower }}</p> # 将变量name最后一个字符串小写
<p>{{ name|title }}</p>
<p>{{ name|truncatechars:3 }}</p> # 将变量name第三个字符串开始截断<p>{{ boddy|join:'+' }}</p> # 列表转字符串<p>{{ boddy2|default:'默认值' }}</p> # 若变量boddy2无值则设置默认值<p>{{ dt|date:'y-m-d' }}</p> # 转成日期格式
<p>{{ dt|date:'Y-M-d' }}</p> """HTML转义"""
<p>{{ code|safe }}</p> # safe:将变量code字符串转义成html格式{% autoescape on %} # 关闭自动转义... # 自动转义字符串内容
{% endautoescape %}{% autoescape off %} # 开启自动转义...
{% endautoescape %}
- 若使用 jinja模板引擎,则可以调用python的函数
<body><h2>jinja2模板语言</h2><hr>{# 可调用python函数range #}{% for i in range(1,10) %}{{ i }}{% endfor %}</body>
</html>
3.继承模板
# 模板快
{% block xxx%}...
{% endblock %}# 继承模板
{% extends 'xxx.html' %}# 继承模板内容:block.super
{% block content %}
{# 默认情况下,子模板会覆盖父模板内容 #}
{# 若想继承父模板内容,需使用block.super #}{{ block.super }}<div><button>content</button></div>
{% endblock %}# 导入其他模板文件:include
{% block head %}
{# 导入其他模板文件 #}{% include 'xxx.html' %}<div><button>head</button></div>
{% endblock %}
三、models模型
- django使用对象关系映射 (0bject Relational Mapping,简称ORM)框架去操控数据库。
models ==> ORM
模型 -> 表
类结构 -> 表结构
对象 -> 表的一条数据
类属性 -> 表的字段
1.常用字段&属性
# 常用字段
CharField:用于存储字符串,可指定最大长度。TextField:用于存储长文本字段,没有长度限制。IntegerField:用于存储整数。FloatField:用于存储浮点数。BooleanField:用于存储布尔值,True或False。DateField:用于存储日期。DateTimeField:用于存储日期和时间。FileField:用于存储文件,文件会被上传到指定目录。ImageField:与FileField类似,但特定于存储图像文件。
# 常用属性
max_length:用于指定字段的最大长度。适用于CharField和TextField等字符串字段类型。null:指定字段是否允许为空。默认为False,即不允许为空。对于关联字段(ForeignKey、OneToOneField等),默认值是True。blank:指定字段在表单中是否可以为空。默认为False,即不能为空。对于关联字段,如果null=True,则默认值为True。default:指定字段的默认值。choices:用于定义字段的选项列表。适用于CharField和IntegerField等字段类型。例如:choices=[(1, ‘男’), (2, ‘女’)]verbose_name:指定字段在后台管理界面中显示的名称。related_name:用于指定反向关系的名称。适用于关联字段(ForeignKey、ManyToManyField等)。unique:指定字段的值是否在数据库中唯一,默认为False。help_text:用于在后台管理界面中显示字段的帮助文本。auto_now:指定每次保存对象时自动更新字段的值为当前时间。适用于DateTimeField和DateField等日期时间字段类型。auto_now_add:指定在创建对象时将字段的值设置为当前时间。适用于DateTimeField和DateField等日期时间字段类型。editable:指定字段是否可编辑。默认为True,表示字段可以在后台管理界面中被编辑。primary_key:指定字段是否为主键。默认为False。db_index:指定是否在数据库中为字段创建索引。默认为False。choices:定义了一个包含键-值对的元组,作为下拉菜单。name / db_column:数据库中的字段名称。upload_to:用于定义上传文件的存储路径。
- models.py
# 注:每次写完模型或添加一个字段都需要进行一次数据迁移,将数据迁移到数据库中!from django.db import modelsclass UserModel(models.Model):# uid 会成为主键,原来默认的id不会创建uid = models.AutoField(auto_created=True,primary_key=True)# CharField:字符串类型# unique:唯一# db_index:数据库索引name = models.CharField(max_length=20,unique=True,db_index=True)# IntegerField:整数类型# default:默认值age = models.IntegerField(default=18)# BooleanFieldL:bool类型sex = models.BooleanField(default=True)# TextField:文本框# null=True:表示可以为空# blank=True:在admin管理页面可以为空info = models.TextField(null=True,blank=True)# FloatField:小数salary = models.FloatField(default=100000.521)# DecimalField:十进制小数# max_digits:最大长度# decimal_places:小数点是几位money = models.DecimalField(max_digits=4,decimal_places=2,default=10.52)
四、设置数据库
- 对象关系映射(Object Relational Mapping),它的实质就是将关系数据(库中的业务数据用对象的形式表示出来并通过面向对象 (Object-Oriented)的方式将这些对象组织起来,实现系统业务逻辑的过程。
1.设置mysql库
# settings.py# 设置mysql
DATABASES = {"default": {"ENGINE": "django.db.backends.mysql","NAME": "myBlog", # 数据库名"USER": "root","PASSWORD": "12345678","HOST": "127.0.0.1","PORT": "3306",}
}
2.mysqldb替换mysql
- python3连接mysql的得使用pymysql,MysqlDB模块300年没更新了,但django默认调用的还是MysQLdb,因此 pymysq有个功能可以让django以为是用了MySQLdb.即在项目目录下的 init.py中加上句代码就好
- 或者直接安装mysqlclient则无需使用该方法:pip install mysqlclient
# __init__.py(项目文件)
import pymysqlpymysql.version_info = (1, 4, 13, "final", 0) # mysqlclient版本问题
pymysql.install_as_MySQLdb()
3.数据迁移
-
同步ORM表结构到数据库(Models.py文件每次写完就需要进行迁移)
-
在项目中如果添加了一个字段,需手动同步到数据库,否则数据库报错没有该字段
-
数据库同步工具:migrations
-
1、生成迁移文件
# app01表示指定的app,若第一次执行,则可以默认不写
python manage.py makemigrations app01
- 2、执行迁移
python manage.py migrate
# settings.py
# 同步app设置,最后写入同步的app名INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','app01',
]
五、URL路由匹配
1.正则匹配
- Django的路由本质上是通过正则表达式来对用户请求的url进行匹配(Django2.0版本前使用正则匹配)
# urls.pyfrom django.urls import path,re_path
from app02 import viewsurlpatterns = [re_path(r'^articles/2003/$',views.article_archive1),# year,month被封装为字典,所以函数形参需写该键值接收re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$',views.article_archive2),# slug:匹配任意字符串re_path(r'^articles/(?P<arg1>[0-9]{4})/(?P<arg2>[0-9]{2})/(?P<slug>[\w-]+)/$',views.article_archive3),
]
# views.pydef article_archive1(request):return HttpResponse('静态article_2003')def article_archive2(request,year,month):return HttpResponse('动态article %s-%s'%(year,month))def article_archive3(request,arg1,arg2,slug):return HttpResponse('动态article %s-%s-%s'%(arg1,arg2,slug))
2.路由匹配&传参
- Django2.0新用法path
- 自定义方法调用 register_converter()
# urls.pyfrom django.urls import path
from app02 import views# path匹配
urlpatterns = [path('articles/<int:year>',views.new_article1),# str:除了'/'外都能匹配path('articles/<int:year>/<str:date>',views.new_article2),path('articles/<int:year>/<int:month>/<slug:arg>',views.new_article3),# path:'/'也能匹配path('articles/<path:arg>',views.new_article4),
]
# views.pydef new_article1(request,year):return HttpResponse('新用法Path %s'%year)def new_article2(request,year,date):return HttpResponse('新用法Path %s %s'%(year,date))def new_article3(request,year,month,arg):return HttpResponse('新用法Path %s %s %s'%(year,month,arg))def new_article4(request,arg):return HttpResponse('新用法Path %s'%(arg))
- 路由传参
# urls.py
urlpatterns = [path('userdetail/<int:uid>',userdetail,name='myuserdetail'),
]# views.py
def userdetail(request,uid):user = userModel.objects.get(pk=uid) # pk:主键return render(request,'userdetail.html',{'user':user}) # 将user参数传入html文件的user变量# userdetail.html
<body><p>名字:{{ user.name }}</p><p>年龄:{{ user.age }}</p>
</body>
- 命名空间
- 若路径有命名空间,则该命名空间下所有相关的路径都需要添加该命名空间
# urls.py
urlpatterns = [path('app01/',include(('app01.urls','app01'),namespace='app01')),
]# app01:urls.py
urlpatterns = [path('userlist/',userlist,name='myuserlist'), # path('userdetail/<int:uid>',userdetail,name='myuserdetail'),
]# userlist.html
<body># 跳转到命名空间为app01下的myuserdetail的html( url/userdetail/id )<a href="{% url 'app01:myuserdetail' user.id %}"></a>
</body>
- 重定向
# views.py
from django.shortcuts import redirect,reverse# 重定向
def myredirect(request):# return redirect('https://www.baidu.com')# 反向解析# redirect(reverse('myuserdetail'),args=(2,)) ==> 'userdetail/2/'return redirect(reverse('myuserdetail',args=(2,))) # args传参return redirect(reverse('myuserdetail',kwargs={'uid':2})) # 关键字传参# 命名空间return redirect(reverse('app01:myuserdetail',args=(2,))) # args传参return redirect(reverse('app01:myuserdetail',kwargs={'uid':2})) # 关键字传参
3.管理多个URL文件
- 当有多个app时,每个app可以有自己的urls.py,只需在顶级urls.py(项目下的urls)中include一下就可以
# urls.pyfrom django.urls import path,includeurlpatterns = [# 包括了app01和app02项目的urlspath('app01/',include("app01.urls")),path('app02/',include("app02.urls"))
]
1.去除重复url路径
# urls.py# 去除重复的部分url
extra_urls = [path('<int:year>/<str:date>', views.new_article2),path('<int:year>/<int:month>/<slug:arg>', views.new_article3),path('<path:arg>', views.new_article4),
]urlpatterns = [# 去除url重复的路径"articles"部分,匹配结果相同path('articles',include(extra_urls))
]
2.视图传参数
# urls.py
urlpatterns = [# 写一个字典,键表示实参名,值表示传入实参值path('articles/<int:year>',views.new_article1,{'version':'v1.0'}),
]
# views.py
# 获取参数version
def new_article1(request,year,version):return HttpResponse('新用法Path %s-%s'%(year,version))
六、View视图
1.HttpRequest对象
- **request.scheme:**返回请求协议(http/https)
- **request.path:**返回请求页的路径
- **request.method:**返回http请求的方法(get/post/put/delete…)
- **request.GET/request.POST:**返回http请求的参数(方法由HTML文件决定)
- **request.content_type:**返回http请求的类型
- **request.FILES:**返回上传的文件数据
- 获取文件数据,需在HTML文件加上属性 enctype=“multipart/form-data” 告知后台这个表单有文件,将文件单独处理。
- **request.META:**包含所有可用 HTTP头的字典
- **request.get_host():**返回网站服务器地址
- **request.get_port():**返回服务器主机端口
- **request.get_full_path():**返回相对路径(相当于request.path)
- **request.build_absolute_uri(‘location’):**返回url的绝对路径,若添加位置,则附加
- **request.COOKIES:**会话技术
- **request.session:**会话技术
- **request.META[‘REMOTE_ADDR’]😗*客户端的ip地址
# views.py
from django.shortcuts import render,HttpResponsedef index(request):print(request.scheme) # httpprint(request.path) # /app02/indexprint(request.method) # getprint(request.content_type) # text/plainprint(request.GET,request.POST) # <QueryDict: {'username': ['xiaof'], 'password': ['lsf20010929']}>print(request.GET.get('username'),request.POST.get('username')) # 获取字典数据print(request.FILES) # <MultiValueDict: {'my_file': [<InMemoryUploadedFile: CSS.jpeg (image/jpeg)>]}>print(request.META['REMOTE_ADDR']) # 127.0.0.1print(request.META) for k,v in request.META.items():print(k,v)print(request.get_host()) # localhost:8000print(request.get_port()) # 8000print(request.get_full_path()) # /app02/indexprint(request.build_absolute_uri('user=abc/pwd=123')) # http://localhost:8000/app02/user=abc/pwd=123print(request.COOKIES) # 浏览器cookies数据print(request.session)return render(request,'form.html')
- 中间件(防火墙)
- url->中间件->view,中间件相当于防火墙,需关闭相应的安全过滤,才能修改浏览器的请求方法
# 项目的settings.pyMIDDLEWARE = [ # 中间件(防火墙)'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',
]
2.HttpResponse对象
# 响应
from django.http import JsonResponse# 1.返回字符串
return HttpResponse('OK')
# 2.返回模板:前后端不分离使用
return render(request,'index.html')
# 3.重定向:跳转页码
return HttpResponseRedirect("/")
# 4.返回JSON:前后端分离使用
return JsonResponse({"data":"hello"})
-
**content_type:**用于填充HTTP的数据类型
- 默认情况下为: “ text/html; charset = utf-8”。
-
**status:**HTTP的状态码
# views.pyfrom django.shortcuts import HttpResponsedef index(request):html = """<form method="POST" enctype="multipart/form-data"><input type="text" name="username"><input type="password" name="password"><input type="file" name="my_file"><input type="submit" value="登录"></form>"""res = HttpResponse(html,content_type="text/plain",status=404)
- **HttpResponseRedirect:**状态码:302,临时重定向一个新的位置
- **HttpResponsePermanentRedirecttt:**状态码:301,永久定向到一个新的位置
from django.shortcuts import HttpResponseRedirect,HttpResponsePermanentRedirectttdef test(request):return HttpResponseRedirect("/") # 临时定向到首页return HttpResponsePermanentRedirecttt("/") # 永久定向到首页
3.CBV-类方法
- CBV(Class-Based-View),CBV就是在视图里使用类处理请求。
- 即可以让我们用类写View。这样做的优点主要下面两种:
- 提高了代码的复用性,可以使用面向对象的技术,比如Mixin(多继承)
- 可以用不同的函数针对不同的HTTP方法处理,而不是通过很多if判断,提高代码可读性
# views.py
from django.views import View# 该类会自动判断调用方法
class TestView(View):def get(self,request):return HttpResponse("Class View get request")def post(self,request):return HttpResponse("Class View post request")pass
- url处理是需要一个可调用的函数,class-based-view提供了一个 as_view() 静态方法
# urls.pyurlpatterns = [path('class_view',views.TestView.as_view()) # 对于类需调用as_view方法
]
- 类继承
# views.py # 类继承
class TestView2(TestView):time = 10
4.通过HttpResponse下载文件
- MIME参考手册:https://www.w3school.com.cn/media/media_mimeref.asp
- 对于文件类型,需参考MIME手册,然后修改 content_type
# views.pydef download_file(request):f = open('static_data/data.xls','rb')res = HttpResponse(f.read(),content_type='application/vnd.ms-excel') # 文件类型为xlsres['Content-Disposition'] = 'attachment; filename="my_data.xls"' # 对下载的文件进行处理return res
5.分页功能
# views.py
from django.core.paginator import Paginatordef paginate(request,page=1):per_page = 10all_data = PersonModel.objects.all()# 分页器paginator = Paginator(all_data,per_page)persons = paginator.page(page) # 获取第page页的数据pages = range(1,per_page+1) # 页面范围,供遍历data = {'persons':persons,'pages':pages}return render(request,'paginate.html',data)
# urls.py
urlspattern = [path('paginate/<int:page>/',paginate,name='paginate'),
]
七、ORM的增删改查语句
1.创建语句
- 方法1:在终端执行以下命令,进入django的数据库环境
python manage.py shell
- 创建数据
from app01 import models# 方法1,object.create创建对象,创建完后数据将自动提交保存到数据库
models.Account.object.create(username = '小枫',email='1848148016@qq.com',password='12345678',
)
# 方法2,先创建数据,再通过save方法提交数据
new_obj = models.Account(username = '小枫',email='1848148016@qq.com',password='12345678',
)
new_obj.save()
- 外键关联 models.ForeignKey(“Account”)
# 方法1,直接在对象里关联 account_id = id值
models.Article.objects.create(title = '如何学Python',content = '日复一日,坚持学习',# 关联的键值必须加上_idaccount_id = 1,pub_date = '2023-7-31',
)
# 方法2,先各自创建表,再进行关联 a1.account = m1对象
# 先创建用户表
m1 = models.Account(username='王五',email='123132@qq.com',password='213123',
)
m1.save()
# 再创建文章表
a1 = models.Article(title='什么是chatggpt',content='人工智能chatgpt',pub_date='2022-10-12',
)
# 关联表
a1.account = m1
a1.save()
- 多对多关联 models.ManyToManyField (“Tag”)
a1 = models.Article(title='什么是chatggpt',content='人工智能chatgpt',account_id = 1,pub_date='2022-10-12',
)
# tags.set,赋值tags对象id为1,3,5
a1.tags.set([1,3,5])
# tags.add,在原记录上加新值tags对象id为1,2,3
a1.tags.add(1,2,3)
articles = models.Article.objects.filter(content__contains='技术') # 过滤出含有关键词的文章
tag = models.Tag.objects.get(id=2) # 获取要关联的标签对象for article in articles:article.tags.add(tag) # 将标签关联到每篇过滤后的文章中
2.查询语句
# __gt:大于
# 查询id大于5的Account对象
models.Account.objects.filter('id__gt=5')
# startswith:以什么开头
# 查询以密码为a开头的Account对象
models.Account.objects.filter('password__startswith='a')
# , : 和
# 查询id大于5的Account对象并且psw是123
models.Account.objects.filter('id__gt=5,password='123')
3.修改语句
# 批量修改
models.Account.objects.filter(id__gt=1).update(password='1234556')# 单条修改
a = models.Account.objects.get(id=1)
a.username = 'job'
a.save()
4.删除语句
- 相关联的表数据也会被删除
# 批量删除
models.Account.objects.filter(id__gt=1).delete()# 单条删除
a = models.Account.objects.get(id=1)
a.delete()
5.Filter过滤条件
- 一般在前面加上is表示对字母大小写不敏感,可返回多个对象
gt:大于
it:小于
ite:小于等于
gte:大于等于
statswith:以什么开头
endswith:以什么结尾
contains:包含
models.Account.objects.filter(password__contains="1")
in:判断是否存在并返回
models.Account.objects.filter(id__in=(2,3))
range:区间过度,可对数字、日期进行过滤
import datetime
start_date = datetime.date(2022,12,15)
end_date = datetime.date(2023,8,1)
models.Account.objects.filter(register_date__range=(start_date,end_date))
date:查具体哪一天内容
models.Account.objects.filter(register_date__date="2023-7-31")
models.Account.objects.filter(register_date__date__gt="2023-7-31") # 配合gt使用
year、month、day:查哪一年、哪一月份、哪一天
models.Account.objects.filter(register_date__year="2023")
models.Account.objects.filter(register_date__month="12")
models.Account.objects.filter(register_date__day="15")
week:查询一年的第几周(52)
week_day:查星期几,从1(星期日)到7(星期六)
models.Account.objects.filter(register_date__week_day=2) # 星期一
hour、mintue、second:查每天的哪小时、哪分钟、哪一秒
models.Account.objects.filter(register_date__hour=8)
models.Account.objects.filter(register_date__mintue=15)
models.Account.objects.filter(register_date__second=15)
regex:正则表达式
models.Account.objects.filter(password__regex=r"^(1)")
- 方法2:在views.py 里写函数,对模型对象进行操作
- 同时需配置路由url
from django.shortcuts import render,HttpResponse
from django.db.models import Max,Min,Sum,Avg,Count
from app01.models import *# Create your views here.
def add_person(request):# 方式1# p = PersonModel()# try:# p.name='xiaoM'# p.age=21# p.save()# except Exception as e:# return HttpResponse('add fail')# return HttpResponse('add success!')# 方式2# try:# p = PersonModel(name='xiaoK',age=22)# p.save()# except Exception as e:# return HttpResponse('add fail')# return HttpResponse('add success!')# 方式3try:PersonModel.objects.create(name='ku',age=23)except Exception as e:return HttpResponse('add fail')return HttpResponse('add success!')def del_person(request):try:PersonModel.objects.filter(age__gt=20).delete()except Exception as e:return HttpResponse('delete fail')return HttpResponse('delete success!')def update_person(request):try:# p = PersonModel.objects.filter(age__gt=23).update(age=100)p = PersonModel.objects.get(id=5)p.age = 18p.save()except Exception as e:print(e)return HttpResponse('update fail')return HttpResponse('update success!')def get_person(request):try:# p = PersonModel.objects.get(id=5)# print(p.name,p.age)p = PersonModel.objects.all()for o in p:print(o.name,o.age)except Exception as e:print(e)return HttpResponse('get fail')return HttpResponse('get success!')# 聚合函数
def aggregate(request):p = PersonModel.objects.aggregate(Max('age'))p = PersonModel.objects.aggregate(Min('age'))p = PersonModel.objects.aggregate(Sum('age'))p = PersonModel.objects.aggregate(Count('age'))p = PersonModel.objects.aggregate(Avg('age'))print(p)# 排序p = PersonModel.objects.all().order_by('age') # 升序p = PersonModel.objects.all().order_by('age','id')p = PersonModel.objects.all().order_by('-age') # 降序print(p)return HttpResponse('Success!')
6.多模块关联关系
-
1.关联分类
- ForeignKey:一对多,将字段定义在多的端中
- ManyToManyField:多对多,将字段定义在两端的任意一端中
- OneToOneField:一对一,将字段定义在任意一端中
-
2.外键关联
- ForeignKey:一对多关系
- OneToOneField:一对一关系
from django.db import models# 创建模型A
class ModelA(models.Model):name = models.CharField(max_length=100)# 创建模型B
class ModelB(models.Model):name = models.CharField(max_length=100)model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE) # 建立一对多关系 model_a = models.OneToOneField(ModelA, on_delete=models.CASCADE) # 建立一对一关联
- on_delete参数
# 注意:修改on_delete参数之后需要重新同步数据库
user_type = models.ForeignKey(UserType,on_delete=models.CASCADE)models.CASCADE默认值(Django1.11) # 表示级联删除,即删除UserType时胡关联的User也会被删除
models.PROTECT # 保护模式,阻止级联删除。(若无外键关联可删除)
models.SET_NULL # 置空模式,设为null,nuLL=True参数必须具备
modeLs.SET_DEFAULT # 置默认值设为默认值,default参数必须具备
models.SET() # 删除的时候重新动态指向一个安体访问对应元素,可传两数
modeLs.DO_NOTHING什么也不做。(Django1.11) # 有外键约束
- 外键查询
# 正向查询:直接调用关联的键查询
print(user.user_type.name)
print(user.user_type.id)
# 反向查询
# 内部自动生成 user_set 属性供反向查询
print(user_type.user_set.all()) # 返回QuerySet
print(user_type.user_set.filter(id=1))# reLated_name:关联名称,设置反向查询的名称,原本使用user_set改为users
user_type = models.ForeignKey(UserType,,reLated_name=users)
print(user_type.users.all())
- 3.多对多关联
from django.db import models# 创建模型A
class ModelA(models.Model):name = models.CharField(max_length=100)model_b = models.ForeignKey('ModelB', on_delete=models.CASCADE)# 创建模型B
class ModelB(models.Model):name = models.CharField(max_length=100)model_a = models.ForeignKey(ModelA, on_delete=models.CASCADE)
# 使用
m1 = ModelA.object.get(id=1)
m1.model_b.all()m2 = ModelB.object.get(id=1)
m2.model_a.all()
7.Queryset对象方法
all():返回所有数据
- 可在models.py 文件中的类加上__ str __方法,在该方法中写上初始化的字符串,调用all方法时将自动调用该初始化字符串
# models.py
class Account(models.Model):"""账户表"""username = models.CharField(max_length=64,unique=True)email = models.EmailField(unique=True)password = models.CharField(max_length=255)register_date = models.DateTimeField(auto_now_add=True)signature = models.CharField("签名",max_length=255,null=True)# str方法在调用all方法时自动调用def __str__(self):return self.username# python manage.py shell
modles.Account.objects.all() # 自动返回该类的初始化str方法
exclude():使用方法与filter()相同,结果与filter相反;排除符合条件的数据
models.Account.objects.exclude(register_date__date='2023-6-15')
values():将数据都封装成一个字典
a = models.Account.objects.all()
a.values()
# 通过键直接取该相应的值(字典)
a.values('username')
order_by():多字段排序
# 通过id进行顺序排序
a.values('id','register_date').order_by('id')
# 通过id进行逆序排序
a.values('id','register_date').order_by('id')# 通过register_date进行顺序,若相同则根据id进行排序
a.values('id','register_date').order_by('register_date','id')
reverse():反转顺序,使用前必须先进行排序
a.values('id','register_date').reverse()
a.order_by('id').reverse()
get():精确查询一条数据(必须存在的数据),返回一个对象
a = models.Account.objects.get(id=1)
a = models.Account.objects.get(id__id=1) # 必须只存在一条大于1的数据
# 直接调用
a.username
8.ORM对象操作
1.单表对象操作
o = models.Artilce.objects.all()[0]
o.title
2.外键关联操作
o = models.Artilce.objects.all()[0]
o.account.username
3.外键反向关联操作
- article_set是动态生成的,被外键关联的类,会自动根据关联的类名加上 _set,通过该属性可以反向查找
a = models.Account.objects.get(name='job')
a.article_set.all()
a.article_set.select_related() # 使用效果与all相同
4.多对多操作
a = models.Article.objects.get(id=5)
a.tags.all()
a.tags.select_related() # 使用效果与all相同
5.多对多反向操作
t = models.Tag.objects.get(name='科技')
t.article_set.all()
a.article_set.select_related()
八、Django会话技术
1、cookie工作原理
2.Cookie
- 使用response的方法
import datetimefrom django.shortcuts import render,HttpResponse,redirect,reverse
from app01.models import *# 获取cookie
def index(request):# cookie# 获取该页面的cookie为userid的值,若无则设为0userid = request.COOKIES.get('userid',0)# 获取登录的用户user = userModel.objects.filter(id=userid).first()return render(request,'index.html',{'user':user})# 设置cookie
def login(request):if request.method == 'GET':return render(request, 'login.html')elif request.method == 'POST':# 1、接收前端提交过来的数据uname = request.POST.get('uname') # 参数为html的name属性值passwd = request.POST.get('passwd')# 2、登录验证users = userModel.objects.filter(username=uname,password=passwd)if users.exists():# 获取当前登录的用户对象user = users.first()response = redirect(reverse('index'))# 3、设置cookieresponse.set_cookie('userid',user.id) # 创建cookie# 设置过期时间# response.set_cookie('userid',user.id,max_age=7*24*3600) # max_age:秒# response.set_cookie('userid',user.id,# expires=datetime.datetime(2023,9,5))response.set_cookie('userid', user.id, # timedelta:时间差expires=datetime.datetime.now()+datetime.timedelta(days=10))# 跳转到登录页面return responsereturn HttpResponse("当前账户不存在或账户密码有误,请重新输入或注册!")# 删除cookie
def logout(request):response = redirect(reverse('index'))# 注销、删除cookieresponse.delete_cookie('userid')request.session.delete(session_key)return response
- session
- 使用request的方法
import datetime
from django.shortcuts import render,HttpResponse,redirect,reverse
from app01.models import *# 获取session
def index(request):# sessionuserid = request.session.get('userid',0)# 获取登录的用户user = userModel.objects.filter(id=userid).first()return render(request,'index.html',{'user':user})# 设置session
def login(request):if request.method == 'GET':return render(request, 'login.html')elif request.method == 'POST':# 1、接收前端提交过来的数据uname = request.POST.get('uname') # 参数为html的name属性值passwd = request.POST.get('passwd')# 2、登录验证users = userModel.objects.filter(username=uname,password=passwd)if users.exists():# 获取当前登录的用户对象user = users.first()response = redirect(reverse('index'))# 3.设置sessionrequest.session['userid'] = user.idrequest.session.set_expiry(7*24*3600) # 设置过期时间return responsereturn HttpResponse("当前账户不存在或账户密码有误,请重新输入或注册!")# 删除session
def logout(request):response = redirect(reverse('index'))# 注销、删除sessionsession_key = request.session.session_keyrequest.session.delete(session_key)return response
2、CSRF
- CSRF全拼为Cross Site Request Forgery,跨站请求伪造
- CSRF指攻击者盗用了你的身份,以你的名义发送恶意请求
- 包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账…
- 造成的问题:个人隐私泄露以及财产安全
防止CSRF
- Django下的CSRF预防机制
django第一次响应来自某个客户端的请求时,会在服务器端随机生成一个 token,把这个token 放在 cookie里。然后每次 POST请求都会带上这个 token,这样就能避免被CSRF攻击。 - 在Post请求时,表单中添加 {% csrf_token %}
<form action="" method="post">{# 禁止csrf安全验证 #}{% csrf_token %}...
</form>
九、ORM-admin后台管理
- 创建一个admin,用于登录 ‘localhost:8000/admin’
# 在项目的目录下执行
python manage.py createsuperuser
1.管理app下的表(模型)
- 注册对应的模型,在该app项目的admin.py上写
# app01/ admin.py
from app01 import modelsadmin.site.register(models.Account)
admin.site.register(models.Article)
admin.site.register(models.Tag)
2.自定制admin(管理界面)
# app01 --admin.py
# admin自定制
class AccountAdmin(admin.ModelAdmin):list_display = ('username','email','register_date','signature') # 显示列表search_fields = ('username','email') # 搜索内容list_filter = ('register_date',) # 过滤list_per_page = 5 #分页,每页几条数据list_editable = ['signature',] # 可在列表修改的属性class ArticleAdmin(admin.ModelAdmin):list_display = ('title','pub_date','account')fields = ('title','content',('pub_date','tags'))# 可自定义显示范围exclude = ('account',) # 与fields相反,不显示某字段date_hierarchy = 'pub_date' # 按日期分类显示fieldsets = (('文章内容',{'fields':('title','content'),'classes':('wide','extrapretty'), # 样式}),('发布相关',{'fields':('account','pub_date'),'classes':('collapse',), # 可折叠}))# 只能针对多对多# filter_vertical = ('tags',)filter_horizontal = ('tags',)radio_fields = {'account':admin.HORIZONTAL} # 由下拉框变单选框autocomplete_fields = ['account',] # 外键自动补全,可用于查询数据admin.site.register(models.Account,AccountAdmin) #模型与管理类相关联
admin.site.register(models.Article,ArticleAdmin)
admin.site.register(models.Tag)
- 在admin管理后台,如果添加的字段可为空时,需在models.py 在该字段添加属性 blank=True
- 一般null和blank同时使用(null作用于数据库)
class Account(models.Model):"""账户表"""signature = models.CharField("签名",max_length=255,null=True,blank=True) # blank与admin配合使用
- 将多对多关联显示到列表
- 在 models.py 上自定义方法,在admin.py引入(若不自定义方法无法显示)
# admin.py
class Article(models.Model):def get_tags(self):return ', '.join([i.name for i in self.tags.all()])# admin.py
class ArticleAdmin(admin.ModelAdmin):list_display = ('title','pub_date','account','get_tags')
-
添加HTML样式
- 在 models.py 上写该属性的html样式,在admin.py上显示
- 注意:添加新字段需要手动将数据同步到数据库,否则数据库没有该数据报错
- python manage.py makemigrations app01
- python mange.py migrate
# models.py from django.utils.html import format_htmlclass Tag(models.Model):color_code = models.CharField(max_length=6)def colored_name(self):return format_html('<span style="color:#{};">{}</span>',self.color_code,self.name,)# admin.py class TagAdmin(admin.ModelAdmin):list_display = ['name','colored_name']
-
自定义表单名称
- 在 models.py 各表类中添加类 Meta,添加属性 verbose_name_plural
- 在各表字段自定义名称,直接在字段添加属性 verbose_name
- 在 models.py 各表类中添加类 Meta,添加属性 verbose_name_plural
# models.pyclass Account(models.Model):class Meta:# verbose_name = '账户' # 存在复数形式verbose_name_plural = '账户'class Article(models.Model):tags = models.ManyToManyField("Tag",verbose_name='标签')class Meta:verbose_name_plural = '文章'class Tag(models.Model): class Meta:verbose_name_plural = '标签'
返回上级目录
import sys
sys.path.append('..') # 返回上级目录