DjangoWeb应用开发实战笔记


Django模板标签笔记

第六章笔记

{% url %} 引用路由的配置地址,生成相应的路由地址
一般是{% url '命名空间:views,py中的函数'%}
改变了命名空间的话,需要重新跑一便数据库
makemirations
并不是数据库的原因
我知道了,写反了

是{% url 'views,py中的函数:命名空间'%}

{% with %}将上下文重新命名
{% block xxx %}重写父类的模板标签

# total=number无需用空格分开
{% with total=number %}
{{ total }}
{% endwith %}
### for标签
特殊的变量来获取for标签的循环信息,总之就是获取索引信息

{% if forloop.counter==1 %} # 获取当前循环的索引,从1开始计算;用法通常用if来判断
forloop.counter() # 获取当前的索引,从0开始计算
.revcounter 索引从最大处开始递减,直到索引为1的位置
.revcounter() 索引从最大处开始递减,直到索引为0的位置
.first 当遍历元素第一项为真时候
.last 当遍历元素最后一项为真时候
.parentloop嵌套的for循环中,获取上一层的For循环

### 自定义标签
在项目根目录下新建个文件夹,然后把该文件夹name加入到settings中,在该文件夹下兴建个templates文件夹和__init__.py文件,接着在templates文件夹下兴建个.py文件
该文件就是要导入的标签
可用{% load xx %}
在该文件中,首先导入template模块,从django中,
然后创建一个模板对象
register = template.Library()
然后定义模板节点类
在该类中需要实现标签的功能
# 然后在声明并把该模板节点类注册
注册用装饰器
@register.tag(name='') # name可以自定义
在下方编写函数,函数的作用是把用改标签定义的字符串传进来,然后在返回修改后的字符,这里实现的功能是反转字符串

@register.tag(name='reversal1') # 这里定义标签名称,下方就是标签函数的内容

def do_reversal(parse, token):

    '''
    函数参数parse是解析器对象,当Django运行时,他将所有标签和过滤器进行加载并生成到parse对象
    在解析模板文件里面的标签时,Django就会从parse对象查找对应的标签信息

    函数参数token时模板文件使用标签时所传递的数据对象,主要包括标签名和数据内容
    do_reversal对参数token使用split_contents()方法(Django内置方法)进行取值处理,从中获取数据value,并将value传递给
    自定义模板节点类ReversalNode
    '''
    try:
        # tag_name是代表标签名,即reversal
        # value是由标签传递的数据
        tag_name, value = token.split_contents() # 在index.html中传进来的字符串就是这里的token;
        # 然后再把它传给value,再把value传给上面的函数类并返回
    except:
        raise template.TemplateSyntaxError('syntax')
    # 调用自定义的模板节点类
    return ReversalNode(value) #ReversalNode类是将value执行字符串反转处理,并生成模板节点对象,用于模板引擎解析HTML语言

定义模版节点类


class ReversalNode(template.Node):
    def __init__(self, value):
        self.value = str(value)

    # 数据反转处理
    def render(self, context):  # 数据处理渲染的函数名不可随便写
        return self.value[::-1]

这里采用的django内置的玩意:
split_contents()方法取值处理 # 方法按空格分隔字符串,但不会分隔引号包裹的部分。

自定义过滤器

过滤器可同时使用多个,用|分隔开
过滤器还可传入参数,过滤器和参数之间用冒号分隔,且两者之间不能留有空格。之后的参数和冒号之间也不能有空格

开发流程

在项目根目录下新建个文件夹,然后把该文件夹name加入到settings中,在该文件夹下兴建个templates文件夹和__init__.py文件,接着在templates文件夹下兴建个.py文件
该文件就是要导入的过滤器
可用{% load xx %}
在该文件中,首先导入template模块,从django中,
然后创建一个模板对象
register = template.Library()
然后申明并定义过滤器
@register.filter(name='xxx') 过滤器的名字,不懈的话默认为下方的函数名
定义过滤器函数

@register.filter(name='replace') # 对函数do_replace()由装饰器@register.filterl处理,并对其进行注册操作
def do_replace(value, agrs):# value代表当前过滤器的模板上下文
    oldValue = agrs.split(':')[0] # args代表过滤器器的参数,函数将过滤器agrs以冒号分隔,用于参数value进行字符串替换操作
    newValue = agrs.split(':')[1]
    return value.replace(oldValue, newValue) # 将处理结果返回

