一、模型部分
1 关于ForeignKey
1.1 级联
在django2版本以上,外键关联的数据需要设置级联更新
xxx = models.ForeignKey(关联的表,on_delete=model.CASCADE)
# 级联操作需要注意,如果是一对一的关联,那没问题应该级联删除
# 但如果是一对多,删除了一个出版社,就把这个出版社的所有书都删了,显然不合理,因为书和出版社只是逻辑联系,不是真的物理关联
# 所以通常我们会断开一对多的关联表
publish = models.ForeignKey(to='Publish',on_delete=models.DO_NOTHING,db_constraint=False)
# db_constraint=False 表示这个外键只有逻辑关联,没有实质的关联
# 级联也应当修改
on_delete = models.DO_NOTHING # 删除出版社的时候,书表什么都不做
on_delete = models.CASCADE # 级联删除
on_delete = models.SET_NULL # 前提是这个字段可以为空
on_delete = models.SET_DEFAULT # 前提是有默认值
1.2 参数
# to 设置要关联的表
# to_field 设置要关联表的字段
# related_name 反向查询的时候替代原来的‘表名_set’
# db_constraint 是否在数据库中创建外键约束,默认为True
2 关于内部类
内部类class Meta提供模型的元数据,元数据不属于任何字段的东西,是对整张表的描述
具体拥有的参数
-
ordering 排序选项
-
ordering = ('pk',) # 需要注意排序的根据是一个元组,所以如果只有一个根据就要加逗号,排序如果出现重复就会根据第二个元素的排序依据进行排序
-
-
db_table 数据库表名
-
db_table = '数据库表名' # 注意此处修改表名是真实的在数据库中的表名被修改了,所以需要重新进行数据迁移
-
-
verbose_name_plural/verbose_name 单复数名称
- 这是在admin后台管理的时候显示的名称,复数后缀会在中文后加s通常用中文的话就用单数
其他相关字段
class UserInfo(models.Model):
nid = models.AutoField(primary_key=True)
username = models.CharField(max_length=32)
class Meta:
# 设置成虚拟表,通常用于通用表,添加一些每个表必有的字段,让其他表继承
abstract = True
# 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
db_table = "table_name"
# 联合索引
index_together = [
("pub_date", "deadline"),
]
# 联合唯一索引
unique_together = (("driver", "restaurant"),)
ordering = ('name',)
# admin中显示的表名称
verbose_name='哈哈'
# verbose_name加s
verbose_name_plural=verbose_name
二、视图函数部分
1 关于markdown使用
import markdown
# 将markdown语法渲染成html样式
article.body = markdown.markdown(article.body,
extensions=[
# 包含 缩写、表格等常用扩展
'markdown.extensions.extra',
# 语法高亮扩展
'markdown.extensions.codehilite',
])
# 这里需要注意,原本的文本格式的文章现在转化成了html代码,如果要展示到前端,就要对数据进行转义
# 复习:转义的两种方式
1 直接在前端参数后面加safe过滤器
<p>{{ article.body|safe }}</p>
2 在后端给html代码做标记
from django.utils.safestring import mark_safe
res = mark_safe(article.body)
2、queryset对象
2.1 可切片
用python的切片语法去限制查询的数据条数
Entry.objects.all()[:5] # (LIMIT 5)
Entry.objects.all()[5:10] # (OFFSET 5 LIMIT 5)
不支持负的索引,切片返回的是一个新的查询集,是由原来的查询集筛选得到的。
2.2 可迭代
取出的是每一个数据对象
2.3 惰性查询
简单来说就是如果只是把查询结果赋值给了一个变量,而没使用这个变量的话,查询语句是不会执行的,只有真正对数据进行操作了才会回过头来执行查询语句。
2.4 缓存机制?
2.5 exists()与iterator()方法
exists
# 由于简单的if判断也会把整个数据对象集放入cache中,但是我们不需要判断这么多就可以用到exists
if 查询集.exists():
# 相当于只从查询集中拿出一条数据进行判断
...
iterator
# 查询得到的数据集可能会非常大,一次性放入内存就会影响性能,我们可以通过iterator把数据集做成一个迭代器
# 注意做成迭代器的特点,取完数据后数据需要重新查询,无法回头
# 每次在内存中只会存在一个数据
objs = Book.objects.all().iterator()
上面两种方法都是为了防止出现cache,所以他可能会增多了我们对数据库的查询,没有完美的方法只有合适的方法。
2.6 orm额外方法
model.Student.object.update_or_create(aa=aa,defaults={'bb=bb'})
# 拿第一个参数作为查询依据,如果存在则修改,如果不存在则新增
3 extra
由于orm对mysql的封装程度太高,有些情况下我们需要用一些复杂的查询就可以通过extra来对查询注入新的sql语句
extra可以指定一个或多个 参数
,例如 select
, where
or tables
. 这些参数都不是必须的,但是你至少要使用一个!要注意这些额外的方式对不同的数据库引擎可能存在移植性问题.(因为你在显式的书写SQL语句),除非万不得已,尽量避免这样做
4.1参数之select
The select
参数可以让你在 SELECT
从句中添加其他字段信息,它应该是一个字典,存放着属性名到 SQL 从句的映射。
queryResult=models.Article
.objects.extra(select={'is_recent': "create_time > '2017-09-05'"})
结果集中每个 Entry 对象都有一个额外的属性is_recent, 它是一个布尔值,表示 Article对象的create_time 是否晚于2017-09-05.
练习:
# in sqlite:
article_obj=models.Article.objects
.filter(nid=1)
.extra(select={"standard_time":"strftime('%%Y-%%m-%%d',create_time)"})
.values("standard_time","nid","title")
print(article_obj)
# <QuerySet [{'title': 'MongoDb 入门教程', 'standard_time': '2017-09-03', 'nid': 1}]>
4.2参数之where
/ tables
您可以使用where
定义显式SQL WHERE
子句 - 也许执行非显式连接。您可以使用tables
手动将表添加到SQL FROM
子句。
where
和tables
都接受字符串列表。所有where
参数均为“与”任何其他搜索条件。
举例来讲:
queryResult=models.Article
.objects.extra(where=['nid in (1,3) OR title like "py%" ','nid>2'])
extra, 额外查询条件以及相关表,排序
models.UserInfo.objects.filter(id__gt=1)
models.UserInfo.objects.all()
# id name age ut_id
models.UserInfo.objects.extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
# a. 映射
# select
# select_params=None
# select 此处 from 表
# b. 条件
# where=None
# params=None,
# select * from 表 where 此处
# c. 表
# tables
# select * from 表,此处
# c. 排序
# order_by=None
# select * from 表 order by 此处
models.UserInfo.objects.extra(
select={'newid':'select count(1) from app01_usertype where id>%s'},
select_params=[1,],
where = ['age>%s'],
params=[18,],
order_by=['-age'],
tables=['app01_usertype']
)
"""
select
app01_userinfo.id,
(select count(1) from app01_usertype where id>1) as newid
from app01_userinfo,app01_usertype
where
app01_userinfo.age > 18
order by
app01_userinfo.age desc
"""
result = models.UserInfo.objects.filter(id__gt=1).extra(
where=['app01_userinfo.id < %s'],
params=[100,],
tables=['app01_usertype'],
order_by=['-app01_userinfo.id'],
select={'uid':1,'sw':"select count(1) from app01_userinfo"}
)
print(result.query)
# SELECT (1) AS "uid", (select count(1) from app01_userinfo) AS "sw", "app01_userinfo"."id", "app01_userinfo"."name", "app01_userinfo"."age", "app01_userinfo"."ut_id" FROM "app01_userinfo" , "app01_usertype" WHERE ("app01_userinfo"."id" > 1 AND (app01_userinfo.id < 100)) ORDER BY ("app01_userinfo".id) DESC
三、路由部分
1 url和path的区别
django1.x版本用的是url
基本用法:
from django.conf.urls import url,include
# 参数部分是放一个{}内部键值对的key在views函数内要以关键字参数接受
# 别名用于反向解析
urlpatterns = [
url(正则表达式, views视图函数,参数,别名),
]
# 有名分组 year = 4位数的数字
re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive),
# 无名分组
re_path(r'^articles/([0-9]{4})/$', views.year_archive),
# 路由分发
path('app01/', include(urls))
# 反向解析的应用场景,当我们视图中或者模版中多次用到了某个url
# 为了防止这个url后期修改导致所有用到的地方都要改,这里通过一个别名反向解析就行
# 两种反向解析
# 在视图函数中
from django.shortcuts import reverse
url = reverse('tag',args=(1,))
>>> /mytag_test/1
# 得到的是url路径,args是拼接在后面的参数
# 在页面中
{% url "别名" 参数 参数%}
django2.x用的大部分是path,也可以导入url去使用,也有结合两种特性的re_path
# path的第一个是一个写死的路径,不支持正则
# 第二个参数是具体的视图函数
# 也可以反向解析
# 在1.x版本的分组到2中变成了转换器拼接在url后面
path('article-create/<int:id>/', views.article_create, name='article_create')
# 内置了5中转化器
str,匹配除了路径分隔符(/)之外的非空字符串,这是默认的形式
int,匹配正整数,包含0。
slug,匹配字母、数字以及横杠、下划线组成的字符串。
uuid,匹配格式化的uuid,如 075194d3-6885-417e-a8a8-6c931e272f00。
path,匹配任何非空字符串,包含了路径分隔符(/)(不能用?)
四、模版部分
1 xss攻击
当我们服务端提供用户提交js代码的接口,就容易受到xss攻击,去用一些js代码影响我们的服务器。
如何解决xss攻击?
python中用了转义,让被设计者标记安全的语言才能显示到页面,内部原理是,如果用户给了<a href = 'meizitu.com'> 点击</a>
这样的标签上传,如果标记安全,那这个字符串就会直接渲染到页面上变成一个标签。
如果我们没有对其转义,他会原封不动的显示在html页面上
展示的就是一些特殊字符,比如>
表示的就是>,所以展示到页面的就是在后端显示的
五、settings
1 静态资源暴露
网站所用的静态文件我们通常都放在static内,比如js,css文件,如果要让前端可以用这些静态文件渲染页面,就需要开放接口,关于网站的静态文件,django已经给我们开放了令牌(功能类似于反向解析),我们只要配置路径即可
STATICFILES_DIRS = [
os.path.join(BASE_DIR,'static')
]
而用户上传的静态文件,也需要专门有一个文件夹来接收,通常我们用media做文件夹的名字,也可以修改
# settings.py
MEDIA_ROOT = os.path.join(BASE_DIR, 'media') # 用户上传的文件就会保存到该文件夹下
# media是文件夹的名字,可以自定义,一般使用media作为名字
用户只要上传文件,就会自动创建media目录,会自动在media内创建相应的上传目录
例如models中,avatar字段
avatar = models.FileField(upload_to='avatar', default='avatar/default.png')
在上传了头像后,media内会自动创建一个avatar文件夹来存放头像
当然我们配置了路径只能在服务端使用这个路径,如果前端要访问后端的资源,就必须要开放相应的接口
在urls配置
固定写法,复制就能使用
from django.views.static import serve
from bbs import settings
url(r'^media/(?P<path>.*)', serve, {'document_root': settings.MEDIA_ROOT})
2 项目修改成中文
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
六、RBAC:基于角色的权限控制(django内置的auth体系)
# RBAC :是基于角色的访问控制(Role-Based Access Control ),公司内部系统
# django的auth就是内置了一套基于RBAC的权限系统
# django中
# 后台的权限控制(公司内部系统,crm,erp,协同平台)
user表 #用户表
permssion表 # 权限表
group表 # 组别表
user_groups表是user和group的中间表
group_permissions表是group和permssion中间表
user_user_permissions表是user和permission中间表
# 前台(主站),需要用三大认证
# 用户和组别是多对多
用户a可以是销售组也可以是开发组
销售组也可以有多个人
# 组别和权限是多对多
销售组的人有销售产品的权限,也有检查产品的权限
检查产品的权限可能管理组也有
# 用户和权限
用户可以有多重权限
一个权限也可以有多个用户拥有
七、django的缓存
1 缓存6中配置位置
-
开发调试(此模式为开发调试使用,实际上不执行任何操作)
-
内存缓存
CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', # 指定缓存使用的引擎 'LOCATION': 'unique-snowflake', # 写在内存中的变量的唯一值 'TIMEOUT':300, # 缓存超时时间(默认为300秒,None表示永不过期) 'OPTIONS':{ 'MAX_ENTRIES': 300, # 最大缓存记录的数量(默认300) 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3) } } }
-
文件缓存
-
数据库缓存
CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', # 指定缓存使用的引擎 'LOCATION': 'cache_table', # 数据库表 'OPTIONS':{ 'MAX_ENTRIES': 300, # 最大缓存记录的数量(默认300) 'CULL_FREQUENCY': 3, # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3) } } }
-
Memcache缓存(使用python-memcached模块连接memcache)
-
Memcache缓存(使用pylibmc模块连接memcache)
2 缓存应用
Django提供了不同粒度的缓存,可以缓存某个页面,可以只缓存一个页面的某个部分,甚至可以缓存整个网站.
2.1 视图中使用缓存
from django.views.decorators.cache import cache_page
import time
from .models import *
@cache_page(15) #超时时间为15秒
def index(request):
t=time.time() #获取当前时间
bookList=Book.objects.all()
return render(request,"index.html",locals())
# index.html
<body>
<h3>当前时间:-----{{ t }}</h3>
<ul>
{% for book in bookList %}
<li>{{ book.name }}--------->{{ book.price }}$</li>
{% endfor %}
</ul>
</body>
# 我们可以看到的效果是15秒内不管前端刷新了几次 t都不会改变
2.2 全局使用缓存
既然是全站缓存,当然要使用Django中的中间件.
用户的请求通过中间件,经过一系列的认证等操作,如果请求的内容在缓存中存在,则使用FetchFromCacheMiddleware获取内容并返回给用户
当返回给用户之前,判断缓存中是否已经存在,如果不存在,则UpdateCacheMiddleware会将缓存保存至Django的缓存之中,以实现全站缓存
缓存整个站点,是最简单的缓存方法
在 MIDDLEWARE_CLASSES 中加入 “update” 和 “fetch” 中间件
MIDDLEWARE_CLASSES = (
‘django.middleware.cache.UpdateCacheMiddleware’, #第一
'django.middleware.common.CommonMiddleware',
‘django.middleware.cache.FetchFromCacheMiddleware’, #最后
)
“update” 必须配置在第一个
“fetch” 必须配置在最后一个
# 视图和模版都不需要配置
2.3 局部视图缓存
# views
from django.views.decorators.cache import cache_page
import time
from .models import *
def index(request):
t=time.time() #获取当前时间
bookList=Book.objects.all()
return render(request,"index.html",locals())
# index.html
{% load cache %}
{% cache 2 'name' %}
<h3>缓存:-----:{{ t }}</h3>
{% endcache %}