第18天课程内容:
- 模板语言的常用标签
- 自定义过滤器与标签
- 模板语言之继承
- ORM的查询API
- 单表查询之双下划线
- 一对多的添加数据方法
mtv补充
遇到的问题:
1、 修改pycharm连接数据库改为mysql时遇到的问题
解决:
需要在项目的__init__.py文件中加
import pymysql
pymysql.install_as_MySQLdb()2、post提交表单数据时,默认会提示403 forbidden,需要在form表单处添加{% csrf_token %},render方法会为它渲染一个随机字符串一并提交给服务端做验证,就可以解决了;
也可以临时在settings里注释掉关于csrf的配置。(不推荐)
自定义标签和过滤器
django提供的标签与过滤器毕竟有限,更多的是需要自己去自定义标签和过滤器,以下是过程:
1、在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag.
2、在app中创建templatetags模块(模块名只能是templatetags)
3、创建任意 .py 文件,如:my_tags.py
from django import template
from django.utils.safestring import mark_safe
register = template.Library()
# 以上为固定格式
@register.filter #自定义过滤器
def multi(x,y):
return x*y
@register.simple_tag#自定义标签
def mult_tag(x,y,z):
return x**y**z4、在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py
{
%
load my_tags
%
}
5、使用simple_tag和filter(如何调用)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
{#自定义过滤器:在通过render方法渲染页面时只支持接受2个参数#}
{% load Mytag %}
<h2>{{ n1|multi:2 }}</h2>
<hr>
{#自定义标签:可以接受多个参数,通过render方法渲染页面时也没问题#}
<h1>{% mult_tag 2 6 2%}</h1>
</body>
</html>注意:filter可以用在if等语句后,simple_tag不可以
{
%
if
num|filter_multi:
30
>
100
%
}
{{ num|filter_multi:
30
}}
{
%
endif
%
}
总结:
tempalte(模板层): 功能: 为了更有逻辑的将数据库中的数据渲染到模板中 模板语法 变量 {{ }} 深度查询: 句点符 . 过滤器: {{var|filter_name:参数}} 标签 {% url %} {% for i in obj %} {% endfor %} {% if %} {%endif%} {% with %} {% csrf_token%} 自定义过滤器与标签: 定义流程: 1、在settings中的INSTALLED_APPS配置当前app,不然django无法找到自定义的simple_tag. 2、在app中创建templatetags模块(模块名只能是templatetags) 3、创建任意 .py 文件,如:my_tags.py: from django import template from django.utils.safestring import mark_safe register = template.Library() # register的名字是固定的,不可改变 @register.filter # 定义过滤器 def multi(x,y): return x*y @register.simple_tag # 定义标签 def multi_tag(x,y,z): return x*y*z 4、在使用自定义simple_tag和filter的html文件中导入之前创建的 my_tags.py {% load my_tags %} 5、使用simple_tag和filter(如何调用) 总结区别: 1、自定义filter只能接受两个参数 2、自定义simple_tag不能与if使用
模板继承 (extend)
Django模版引擎中最强大也是最复杂的部分就是模版继承了。模版继承可以让您创建一个基本的“骨架”模版,它包含您站点中的全部元素,并且可以定义能够被子模版覆盖的 blocks 。
通过从下面这个例子开始,可以容易的理解模版继承:
View Code这个模版,我们把它叫作
base.html
, 它定义了一个可以用于两列排版页面的简单HTML骨架。“子模版”的工作是用它们的内容填充空的blocks。在这个例子中,
block
标签定义了三个可以被子模版内容填充的block。block
告诉模版引擎: 子模版可能会覆盖掉模版中的这些位置。子模版可能看起来是这样的:
View Code
extends
标签是这里的关键。它告诉模版引擎,这个模版“继承”了另一个模版。当模版系统处理这个模版时,首先,它将定位父模版——在此例中,就是“base.html”。那时,模版引擎将注意到
base.html
中的三个block
标签,并用子模版中的内容来替换这些block。根据blog_entries
的值,输出可能看起来是这样的:View Code请注意,子模版并没有定义
sidebar
block,所以系统使用了父模版中的值。父模版的{% block %}
标签中的内容总是被用作备选内容(fallback)。这种方式使代码得到最大程度的复用,并且使得添加内容到共享的内容区域更加简单,例如,部分范围内的导航。
这里是使用继承的一些提示:
如果你在模版中使用
{% extends %}
标签,它必须是模版中的第一个标签。其他的任何情况下,模版继承都将无法工作。在base模版中设置越多的
{% block %}
标签越好。请记住,子模版不必定义全部父模版中的blocks,所以,你可以在大多数blocks中填充合理的默认内容,然后,只定义你需要的那一个。多一点钩子总比少一点好。如果你发现你自己在大量的模版中复制内容,那可能意味着你应该把内容移动到父模版中的一个
{% block %}
中。If you need to get the content of the block from the parent template, the
{{ block.super }}
variable will do the trick. This is useful if you want to add to the contents of a parent block instead of completely overriding it. Data inserted using{{ block.super }}
will not be automatically escaped (see the next section), since it was already escaped, if necessary, in the parent template.为了更好的可读性,你也可以给你的
{% endblock %}
标签一个 名字 。例如:{% block content %} ... {% endblock content %}
在大型模版中,这个方法帮你清楚的看到哪一个
{% block %}
标签被关闭了。最后,请注意您并不能在一个模版中定义多个相同名字的
block
标签。这个限制的存在是因为block标签的作用是“双向”的。这个意思是,block标签不仅提供了一个坑去填,它还在 _父模版_中定义了填坑的内容。如果在一个模版中有两个名字一样的block
标签,模版的父模版将不知道使用哪个block的内容。
ORM相关
ORM
映射关系:
表名 <-------> 类名 字段 <-------> 属性 表记录 <------->类实例对象创建表(建立模型)
实例:我们来假定下面这些概念,字段和关系
作者模型:一个作者有姓名和年龄。
作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
出版商模型:出版商有名称,所在城市以及email。
书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
模型建立如下:
class Author(models.Model): nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) age=models.IntegerField() # 与AuthorDetail建立一对一的关系 authorDetail=models.OneToOneField(to="AuthorDetail") class AuthorDetail(models.Model): nid = models.AutoField(primary_key=True) birthday=models.DateField() telephone=models.BigIntegerField() addr=models.CharField( max_length=64) class Publish(models.Model): nid = models.AutoField(primary_key=True) name=models.CharField( max_length=32) city=models.CharField( max_length=32) email=models.EmailField() class Book(models.Model): nid = models.AutoField(primary_key=True) title = models.CharField( max_length=32) publishDate=models.DateField() price=models.DecimalField(max_digits=5,decimal_places=2) keepNum=models.IntegerField()<br> commentNum=models.IntegerField() # 与Publish建立一对多的关系,外键字段建立在多的一方 publish=models.ForeignKey(to="Publish",to_field="nid") # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表 authors=models.ManyToManyField(to='Author')通过logging可以查看翻译成的sql语句
LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }注意事项:
1、 表的名称
myapp_modelName
,是根据 模型中的元数据自动生成的,也可以覆写为别的名称2、
id
字段是自动添加的3、对于外键字段,Django 会在字段名上添加"_id" 来创建数据库中的列名
4、这个例子中的
CREATE TABLE
SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。5、定义好模型之后,你需要告诉Django _使用_这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加
models.py
所在应用的名称。6、外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。
字段选项
每个字段有一些特有的参数,例如,CharField需要max_length参数来指定
VARCHAR
数据库字段的大小。还有一些适用于所有字段的通用参数。 这些参数在文档中有详细定义,这里我们只简单介绍一些最常用的:View Code(1)null 如果为True,Django 将用NULL 来在数据库中存储空值。 默认值是 False. (1)blank 如果为True,该字段允许不填。默认为False。 要注意,这与 null 不同。null纯粹是数据库范畴的,而 blank 是数据验证范畴的。 如果一个字段的blank=True,表单的验证将允许该字段是空值。如果字段的blank=False,该字段就是必填的。 (2)default 字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用。 (3)primary_key 如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True, Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键行为, 否则没必要设置任何一个字段的primary_key=True。 (4)unique 如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的 (5)choices 由二元组组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。 这是一个关于 choices 列表的例子: YEAR_IN_SCHOOL_CHOICES = ( ('FR', 'Freshman'), ('SO', 'Sophomore'), ('JR', 'Junior'), ('SR', 'Senior'), ('GR', 'Graduate'), ) 每个元组中的第一个元素,是存储在数据库中的值;第二个元素是在管理界面或 ModelChoiceField 中用作显示的内容。 在一个给定的 model 类的实例中,想得到某个 choices 字段的显示值,就调用 get_FOO_display 方法(这里的 FOO 就是 choices 字段的名称 )。例如: from django.db import models class Person(models.Model): SHIRT_SIZES = ( ('S', 'Small'), ('M', 'Medium'), ('L', 'Large'), ) name = models.CharField(max_length=60) shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES) >>> p = Person(name="Fred Flintstone", shirt_size="L") >>> p.save() >>> p.shirt_size 'L' >>> p.get_shirt_size_display() 'Large'一旦你建立好数据模型之后,django会自动生成一套数据库抽象的API,可以让你执行关于表记录的增删改查的操作。
查询表记录
查询相关API
<1> all(): 查询所有结果 <2> filter(**kwargs): 它包含了与所给筛选条件相匹配的对象 <3> get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个, 如果符合筛选条件的对象超过一个或者没有都会抛出错误。 <5> exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象 <4> values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列 <9> values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 <6> order_by(*field): 对查询结果排序 <7> reverse(): 对查询结果反向排序 <8> distinct(): 从返回结果中剔除重复纪录 <10> count(): 返回数据库中匹配查询(QuerySet)的对象数量。 <11> first(): 返回第一条记录 <12> last(): 返回最后一条记录 <13> exists(): 如果QuerySet包含数据,就返回True,否则返回False双下划线之单表查询
models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据 models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in models.Tb1.objects.filter(name__contains="ven") models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and startswith,istartswith, endswith, iendswith基于对象的跨表查询
一对多查询(Publish 与 Book)
正向查询(按字段:publish):
# 查询nid=1的书籍的出版社所在的城市<br> book_obj=Book.objects.get(nid=1)<br>print(book_obj.publish.city) # book_obj.publish 是nid=1的书籍对象关联的出版社对象反向查询(按表名:book_set):
# 查询 人民出版社出版过的所有书籍 publish=Publish.objects.get(name="人民出版社") book_list=publish.book_set.all() # 与人民出版社关联的所有书籍对象集合 for book_obj in book_list: print(book_obj.title)一对一查询(Author 与 AuthorDetail)
正向查询(按字段:authorDetail):
# 金瓶眉所有作者的名字以及手机号 book_obj=Book.objects.filter(title="金瓶眉").first() authors=book_obj.authors.all() for author_obj in authors: print(author_obj.name,author_obj.authorDetail.telephone)反向查询(按表名:book_set):
# 查询egon出过的所有书籍的名字 author_obj=Author.objects.get(name="egon") book_list=author_obj.book_set.all() #与egon作者相关的所有书籍 for book_obj in book_list: print(book_obj.title)注意:
你可以通过在 ForeignKey() 和ManyToManyField的定义中设置 related_name 的值来覆写 FOO_set 的名称。例如,如果 Article model 中做一下更改: publish = ForeignKey(Blog, related_name='bookList'),那么接下来就会如我们看到这般:
# 查询 人民出版社出版过的所有书籍 publish=Publish.objects.get(name="人民出版社") book_list=publish.bookList.all() # 与人民出版社关联的所有书籍对象集合基于双下划线的跨表查询
Django 还提供了一种直观而高效的方式在查询(lookups)中表示关联关系,它能自动确认 SQL JOIN 联系。要做跨关系查询,就使用两个下划线来链接模型(model)间关联字段的名称,直到最终链接到你想要的 model 为止。
# 练习1: 查询人民出版社出版过的所有书籍的名字与价格(一对多) # 正向查询 按字段:publish queryResult=Book.objects .filter(publish__name="人民出版社") .values_list("title","price") # 反向查询 按表名:book queryResult=Publish.objects .filter(name="人民出版社") .values_list("book__title","book__price") # 练习2: 查询egon出过的所有书籍的名字(多对多) # 正向查询 按字段:authors: queryResult=Book.objects .filter(authors__name="yuan") .values_list("title") # 反向查询 按表名:book queryResult=Author.objects .filter(name="yuan") .values_list("book__title","book__price") # 练习3: 查询人民出版社出版过的所有书籍的名字以及作者的姓名 # 正向查询 queryResult=Book.objects .filter(publish__name="人民出版社") .values_list("title","authors__name") # 反向查询 queryResult=Publish.objects .filter(name="人民出版社") .values_list("book__title","book__authors__age","book__authors__name") # 练习4: 手机号以151开头的作者出版过的所有书籍名称以及出版社名称 queryResult=Book.objects .filter(authors__authorDetail__telephone__regex="151") .values_list("title","publish__name")注意:
反向查询时,如果定义了related_name ,则用related_name替换表名,例如: publish = ForeignKey(Blog, related_name='bookList'):
# 练习1: 查询人民出版社出版过的所有书籍的名字与价格(一对多) # 反向查询 不再按表名:book,而是related_name:bookList queryResult=Publish.objects .filter(name="人民出版社") .values_list("bookList__title","bookList__price")