字符串replace用法

srt = 'je kk'
a = srt.replace('je','ss') # replace返回一个对象
print(a) # ss kk

前端页面

替换前:{{ value }}

替换后: {{ value | replace1:'Python:Django' }}

一般模板变量没有匹配到的话并不会报错

就是他发挥作用与否,你很难知道,所以一般写模板变量之前必须确定该变量是否有效,即是否在views.py中定义了,定义了的话是否从数据表中提取的,提取出来的字段是什么类型的,这些都应该清楚

常见过滤器


Jinja2模板引擎的用法

一般是和Django两个模板共存的,在项目所在的settings.py文件夹兴建个jinja2.py文件,然后编写如下函数

from django.contrib.staticfiles.storage import staticfiles_storage
from django.urls import reverse
from jinja2 import Environment

# 将jinja2模版设置到项目环境
def environment(**options):
    env = Environment(**options) #jinja2的类Environment实例化
    env.globals.update({ # 实例化的对象env用于对接Django的运行环境
        'static': staticfiles_storage.url,
        'url': reverse,
    })
    return env

接着就是将该定义的enviroment()函数写道配置文件settings.py中。
在配置属性TEMPLATES中新增Jinja2模板引擎

  {
        'BACKEND': 'django.template.backends.jinja2.Jinja2', # BACKEND设置Jinja2模板引擎
        'DIRS': [
            os.path.join(BASE_DIR, 'templates'), # DIRS指向项目的模板文件夹templates
        ],
        'APP_DIRS': True,
        'OPTIONS': {
            'environment': 'MyDjango.jinja2.environment' # MyDjango为settings.py所在的文件夹
        },
    },

jinja2区别于django的语法

   {{ value['name'] }}

jinja2的模板语法

在继承模板标签上和django一样,在他的static函数,url函数和过滤器使用情况来看,他们更象函数,都加了(),static(' '),url(''),replace(' '),过滤器和参数之间并没有用分号连接。
for函数的模板变量也不一样

loop.index 循环的当前迭代(索引从1开始)
loop.index() 循环的当前迭代(索引从0开始)
loop.revindex 循环结束时的迭代次数(索引从1开始)
loop.revindex() 循环结束时的迭代次数(索引从0开始)
loop.first 如果是第一次迭代,就为True
loop.last 如果是最后依次迭代,就为True
xx.length 序列中的项目数,即总循环数
xx.cycle 辅助函数,用于在序列列表之间循环
xx,depth 当前递归循环的深度,从1级开始
xx.depth() 当前递归循环的深度,从0级开始
xx.previtem 上一次迭代中的对象
xx.nextitem 下一次迭代中的对象
xx.changed(value) 若上次迭代的值,与当前迭代的值不同则返回True

常用过滤器的名称也不一样

abs {{value|abs}} 设置数值的绝对值
default {{value|default('new')}} 设置默认值
escape {{value|escape}} 转义字符,转成HTML语法
first {{value|first}}  获取上下文的第一个元素
last {{value|first}}  获取上下文的最后一个元素
length {{value|length}}  获取上下文的长度
join   {{value|join('-')}} 功能与python的join语法一致
safe   将上下文转义处理
int 将上下文转换为int类型
float 将上下文转换为float类型
lower 将字符串转化为小写
upper 将字符串转化为大写
replace {{value|replace('a','b')}}字符串的替换
truncate {{value|truncate(9,true)}} 字符串的阶段
striptags 删除字符串中所有的HTML标签
trim 截取字符串前面和后面的空白字符
string 将上下文转换成字符串
wordcount 计算长字符串的单词个数

自定义过滤器

该自定义标签并不需要像django导入模板
jinja2的过滤器并不是写在新创建的文件里,而是写在刚刚定义jinja2的
jinja2.py文件中
具体
定义个函数,该函数名作为过滤器,该函数的逻辑就是过滤器的处理手段,

# 关键字参数的jinja2,默认值,如果有参数,则是其他参数
def myReplace(value, old='Jinja2', new='Django'):
    return str(value).replace(old, new)

