Django 框架
创建项目:
1 | $ django-admin.py startproject test_django |
- test_django: 项目的容器。
- manage.py: 一个实用的命令行工具,以各种方式与该 Django 项目进行交互。
- test_django/init.py: 一个空文件,告诉 Python 该目录是一个 Python 包。
- test_django/asgi.py: 一个 ASGI 兼容的 Web 服务器的入口,以便运行项目。
- test_django/settings.py: 该 Django 项目的设置/配置。
- test_django/urls.py: 该 Django 项目的 URL 声明; 一份由 Django 驱动的网站”目录”。
- test_django/wsgi.py: 一个 WSGI 兼容的 Web 服务器的入口,以便运行项目。
CGI(通用网关接口, Common Gateway Interface):定义客户端与Web服务器的交流方式。正常情况下客户端发来一个请求,根据
HTTP
协议服务器解析请求内容,根据内容操作——例如服务器返回一个HTML
页面,并且根据HTTP
协议构建返回内容的响应格式。涉及到TCP
连接、HTTP
原始请求和相应格式的工作,由 CGI 完成。ASGI(异步网关协议接口):介于网络协议服务和
Python
应用之间的标准接口,用于解决Python
常用的WSGI
不支持当前Web
开发中的一些新协议标准,是WSGI
的扩展。WSGI(Web服务器网关接口,Python Web Server Gateway Interface):
Python
为了解决Web服务器端与客户端之间的通信问题而产生的接口——基于Python
的以CGI
为标准的一些扩展,支持HTTP
协议
视图和 URL 配置
新建文件views.py:
1 | $ tree |
1 | from django.http import HttpResponse |
urls.py中配置url;Django path() 函数:
1 | path(route, view, kwargs=None, name=None) |
- route: 字符串,表示 URL 规则,与之匹配的 URL 会执行对应的第二个参数 view。
- view: 用于执行与正则表达式匹配的 URL 请求。(来自views的函数)
- kwargs: 视图使用的字典类型的参数。
- name: 用来反向获取 URL。
templates
1 | test_django/ |
模板是一个html文本,用于分离文档的表现形式和内容。
修改settings.py,将字典TEMPLATES
的DIRS
键改为 [BASE_DIR, "/templates",]
修改views.py,以向模板提交数据:
1 | from django.shortcuts import render |
模板语法:
变量:
1
2view.py:{"HTML变量名" : "views变量名"}
HTML:{{变量名}}列表:
- html中用 . 索引下标取出对应的元素:views_list.0
- view: return render(request, “runoob.html”, {“views_list”: views_list})
字典:
- .html中,用 .键 取出对应的值:views_dict.name
- view:return render(request, “runoob.html”, {“views_dict”: views_dict})
filter:
- .html中:
{{ 变量名 | 过滤器:“可选参数” }}
,过滤器可以在变量被显示前修改它;一个过滤器的输出又可以作为下一个的输入
- .html中:
if:
1
2
3{% if condition %}
... display
{% endif %}for:
1
2
3
4
5
6
7
8
9
10
11{% for athlete in athlete_list %}
<li>{{ athlete.name }}</li>
{% endfor %}
{% for athlete in athlete_list reversed %}
...
{% endfor %}
{% for i,j in views_dict.items %}
{{ i }}---{{ j }}
{% endfor %}内部通过 变量获取循环序号
- forloop.counter: 顺序获取循环序号,从 1 开始计算
- forloop.counter0: 顺序获取循环序号,从 0 开始计算
- forloop.revcounter: 倒叙获取循环序号,结尾序号为 1
- forloop.revcounter0: 倒叙获取循环序号,结尾序号为 0
- forloop.first(一般配合if标签使用): 第一条数据返回 True,其他数据返回 False
- forloop.last(一般配合if标签使用): 最后一条数据返回 True,其他数据返回 False
{% empty %}
从句:在循环为空的时候执行1
2
3
4
5{% for i in listvar %}
{{ forloop.counter0 }}
{% empty %}
空空如也~
{% endfor %}
ifequal/ifnotequal:
- 比较两个值,相等时,显示在
{% ifequal %}` 和 `{% endifequal %}
之中所有的值
- 比较两个值,相等时,显示在
Django 注释使用
{% include %}
:在模板中包含其它的模板的内容csrf_token:用于form表单中,跨站请求伪造保护
- 用
{% csrf_token %}
标签,在 form 表单提交数据时,才会成功
- 用
自定义标签和过滤器
创建 templatetags 目录——只能是此名字——并新建.py
1
2
3
4
5
6
7
8
9
10test_django/
|-- test_django
| |-- __init__.py
| |-- __init__.pyc
| |-- settings.py
...
|-- manage.py
`-- templatetags
`-- templates
| |-- my_tags.py1
2
3from django import template
register = template.Library() #必须是registersettings.py:
1
2
3
4
5
6
7
8
9
10
11TEMPLATES = [
{
...
'OPTIONS': {
'context_processors': [...],
"libraries":{ # 添加这边三行配置
'my_tags':'templatetags.my_tags' # 添加这边三行配置
} # 添加这边三行配置
},
},
]装饰器 @register.simple_tag 自定义标签,装饰器 @register.filter 自定义过滤器(参数最多只能2个)
1
2
3
4
5
6
def my_tag1(v1, v2, v3):
return v1 * v2 * v3
def my_filter(v1, v2):
return v1 * v2使用自定义标签和过滤器前,要在 html 文件 body 的最上方中导入该 py 文件
{% load my_tags %}
1
2{% my_tag1 11 22 33 %}
{{ 11|my_filter:22 }}定义标签时,用上 mark_safe 方法,令标签语义化,相当于 jQuery 中的 html() 方法
1
2
3
4
5from django.utils.safestring import mark_safe
def my_html(v1, v2):
temp_html = "<input type='text' id='%s' class='%s' />" %(v1, v2)
return mark_safe(temp_html)1
{"zzz" "xxx" } my_html
静态文件配置
创建文件夹statics
settings.py加入:
1
2
3
4STATIC_URL = '/static/' # 别名
STATICFILES_DIRS = [
os.path.join(BASE_DIR, "statics"),
]statics 目录下创建 css 目录,js 目录,images 目录,plugins 目录, 分别放 css文件,js文件,图片,插件
bootstrap 框架放入plugins,HTML 文件的 head 标签中引入 bootstrap
1
<link rel="stylesheet" href="/static/plugins/bootstrap-3.3.7-dist/css/bootstrap.css">
模板中加入
{% load static %}
1
2{% load static %}
{{name}}<img src="{% static "images/runoob-logo.png" %}" alt="runoob-logo">
模板继承
以继承的方式实现复用
父模板(不同的预留区域名字不能相同)
1
2
3{% block 名称 %}
预留给子模板的区域,可以设置设置默认内容
{% endblock 名称 %}子模板(假定base.html为父模板路径)
1
2
3
4{% extends "base.html"%}
{ % block 名称 % }
内容
{% endblock 名称 %}
Model
为数据库提供调用API
Django 使用自带的 ORM——Object Relational Mapping,对象关系映射——代码到python对象到ORM到sql语句到数据库,只能操作到数据表的语法,database要自己率先创建
settings.py 中修改 DATABASES 配置项
1
2
3
4
5
6
7
8
9
10
11DATABASES = {
'default':
{
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'runoob', # 数据库名称
'HOST': '127.0.0.1', # 数据库地址,本机 ip 地址 127.0.0.1
'PORT': 3306, # 端口
'USER': 'root', # 数据库用户名
'PASSWORD': '123456', # 数据库密码
}
}告诉 Django 使用 pymysql 模块连接 mysql 数据库
1
2
3# 在与 settings.py 同级目录下的 __init__.py 中引入模块和进行配置
import pymysql
pymysql.install_as_MySQLdb()
定义模型
Django 规定要使用模型,必须创建一个 app
1
2
3
4
5
6
7
8
9
10
11
12
13$ django-admin.py startapp TestModel
$ tree
.
test_django
|-- test_django
|-- manage.py
...
|-- TestModel
| |-- __init__.py
| |-- admin.py
| |-- models.py
| |-- tests.py
| `-- views.py修改 models.py
1
2
3
4
5# models.py
from django.db import models
class Test(models.Model): # 类名代表了数据库表名,类的字段代表数据表中的字段(name),数据类型为CharField(相当于varchar)、DateField(相当于datetime),max_length 参数限定长度
name = models.CharField(max_length=20)Django 会自动添加一个 id 作为主键。
settings.py 的 INSTALLED_APPS 添加
TestModel
运行
1
2
3$ python3 manage.py migrate # 创建表结构
$ python3 manage.py makemigrations TestModel # 让 Django 知道我们在我们的模型有一些变更
$ python3 manage.py migrate TestModel # 创建表结构
数据库操作
在 app 目录下新建 testdb.py
在 urls.py 中增加 urlpatterns 元素
1
2
3
4
5from TestModel import testdb
urlpatterns = [
path('runoob/', views.runoob),
path('testdb/', testdb.testdb),
]添加数据:先创建对象,再 save
1
2
3
4
5
6from TestModel.models import Test
# 数据库操作
def testdb(request):
test1 = Test(name='runoob')
test1.save()
return HttpResponse("<p>数据添加成功!</p>")获取数据:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21def testdb(request):
# 初始化
response = ""
response1 = ""
# 通过objects模型管理器的all()获得所有数据行,即SELECT * FROM
list = Test.objects.all()
# filter相当于SQL中的WHERE,可设置条件过滤结果
response2 = Test.objects.filter(id=1)
# 获取单个对象
response3 = Test.objects.get(id=1)
# 限制返回的数据 相当于 SQL 中的 OFFSET 0 LIMIT 2;
Test.objects.order_by('name')[0:2]
#数据排序
Test.objects.order_by("id")
# 上面的方法可以连锁使用
Test.objects.filter(name="runoob").order_by("id")
# 输出所有数据
for var in list:
response1 += var.name + " "
response = response1
return HttpResponse("<p>" + response + "</p>")更新数据:修改数据用 save() 或 update():
1
2
3
4
5
6
7
8
9
10
11
12
13def testdb(request):
# 修改其中一个id=1的name字段,再save,相当于SQL中的UPDATE
test1 = Test.objects.get(id=1)
test1.name = 'Google'
test1.save()
# 另外一种方式
#Test.objects.filter(id=1).update(name='Google')
# 修改所有的列
# Test.objects.all().update(name='Google')
return HttpResponse("<p>修改成功</p>")删除数据:调用该对象的delete()
1
2
3
4
5
6
7
8
9
10
11
12def testdb(request):
# 删除id=1的数据
test1 = Test.objects.get(id=1)
test1.delete()
# 另外一种方式
# Test.objects.filter(id=1).delete()
# 删除所有数据
# Test.objects.all().delete()
return HttpResponse("<p>删除成功</p>")
表单
Get:
1
2
3
4
5
6
7
8
9
10
11
12# 表单
def search_form(request):
return render(request, 'search_form.html')
# 接收请求数据
def search(request):
request.encoding='utf-8'
if 'q' in request.GET and request.GET['q']:
message = '你搜索的内容为: ' + request.GET['q']
else:
message = '你提交了空表单'
return HttpResponse(message)Post:POST 方法提交的表格,必须有
{% csrf_token %}
1
2
3
4
5
6
7from django.views.decorators import csrf
# 接收POST请求数据
def search_post(request):
ctx ={}
if request.POST:
ctx['rlt'] = request.POST['q']
return render(request, "post.html", ctx)每个视图函数的第一个参数是 HttpRequest 对象(request 对象)
- GET和POST属性是django.http.QueryDict类的实例
- 类似字典的自定义类,处理单键对应多值的情况
- **get()**:返回字符串,取出该键的最后一个值
视图views
- views.py
- request 对象
- request.path:获取 URL 中的路径部分
- request.body
- request.method:获取当前请求的方式(post/get)
- httpresponse 对象
- HttpResponse():返回文本,参数为字符串,字符串中写文本内容
- render():返回文本,第一个参数为 request,第二个参数为字符串(页面名称),第三个参数为字典(可选参数,向页面传递的参数)——
render(request,"runoob.html",{"name":name})
——一般用于页面跳转 - redirect():重定向,跳转新页面;一般用于 form 表单提交后,跳转到新页面——
redirect("/index/")
路由
urls.py
根据用户请求的 URL 链接来判断对应的处理程序,返回处理结果——URL 与 Django 的视图建立映射关系
普通路径:path();正则路径:re_path()
路由分发:
Django 项目里多个app目录共用一个 urls 容易造成混淆——使用路由分发(include),让每个app目录都单独拥有自己的 urls
每个 app 目录里都创建一个 urls.py 文件
项目名称目录下的 urls 文件里,统一将路径分发给各个 app 目录
1
2
3
4
5urlpatterns = [
path('admin/', admin.site.urls),
path("app01/", include("app01.urls")),
path("app02/", include("app02.urls")),
]
反向解析
命名空间
Admin管理
自动管理工具已在 INSTALLED_APPS 、urls.py 配置好
通过命令
python manage.py createsuperuser
来创建超级用户admin 界面管理某个数据模型
admin.py:Test为Model类
1
2
3# Register your models here.
admin.site.register(Test)
# admin.site.register([Test, Contact, Tag])修改某一个界面:在表Contact的添加里限定添加的字段、搜索
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17# Register your models here.
class ContactAdmin(admin.ModelAdmin):
list_display = ('name','age', 'email') # list
search_fields = ('name',) # 搜索框
inlines = [TagInline] # Inline 让表 Tag 附加在 Contact 的编辑页面上显示
fieldsets = (
['Main',{
'fields':('name','email'),
}],
['Advance',{
'classes': ('collapse',), # classes 说明它所在的部分的 CSS 格式
'fields': ('age',),
}] # 将输入栏分块
)
admin.site.register(Contact, ContactAdmin)
表的实例
单个表
models.py
1
2
3
4
5
6class Book(models.Model):
id = models.AutoField(primary_key=True) # id 会自动创建,可以手动写入
title = models.CharField(max_length=32) # 书籍名称
price = models.DecimalField(max_digits=5, decimal_places=2) # 书籍价格
publish = models.CharField(max_length=32) # 出版社名称
pub_date = models.DateField() # 出版时间views.py:
books = models.Book.objects.filter(pk=5)
,pk为primary keyexclude() :用于查询不符合条件的数据
get() :符合条件的对象只能为一个,如果符合筛选条件的对象超过了一个或者没有都会报错
order_by() :对查询结果进行排序
1
2books = models.Book.objects.order_by("price") # 查询所有,按照价格升序排列
books = models.Book.objects.order_by("-price") # 查询所有,按照价格降序排列count() :查询数据的数量
first() :返回第一条数据
**last()**:返回最后一条数据
exists() :判断的数据类型只能为 QuerySet 类型数据,返回 true 和 false
values() :用于查询部分字段的数据,返回一个可迭代的字典序列,字典里的键是字段,值是数据
1
2
3
4
5def add_book(request):
# 查询所有的id字段和price字段的数据
books = models.Book.objects.values("pk","price")
print(books[0]["price"],type(books)) # 得到的是第一条记录的price字段的数据
return HttpResponse("<p>查找成功!</p>")**values_list()**:返回元组,只有数据
**distinct()**:对数据去重
filter() :基于双下划线的模糊查询(exclude 同理)。filter 中运算符号只能使用=
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25def add_book(request):
# 查询价格为200或者300的数据
books = models.Book.objects.filter(price__in=[200,300])
# 查询价格大于200的数据
books = models.Book.objects.filter(price__gt=200)
# 查询价格大于等于200的数据
books = models.Book.objects.filter(price__gte=200)
# 查询价格小于300的数据
books = models.Book.objects.filter(price__lt=300)
# 查询价格小于等于300的数据
books = models.Book.objects.filter(price__lte=300)
# 在。。。之间,左闭右闭
books = models.Book.objects.filter(price__range=[200,300])
# 包含
books = models.Book.objects.filter(title__contains="菜")
# 不区分大小写的包含
books = models.Book.objects.filter(title__icontains="python")
# 以指定字符开头
books = models.Book.objects.filter(title__startswith="菜")
books = models.Book.objects.filter(title__endswith="教程")
# DateField 数据类型的年份、月份、天
books = models.Book.objects.filter(pub_date__year=2008)
books = models.Book.objects.filter(pub_date__month=10)
books = models.Book.objects.filter(pub_date__day=1)
return HttpResponse("<p>查找成功!</p>")QuerySet 类型数据.update(字段名=更改的数据),返回值:整数,受影响的行数 / 模型类的对象.属性 = 更改的属性值,模型类的对象.save()
多个表
models.py:模型类存在外键,创建数据时,要先创建外键关联的模型类的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=5, decimal_places=2)
pub_date = models.DateField()
publish = models.ForeignKey("Publish", on_delete=models.CASCADE) # 外键在一对多的多中设置
authors = models.ManyToManyField("Author")
class Publish(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=64)
email = models.EmailField() # EmailField 数据类型是邮箱格式, 继承 CharField
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.SmallIntegerField()
au_detail = models.OneToOneField("AuthorDetail", on_delete=models.CASCADE) # OneToOneField = ForeignKey(...,unique=True)设置一对一
class AuthorDetail(models.Model):
gender_choices = (
(0, "女"),
(1, "男"),
(2, "保密"),
)
gender = models.SmallIntegerField(choices=gender_choices)
tel = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
birthday = models.DateField()添加数据
一对多
1
2
3
4
5
6
7
8
9def add_book(request):
# 获取出版社对象
pub_obj = models.Publish.objects.filter(pk=1).first()
# 获取出版社对象的id
pk = pub_obj.pk
# 给书籍的关联出版社字段 publish_id 传出版社对象的id
book = models.Book.objects.create(title="冲灵剑法", price=100, pub_date="2004-04-04", publish_id=pk)
print(book, type(book))
return HttpResponse(book)多对多
1
2
3
4
5
6
7
8
9def add_book(request):
# 获取作者对象
chong = models.Author.objects.filter(name="令狐冲").first()
# 获取作者对象的id
pk = chong.pk
# 获取书籍对象
book = models.Book.objects.filter(title="冲灵剑法").first()
# 给书籍对象的 authors 属性用 add 方法传作者对象的id
book.authors.add(pk)
查询
基于对象的跨表查询
正向:属性名称 反向:小写类名_set
1
2
3
4# 查询主键为 1 的书籍的出版社所在的城市(正向)
res = models.Book.objects.filter(pk=10).first().publish.city
# 查询明教出版社出版的书籍名(反向)
pub = models.Publish.objects.filter(name="明教出版社").first().book_set.all()
基于双下划线的跨表查询
正向:属性名称__ 跨表的属性名称 反向:小写类名__跨表的属性名称
1
2
3
4
5
6# 查询菜鸟出版社出版过的所有书籍的名字与价格
res = models.Book.objects.filter(publish__name="菜鸟出版社").values_list("title", "price")
res = models.Publish.objects.filter(name="菜鸟出版社").values_list("book__title","book__price")
# 查询任我行出过的所有书籍的名字
res = models.Book.objects.filter(authors__name="任我行").values_list("title")
res = models.Author.objects.filter(name="任我行").values_list("book__title")
用户认证(Auth)组件
一般用在用户的登录注册上,用于判断当前的用户是否合法,并跳转到登陆成功或失败页面
模块:
1
2
3
4
5# 认证模块
from django.contrib import auth
# 对应数据库
from django.contrib.auth.models import User用户创建:
1
2
3
4from django.contrib.auth.models import User
User.objects.create(username='runboo',password='123') # 创建一个普通用户,密码是明文的
User.objects.create_user(username='runbooo',password='123') # 创建一个普通用户,密码是密文的
User.objects.create_superuser(username='runboooo',password='123',email='runboo@163.com') # 创建一个超级用户,密码是密文的,要多传一个邮箱 email 参数views.py.login()
验证用户的用户名和密码使用 authenticate()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19def login(request):
if request.method == "GET":
return render(request, "login.html")
username = request.POST.get("username")
password = request.POST.get("pwd")
valid_num = request.POST.get("valid_num")
keep_str = request.session.get("keep_str")
if keep_str.upper() == valid_num.upper():
user_obj = auth.authenticate(username=username, password=password)
print(user_obj.username)
if not user_obj:
return redirect("/login/")
else:
auth.login(request, user_obj)
path = request.GET.get("next") or "/index/"
print(path)
return redirect(path)
else:
return redirect("/login/")验证成功返回用户对象,反之,返回 None
views.py.logout()
注销用户,清空 session 信息,将 request.user 赋值为匿名用户
1
2
3
4def logout(request):
ppp = auth.logout(request)
print(ppp) # None
return redirect("/login/")
给需要登录成功后才能访问的页面统一加装饰器
1
2
3
4from django.contrib.auth.decorators import login_required
def index(request):
return HttpResponse("index页面。。。")返回登录前的页面
1
2
3# 如果直接输入 login、get() 就取不到值,path 可以自定义设置返回的页面
path = request.GET.get("next") or "/index/"
return redirect(path)