Django框架详解 概述 Django采用MVC架构设计的开源的WEB快速开发框架
优点:
缺点:框架重、同步阻塞
Django的设计目标就是一款大而全,便于企业快速开发项目的框架,因此企业广泛采用
项目构建 启动Pycham,点击Create New Project
。选择建立新的虚拟环境(每个项目独立开发,所以需要独立的虚拟环境),选择基于开发的解释器版本
安装Django Django的安装参考:https://www.djangoproject.com/download/
Python版本依赖,参考:https://docs.djangoproject.com/en/3.2/faq/install/#what-python-version-can-i-use-with-django
Django version
Python版本
1.8
2.7, 3.2 (until the end of 2016), 3.3, 3.4, 3.5
1.9, 1.10
2.7, 3.4, 3.5
1.11(LTS)
2.7, 3.4, 3.5, 3.6, 3.7 (added in 1.11.17)
2.0
3.4, 3.5, 3.6, 3.7
2.1
3.5, 3.6, 3.7
2.2(LTS)
3.5, 3.6, 3.7, 3.8(2.2.8), 3.9 (added in 2.2.17)
3.0
3.6, 3.7, 3.8, 3.9 (added in 3.0.11)
3.1
3.6, 3.7, 3.8, 3.9 (added in 3.1.3)
3.2(LTS)
3.6, 3.7, 3.8, 3.9
长期支持版本是企业的选择,由于最新版是3.2是LTS,本次采用它
Django3.x兼容2.x版本。相比于2.x版本
菜单 view/Tool Windows/Terminal
打开命令行,其运行当前工作路径正好是项目根目录,而且(DjangoTest) E:\ClassProjects\DjangoTest
中 (DjangoTest) 说明使用了虚拟环境。就在这里安装Django
注意:本文如若未特殊声明,所有的命令操作都在项目根目录下
1 2 3 $ pip install django==3.2 .7 Installing collected packages: pytz, sqlparse, asgiref, django Successfully installed asgiref-3.5 .2 django-3.2 .7 pytz-2022.1 sqlparse-0.4 .2
脚手架构建 Django安装完,提供了一个命令 django-admin
,它实际是虚拟环境路径中,Lib/site-packages/django/bin
下的 django-admin.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 $ django-admin Type 'django-admin help <subcommand>' for help on a specific subcommand. Available subcommands: [django] makemigrations migrate runserver startapp startproject $ django-admin help startproject
上面这些都是最常用的命令,这里只需要使用 startproject 来构建一个Django项目目录结构和基础文件
1 $ django-admin startproject salary .
构建一个项目叫做blog,注意最后有个点 ,表示在当前目录即项目根目录创建
1 2 3 4 5 6 7 项目根目录 ├─ manage.py └─ salary ├─ settings.py ├─ urls.py ├─ wsgi.py └─ __init__.py
重要文件说明
manage.py:本项目管理的命令行工具。应用创建、数据库迁移等都使用它完成
salary/settings.py :本项目的全局核心配置文件
应用、数据库配置
模板、静态文件
中间件、日志
第三方插件配置
blog/urls.py :URL路径映射配置。项目初始,只配置了/admin的路由
blog/wsgi:定义WSGI接口信息。部署用,一般无需改动
MySQL数据库驱动 Django支持MySQL 5.5+
Django官方推荐使用本地驱动mysqlclient 1.3.7 +
1 $ pip install mysqlclient
创建应用 创建应用 employee
1 $ python manage.py startapp employee
创建应用后,项目根目录下产生一个employee目录,有如下文件:
admin.py:应用后台管理声明文件
models.py :模型层Model类定义
views.py :定义URL响应函数或类
migrations包:数据迁移文件生成目录
apps.py:应用的信息定义文件
配置 salary/settings.py是全局配置文件
注册应用 注册应用,可以做迁移migrate、做后台管理Admin等,一般建议注册
1 2 3 4 5 6 7 8 9 INSTALLED_APPS = [ 'django.contrib.admin' , 'django.contrib.auth' , 'django.contrib.contenttypes' , 'django.contrib.sessions' , 'django.contrib.messages' , 'django.contrib.staticfiles' , 'employee' , ]
数据库配置 用数据库,需要修改默认的数据库配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 DATABASES = { 'default' : { 'ENGINE' : 'django.db.backends.mysql' , 'NAME' : '数据库名' , 'USER' : '用户' , 'PASSWORD' : '密码' , 'HOST' : '127.0.0.1' , 'PORT' : '3306' , } } DATABASES = { 'default' : { 'ENGINE' : 'django.db.backends.mysql' , 'NAME' : 'test' , 'USER' : 'root' , 'PASSWORD' : 'wjxyzs@qq.com' , 'HOST' : 'localhost' , 'PORT' : '3306' , } }
配置项
说明
HOST
数据库主机。缺省是空字符串,代表localhost。如果是’/‘开头表示使用UnixSocket连接
POST
端口
USER
用户名
PASSWORD
密码
NAME
库名
OPTIONS
选项,字典类型,参考MySQL文档
数据库引擎ENGINE
内建的引擎有
'django.db.backends.postgresql'
'django.db.backends.mysql'
'django.db.backends.sqlite3'
'django.db.backends.oracle'
本地化和时区 1 2 3 LANGUAGE_CODE = 'zh-Hans' TIME_ZONE = 'Asia/Shanghai' USE_TZ = True
日志 Django的日志配置在settings.py中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 LOGGING = { 'version' : 1 , 'disable_existing_loggers' : False , 'handlers' : { 'console' : { 'class' : 'logging.StreamHandler' , }, }, 'loggers' : { 'django.db.backends' : { 'handlers' : ['console' ], 'level' : 'DEBUG' , }, }, }
配置后,就可以在控制台看到执行的SQL语句
注意,settings.py中必须DEBUG=True ,同时loggers的leve是DEBUG,否则从控制台看不到SQL语句
Django内建loggers可以参考:https://docs.djangoproject.com/en/3.2/topics/logging/#django-db-backends
迁移 迁移:指的是把Django中定义的Model类和属性,转换成数据库中表和字段的过程
迁移一般需要两个过程:
制作迁移文件
迁移
Django内部也有应用,它们也需要表。这些表的迁移文件已经生成了,只需要迁移
1 $ python manage.py migrate
迁移后,数据库中产生下面这些表:auth_group, auth_group_permissions, auth_permission,auth_user, auth_user_groups, auth_user_user_permissions, django_admin_log,django_content_type, django_migrations, django_session
运行 1 $ python manage.py runserver
访问 http://127.0.0.1:8000/ 即可
这就是一个Django项目构建基本流程,之后项目构建方式、配置都大同小异
ORM ORM,对象关系映射, 对象 和 关系 之间的映射。这样就可以使用面向对象的方式来操作数据库中的 表。
1 2 3 4 table => class # 表映射为类 row => object # 行映射为实例 column => property # 字段映射为属性
举例,有表student,字段为id int,name varchar,age int 映射到Python为
1 2 3 4 5 6 7 8 9 10 class Student :id = ?某类型字段 name = ?某类型字段 age = ?某类型字段 class Student : def __init__ (self) : self.id = ? self.name = ? self.age = ?
Django ORM
对模型对象的CRUD,被Django ORM转换成相应的SQL语句以操作不同的数据源。
Model模型 字段类型
字段类
说明
AutoField(***)
自增的整数字段。 如果不指定,django会为模型类自动增加主键字段
BooleanField
布尔值字段,True和False 对应表单控件CheckboxInput
NullBooleanField
比BooleanField多一个null值
CharField(***)
字符串,max_length设定字符长度 对应表单控件TextInput
TextField
大文本字段,一般超过4000个字符使用 对应表单控件Textarea
IntegerField(***)
整数字段
BigIntegerField
更大整数字段,8字节
DecimalField
使用Python的Decimal实例表示十进制浮点数。max_digits总位数, decimal_places小数点后的位数
FloatField
Python的Float实例表示的浮点数
DateField(***)
使用Python的datetime.date实例表示的日期 auto_now=False每次修改对象自动设置为当前时间。 auto_now_add=False对象第一次创建时自动设置为当前时间。 auto_now_add、auto_now、default互斥 对应控件为TextInput,关联了一个Js编写的日历控件
TimeField
使用Python的datetime.time实例表示的时间,参数同上
DateTimeField
使用Python的datetime.datetime实例表示的时间,参数同上
FileField
一个上传文件的字段
ImageField
继承了FileField的所有属性和方法,但是对上传的文件进行校验,确保 是一个有效的图片
EmailField(***)
能做Email检验,基于CharField,默认max_length=254
GenericIPAddressField
支持IPv4、IPv6检验,缺省对应文本框输入
URLField
能做URL检验,基于基于CharField,默认max_length=200
缺省主键 缺省情况下,Django的每一个Model都有一个名为id的AutoField字段,如下
1 id=models.AutoField(primary_key=True )
如果显式定义了主键,这种缺省主键就不会被创建了。
Python之禅中说“显式优于隐式”,所以,如果有必要,还是尽量使用自己定义的主键,哪怕该字段名就 是id,也是一种不错的选择。
Django 3.2中增加了 DEFAULT_AUTO_FIELD 对缺省主键字段类型进行设置。
字段选项 参考 https://docs.djangoproject.com/en/3.2/ref/models/fields/#field-options
值
说明
db_column
表中字段的名称。如果未指定,则使用属性名
primary_key
是否主键
unique
是否是唯一键
default
缺省值。这个缺省值不是数据库字段的缺省值,而是新对象产生的时候被填入的 缺省值
null
表的字段是否可为null,默认为False
blank
Django表单验证中,是否可以不填写,默认为False
db_index
字段是否有索引
关系类型字段类
类
说明
ForeignKey
外键,表示一对多ForeignKey(‘production.Manufacturer’) 自关联ForeignKey(‘self’)
ManyToManyField
表示多对多
OneToOneField
表示一对一
Model类
项目目录DjangoTest下employee应用目录下的models.py,这里就是定义Model类的地方
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 30 31 32 33 34 35 36 37 from django.db import models""" CREATE TABLE `employees` ( `emp_no` int(11) NOT NULL, `birth_date` date NOT NULL, `first_name` varchar(14) NOT NULL, `last_name` varchar(16) NOT NULL, `gender` smallint(6) NOT NULL DEFAULT '1' COMMENT 'M=1, F=2', `hire_date` date NOT NULL, PRIMARY KEY (`emp_no`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; """ class Employee (models.Model) : class Gender (models.IntegerChoices) : MAN = 1 , '男' FEMALE = 2 , '女' class Meta : db_table = 'employees' emp_no = models.IntegerField(primary_key=True , verbose_name='工号' ) birth_date = models.DateField() first_name = models.CharField(max_length=14 , verbose_name='名' ) last_name = models.CharField(max_length=16 , verbose_name='姓' ) gender = models.SmallIntegerField(choices=Gender.choices, verbose_name='性别' ) hire_date = models.DateField() @property def name (self) : return "{} {}" .format(self.last_name, self.first_name) def __repr__ (self) : return "<E {}, {}>" .format(self.emp_no, self.name) __str__ = __repr__
在项目根目录编写一个test.py,内容如下
1 2 3 4 5 6 7 8 9 10 11 12 13 import osimport djangoos.environ.setdefault('DJANGO_SETTINGS_MODULE' , 'salary.settings' ) django.setup(set_prefix=False ) from employee.models import Employeeemps = Employee.objects.all() print(type(emps)) print(*emps, sep='\n' ) print(emps[0 ].gender, emps[0 ].get_gender_display())
https://docs.djangoproject.com/en/3.2/ref/models/fields/#django.db.models.Field.choices
管理器 管理器非常重要,有了它才能操作数据库。 每一个非抽象的Model类必须有一个Manager实例。如果不指定,Django会默认指定一个Manager,就是属性objects。
参考:https://docs.djangoproject.com/en/3.2/topics/db/managers/
Django查询 和DML一样,增删改不难,最复杂的就是查询。
查询集 如果查的是一批数据,那么返回的是一个结果的集合
它是django.db.models.query.QuerySet的实例 可迭代对象
1、惰性求值
创建查询集不会带来任何对数据库的访问,直到调用方法使用数据时,才会访问数据库。
参考:https://docs.djangoproject.com/en/3.2/ref/models/querysets/#when-querysets-are-evaluated
2、缓存
参考:https://docs.djangoproject.com/en/3.2/topics/db/queries/#caching-and-querysets
每一个查询集都包含一个缓存,来最小化对数据库的访问。
新建查询集,缓存为空。首次对查询集求值时,会发生数据库查询,Django会把查询的结果存在这个缓 存中,并返回请求的结果,接下来对查询集求值将使用缓存的结果。
通过日志,观察下面的2个例子是要看真正生成的语句了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from employee.models import Employeeemps = Employee.objects.all() print(1 , emps) print(2 , emps) print(3 , emps[0 ]) print(4 , emps[0 ]) print(emps._result_cache) from employee.models import Employeeemps = Employee.objects.all() print(2 , emps) print(3 , emps[0 ]) print(4 , emps[:]) print(emps._result_cache)
限制查询集(切片) 分页功能实现,使用限制查询集。 查询集对象可以直接使用索引下标的方式(不支持负索引),相当于SQL语句中的limit和offset子句。
注意:使用切片返回的新的结果集,依然是惰性求值,不会立即查询。但是使用了切片步长,会立即查询
1 2 3 4 5 qs = Employee.objects.all()[10 :15 ] qs = Employee.objects.all()[20 :30 ]
注意:在使用print函数打印结果集的时候,看到SQL语句有自动添加的LIMIT 21,这是怕打印的太长了。 使用for循环迭代就没了。
结果集方法
名称
返回值类型
说明
all()
QuerySet
filter()
QuerySet
过滤,返回满足条件的数据
exclude()
QuerySet
排除,排除满足条件的数据
order_by()
QuerySet
排序,注意参数是字符串
values()
QuerySet
返回集合内的元素是字典,字典内是字段和值的键值对
返回值如果是QuerySet类型,可以链式调用。
filter(k1=v1).filter(k2=v2) 等价于 filter(k1=v1, k2=v2)
filter(pk=10)这里pk指的就是主键, 不用关心主键字段名,当然也可以使用使用主键名 filter(emp_no=10)
1 2 3 4 5 mgr = Employee.objects print(mgr.all()) print(mgr.values()) print(mgr.filter(pk=10010 ).values()) print(mgr.exclude(emp_no=10001 )) print(mgr.exclude(emp_no=10002 ).order_by('emp_no' )) print(mgr.exclude(emp_no=10002 ).order_by('-pk' )) print(mgr.exclude(emp_no=10002 ).order_by('gender' , '-pk' ).values())
filter(pk=10010) 这是关键字传参,所以不能写成 filter(pk!=10010) 或 filter(pk>=10010)
返回单个值的方法
名称
说明
get()
严格返回满足条件的单个对象 如果未能返回对象则抛出DoesNotExist异常;如果能返回多条,抛出 MultipleObjectsReturned异常
count()
返回当前查询的总条数
first()
返回第一个对象
last()
返回最后一个对象
exist()
判断查询集中是否有数据,如果有则返回True
1 2 3 4 mgr = Employee.objects print(mgr.filter(pk=10010 ).get()) print(mgr.get(pk=10001 )) print(mgr.first()) print(mgr.exclude(pk=10010 ).last()) print(mgr.exclude(pk=10010 ).count())
字段查询(Field Lookup)表达式 参考:https://docs.djangoproject.com/en/3.2/ref/models/querysets/#field-lookups 字段查询表达式可以作为filter()、exclude()、get()的参数,实现where子句。 语法:属性名称__比较运算符=值
。注意:属性名和运算符之间使用双下划线
名称
举例
说明
exact
filter(isdeleted=False) filter(isdeleted__exact=False)
严格等于,可省略不写
contains
exclude(title__contains=’天’)
是否包含,大小写敏感,等价于 like binary ‘%天%’ 模糊匹配效率很低
startswith、endswith
filter(title__startswith=’天’)
以什么开头或结尾,大小写敏感
isnull、 isnotnull
filter(title__isnull=False)
是否为null
iexact、icontains、 istartswith、iendswith
i的意思是忽略大小写
in
filter(pk__in=[1,2,3,100])
是否在指定范围数据中
gt、gte、lt、lte
filter(id__gt=3)
filter(pk__lte=6)
filter(pub_date__gt=date(2000,1,1))
大于、小于等
year、month、day、week_day、hour、minute、second
filter(pub_date__year=2000)
对日期类型属性处理
1 2 3 4 5 6 mgr = Employee.objects print(mgr.filter(emp_no__exact=10010 )) print(mgr.filter(pk__in=[10010 , 10009 ])) print(mgr.filter(last_name__startswith='P' )) print(mgr.filter(pk__in=[1000 ,1001 ,1002 ]) print(mgr.exclude(pk__gt=10003 ))
Q对象 虽然Django提供传入条件的方式,但是不方便,它还提供了Q对象来解决。
Q对象是django.db.models.Q,可以使用&、|操作符来组成逻辑表达式。 ~ 表示not。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from django.db.models import Qmgr = Employee.objects print(mgr.filter(Q(pk__lt=10006 ))) print(mgr.filter(pk__gt=10003 ).filter(pk__lt=10006 )) print(mgr.filter(pk__gt=10003 , pk__lt=10006 )) print(mgr.filter(Q(pk__gt=10003 ), Q(pk__lt=10006 ))) print(mgr.filter(Q(pk__gt=10003 ) & Q(pk__lt=10006 ))) print(mgr.filter(pk__gt=10003 ) & mgr.filter(pk__lt=10006 )) print(mgr.filter(pk__in=[10003 , 10006 ])) print(mgr.filter(Q(pk=10003 ) | Q(pk=10006 ))) print(mgr.filter(pk=10003 ) | mgr.filter(pk=10006 )) print(mgr.filter(~Q(pk__gt=10003 )))
可使用&|和Q对象来构造复杂的逻辑表达式,可以使用一个或多个Q对象。
如果混用关键字参数和Q对象,那么Q对象必须位于关键字参数的前面。
聚合、分组 aggregate() 返回字典,方便使用
1 2 3 4 5 6 7 8 from employee.models import Employeefrom django.db.models import Q, Avg, Sum, Max, Min, Countmgr = Employee.objects print(mgr.filter(pk__gt=10010 ).count()) print(mgr.filter(pk__gt=10010 ).aggregate(Count('pk' ), Max('pk' ))) print(mgr.filter(pk__lte=10010 ).aggregate(Avg('pk' ))) print(mgr.aggregate(Max('pk' ), min=Min('pk' )))
annotate()方法用来分组聚合,返回查询集
1 2 3 4 5 6 mgr = Employee.objects print(mgr.filter(pk__gt=10010 ).aggregate(Count('pk' ))) s = mgr.filter(pk__gt=10010 ).annotate(Count('pk' )) for x in s: print(x) print(x.__dict__)
values()方法,放在annotate前就是指定分组字段,之后就是取结果中的字段
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 30 31 32 33 mgr = Employee.objects s = mgr.filter(pk__gt=10010 ).values('gender' ).annotate(Count('pk' )) for x in s: print(x) <QuerySet [{'gender' : 2 , 'pk__count' : 3 }, {'gender' : 1 , 'pk__count' : 7 }]> {'gender' : 2 , 'pk__count' : 3 } {'gender' : 1 , 'pk__count' : 7 } mgr = Employee.objects s= mgr.filter(pk__gt=10010 ).values('gender' ).annotate(c=Count('pk' )).order_by(' -c' ) print(s) for x in s: print(x) <QuerySet [{'gender' : 1 , 'c' : 7 }, {'gender' : 2 , 'c' : 3 }]> {'gender' : 1 , 'c' : 7 } {'gender' : 2 , 'c' : 3 } mgr = Employee.objects s = mgr.filter(pk__gt=10010 ).values('gender' ).annotate( Avg('pk' ), c=Count('pk' ) ).order_by('-c' ).values('pk__avg' , 'c' ) print(s) for x in s: print(x) <QuerySet [{'pk__avg' : 10015.5714 , 'c' : 7 }, {'pk__avg' : 10015.3333 , 'c' : 3 }]> {'pk__avg' : 10015.5714 , 'c' : 7 } {'pk__avg' : 10015.3333 , 'c' : 3 }
Django ORM 一对多 员工和工资表是一对多关系
1 2 3 4 5 6 7 8 9 10 11 """ CREATE TABLE `salaries` ( `emp_no` int(11) NOT NULL, `salary` int(11) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`from_date`), CONSTRAINT `salaries_ibfk_1` FOREIGN KEY (`emp_no`) REFERENCES `employees` (`emp_no`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8; """
联合主键问题 SQLAlchemy提供了联合主键支持,但是Django至今都没有支持。 Django只支持单一主键,这也是我提倡的。但对于本次基于Django测试的表就只能增加一个单一主键了。
原因,请参看:https://code.djangoproject.com/wiki/MultipleColumnPrimaryKeys 。Django 到目前为止也没有提供这种Composite primary key Django不能直接添加自己的2个字段的联合主键,我们手动为表创建一个自增id主键。操作顺序如下:
取消表所有联合主键,并删除所有外键约束后保存,成功再继续
为表增加一个id字段,自增、主键。保存,如果成功,它会自动填充数据
重建原来的外键约束即可
模型构建 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 from django.db import modelsclass Employee (models.Model) : class Gender (models.IntegerChoices) : MAN = 1 , '男' FEMALE = 2 , '女' class Meta : db_table = 'employees' emp_no = models.IntegerField(primary_key=True , verbose_name='工号' ) birth_date = models.DateField() first_name = models.CharField(max_length=14 , verbose_name='名' ) last_name = models.CharField(max_length=16 , verbose_name='姓' ) gender = models.SmallIntegerField(choices=Gender.choices, verbose_name='性别' ) hire_date = models.DateField() @property def name (self) : return "{} {}" .format(self.last_name, self.first_name) def __repr__ (self) : return "<E {}, {}>" .format(self.emp_no, self.name) __str__ = __repr__ class Salary (models.Model) : class Meta : db_table = 'salaries' emp_no = models.ForeignKey('Employee' , on_delete=models.CASCADE) from_date = models.DateField() salary = models.IntegerField(verbose_name='工资' ) to_date = models.DateField() def __repr__ (self) : return "<S {}, {}, {}>" .format(self.pk, self.emp_no, self.salary) __str__ = __repr__ from employee.models import Employee, Salarymgr = Employee.objects print(mgr.filter(pk=10004 )) print(Salary.objects.all()) emp_no = models.ForeignKey('Employee' , on_delete=models.CASCADE, db_column='emp_no' )
ForeignKey还有一个选项to_field,表示关联到主表的哪个字段,默认使用主键,如果需要指定其它字 段,要求必须是唯一键字段。
特殊属性 增加了外键ForeignKey后,Django会对一端和多端增加一些新的类属性,查看类属性就可以看到
1 2 3 4 5 6 7 8 print(*Employee.__dict__.items(), sep='\n' ) print(*Salary.__dict__.items(), sep='\n' )
从一端往多端查 .salary_set
从多端往一端查 .emp_no
查询 1 2 3 4 5 6 7 8 9 10 11 12 empmgr = Employee.objects print(empmgr.get(pk=10004 ).salary_set.all()) `salaries`.`salary`, `salaries`.`to_date` FROM `salaries` WHERE `salaries`.`emp_no` = 10004 LIMIT 21 ; args=(10004 ,)
如果觉得salary_set不好用,可以使用related_name
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Salary (models.Model) : emp_no = models.ForeignKey('Employee' , on_delete=models.CASCADE, null=False , db_column='emp_no' , related_name='salaries' ) print(empmgr.get(pk=10004 ).salaries.all()) empmgr = Employee.objects emp = empmgr.get(pk=10004 ) print(emp.salaries.all()) print(emp.salaries.values('emp_no' , 'from_date' , 'salary' )) print(emp.salaries.filter(salary__gt=55000 ).all()) slist = list(salmgr.filter(emp_no=10004 )).filter(salary__gt=55000 ) for s in slist: print(s.emp_no.name, s.emp_no_id, s.salary) 这种查询会导致列表中的n个Salary实例填充其中emp_no属性,会查n此数据库 所以,从salaries表往employees表查不合适,虽然可以改进,但是还是别扭,用的少
distinct
1 2 3 4 5 6 7 8 9 10 11 12 13 print(salarymgr.values('emp_no' ).distinct()) emps = salarymgr.filter(salary__gt=55000 ).values('emp_no' ).distinct() print(type(emps)) print(emps) print(empmgr.filter(emp_no__in=[d.get('emp_no' ) for d in emps])) print(empmgr.filter(emp_no__in=map(lambda x:x.get('emp_no' ), emps))) print(empmgr.filter(emp_no__in=emps))
raw的使用 如果查询非常复杂,使用Django不方便,可以直接使用SQL语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 empmgr = Employee.objects sql = """\ SELECT DISTINCT e.emp_no, e.first_name, e.last_name FROM employees e JOIN salaries s ON e.emp_no=s.emp_no WHERE s.salary > 55000 """ emps = empmgr.raw(sql) print(type(emps)) [<Employee: 10001 Georgi Facello>, <Employee: 10002 Bezalel Simmel>,<Employee: 10004 Chirstian Koblick>] sql = """\ select e.emp_no, e.first_name, e.last_name, s.salary from employees e join salaries s on e.emp_no = s.emp_no where s.salary > 70000 """ for x in empmgr.raw(sql): print(x.__dict__) print(x.name, x.salary)
多对多 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 CREATE TABLE `departments` ( `dept_no` char(4 ) NOT NULL, `dept_name` varchar(40 ) NOT NULL, PRIMARY KEY (`dept_no`), UNIQUE KEY `dept_name` (`dept_name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; CREATE TABLE `dept_emp` ( `emp_no` int(11 ) NOT NULL, `dept_no` char(4 ) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`dept_no`), KEY `dept_no` (`dept_no`), CONSTRAINT `dept_emp_ibfk_1` FOREIGN KEY (`emp_no`) REFERENCES `employees` (`emp_no`) ON DELETE CASCADE, CONSTRAINT `dept_emp_ibfk_2` FOREIGN KEY (`dept_no`) REFERENCES `departments` (`dept_no`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
联合主键问题依然存在,所以做法同上。修改dept_emp表,增加id自增主键。
注意:这种主键的修改在设计表的阶段就应该考虑好,而不是事后修改。我们这里是故意造成这种现象来说明问题。
构建模型 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 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 from django.db import modelsclass Department (models.Model) : class Meta : db_table = 'departments' dept_no = models.CharField(primary_key=True , max_length=4 ) dept_name = models.CharField(max_length=40 , null=False , unique=True ) def __repr__ (self) : return "<Department: {} {}>" .format( self.dept_no, self.dept_name) __str__ = __repr__ class Dept_emp (models.Model) : id = models.AutoField(primary_key=True ) emp_no = models.ForeignKey( to='Employee' , on_delete=models.CASCADE, db_column='emp_no' ) dept_no = models.ForeignKey(to='Department' , on_delete=models.CASCADE, max_length=4 , db_column='dept_no' ) from_date = models.DateField(null=False ) to_date = models.DateField(null=False ) class Meta : db_table = 'dept_emp' def __repr__ (self) : return "<DeptEmp: {} {}>" .format(self.emp_no, self.dept_no) __str__ = __repr__ import osimport djangoos.environ.setdefault('DJANGO_SETTINGS_MODULE' , 'salary.settings' ) django.setup() from employee.models import Employee, Departmentempmgr = Employee.objects deptmgr = Department.objects emp = empmgr.filter(pk=10010 ).get() depts = emp.dept_emp_set.all() for x in depts: print(type(x), x) e = x.emp_no print(type(e), e) d = x.dept_no print(type(d), d) print(e.emp_no, e.name, d.dept_no, d.dept_name)
Q:如何通过员工直接查部门信息呢?
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 class Department (models.Model) : class Meta : db_table = "departments" dept_no = models.CharField(max_length=4 , primary_key=True ) dept_name = models.CharField(max_length=40 , unique=True ) emps = models.ManyToManyField(Employee, through="Dept_emp" ) def __str__ (self) : return "<D {}, {}>" .format(self.pk, self.dept_name) import osimport djangoos.environ.setdefault('DJANGO_SETTINGS_MODULE' , 'salary.settings' ) django.setup(set_prefix=False ) from employee.models import Employee, Salary, Department, Dept_empemgr = Employee.objects dmgr = Department.objects emp = emgr.get(pk=10010 ) for d in emp.department_set.all(): print(d)
迁移 如果建立好模型类,想从这些类来生成数据库的表,使用下面语句。
1 2 3 4 5 6 $ python manage.py makemigrations $ python manage.py migrate $ python manage.py migrate employee
总结 在开发中,一般都会采用ORM框架,这样就可以使用对象操作表了。 Django中,定义表映射的类,继承自Model类。Model类使用了元编程,改变了元类。 使用Field实例作为类属性来描述字段。 使用ForeignKey来定义外键约束。
是否使用外键约束?
力挺派 能使数据保证完整性一致性
弃用派 开发难度增加,大量数据的时候影响插入、修改、删除的效率。 在业务层保证数据的一致性。
后台管理 站点管理 如果要使用Django,必须提供一个管理员用户,目前用户表auth_user表没有记录。
1 2 $ python manage.py createsuperuser $ python manage.py runserver
浏览器中访问 http://127.0.0.1:8000/admin/ 即可看到登录界面。
登录后,并没有看到自己建立的Model类对应的表。这是因为有2步必须做
1.注册应用
1 2 3 4 5 6 7 8 9 INSTALLED_APPS = [ 'django.contrib.admin' , 'django.contrib.auth' , 'django.contrib.contenttypes' , 'django.contrib.sessions' , 'django.contrib.messages' , 'django.contrib.staticfiles' , 'employee' , ]
2.注册Model类 1 2 3 4 5 from django.contrib import adminfrom .models import Employee, Departmentadmin.site.register(Employee) admin.site.register(Department)
路由 按照WSGI原理代码,所有请求都交给一个app处理,如何做到不同请求对应不同的处理? 最简单的方式就是,进入app后,不同的URL做不同的处理。也就是,建立URL和处理函数之间的映射。
路由 :根据访问的路径Path不同调度不同的处理函数。可以对路径Path采用模式匹配。
Django 3.x使用path、re_path定义,这和2.x略有区别。
主路由 path参考:https://docs.djangoproject.com/en/3.2/topics/http/urls/#example 主目录中的urls.py是路径匹配的入口,它定义在 settings.py 中 ROOT_URLCONF = ‘salary.urls’ 。 约定,称主目录中的路由配置文件urls.py为 主路由 或 一级路由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from django.contrib import adminfrom django.urls import pathfrom django.http import HttpResponse print('=' * 30 ) print(request) print('args' , [(type(x), x) for x in args]) print('kwargs' , [(type(k),k, type(v),v) for k,v in kwargs.items()]) print('=' * 30 ) return HttpResponse() urlpatterns = [ path('admin/' , admin.site.urls), path('test/<course>/<year>/' , path_test), path('test/<str:course>/<int:year>/' , path_test), ]
路径转换器
URL
path模式
参数解析
/test/python/2008 /test/python/2008/
test///
默认转换器类型为str [‘course’, <class ‘str’>, ‘python’] [‘year’, <class ‘str’>, ‘2008’]
/test/python/2008 /test/python/2008/
test/str:course /int:year /
[‘course’, <class ‘str’>, ‘python’] [‘year’, <class ‘int’>, 2008]
从测试结果看,如果和匹配上了,采用的是关键字传参 。
URL
path模式
/test/python/2008
test//
匹配
/test/python/2008
test//
失败
/test/python/2008
test//
301跳转到/test/python/2008/
/test/python/2008
test//
匹配
由于之后所有的链接都是程序中提取写好的,所以,一定可以保证程序员写的URL是正确的。建议,定 义path模式时,使用 xxx/ ,以斜杠 “/“ 结尾。在Django官网例子中就是这样定义的。
re_path 支持正则表达式,参考 https://docs.djangoproject.com/en/3.2/topics/http/urls/#using-regular-expressions
二级路由 如果所有路由都定义在主路由文件中,太乱太臃肿,解决的办法就是二级路由,即路由到应用的路有文 件。但是脚手架建立的应用文件中缺少urls.py,手动在应用目录建立一个。
1.主路由文件
1 2 3 4 5 6 7 from django.contrib import adminfrom django.urls import path, includeurlpatterns = [ path('admin/' , admin.site.urls), path('emp/' , include('employee.urls' )), ]
2.应用路由文件 在employee目录下新建urls.py。
1 2 3 4 5 6 from django.urls import pathfrom .views import test_indexurlpatterns = [ path('' , test_index), ]
在employee/view.py中增加index视图函数
1 2 3 4 from django.http import HttpRequest, HttpResponsedef test_index (request:HttpRequest) : return HttpResponse(b'magedu.com' )
访问 http://127.0.0.1:8000/emp/ 即可。
Django模板 模板 用户通过浏览器访问的目的是为了看到数据嵌在网页中,也就是说给浏览器提供一个网页HTML即可。 网页内容分为:
生成环境中,会对静态、动态的内容的访问进行区别对待,这就是 动静分离 。
模板配置 参考:https://docs.djangoproject.com/en/3.2/topics/templates/#configuration 在主配置文件 settings.py 中 TEMPLATES
DIRS:依次搜索模板的目录列表
BASE_DIR / 'templates'
表示项目根目录下templates目录
APP_DIRS:布尔值。告诉引擎是否在 INSTALLED_APPS 中应用目录中搜索模板。
True表示 DjangoTemplates 引擎会在每一个应用目录下的templates目录中搜索模板
公共模板路径可以配置在DIRS列表中,应用自己使用的模板定义在应用目录下的templates目录中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from pathlib import PathBASE_DIR = Path(__file__).resolve().parent.parent TEMPLATES = [ { 'BACKEND' : 'django.template.backends.django.DjangoTemplates' , 'DIRS' : [BASE_DIR / 'templates' ], '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' , ], }, }, ]
在项目根目录下创建 templates 目录,存放模板文件。
模板渲染 模板页 templates/index.html
1 2 3 4 5 6 7 8 9 10 11 <!DOCTYPE html > <html lang="en"> <head > <meta charset="UTF-8"> <title>马哥教育Django首页</title> </head> <body > 我是模板页,数据{{content}} </body> </html>
模板处理 模板处理的2个步骤
加载模板文件,读取文件内容
渲染,替换内容中占位符,返回最终的字符串
render 函数 1 render(request, template_name, context=None , content_type=None , status=None ,using=None )
template_name模板名
context渲染用上下文字典
status响应报文的状态码
返回HttpResponse对象
1 2 3 4 5 6 7 8 from django.shortcuts import renderfrom django.http import HttpRequest, HttpResponsedef test_index (request:HttpRequest) : context = {'test' :'测试字符串' } content = render(request, 'index.html' , context) return HttpResponse(content)
DTL语法 Django的模板引擎默认使用Django template language (DTL)构建。
变量 变量名由字母、数字、下划线、点号组成。
点号使用的时候,例如foo.bar,遵循以下顺序:
字典查找,例如 foo[“bar”],把 foo 当做字典,bar 当做 key
属性或方法的查找,例如 foo.bar,把 foo 当做对象,bar 当做属性或方法
数字索引查找,例如 foo[bar],把 foo 当做列表一样,使用索引访问
1 2 3 4 5 6 7 8 9 10 11 12 13 def index (request:HttpRequest) : """视图函数:请求进来返回响应""" mydict = { 'a' :100 , 'b' :0 , 'c' :list(range(10 ,20 )), 'd' :'abc' , 'date' :datetime.datetime.now() } context = {'content' :'www.magedu.com' , 'mydict' :mydict} return render(request, 'index.html' , context)
如果变量未能找到,则缺省插入空字符串 在模板中调用方法,不能加小括号 ,自然也不能传递参数。
{{ mydict.a }}符合第一条,当做字典的key就可以访问到了
{{ mydict.keys }} 这样是对的,不能写成{{ mydict.keys() }}。符合第二条,当做mydict对象的属性或方法。
{{ mydict.c.0 }} 符合第三条。
模板标签
标签采用{% tag %}语法。
if/else 标签 基本语法格式如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 {% if condition %} ... display {% endif %} 或 {% if condition1 %} ... display 1 {% elif condition2 %} ... display 2 {% else %} ... display 3 {% endif %}
条件也支持and、or、not 注意,因为这些标签是断开的,所以不能像Python一样使用缩进就可以表示出来,必须有个结束标签, 例如endif、endfor。
for 标签:https://docs.djangoproject.com/en/2.0/ref/templates/builtins/#for
1 2 3 4 5 6 7 8 9 <ul> {% for athlete in athlete_list %} <li>{{ athlete.name }}</li> {% endfor %} </ul> {% for person in person_list %} <li>{{ person.name }}</li> {% endfor %}
变量
说明
forloop.counter
当前循环从1开始的计数
forloop.counter0
当前循环从0开始的计数
forloop.revcounter
从循环的末尾开始倒计数到1
forloop.revcounter0
从循环的末尾开始到计数到0
forloop.first
第一次进入循环
forloop.last
最后一次进入循环
forloop.parentloop
循环嵌套时,内层当前循环的外层循环
给标签增加一个 reversed 使得该列表被反向迭代:
1 2 3 4 5 {% for athlete in athlete_list reversed %} ... {% empty %} ... 如果被迭代的列表是空的或者不存在,执行empty {% endfor %}
可以嵌套使用 {% for %} 标签:
1 2 3 4 5 6 7 8 {% for athlete in athlete_list %} <h1>{{ athlete.name }}</h1> <ul> {% for sport in athlete.sports_played %} <li>{{ sport }}</li> {% endfor %} </ul> {% endfor %}
Pycharm模板定义
按照上述方法可以定义诸多Django模板标签。
testfor.html模板
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 30 31 32 33 34 35 36 37 <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <title>测试for</title> </head> <body> 字典是dict(zip('abced' , range(1 ,6 ))) <ul> {% for k,v in dct.items %} <li>{{forloop.counter}} {{k}} {{v}}</li> {% endfor %} </ul> <hr> <ul> {% for k,v in dct.items %} <li>{{forloop.counter0}} {{k}} {{v}}</li> {% endfor %} </ul> <hr> <ul> {% for k,v in dct.items %} {{ forloop.first }} {{ forloop.last }} <li>{{forloop.revcounter0}} {{k}} {{v}}</li> {% endfor %} </ul> <hr> <ul> {% for k,v in dct.items %} <li>{{forloop.revcounter}} {{k}} {{v}}</li> {% endfor %} </ul> <hr> </body> </html>
ifequal/ifnotequal 标签
{% ifequal %} 标签比较两个值,当他们相等时,显示在 {% ifequal %} 和 {% endifequal %} 之中所有的 值。
下面的例子比较两个模板变量 user 和 currentuser :
1 2 3 {% ifequal user currentuser %} <h1>Welcome!</h1> {% endifequal %}
和 {% if %} 类似, {% ifequal %} 支持可选的 {% else%} 标签:
1 2 3 4 5 {% ifequal section 'sitenews' %} <h1>Site News</h1> {% else %} <h1>No News Here</h1> {% endifequal %}
其他标签
csrf_token 用于跨站请求伪造保护,防止跨站攻击的。
{% csrf_token %}
#### 注释标签
单行注释 {# #}。
多行注释 {% comment %} ... {% endcomment %}.
1 2 3 4 { {% comment %} 这是多行注释 {% endcomment %}.
过滤器 模板过滤器可以在变量被显示前修改它。
1 2 3 4 {{ 变量|过滤器 }} 过滤器使用管道字符 | ,例如 {{ name|lower }}, {{ name }} 变量被过滤器 lower 处理后,文档大写转换文本为小写。 过滤管道可以被套接,一个过滤器管道的输出又可以作为下一个管道的输入。例如 {{ my_list|first|upper }},将列表第一个元素并将其转化为大写。
**过滤器传参**
有些过滤器可以传递参数,过滤器的参数跟随冒号之后并且总是以双引号包含。
例如:{{ bio|truncatwords:"30" }},截取显示变量 bio 的前30个词。 {{ my_list|join:"," }},将my_list的所有元素使用,逗号连接起来
其他过滤器
| 过滤器 | 说明 | 举例 |
| :-------------: | :----------------------------------------------------------: | :----------------------------------------------------------: |
| cut | 切掉字符串中的指定字符 | {{ value }} |
| lower | 转换为小写 | |
| upper | 转换为大写 | |
| truncatewords | 保留指定个数的**单词** | {{ bio \| truncatwords:"30" }} |
| join | 对序列拼接 | {{ d.e \| join:"//" }} |
| first | 取序列第一个元素 | |
| last | 取序列最后元素 | |
|
yesno | 变量可以是True、False、None yesno的参数给定逗号分隔的三个值,返回3个值中的一个。
True对应第一个
False对应第二个
None对应第三个 如果参数只有2个,None等效False处理 | {{ value \| yesno:"yeah,no,maybe" }} |
| add | 加法。参数是负数就是减法 | 数字加{{ value \| add:"100" }}
列表合并{{ mylist \| add:newlist }} |
| divisibleby | 能否被整除 | {{ value \| divisibleby:"3" }} 能被3整 除返回True |
| addslashes | 在反斜杠、单引号或者双引号前面加上反斜杠 | {{ value \| addslashes }} |
| length | 返回变量的长度 | {% if my_list \| ength > 1 %} |
| default | 变量等价False则使用缺省值 | {{ value \| default:"nothing" }} |
| default_if_none | 变量为None使用缺省值 | {{ value \| default_if_none:"nothing" }} |
|
date |
格式化 date 或者 datetime 对象 | Y 2000 年
m 01~12 月
d 01~31 日
y 20年
n 1~12 月
j 1~31 日 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 {{ mydict.d.upper }} 调用字符串对象upper函数 {{ mydict.d|upper }} 字符串通过过滤器upper转成大写 {{ mydict.d.0 }} {{ mydict.d|first }} 0 表示[0 ],first是过滤器 {{ mydict.d|join:',' }} {{ mydict.b|yesno:'nonzero,zero' }} {{ mydict.a|add:'11' }} {{ mydict.a|add:mydict.a }} {{ mydict.a|add:'11' |divisibleby:3 }} {{ mydict.a|divisibleby:3 }} {{ mydict.c|length }} {% if mydict.c|length > 50 %} {{ mydict.c }} {% endif %} {{ mydict.date|date:'Y-n-j Y-m-d' }}
时间的格式字符查看:https://docs.djangoproject.com/en/2.2/ref/templates/builtins/#date
过滤器参考:https://docs.djangoproject.com/en/2.2/ref/templates/builtins/#
静态文件 使用模板不免用到静态文件,如何引入静态文件呢? 首先看看静态配置是什么?提供一个视图函数,如下
1 2 3 4 5 6 7 8 9 10 11 12 13 from django.shortcuts import renderfrom django.conf import settingsdef test_index (request) : print('~' * 30 ) static_info = list(filter(lambda x: x.find('STATIC' )!=-1 , dir(settings))) for x in static_info: print(x, getattr(settings, x)) print('~' * 30 ) return render(request, 'index.html' , {'users' :[1 , 2 , 'a' , 'b' ]})
1 2 3 4 5 6 7 8 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ STATICFILES_DIRS [] STATICFILES_FINDERS ['django.contrib.staticfiles.finders.FileSystemFinder' , 'django.contrib.staticfiles.finders.AppDirectoriesFinder' ]STATICFILES_STORAGE django.contrib.staticfiles.storage.StaticFilesStorage STATIC_ROOT None STATIC_URL /static/ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
官方参考:https://docs.djangoproject.com/en/3.2/ref/settings/#settings-staticfiles
Django 的静态需要在 settings.py 中配置
settings 中 INSTALLED_APPS 确保有 django.contrib.staticfiles
STATIC_URL = ‘/static/‘,URL访问的逻辑路径前缀
STATICFILES_DIRS = [],静态文件搜索路径
STATICFILES_FINDERS,搜索器
django.contrib.staticfiles.finders.FileSystemFinder搜索STATICFILES_DIRS的目录 django.contrib.staticfiles.finders.AppDirectoriesFinder搜索每一个app下面的static子目录
1 2 3 4 STATIC_URL = '/static/' STATICFILES_DIRS = [ BASE_DIR / 'static' ]
模板中使用如下
1 2 3 4 5 6 7 8 9 10 11 12 13 <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8"> <title>马哥教育Django首页</title> {% load static %} <!-- <script src="/static/js/jquery-3.6.0.min.js"></script>--> <script src="{% static 'js/jquery-3.6.0.min.js' %}"></script> </head> <body> <div id="app"></div> </body> </html>
https://docs.djangoproject.com/en/3.2/ref/templates/builtins/#static
js/jquery-3.6.0.min.js可以
BASE_DIR 下 static 下的 js/jquery-3.6.0.min.js
BASE_DIR 下 employee 下 static 下的 js/jquery-3.6.0.min.js
它们逻辑路径都是/static/js/jquery-3.6.0.min.js,搜索到匹配文件就立即返回,不在继续寻找。
模板习题 奇偶行列表输出 使用下面字典my_dict的c的列表,在模板网页中列表ul输出多行数据
要求奇偶行颜色不同 每行有行号(从1开始) 列表中所有数据都增大100
1 2 3 4 5 6 7 8 9 10 11 12 13 14 from django.http import HttpResponse, HttpRequestfrom django.shortcuts import renderdef index (request:HttpRequest) : """视图函数:请求进来返回响应""" my_dict = { 'a' :100 , 'b' :0 , 'c' :list(range(10 ,20 )), 'd' :'abc' , 'date' :datetime.datetime.now() } context = {'content' :'www.magedu.com' , 'my_dict' :my_dict} return render(request, 'index.html' , context)
模板代码如下
1 2 3 4 5 6 7 <ul> {%for line in my_dict.c %} <li style='color:{{forloop.counter|divisibleby:"2"|yesno:"red,blue"}}' > {{forloop.counter}} {{line|add:"100" }} </li> {%endfor%} </ul>
打印九九方阵 1 2 3 1 *1 =1 1 *2 =2 ... 1 *9 =9 ... 9 *1 =1 9 *2 =18. .. 9 *9 =81
使用把上面所有的数学表达式放到HTML表格对应的格子中
方法一:由视图函数提供数据 为了简单,直接准备一个排好输出顺序的列表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 from django.http import HttpResponse, HttpRequestfrom django.shortcuts import renderdef index (request:HttpRequest) : data = ['{}*{}={}' .format(j, i, j*i) for i in range(1 , 10 ) for j in range(1 , 10 )] return render(request, 'matrix.html' , {'data' :data}) <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <title>九九方阵</title> </head> <body> <table> {% for x in data %} {% if forloop.counter0|divisibleby:9 %}<tr>{% endif %} <td>{{x}}</td> {% if forloop.counter|divisibleby:9 %}</tr>{% endif %} {% endfor %} </table> </body> </html>
方法二:内建标签 widthratio
widthratio 本意是计算宽度比率的。
widthratio 用法: {% widthratio value max_value max_width %}
举例
{% widthratio 175 200 100 %}
value是175,max_value是200,max_width是100。 因此,175/200=0.875得到数值比值,再利用比率计算出宽度0.875*100=87.5,四舍五入到88
{% widthratio i 1 j as product %} 别名就可以在后面引用这个变量product
1 2 3 4 5 6 from django.http import HttpResponse, HttpRequestfrom django.shortcuts import renderdef index (request:HttpRequest) : context = {'loop' :list(range(1 ,10 ))} return render(request, 'matrix.html' , context)
matrix.html模板如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <title>九九方阵</title> </head> <body> 九九方阵表格打印--widthratio <hr> <table> {% for i in loop %} <tr style="background-color: {{forloop.counter|divisibleby:2|yesno:'#F0F0F0,#CCC'}}" > {% for j in loop %} <td> {{forloop.counter}}*{{forloop.parentloop.counter}}={% widthratio i 1 j %} </td> {% endfor %} </tr> {% endfor %} </table> <hr> </body> </html>
使用cycle标签来替换奇偶行变色代码。cycle标签,就是轮询所有其后给出的所有参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <title>九九方阵</title> </head> <body> 九九方阵表格打印--widthratio <hr> <table> {% for i in loop %} <tr style="background-color:{% cycle '#CCC' '#F0F0F0' '#FFF' %}" > {% for j in loop %} <td> {{forloop.counter}}*{{forloop.parentloop.counter}}={% widthratio forloop.counter 1 forloop.parentloop.counter %} </td> {% endfor %} </tr> {% endfor %} </table> <hr> </body> </html>
方法三:自定义filter 参考 https://docs.djangoproject.com/en/2.2/howto/custom-template-tags/#writing-custom-template-filters
构建自定义的模板的包和模块 在INSTALLED_APPS注册的应用user下构建templatetags包,一定要有 init .py 文件。
templatetags下构建自己的filter的模块,这里起名为 。其中代码如下
1 2 3 4 5 from django.template import Libraryregister = Library() @register.filter('multiply') # 使用装饰器注册 def multiply(a, b):# 只能1到2个参数 return int(a)*int(b)
定义模板
matrix.html模板中要 load myfilters 模块,使用已经注册的filter multiply,模板内容如下
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 <!DOCTYPE html> <html lang="en" > <head> <meta charset="UTF-8" > <title>九九方阵</title> </head> <body> 九九方阵表格打印 {% load myfilters %} <hr> <table> {% for i in loop %} <tr style="background-color: {{forloop.counter|divisibleby:2|yesno:'#F0F0F0,#CCC'}}" > {% for j in loop %} <td> {{forloop.counter}}*{{forloop.parentloop.counter}}= {{forloop.counter|multiply:forloop.parentloop.counter}} </td> {% endfor %} </tr> {% endfor %} </table> <hr> </body> </html>
使用模板
1 2 3 4 5 6 from django.http import HttpResponse, HttpRequestfrom django.shortcuts import renderdef index (request:HttpRequest) : context = {'loop' :list(range(1 ,10 ))} return render(request, 'matrix.html' , context)
注意加载自定义标签,需要重启Django服务。 模板文件可以放在应用的templates目录中,例如user/templates/multiply.html。
链接处理 可以通过模板url标签计算动态的url,应用在模板中。
https://docs.djangoproject.com/en/2.2/topics/http/urls/#reverse-resolution-of-urls
1 2 3 4 5 6 7 urlpatterns = [ path('login' , userlogin, name='user-login' ), ] <a href="{% url 'user-login' %}">登录</a>
在Django代码中可以使用reverse函数通过name获得url
1 2 fromdjango.urlsimportpath,reverse print(reverse('user-login' ))