然后env.filters把过滤器注册到Jinja2引擎

env.filters['myReplace'] = myReplace # env.filters把过滤器注册到Jinja2引擎
    return env

第七章笔记

ORM框架为数据库提供了统一的框架的API
在models.py中实际上是定义不同类型的字段
djnago\db\models\fields的init和files文件里中有各种模型字段
下方有28个数据字段

__all__ = [
    'AutoField', 'BLANK_CHOICE_DASH', 'BigAutoField', 'BigIntegerField',
    'BinaryField', 'BooleanField', 'CharField', 'CommaSeparatedIntegerField',
    'DateField', 'DateTimeField', 'DecimalField', 'DurationField',
    'EmailField', 'Empty', 'Field', 'FieldDoesNotExist', 'FilePathField',
    'FloatField', 'GenericIPAddressField', 'IPAddressField', 'IntegerField',
    'NOT_PROVIDED', 'NullBooleanField', 'PositiveIntegerField',
    'PositiveSmallIntegerField', 'SlugField', 'SmallIntegerField', 'TextField',
    'TimeField', 'URLField', 'UUIDField',
]

1.自增长类型,int,长度11为
3.自增长类型,bigint,长度20位
6.字符类型
待补充...

模型字段的参数

 possibles = {
            "verbose_name": None,
            "primary_key": False,
            "max_length": None,
            "unique": False,
            "blank": False,
            "null": False,
            "db_index": False,
            "default": NOT_PROVIDED,
            "editable": True,
            "serialize": True,
            "unique_for_date": None,
            "unique_for_month": None,
            "unique_for_year": None,
            "choices": [],
            "help_text": '',
            "db_column": None,
            "db_tablespace": None,
            "auto_created": False,
            "validators": [],
            "error_messages": None,
        }

特殊参数:
日期类DateField和TimeField的特殊参数auto_now_add和auto_now,字段FileFiled和ImageField的特殊参数upload_to

str

可用于外键查询,比如模型A设有外键字段F,外键字段F关联模型B,当查询模型A时,外键字段F会将模型B的函数__str__返回值作为字段内容
__str__只返回字符串类型的字段,如果字段是振兴或者日期型的,就需要通过str()函数将其转换为字符类型

Meta

Meta有19个属性

待补充

默认情况为返回值为函数名+外键

开发自定义ORM框架流程

Model类继承并重写元类进而实现
具体时编写模型基本类(field),模型字段(字符,整数等),元类,,然后再编写Model类继承元类
最后在编写自己的数据表,达到ORM框架,可以执行相应SQL语句的目的。
代码待分析

待补充

数据迁移

执行makemigrations后,会在migrations生成个0001_initial.py文件,这是models.py定义生成的数据表的脚本代码。
脚本代码被migrate指令执行。根据脚本代码创建相应内容的数据表
Django内置的功能数据表分别是:
会话Session、用户认证管理和Admin后台管理系统

通过新增的模型创建数据表,删除对应模型字段等

都可用makemigrations和migrate指令;
Django会将该文件的执行记录保存在数据表django_migrations中
migrate还可以指定应用下的模型字段更新删除,找到那个makemigrations生成的数据表脚本文件,然后执行migrate时指定 应用名 脚本文件 即可

还提供了sqlmigrate指令

用来执行sql语句
和migrate差不多
sqlmigrate 应用名 脚本文件名;
这样可以在命令行中看到生成的sql语句;但它并不会执行,也就是不会生成相应的数据表

数据导入导出

当用数据可视化工具时,导入某个表的数据时,如果当前表设有外键字段,就必须将外键字段关联的数据表的数据导入,在执行当前数据表的数据导入操作
django还为我们提供数据导入导出的指令操作
loaddata;dumpdata来实现数据的导入导出操作(导入导出均为json格式)
一般导入导出最好为整个应用或整个项目
导出会经常碰到导出为空;有可能自己没写数据
导入loaddata碰到编码报错,解码问题

数据表关系

数据表存在的关系
一对一 OneToOneField
两个表的字段id不会重复,并且同一表中不会有重复ID(这句话咋理解);一张数据表设有很多字段,将常用的字段抽取出来组成一个新的数据表。
一对多 ForeignKey (最常用)
第一张表的某一行数据可以与第二张表的一道多行数据进行关联,但是第二张表的每一行数据只能与第一张表的某一行进行关联。
多对多 ManyToManyField
设有特殊的参数如下(3种数据库关系):

