django 知识点扩展

一、模型部分

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子句。

wheretables都接受字符串列表。所有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页面上

展示的就是一些特殊字符,比如&gt表示的就是>,所以展示到页面的就是在后端显示的

五、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中配置位置

  1. 开发调试(此模式为开发调试使用,实际上不执行任何操作)

  2. 内存缓存

    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)
      }  
     }
    }
    
  3. 文件缓存

  4. 数据库缓存

    CACHES = {
     'default': {
      'BACKEND': 'django.core.cache.backends.db.DatabaseCache',  # 指定缓存使用的引擎
      'LOCATION': 'cache_table',          # 数据库表    
      'OPTIONS':{
       'MAX_ENTRIES': 300,           # 最大缓存记录的数量(默认300)
       'CULL_FREQUENCY': 3,          # 缓存到达最大个数之后,剔除缓存个数的比例,即:1/CULL_FREQUENCY(默认3)
      }  
     }   
    }
    
  5. Memcache缓存(使用python-memcached模块连接memcache)

  6. 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 %}
原文地址:https://www.cnblogs.com/hz2lxt/p/13530502.html