to 必选参数,关联的模型名称
ondelete:必选参数,设置数据的删除模式,删除模式包括:CASCADE、PROTECT、SET_NULL、SET_DEFAULT、SET和DO_NOTHING
limit_choices_to:设置外键的下拉框选项,用于模型表单和后台系统
related_name:用于模型之间的关联查询,如反向查询
related_query_name:设置模型的查询名称,用于filter或get查询,若设置参数related_name,则以该参数为默认值,若没有设置,则以模型名称的小写为默认值。
to_field:设置外键与其他模型字段的关联性,默认关联主键,若要关联其他字段,则该字段必须具有唯一性
db_constraint:在数据库中是否创建外键约束,默认为True
swappable:设置关联模型的替换功能,默认值为True,比如模型A关联模型B,想让模型C继承并替换模型B,使得模型A与模型C之间关联
symmetrical:仅限于ManytoManyField,设置多对多字段之间的对称模式
through:仅限于ManytoManyField,设置自定义模型C的字段,确认模型C的哪些模型字段用于管理模型A和B的多对多关系
db_table:仅限于ManytoManyField,设置和存储多对多关系的数据表设置表名称

数据表操作

json类型的数据格式外面时大括号[]
可在shell中实现
python manage.py shell
然后就是导入models的数据了

 python manage.py shell
 from index.models import * # 导入整个模块,你要知晓数据表的名字

报错;

IntegrityError: FOREIGN KEY constraint failed

外键字段的名称要以数据表为主,有时候他自动给外键名后加_id后缀
,有可能外键字段对应的那个表的外键字段名为id

具体插入数据过程

v = Vocation() # 模型数据表实例化
 v.job = '教师'
v.title = '教书育人'
v.payment = 100 # 字段属性不要错,整形与字符串
v.name_id = 3 # 外键
v.save() # 保存
v,id # 查看新增的数据主键(一般就是第几个)

还有其他三种方式可进行数据插入

1.使用create方法实现数据插入
v = Vocation.objects.create(job='jj',title='hh',payment='0',name_id=4)
v.id
2.同样使用create方法,但数据以字典格式表示
d = dict(job='zz',title='xx',payment=00,name_id=4) # IntegerField字段00允许,01不可
v = Vocation.objects.create(**d)
v.id
3.在实例化时直接设置属性值
v = Vocation(job='aa',title='ss',payment=1,name_id=4)
 v.save()
 v.id

为保证插入的数据不重复,我们需要对数据进行去重操作,如果插入的数据不存在,则插入
默认情况插入是不考虑是否有重复数据的
正常是在插入数据之前查询,然后再插入
django提供了get_or_create方法
get_or_create方法根据模型字段的值与数据表中的数据进行判断
处主键除外,只要有一个数据不同就插入,插入返回True,否则就不插入
,返回False.

 d = dict(job='zz',title='xx',payment=00,name_id=4)
 v = Vocation.objects.get_or_create(**d)
v[0].id # 这里返回的是个元组,一般取第一个参数,一般也只有第一个参数

判断当前数据在数据表里是否存在(update_or_create)

若存在,则更新,否则,新增
修改的数据加在defaults参数上

d = dict(job='zz',title='xx',payment=00,name_id=4)
v = Vocation.objects.update_or_create(**d,defaults={'title':'java'})
v[0].title # Out[35]: 'java'

对模型执行数据批量插入操作(bulk_create)

将数据对象以列表或元组的形式传入bulk_create方法即可。

 v1 = Vocation(job='aa1',title='ss1',payment=1,name_id=4)
 v2 = Vocation(job='aa2',title='ss2',payment=1,name_id=4)
 obj_list = [v1,v2]
v = Vocation.objects.bulk_create(obj_list)
v # [, ]

数据修改

一般先查询
v = name.objects.get(id=xxx)
v.xxx= xxx
v.save() # 即可
还可用update更新

批量更新一条或多条数据,查询方法使用filter
# filter以列表格式返回,查询结果可能是一条或多条数据
Vocation.objects.filter(job='aa').update(job='u_aa') # Out[43]: 2
## 更新数据以字典格式表示
 d = dict(job='uu_aa')
Vocation.objects.filter(job='u_aa').update(**d) # 2
### 不使用查询方法,默认对全表的数据进行更新
Vocation.objects.update(payment=6666) # Out[47]: 12
### 使用内置F方法实现数据对的自增或自减
### F方法还可以在annotate或filter方法里使用
from django.db.models import F
 v = Vocation.objects.filter(job='uu_aa')
# 将payment字段整体加1
v.update(payment=F('payment')+1) # Out[50]: 2
###在django2.2以上新增了数据批量更新方法bulk_update,使用方法与bulk_creat类似

到这里我们已经学了,数据的新增,修改,

接着是删除

删除表中全部数据
 Vocation.objects.all().delete()
删除一条id为1的数据
Vocation.objects.get(id=1).delete() # (1, {'index.Vocation': 1})
删除多条数据
 Vocation.objects.filter(job='uu_aa').delete() # (2, {'index.Vocation': 2})

删除数据设有外键字段,同样会删除该字段
PersonInfo.objects.get(id=3).delete() # Vocation删除了2条数据,PersonInfo删除了1条数据
# Out[54]: (3, {'index.Vocation': 2, 'index.PersonInfo': 1})

一对多中的那个一都是被动的被另外一个数据表以ForeignKey关联

外键字段的on_delete字段用于设置数据删除模式

CASCADE 级联删除。Django 模拟 SQL 约束 ON DELETE CASCADE 的行为,并删除包含 ForeignKey 的对象。
PROTECT 通过提高ProtectedError的子类来 防止删除引用的对象
SET_NULL设置为ForeignKey空;这只有在null是 时才有可能 True
SET_DEFAULT[源代码]
将 设置ForeignKey为其默认值;ForeignKey必须设置默认值 。
SET() [源代码]
将 设置为ForeignKey传递给 的值 SET(),或者如果传入可调用对象,则为调用它的结果。在大多数情况下,需要传递一个可调用对象以避免在导入 models.py 时执行查询:
DO_NOTHING[源代码]
不采取行动。如果您的数据库后端强制执行参照完整性,IntegrityError除非您手动向数据库字段添加 SQL约束,否则这将导致错误。ON DELETE

书本中的更简洁

稍后补充;下一篇博文中有

数据查询

有单表查询,多表查询,子查询和联合查询等

使用ORM框架提供的API达到查询的目的

单表查询,即在一个表中查询

 from index.models import *
## 全表查询
v = Vocation.objects.all()
v[0].job # Out[4]: '文员'
## 查询前3条数据
Sql语句中的LIMIT方法,全表查询返回的是个列表

查询某个字段用values

 values_list方法,数据以列表返回,列表元素以元组表示
## 使用get方法查询返回的是单个字段``,查询字段必须是主键或者唯一约束的字段,并且查询的数据必须存在,若重复或不存在则报错
## 使用filter方式查询,返回的是个列表`]>`
查询字段没有限制,返回的是个列表,查询为空则返回空列表。
## SQL的and查询主要在filter里面添加多个查询条件,在django中表示用逗号(,)隔开即可
## SQL的or查询需要引入Q,编写格式:Q(filter=value)|Q(filter=value)
### 多个Q之间使用'|'隔开
##SQL:select * from index_vocation where job='网站设计' or id=9

 from django.db.models import Q
v = Vocation.objects.filter(Q(job='网站设计' )|Q(id=9))
v # ` ]>`
v[0].job # 网站设计

数据库的查询方法介绍了很多,还介绍了filter和get的匹配符

该下方就是大致看了下,代码没敲;这里又使用了很多其他关键字

多表查询又分为正向查询和反向查询

正向查询为多的那个表查询单个的表,也就是设有外键与其他表有关联的表,反向查询即相反

执行SQL语句

一些复杂的查询很难在使用ORM的API,所以引入SQL语句的执行方法
extra:结果集修改器,一种提供额外查询参数的机制
raw:执行原始SQL并返回模型实例对象
execute:直接执行自定义SQL

数据库事务

ACID
Atomicity:原子性
Consistency:一致性
隔离性:Isolation
持久性:Durability

多数据库的连接

待补充