Models
数据库的配置
1 django默认支持sqlite,mysql, oracle,postgresql数据库。
<1> sqlite
django默认使用sqlite的数据库,默认自带sqlite的数据库驱动
引擎名称:django.db.backends.sqlite3
<2> mysql
引擎名称:django.db.backends.mysql
2 mysql驱动程序
MySQLdb(mysql python)
mysqlclient
MySQL
PyMySQL(纯python的mysql驱动程序)
3 在django的项目中会默认使用sqlite数据库,在settings里有如下设置:
如果我们想要更改数据库,需要修改如下:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME':'django_com', 'USER':'root', 'PASSWORD':'', } }
注意:NAME即数据库的名字,在mysql连接前该数据库必须已经创建,而上面的sqlite数据库下的db.sqlite3则是项目自动创建
USER和PASSWORD分别是数据库的用户名和密码。
设置完后,再启动我们的Django项目前,我们需要激活我们的mysql。
然后,启动项目,会报错:no module named MySQLdb
这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb对于py3有很大问题,所以我们需要的驱动是PyMySQL
所以,我们只需要找到项目名文件下的__init__,在里面写入:
1
2
|
import
pymysql
pymysql.install_as_MySQLdb()
|
问题解决!
ORM(对象关系映射)
用于实现面向对象编程语言里不同类型系统的数据之间的转换,换言之,就是用面向对象的方式去操作数据库的创建表以及增删改查等操作。
优点: 1 ORM使得我们的通用数据库交互变得简单易行,而且完全不用考虑该死的SQL语句。快速开发,由此而来。
2 可以避免一些新手程序猿写sql语句带来的性能问题。
比如 我们查询User表中的所有字段:
新手可能会用select * from auth_user,这样会因为多了一个匹配动作而影响效率的。
缺点:1 性能有所牺牲,不过现在的各种ORM框架都在尝试各种方法,比如缓存,延迟加载登来减轻这个问题。效果很显著。
2 对于个别复杂查询,ORM仍然力不从心,为了解决这个问题,ORM一般也支持写raw sql。
下面要开始学习Django ORM语法了,为了更好的理解,我们来做一个基本的 书籍/作者/出版商 数据库结构。 我们这样做是因为 这是一个众所周知的例子,很多SQL有关的书籍也常用这个举例。
表(模型)的创建:
实例:我们来假定下面这些概念,字段和关系
作者模型:一个作者有姓名。
作者详细模型:把作者的详情放到详情表,包含性别,email地址和出生日期,作者详情模型和作者模型之间是一对一的关系(one-to-one)(类似于每个人和他的身份证之间的关系),在大多数情况下我们没有必要将他们拆分成两张表,这里只是引出一对一的概念。
出版商模型:出版商有名称,地址,所在城市,省,国家和网站。
书籍模型:书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many),一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many),也被称作外键。
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
|
from
django.db
import
models<br>
class
Publisher(models.Model):
name
=
models.CharField(max_length
=
30
, verbose_name
=
"名称"
)
address
=
models.CharField(
"地址"
, max_length
=
50
)
city
=
models.CharField(
'城市'
,max_length
=
60
)
state_province
=
models.CharField(max_length
=
30
)
country
=
models.CharField(max_length
=
50
)
website
=
models.URLField()
class
Meta:
verbose_name
=
'出版商'
verbose_name_plural
=
verbose_name
def
__str__(
self
):
return
self
.name
class
Author(models.Model):
name
=
models.CharField(max_length
=
30
)
def
__str__(
self
):
return
self
.name
class
AuthorDetail(models.Model):
sex
=
models.BooleanField(max_length
=
1
, choices
=
((
0
,
'男'
),(
1
,
'女'
),))
email
=
models.EmailField()
address
=
models.CharField(max_length
=
50
)
birthday
=
models.DateField()
author
=
models.OneToOneField(Author)
class
Book(models.Model):
title
=
models.CharField(max_length
=
100
)
authors
=
models.ManyToManyField(Author)
publisher
=
models.ForeignKey(Publisher)
publication_date
=
models.DateField()
price
=
models.DecimalField(max_digits
=
5
,decimal_places
=
2
,default
=
10
)
def
__str__(
self
):
return
self
.title
|
注意:记得在settings里的INSTALLED_APPS中加入'app01',然后再同步数据库。
分析代码:
1 每个数据模型都是django.db.models.Model的子类,它的父类Model包含了所有必要的和数据库交互的方法。并提供了一个简介漂亮的定义数据库字段的语法。
2 每个模型相当于单个数据库表(多对多关系例外,会多生成一张关系表),每个属性也是这个表中的字段。属性名就是字段名,它的类型(例如CharField)相当于数据库的字段类型(例如varchar)。大家可以留意下其它的类型都和数据库里的什么字段对应。
3 模型之间的三种关系:一对一,一对多,多对多。
一对一:实质就是在主外键(author_id就是foreign key)的关系基础上,给外键加了一个UNIQUE=True的属性;
一对多:就是主外键关系;
多对多:彼此一对多,自动创建第三张表
4 模型常用的字段类型以及参数
# AutoField # 一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段; 如果你不指定主键的话,系统会自动添加一个主键字段到你的 model.(参阅 _自动主键字段) # BooleanField # A true/false field. admin 用 checkbox 来表示此类字段. # CharField # 字符串字段, 用于较短的字符串. # # 如果要保存大量文本, 使用 TextField. # # admin 用一个 <input type="text"> 来表示此类字段 (单行输入). # # CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数. # # CommaSeparatedIntegerField # 用于存放逗号分隔的整数值. 类似 CharField, 必须要有 maxlength 参数. # DateField # 一个日期字段. 共有下列额外的可选参数: # # Argument 描述 # auto_now 当对象被保存时,自动将该字段的值设置为当前时间.通常用于表示 "last-modified" 时间戳. # auto_now_add 当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间. # admin 用一个文本框 <input type="text"> 来表示该字段数据(附带一个 JavaScript 日历和一个"Today"快键. # # DateTimeField # 一个日期时间字段. 类似 DateField 支持同样的附加选项. # admin 用两上文本框 <input type="text"> 表示该字段顺序(附带JavaScript shortcuts). # # EmailField # 一个带有检查 Email 合法性的 CharField,不接受 maxlength 参数. # FileField # 一个文件上传字段. # # 要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径. 这个路径必须包含 strftime formatting, 该格式将被上载文件的 date/time 替换(so that uploaded files don't fill up the given directory). # # admin 用一个``<input type="file">``部件表示该字段保存的数据(一个文件上传部件) . # # 在一个 model 中使用 FileField 或 ImageField 需要以下步骤: # # 在你的 settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件. (出于性能考虑,这些文件并不保存到数据库.) 定义 MEDIA_URL 作为该目录的公共 URL. 要确保该目录对 WEB 服务器用户帐号是可写的. # 在你的 model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django 使用 MEDIA_ROOT 的哪个子目录保存上传文件. # 你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT). 出于习惯你一定很想使用 Django 提供的 get_<fieldname>_url 函数.举例来说,如果你的 ImageField 叫作 mug_shot, 你就可以在模板中以 {{ object.get_mug_shot_url }} 这样的方式得到图像的绝对路径. # FilePathField # 可选项目为某个特定目录下的文件名. 支持三个特殊的参数, 其中第一个是必须提供的. # # 参数 描述 # path 必需参数. 一个目录的绝对文件系统路径. FilePathField 据此得到可选项目. Example: "/home/images". # match 可选参数. 一个正则表达式, 作为一个字符串, FilePathField 将使用它过滤文件名. 注意这个正则表达式只会应用到 base filename 而不是路径全名. Example: "foo.*\.txt^", 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif. # recursive 可选参数.要么 True 要么 False. 默认值是 False. 是否包括 path 下面的全部子目录. # 这三个参数可以同时使用. # # 我已经告诉过你 match 仅应用于 base filename, 而不是路径全名. 那么,这个例子: # # FilePathField(path="/home/images", match="foo.*", recursive=True) # ...会匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif # # FloatField # 一个浮点数. 必须 提供两个 参数: # # 参数 描述 # max_digits 总位数(不包括小数点和符号) # decimal_places 小数位数 # 举例来说, 要保存最大值为 999 (小数点后保存2位),你要这样定义字段: # # models.FloatField(..., max_digits=5, decimal_places=2) # 要保存最大值一百万(小数点后保存10位)的话,你要这样定义: # # models.FloatField(..., max_digits=19, decimal_places=10) # admin 用一个文本框(<input type="text">)表示该字段保存的数据. # # ImageField # 类似 FileField, 不过要校验上传对象是否是一个合法图片.它有两个可选参数:height_field 和 width_field,如果提供这两个参数,则图片将按提供的高度和宽度规格保存. # # 该字段要求 Python Imaging Library. # # IntegerField # 用于保存一个整数. # # admin 用一个``<input type="text">``表示该字段保存的数据(一个单行编辑框) # # IPAddressField # 一个字符串形式的 IP 地址, (i.e. "24.124.1.30"). # # admin 用一个``<input type="text">``表示该字段保存的数据(一个单行编辑框) # # NullBooleanField # 类似 BooleanField, 不过允许 NULL 作为其中一个选项. 推荐使用这个字段而不要用 BooleanField 加 null=True 选项. # # admin 用一个选择框 <select> (三个可选择的值: "Unknown", "Yes" 和 "No" ) 来表示这种字段数据. # # PhoneNumberField # 一个带有合法美国风格电话号码校验的 CharField``(格式: ``XXX-XXX-XXXX). # PositiveIntegerField # 类似 IntegerField, 但取值范围为非负整数(这个字段应该是允许0值的....所以字段名字取得不太好,无符号整数就对了嘛). # PositiveSmallIntegerField # 类似 PositiveIntegerField, 取值范围较小(数据库相关) # SlugField # "Slug" 是一个报纸术语. slug 是某个东西的小小标记(短签), 只包含字母,数字,下划线和连字符.它们通常用于URLs. # # 若你使用 Django 开发版本,你可以指定 maxlength. 若 maxlength 未指定, Django 会使用默认长度: 50. 在以前的 Django 版本,没有任何办法改变 50 这个长度. # # 这暗示了 db_index=True. # # 它接受一个额外的参数: prepopulate_from, which is a list of fields from which to auto-populate the slug, via JavaScript, in the object's admin form: # # models.SlugField(prepopulate_from=("pre_name", "name")) # prepopulate_from 不接受 DateTimeFields. # # admin 用一个``<input type="text">``表示 SlugField 字段数据(一个单行编辑框) # # SmallIntegerField # 类似 IntegerField, 不过只允许某个取值范围内的整数.(依赖数据库) # # TextField # 一个容量很大的文本字段. # # admin 用一个 <textarea> (文本区域)表示该字段数据.(一个多行编辑框). # # TimeField # A time. Accepts the same auto-population options as DateField 和 DateTimeField. # # admin 用一个 <input type="text"> 文本框表示该字段保存的数据(附加一些JavaScript shortcuts). # # URLField # 用于保存 URL. 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在(即URL是否被有效装入且没有返回404响应). # # admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框) # # USStateField # 一个两字母的美国州名缩写. # # admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框) # # XMLField # 一个校验值是否为合法XML的 TextField,必须提供参数: schema_path, 它是一个用来校验文本的 RelaxNG schema 的文件系统路径.
表的操作(增删改查):
1 增(create save)
>>> from app01.models import * >>> Author.objects.create(name='Alvin') <Author: Alvin> >>> AuthorDetail.objects.create(sex=False,email='916852314@qq.com',address='bejing',birthday='1995-3-16',author_id=1) <AuthorDetail: AuthorDetail object> >>> pub=Publisher() >>> pub.name='河大出版社' >>> pub.address='保定' >>> pub.city='保定' >>> pub.state_province='河北' >>> pub.country='China' >>> pub.website='http://www.beida.com' >>> pub.save()
注意:如果每次创建一个对象,想显示对应的raw sql,需要在settings加上日志记录部分:
LOGGING = { # 'version': 1, # 'disable_existing_loggers': False, # 'handlers': { # 'console':{ # 'level':'DEBUG', # 'class':'logging.StreamHandler', # }, # }, # 'loggers': { # 'django.db.backends': { # 'handlers': ['console'], # 'propagate': True, # 'level':'DEBUG', # }, # } # }
那么如何创建存在一对多或多对多关系的一本书的信息呢?
>>> Book.objects.create(title='php',publisher=pub,publication_date='2017-7-7') <Book: php> >>> author1=Author.objects.get(id=1) >>> author2=Author.objects.get(name='alvin') >>> book=Book.objects.get(id=1) >>> book.authors.add(author1,author1) >>> book.authors.add(author1,author2)
2 改:
update和save方法
实例:
注意:<1> 不能用get的原因是:update是QuerySet对象的方法,get返回的是一个model对象,它没有update方法,而filter返回的是一个QuerySet对象(filter里面的条件可能有多个条件符合,比如name='alvin',可能有两个name='alvin'的行数据)。
<2> filter:
1
2
|
>>> Publisher.objects.
filter
(name__contains
=
"press"
)
[ <Publisher: Apress>]
|
在 name 和 contains 之间有双下划线。和Python一样,Django也使用双下划线来表明会进行一些魔术般的操作。这里,contains部分会被Django翻译成LIKE语句:
1
2
3
|
SELECT
id
, name, address, city, state_province, country, website
FROM books_publisher
WHERE name LIKE
'%press%'
;
|
其他的一些查找类型有:icontains(大小写不敏感的LIKE),startswith和endswith, 还有range
<3> 在“插入和更新数据”小节中,我们有提到模型的save()方法,这个方法会更新一行里的所有列。 而某些情况下,我们只需要更新行里的某几列。例如说我们现在想要将Apress Publisher的名称由原来的”Apress”更改为”Apress Publishing”。若使用save()方法,如:
1
2
3
|
>>> p
=
Publisher.objects.get(name
=
'Apress'
)
>>> p.name
=
'Apress Publishing'
>>> p.save()
|
这等同于如下SQL语句:
1
2
3
4
5
6
7
8
9
10
11
12
|
SELECT
id
, name, address, city, state_province, country, website
FROM books_publisher
WHERE name
=
'Apress'
;
UPDATE books_publisher
SET
name
=
'Apress Publishing'
,
address
=
'2855 Telegraph Ave.'
,
city
=
'Berkeley'
,
state_province
=
'CA'
,
country
=
'U.S.A.'
,
website
=
'http://www.apress.com'
WHERE
id
=
52
;
|
注意在这里我们假设Apress的ID为52)
在这个例子里我们可以看到Django的save()方法更新了不仅仅是name列的值,还有更新了所有的列。 若name以外的列有可能会被其他的进程所改动的情况下,只更改name列显然是更加明智的。 更改某一指定的列,我们可以调用结果集(QuerySet)对象的update()方法: 示例如下:
1
|
>>> Publisher.objects.
filter
(
id
=
52
).update(name
=
'Apress Publishing'
)
|
与之等同的SQL语句变得更高效,并且不会引起竞态条件。
1
2
3
|
UPDATE books_publisher
SET
name
=
'Apress Publishing'
WHERE
id
=
52
;
|
update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录。 以下示例演示如何将所有Publisher的country字段值由’U.S.A’更改为’USA’:
1
2
|
>>> Publisher.objects.
all
().update(country
=
'USA'
)
2
|
update()方法会返回一个整型数值,表示受影响的记录条数。 在上面的例子中,这个值是2。
多对多关系的构建
我们当然可以直接在第三张表里插入数据来构建,那数据库如果没有第三张表还有没有别的办法呢?
# db: 两本书Python和Go 两个作者;alex alvin ret=models.Book.objects.get(id=1) obj_authors=models.Author.objects.all() ret.authors.add(*obj_authors)#ret.authors.add(*[1,2,3]) ret.authors.remove(1) # 反过来呢? ret=models.Author.objects.get(id=2) obj_book=models.Book.objects.get(id=1) ret.book_set.add(obj_book)
3 查
>>> Publisher.objects.all()
[<Publisher: 中国机械出版社>, <Publisher: American publisher>]
注意:
这相当于这个SQL语句:
1
2
|
SELECT
id
, name, address, city, state_province, country, website
FROM books_publisher;
|
注意到Django在选择所有数据时并没有使用 SELECT* ,而是显式列出了所有字段。
惰性机制:
所谓惰性机制:Publisher.objects.all()只是返回了一个QuerySet(查询结果集对象),并不会马上执行sql,而是当调用QuerySet的时候才执行。
QuerySet特点:
1 可迭代的
2 可切片
3.1 查询API
查询相关API: # # <1>get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。 # # <2>all(): 查询所有结果 # # <3>filter(**kwargs): 它包含了与所给筛选条件相匹配的对象 # # <4>exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象 # # <5>order_by(*field): 对查询结果排序 # # <6>reverse(): 对查询结果反向排序 # # <7>distinct(): 从返回结果中剔除重复纪录 # # <8>values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一 # # 系列 model的实例化对象,而是一个可迭代的字典序列 # # <9>values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 # # <10>count(): 返回数据库中匹配查询(QuerySet)的对象数量。 # # <11>first(): 返回第一条记录,等价于[:1][0] # # <12>last(): 返回最后一条记录,等价于[::1][0] # # <13>exists(): 如果QuerySet包含数据,就返回True,否则返回False。
实例测试:
1 查询id为2的书籍信息,并只显示书籍名称和出版日期
>>> Book.objects.filter(id=2).values("title","publication_date") [{'title': 'python Gone', 'publication_date': datetime.date(2019, 5, 24)}]
2 查询所有的出版信息,并按id降序排列,并尝试使用reverse方法进行反向排序。
>>> Publisher.objects.all().order_by("id") [<Publisher: 中国机械出版社>, <Publisher: Boo>, <Publisher: 人民出版社>] >>> Publisher.objects.all().order_by("id").reverse() [<Publisher: 人民出版社>, <Publisher: Boo>, <Publisher: 中国机械出版社>] >>> Publisher.objects.all().order_by("-id") [<Publisher: 人民出版社>, <Publisher: Boo>, <Publisher: 中国机械出版社>]
3 查询出版社所在的城市信息,城市信息不要重复
>>> Publisher.objects.all().values("city").distinct() [{'city': '北京'}, {'city': 'beijing'}]
4 查询城市是北京的出版社,尝试使用exclude方法
>>> Publisher.objects.all().filter(city='beijing') [<Publisher: Boo>]
5 查询男作者的数量
>>> AuthorDetail.objects.filter(sex=0).count()
1
总结:
# 获取个数 # # models.Tb1.objects.filter(name='seven').count() # 大于,小于 # # models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值 # models.Tb1.objects.filter(id__lt=10) # 获取id小于10的值 # models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 # in # # models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据 # models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in # contains # # models.Tb1.objects.filter(name__contains="ven") # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 # models.Tb1.objects.exclude(name__icontains="ven") # range # # models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and # 其他类似 # # startswith,istartswith, endswith, iendswith, # order by # # models.Tb1.objects.filter(name='seven').order_by('id') # asc # models.Tb1.objects.filter(name='seven').order_by('-id') # desc # limit 、offset # # models.Tb1.objects.all()[10:20] # group by from django.db.models import Count, Min, Max, Sum # models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num')) # SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"
3.2 多表关联查询(******)
from django.shortcuts import render,HttpResponse # Create your views here. from app01 import models def hello(req): #db存储:Python和Go两本书同时绑定了人大一家出版社 # id=1的书即Python有两个作者:alex,alvin # id=2的书即Go有一个作者: alex ret=models.Book.objects.all() print(ret)#[<Book: Book object>, <Book: Book object>] print(type(ret))#<class 'django.db.models.query.QuerySet'> ret2=models.Book.objects.first() print(type(ret2))#<class 'app01.models.Book'> #正向查找(对象) print(ret2.title) #Python print(ret2.publisher) # Publisher object print(ret2.publisher.name)# 人大出版社 print(ret2.publisher.city)# 北京 #反向查找(对象) ret3=models.Publisher.objects.first() ret3_bookobj=ret3.book_set.all() print(ret3_bookobj)#[<Book: Book object>, <Book: Book object>] print(type(ret3_bookobj))#<class 'django.db.models.query.QuerySet'> #####****************######### ret4=models.Book.objects.filter(title='Python').values('id') print(type(ret4)) print(ret4)#[{'id': 1}] #正向查找(条件) ret5=models.Book.objects.filter(title='Python').values('publisher__city') print(ret5)#[{'publisher__city': '北京'}] ret6=models.Book.objects.filter(publisher__address='北京').values('publisher__name') print(ret6)#[{'publisher__name': '人大出版社'}, {'publisher__name': '人大出版社'}] #反向查找(条件) ret7=models.Publisher.objects.filter(book__title='Python').values('name') print(ret7)#[{'name': '人大出版社'}] 注意,book__title中的book就是Publisher的关联表名 ret8=models.Publisher.objects.filter(book__title='Python').values('book__authors') print(ret8)#[{'book__authors': 1}, {'book__authors': 2}] # 多对多关系的 ret9=models.Author.objects.filter(book__title='Python').values('name') print(ret9)#[{'name': 'alex'}, {'name': 'alvin'}] return HttpResponse("ok")
3.3 聚合查询和分组查询
1 aggregate(*args,**kwargs):通过对QuerySet进行计算,返回一个聚合值的字典。aggregate()中每一个参数都指定一个包含在字典中的返回值。用于聚合查询。
2 annotate(*args,**kwargs):可以为QuerySet每一个对象添加注解。可以通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),用于分组查询
聚合函数(Aggregation Functions) 所在位置:django.db.models
1 Avg: 返回所给字段的平均值
2 Count: 根据所给的关联字段返回被关联的model的数量
3 Max:返回所给字段的最大值
4 Sum:计算所给字段的总和
实例:
1 查询中国邮电大学出版社出版了多少本书?
2 查询alex出的书总价格
3 查询各个作者出的书的总价格
这里就涉及到分组了,分组条件是authors__name,
4 查询各个出版社最便宜的书价是多少
3.4
仅仅靠单一的关键字参数查询已经很难满足查询要求。此时Django为我们提供了F和Q查询:
# F 使用查询条件的值 # # from django.db.models import F # models.Tb1.objects.update(num=F('num')+1) # Q 构建搜索条件 from django.db.models import Q # con = Q() # # q1 = Q() # q1.connector = 'OR' # q1.children.append(('id', 1)) # q1.children.append(('id', 10)) # q1.children.append(('id', 9)) # # q2 = Q() # q2.connector = 'OR' # q2.children.append(('c1', 1)) # q2.children.append(('c1', 10)) # q2.children.append(('c1', 9)) # # con.add(q1, 'AND') # con.add(q2, 'AND') # # models.Tb1.objects.filter(con) # # from django.db import connection # cursor = connection.cursor() # cursor.execute("""SELECT * from tb where name = %s""", ['Lennon']) # row = cursor.fetchone()
from django.db.models import Q #1 Q对象(django.db.models.Q)可以对关键字参数进行封装,从而更好地应用多个查询 q1=models.Book.objects.filter(Q(title__startswith='P')).all() print(q1)#[<Book: Python>, <Book: Perl>] # 2、可以组合使用&,|操作符,当一个操作符是用于两个Q的对象,它产生一个新的Q对象。 Q(title__startswith='P') | Q(title__startswith='J') # 3、Q对象可以用~操作符放在前面表示否定,也可允许否定与不否定形式的组合 Q(title__startswith='P') | ~Q(pub_date__year=2005) # 4、应用范围: # # Each lookup function that takes keyword-arguments (e.g. filter(), # exclude(), get()) can also be passed one or more Q objects as # positional (not-named) arguments. If you provide multiple Q object # arguments to a lookup function, the arguments will be “AND”ed # together. For example: Book.objects.get( Q(title__startswith='P'), Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) ) ''' ... roughly translates into the SQL: SELECT * from polls WHERE question LIKE 'P%' AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06') ''' # 5、Q对象可以与关键字参数查询一起使用,不过一定要把Q对象放在关键字参数查询的前面。 # 正确: Book.objects.get( Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)), title__startswith='P') # 错误: Book.objects.get( question__startswith='P', Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
4 删除
1
2
|
>>> Book.objects.
filter
(
id
=
1
).delete()
(
3
, {
'app01.Book_authors'
:
2
,
'app01.Book'
:
1
})
|
我们表面上删除了一条信息,实际却删除了三条,因为我们删除的这本书在Book_authors表中有两条相关信息,这种删除方式就是django默认的级联删除。
Template基础
你可能已经注意到我们在例子视图中返回文本的方式有点特别。 也就是说,HTML被直接硬编码在 Python 代码之中。
1
2
3
4
|
def
current_datetime(request):
now
=
datetime.datetime.now()
html
=
"<html><body>It is now %s.</body></html>"
%
now
return
HttpResponse(html)
|
尽管这种技术便于解释视图是如何工作的,但直接将HTML硬编码到你的视图里却并不是一个好主意。 让我们来看一下为什么:
-
对页面设计进行的任何改变都必须对 Python 代码进行相应的修改。 站点设计的修改往往比底层 Python 代码的修改要频繁得多,因此如果可以在不进行 Python 代码修改的情况下变更设计,那将会方便得多。
-
Python 代码编写和 HTML 设计是两项不同的工作,大多数专业的网站开发环境都将他们分配给不同的人员(甚至不同部门)来完成。 设计者和HTML/CSS的编码人员不应该被要求去编辑Python的代码来完成他们的工作。
-
程序员编写 Python代码和设计人员制作模板两项工作同时进行的效率是最高的,远胜于让一个人等待另一个人完成对某个既包含 Python又包含 HTML 的文件的编辑工作。
基于这些原因,将页面的设计和Python的代码分离开会更干净简洁更容易维护。 我们可以使用 Django的 模板系统 (Template System)来实现这种模式,这就是本章要具体讨论的问题。
Django 模版基本语法
1
2
3
4
5
|
>>>
from
django.template
import
Context, Template
>>> t
=
Template(
'My name is {{ name }}.'
)
>>> c
=
Context({
'name'
:
'Stephane'
})
>>> t.render(c)
u
'My name is Stephane.'
|
同一模板,多个上下文
一旦有了 模板 对象,你就可以通过它渲染多个context, 例如:
1
2
3
4
5
6
7
8
9
|
>>>
from
django.template
import
Template, Context
>>> t
=
Template(
'Hello, {{ name }}'
)
>>>
print
t.render(Context({
'name'
:
'John'
}))
Hello, John
>>>
print
t.render(Context({
'name'
:
'Julie'
}))
Hello, Julie
>>>
print
t.render(Context({
'name'
:
'Pat'
}))
Hello, Pat
|
无论何时我们都可以像这样使用同一模板源渲染多个context,只进行 一次模板创建然后多次调用render()方法渲染会更为高效:
1
2
3
4
5
6
7
8
9
|
# Low
for
name
in
(
'John'
,
'Julie'
,
'Pat'
):
t
=
Template(
'Hello, {{ name }}'
)
print
t.render(Context({
'name'
: name}))
# Good
t
=
Template(
'Hello, {{ name }}'
)
for
name
in
(
'John'
,
'Julie'
,
'Pat'
):
print
t.render(Context({
'name'
: name}))
|
Django 模板解析非常快捷。 大部分的解析工作都是在后台通过对简短正则表达式一次性调用来完成。 这和基于 XML 的模板引擎形成鲜明对比,那些引擎承担了 XML 解析器的开销,且往往比 Django 模板渲染引擎要慢上几个数量级。
from django.shortcuts import render,HttpResponse from django.template.loader import get_template #记得导入 # Create your views here. import datetime from django.template import Template,Context # def current_time(req): #原始的视图函数 # now=datetime.datetime.now() # html="<html><body>现在时刻:<h1>%s.</h1></body></html>" %now # return HttpResponse(html) # def current_time(req): #django模板修改的视图函数 # now=datetime.datetime.now() # t=Template('<html><body>现在时刻是:<h1 style="color:red">{{current_date}}</h1></body></html>') #t=get_template('current_datetime.html') # c=Context({'current_date':now}) # html=t.render(c) # return HttpResponse(html) #另一种写法(推荐) def current_time(req): now=datetime.datetime.now() return render(req, 'current_datetime.html', {'current_date':now})
深度变量的查找
在到目前为止的例子中,我们通过 context 传递的简单参数值主要是字符串,然而,模板系统能够非常简洁地处理更加复杂的数据结构,例如list、dictionary和自定义的对象。
在 Django 模板中遍历复杂数据结构的关键是句点字符 (.)。
最好是用几个例子来说明一下。 比如,假设你要向模板传递一个 Python 字典。 要通过字典键访问该字典的值,可使用一个句点:
1
2
3
4
5
6
|
>>>
from
django.template
import
Template, Context
>>> person
=
{
'name'
:
'Sally'
,
'age'
:
'43'
}
>>> t
=
Template(
'{{ person.name }} is {{ person.age }} years old.'
)
>>> c
=
Context({
'person'
: person})
>>> t.render(c)
u
'Sally is 43 years old.'
|
同样,也可以通过句点来访问对象的属性。 比方说, Python 的 datetime.date 对象有 year 、 month 和 day 几个属性,你同样可以在模板中使用句点来访问这些属性:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
>>>
from
django.template
import
Template, Context
>>>
import
datetime
>>> d
=
datetime.date(
1993
,
5
,
2
)
>>> d.year
1993
>>> d.month
5
>>> d.day
2
>>> t
=
Template(
'The month is {{ date.month }} and the year is {{ date.year }}.'
)
>>> c
=
Context({
'date'
: d})
>>> t.render(c)
u
'The month is 5 and the year is 1993.'
|
这个例子使用了一个自定义的类,演示了通过实例变量加一点(dots)来访问它的属性,这个方法适用于任意的对象。
1
2
3
4
5
6
7
8
|
>>>
from
django.template
import
Template, Context
>>>
class
Person(
object
):
...
def
__init__(
self
, first_name, last_name):
...
self
.first_name,
self
.last_name
=
first_name, last_name
>>> t
=
Template(
'Hello, {{ person.first_name }} {{ person.last_name }}.'
)
>>> c
=
Context({
'person'
: Person(
'John'
,
'Smith'
)})
>>> t.render(c)
u
'Hello, John Smith.'
|
点语法也可以用来引用对象的* 方法*。 例如,每个 Python 字符串都有 upper() 和 isdigit() 方法,你在模板中可以使用同样的句点语法来调用它们:
1
2
3
4
5
6
|
>>>
from
django.template
import
Template, Context
>>> t
=
Template(
'{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}'
)
>>> t.render(Context({
'var'
:
'hello'
}))
u
'hello -- HELLO -- False'
>>> t.render(Context({
'var'
:
'123'
}))
u
'123 -- 123 -- True'
|
注意这里调用方法时并* 没有* 使用圆括号 而且也无法给该方法传递参数;你只能调用不需参数的方法。 (我们将在本章稍后部分解释该设计观。)
最后,句点也可用于访问列表索引,例如:
1
2
3
4
5
|
>>>
from
django.template
import
Template, Context
>>> t
=
Template(
'Item 2 is {{ items.2 }}.'
)
>>> c
=
Context({
'items'
: [
'apples'
,
'bananas'
,
'carrots'
]})
>>> t.render(c)
u
'Item 2 is carrots.'
|
include 模板标签
在讲解了模板加载机制之后,我们再介绍一个利用该机制的内建模板标签: {% include %} 。该标签允许在(模板中)包含其它的模板的内容。 标签的参数是所要包含的模板名称,可以是一个变量,也可以是用单/双引号硬编码的字符串。 每当在多个模板中出现相同的代码时,就应该考虑是否要使用 {% include %} 来减少重复。
extend(继承)模板标签
到目前为止,我们的模板范例都只是些零星的 HTML 片段,但在实际应用中,你将用 Django 模板系统来创建整个 HTML 页面。 这就带来一个常见的 Web 开发问题: 在整个网站中,如何减少共用页面区域(比如站点导航)所引起的重复和冗余代码?
解决该问题的传统做法是使用 服务器端的 includes ,你可以在 HTML 页面中使用该指令将一个网页嵌入到另一个中。 事实上, Django 通过刚才讲述的 {% include %} 支持了这种方法。 但是用 Django 解决此类问题的首选方法是使用更加优雅的策略—— 模板继承 。
本质上来说,模板继承就是先构造一个基础框架模板,而后在其子模板中对它所包含站点公用部分和定义块进行重载。
让我们通过修改 current_datetime.html 文件,为 current_datetime 创建一个更加完整的模板来体会一下这种做法:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01//EN"
>
<html lang
=
"en"
>
<head>
<title>The current time<
/
title>
<
/
head>
<body>
<h1>My helpful timestamp site<
/
h1>
<p>It
is
now {{ current_date }}.<
/
p>
<hr>
<p>Thanks
for
visiting my site.<
/
p>
<
/
body>
<
/
html>
|
这看起来很棒,但如果我们要为 hours_ahead 视图创建另一个模板会发生什么事情呢?
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01//EN"
>
<html lang
=
"en"
>
<head>
<title>Future time<
/
title>
<
/
head>
<body>
<h1>My helpful timestamp site<
/
h1>
<p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.<
/
p>
<hr>
<p>Thanks
for
visiting my site.<
/
p>
<
/
body>
<
/
html>
|
很明显,我们刚才重复了大量的 HTML 代码。 想象一下,如果有一个更典型的网站,它有导航条、样式表,可能还有一些 JavaScript 代码,事情必将以向每个模板填充各种冗余的 HTML 而告终。
解决这个问题的服务器端 include 方案是找出两个模板中的共同部分,将其保存为不同的模板片段,然后在每个模板中进行 include。 也许你会把模板头部的一些代码保存为 header.html 文件:
1
2
3
|
<!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01//EN"
>
<html lang
=
"en"
>
<head>
|
你可能会把底部保存到文件 footer.html :
1
2
3
4
|
<hr>
<p>Thanks
for
visiting my site.<
/
p>
<
/
body>
<
/
html>
|
对基于 include 的策略,头部和底部的包含很简单。 麻烦的是中间部分。 在此范例中,每个页面都有一个<h1>My helpful timestamp site</h1> 标题,但是这个标题不能放在 header.html 中,因为每个页面的 <title> 是不同的。 如果我们将 <h1> 包含在头部,我们就不得不包含 <title> ,但这样又不允许在每个页面对它进行定制。 何去何从呢?
Django 的模板继承系统解决了这些问题。 你可以将其视为服务器端 include 的逆向思维版本。 你可以对那些不同 的代码段进行定义,而不是 共同 代码段。
第一步是定义 基础模板 , 该框架之后将由 子模板 所继承。 以下是我们目前所讲述范例的基础模板:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01//EN"
>
<html lang
=
"en"
>
<head>
<title>{
%
block title
%
}{
%
endblock
%
}<
/
title>
<
/
head>
<body>
<h1>My helpful timestamp site<
/
h1>
{
%
block content
%
}{
%
endblock
%
}
{
%
block footer
%
}
<hr>
<p>Thanks
for
visiting my site.<
/
p>
{
%
endblock
%
}
<
/
body>
<
/
html>
|
这个叫做 base.html 的模板定义了一个简单的 HTML 框架文档,我们将在本站点的所有页面中使用。 子模板的作用就是重载、添加或保留那些块的内容。 (如果你一直按顺序学习到这里,保存这个文件到你的template目录下,命名为 base.html .)
我们使用模板标签: {% block %} 。 所有的 {% block %} 标签告诉模板引擎,子模板可以重载这些部分。 每个{% block %}标签所要做的是告诉模板引擎,该模板下的这一块内容将有可能被子模板覆盖。
现在我们已经有了一个基本模板,我们可以修改 current_datetime.html 模板来 使用它:
1
2
3
4
5
6
7
|
{
%
extends
"base.html"
%
}
{
%
block title
%
}The current time{
%
endblock
%
}
{
%
block content
%
}
<p>It
is
now {{ current_date }}.<
/
p>
{
%
endblock
%
}
|
再为 hours_ahead 视图创建一个模板,看起来是这样的:
1
2
3
4
5
6
7
|
{
%
extends
"base.html"
%
}
{
%
block title
%
}Future time{
%
endblock
%
}
{
%
block content
%
}
<p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.<
/
p>
{
%
endblock
%
}
|
看起来很漂亮是不是? 每个模板只包含对自己而言 独一无二 的代码。 无需多余的部分。 如果想进行站点级的设计修改,仅需修改 base.html ,所有其它模板会立即反映出所作修改。
以下是其工作方式。 在加载 current_datetime.html 模板时,模板引擎发现了 {% extends %} 标签, 注意到该模板是一个子模板。 模板引擎立即装载其父模板,即本例中的 base.html 。
此时,模板引擎注意到 base.html 中的三个 {% block %} 标签,并用子模板的内容替换这些 block 。因此,引擎将会使用我们在 { block title %} 中定义的标题,对 {% block content %} 也是如此。 所以,网页标题一块将由{% block title %}替换,同样地,网页的内容一块将由 {% block content %}替换。
注意由于子模板并没有定义 footer 块,模板系统将使用在父模板中定义的值。 父模板 {% block %} 标签中的内容总是被当作一条退路。
继承并不会影响到模板的上下文。 换句话说,任何处在继承树上的模板都可以访问到你传到模板中的每一个模板变量。
你可以根据需要使用任意多的继承次数。 使用继承的一种常见方式是下面的三层法:
-
创建 base.html 模板,在其中定义站点的主要外观感受。 这些都是不常修改甚至从不修改的部分。
-
为网站的每个区域创建 base_SECTION.html 模板(例如, base_photos.html 和 base_forum.html )。这些模板对base.html 进行拓展,并包含区域特定的风格与设计。
-
为每种类型的页面创建独立的模板,例如论坛页面或者图片库。 这些模板拓展相应的区域模板。
这个方法可最大限度地重用代码,并使得向公共区域(如区域级的导航)添加内容成为一件轻松的工作。
以下是使用模板继承的一些诀窍:
-
如果在模板中使用 {% extends %} ,必须保证其为模板中的第一个模板标记。 否则,模板继承将不起作用。
-
一般来说,基础模板中的 {% block %} 标签越多越好。 记住,子模板不必定义父模板中所有的代码块,因此你可以用合理的缺省值对一些代码块进行填充,然后只对子模板所需的代码块进行(重)定义。 俗话说,钩子越多越好。
-
如果发觉自己在多个模板之间拷贝代码,你应该考虑将该代码段放置到父模板的某个 {% block %} 中。
-
如果你需要访问父模板中的块的内容,使用 {{ block.super }}这个标签吧,这一个魔法变量将会表现出父模板中的内容。 如果只想在上级代码块基础上添加内容,而不是全部重载,该变量就显得非常有用了。
-
不允许在同一个模板中定义多个同名的 {% block %} 。 存在这样的限制是因为block 标签的工作方式是双向的。 也就是说,block 标签不仅挖了一个要填的坑,也定义了在父模板中这个坑所填充的内容。如果模板中出现了两个相同名称的 {% block %} 标签,父模板将无从得知要使用哪个块的内容。
Template进阶
一模版的组成
HTML代码+逻辑控制代码
二 逻辑控制代码的组成
1 变量(使用双大括号来引用变量)
{{var_name}}
2 标签(tag)的使用(使用大括号和百分比的组合来表示使用tag)
{%load staticfiles%}
3 过滤器(filter)的使用
{{ship_date|date:"Fj,Y"}},ship_date变量传给data过滤器,date过滤器通过使用"FJ,Y"这几个参数来格式化日期数据。"|"代表类似Unix命令中的管道操作。
三 常用标签
1 {%if%} 的使用
可以使用你的and,or,not来组织的逻辑。但不允许and和or同时出现在条件语句中。新版本中已经支持了{%elif%} 这样的用法。
2 {% for %}的使用
用来循环一个序列, 还可以使用reserser关键字来进行倒序遍历,一般可以先用if语句判断序列是否为空,再进行遍历;还可以使用empty关键字来进行为空时的跳转
1
2
3
4
5
6
7
8
9
10
11
12
13
|
{{li }}
{
%
for
i
in
li
%
}
<li>{{ forloop.counter0 }}
-
-
-
-
{{ i }}<
/
li>
{
%
empty
%
}
<li>this
is
empty!<
/
li>
{
%
endfor
%
}
{
# [11, 22, 33, 44, 55]#}
{
# 0----11#}
{
# 1----22#}
{
# 2----33#}
{
# 3----44#}
{
# 4----55#}
|
3 {%csrf_token%}
用于生成csrf_token的标签,用于防治跨站攻击验证。
注意如果你在view的index里用的是render_to_response方法,此时标签由于没有上下文的关系所以没有效果,需要:
1
2
|
from
django.template
import
RequestContext ,Template
return
render_to_response(
"index.html"
,
locals
(),context_instance
=
RequestContext(req))
|
或者直接使用render。
其实,这里是会生成一个input标签,和其他表单标签一起提交给后台的。
4 {% load %}: 加载标签库
5 {% url %}: 引用路由配置的地址
1
2
3
4
5
|
<form action
=
"{% url "
bieming
"%}"
>
<
input
type
=
"text"
>
<
input
type
=
"submit"
value
=
"提交"
>
{
%
csrf_token
%
}
<
/
form>
|
6 {% with %}:用更简单的变量名替代复杂的变量名
1
2
3
|
{
%
with total
=
fhjsaldfhjsdfhlasdfhljsdal
%
}
{{ total }}
{
%
endwith
%
}
|
7 {% verbatim %}: 禁止render
1
2
3
|
{
%
verbatim
%
}
{{ hello }}
{
%
endverbatim
%
}
|
四 常用过滤器
1 add : 给变量加上相应的值
2 addslashes: 给变量中的引号前加上斜线
3 capfirst : 首字母大写
4 cut : 从字符串中移除指定的字符
5 date : 格式化日期字符串
6 default : 如果值是False,就替换成设置的默认值,否则就是用本来的值
7 default_if_none:如果值是None,就替换成设置的默认值,否则就使用本来的值
1
2
3
4
5
6
7
8
9
10
11
|
{
#value1="aBcDe" #}
{{ value1|upper }}<br>
{
# value2=5#}
{{ value2|add:
3
}}<br>
{
# value3='he llo wo r ld'#}
{{ value3|cut:
' '
}}<br>
{
# import datetime#}
{
# value4=datetime.datetime.now()#}
{{ value4|date:
'Y-m-d'
}}<br>
{
# value5=[]#}
{{ value5|default:
'空的'
}}<br>
|
1
|
{
# value6='<a href="#">跳转</a>'#}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
{{ value6 }}
{
%
autoescape off
%
}
{{ value6 }}
{
%
endautoescape
%
}
{{ value6|safe }}<br>
{{ value6|striptags }}
{
# value7='1234'#}
{{ value7|filesizeformat }}<br>
{{ value7|first }}<br>
{{ value7|length }}<br>
{{ value7|
slice
:
":-1"
}}<br>
{
# value8='http://www.baidu.com/?a=1&b=3'#}
{{ value8|urlencode }}<br>
{
# value9='hello I am yuan'#}
{{ value9|truncatechars:
'6'
}}<br>
{{ value9|truncatewords:
'2'
}}<br>
|
自定义simple_tag
a、在app中创建templatetags模块
b、创建任意 .py 文件,如:add100.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#!/usr/bin/env python
#coding:utf-8
from
django
import
template
from
django.utils.safestring
import
mark_safe
from
django.template.base
import
resolve_variable, Node, TemplateSyntaxError
register
=
template.Library()
@register
.simple_tag
def
my_add100(v1):
return
v1
+
100
@register
.simple_tag
def
my_input(
id
,arg):
result
=
"<input type='text' id='%s' class='%s' />"
%
(
id
,arg,)
return
mark_safe(result)
|
c、在使用自定义simple_tag的html文件中导入之前创建的 add100.py 文件名
1
|
{
%
load add100
%
}
|
d、使用simple_tag
1
2
|
{
%
my_add100
3
%
}
{
%
my_input
'id_username'
'hide'
%
}
|
e、在settings中配置当前app,不然django无法找到自定义的simple_tag
INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'app01', )
FORM
form
一 什么是Form?什么是DjangoForm?
Django表单系统中,所有的表单类都作为django.forms.Form的子类创建,包括ModelForm
关于django的表单系统,主要分两种
基于django.forms.Form:所有表单类的父类
基于django.forms.ModelForm:可以和模型类绑定的Form
需求:向数据库的Info表中添加一些新的个人信息
二 不使用Django Form的情况
#***************urls.py urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^add_info/', views.add_info,name='add_info'), ] ##************************view.py from django.shortcuts import render,HttpResponse # Create your views here. from app01.models import * def add_info(req): if req.method=='POST': name=req.POST.get('name') age=req.POST.get('age') sex=req.POST.get('sex') birthday=req.POST.get('birthday') qualification=req.POST.get('qualification') job=req.POST.get('job') Info.objects.create(name=name, age=age, sex=sex, birthday=birthday, qualification=qualification, job=job ) return HttpResponse('添加成功!') return render(req,'add_info.html') #*****************************add_info.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加个人信息</title> </head> <body> <form action="{% url 'add_info' %}" method="post"> 姓名<input type="text" name="name"><br> 性别<input type="text" name="sex"><br> 年龄<input type="text" name="age"><br> 生日<input type="text" name="birthday"><br> 学历<input type="text" name="qualification"><br> 工作<input type="text" name="job"><br> <input type="submit" value="提交"><br> {% csrf_token %} </form> </body> </html>
三 使用Form的情况
#-----------------------------------------urls.py #----------------------------------------- from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^add_info/', views.add_info,name='add_info'), ] #-----------------------------------------models.py #----------------------------------------- from django.db import models # Create your models here. class Info(models.Model): name = models.CharField(max_length=64) sex = models.CharField(max_length=64) birthday = models.CharField(max_length=64) age=models.CharField(max_length=64) qualification=models.CharField(max_length=64) job=models.CharField(max_length=64) email=models.CharField(max_length=64,default='') class Hobby(models.Model): item=models.CharField(max_length=64) #-----------------------------------------form.py #----------------------------------------- from django import forms from app01 import models from django.core.exceptions import ValidationError class Info_form(forms.Form): def validate_name(value): try: models.Info.objects.get(name=value) raise ValidationError('%s 的信息已经存在!'%value) except models.Info.DoesNotExist: pass sex_choice=((0,'男'), (1,'女'))#select的数据可以像这样写,也可以在另外一张表中动态去拿 name = forms.CharField(validators=[validate_name],label='姓名',error_messages={'required':'必填'}) age = forms.CharField(label='年龄',error_messages={'required':'必填'}) # sex = forms.CharField(label='性别',error_messages={'required':'必填',},) sex=forms.IntegerField(widget=forms.widgets.Select(choices=sex_choice, attrs={'class':'setform2'} )) birthday = forms.CharField(label='生日',error_messages={'required':'必填'}) qualification = forms.CharField(label='学历',error_messages={'required':'必填'}, widget=forms.TextInput(attrs={'class':'formset', 'placeholder':'本科' } )) email=forms.EmailField(max_length=100,min_length=10) job = forms.CharField(label='工作',error_messages={'required':'必填'}) def __init__(self,*args,**kwargs): super(Info_form,self).__init__(*args,**kwargs) self.fields['hobby']=forms.CharField(widget=forms.widgets.Select(choices=models.Hobby.objects.values_list('id','item'))) #-------------------------------------------------------views.py #------------------------------------------------------- from django.shortcuts import render,HttpResponse # Create your views here. from app01.models import * from app01.forms import * def add_info(req): if req.method=='POST': Info_form_obj=Info_form(req.POST) if Info_form_obj.is_valid(): Info.objects.create(name=Info_form_obj.cleaned_data['name'], age=Info_form_obj.cleaned_data['age'], sex=Info_form_obj.cleaned_data['sex'], birthday=Info_form_obj.cleaned_data['birthday'], qualification=Info_form_obj.cleaned_data['qualification'], job=Info_form_obj.cleaned_data['job'] ) return HttpResponse('添加成功!') else: error_obj=Info_form_obj.errors print('***************') print(type(error_obj))#<class 'django.forms.utils.ErrorDict'> print(error_obj['name'][0])#必填 print(error_obj.get('age'))#<ul class="errorlist"><li>必填</li></ul> return render(req,'add_info.html',{'form_obj':Info_form_obj,'error_obj':error_obj}) Info_form_obj=Info_form() return render(req,'add_info.html',{'form_obj':Info_form_obj}) #------------------------------------------------------add_info.html #------------------------------------------------------- <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加个人信息</title> <style> .formset{ color: rebeccapurple; border: dashed cadetblue; } </style> </head> <body> <form action="{% url 'add_info' %}" method="post"> <p>姓名{{ form_obj.name }}{{ error_obj.name.0 }}</p> <p>年龄{{ form_obj.age }}{{ error_obj.age.0 }}</p> <p>生日{{ form_obj.birthday }}{{ error_obj.birthday.0 }}</p> <p>工作{{ form_obj.job }}<span>{{ error_obj.job }}</span></p> <p>学历{{ form_obj.qualification }}<span>{{ error_obj.qualification }}</span></p> <p>性别{{ form_obj.sex }}<span>{{ error_obj.sex }}</span></p> <p>邮箱{{ form_obj.email }}<span>{{ error_obj.email }}</span></p> <p>爱好{{ form_obj.hobby }}<span>{{ error_obj.hobby }}</span></p> {# {{ form_obj.as_p }}#} <input type="submit" value="提交"><br> {% csrf_token %} </form> </body> </html>
四 使用ModelForm的情况
# #-----------------------------------------form.py # #----------------------------------------- from django import forms from app01 import models class Info_form(forms.ModelForm): class Meta: model=models.Info exclude=('id',) def __init__(self,*args,**kwargs): super(Info_form,self).__init__(*args,**kwargs) self.fields['hobby']=forms.CharField(widget=forms.widgets.Select(choices=models.Hobby.objects.values_list('id','item'))) # #-------------------------------------------------------views.py # #------------------------------------------------------- from django.shortcuts import render,HttpResponse from app01.forms import * def add_info(req): if req.method=='POST': Info_form_obj=Info_form(req.POST) if Info_form_obj.is_valid(): Info_form_obj.save()#替换create return HttpResponse('添加成功!') else: error_obj=Info_form_obj.errors return render(req,'add_info.html',{'form_obj':Info_form_obj,'error_obj':error_obj}) Info_form_obj=Info_form() return render(req,'add_info.html',{'form_obj':Info_form_obj})
Model Form下的验证
django提供了3种方式来验证表单
实例:自定义验证,不能插入重名的出版社名称。
一 表单字段的验证器(和form是一样的)
二 clean_filedname,验证字段,针对某个字段进行验证。
三 表单clean方法,可针对整个表单进行验证
像注册时需要输入两次密码的验证,用clean来做就非常好,因为前两种都只是针对某一个字段进行验证,而确认密码则需要将两个字段信息都拿来进行匹配。
文件上传
自定义文件上传
1
2
3
4
5
6
7
8
9
10
|
def
postfile(req):
if
req.method
=
=
'POST'
:
obj
=
req.FILES.get(
'Yuan_file'
)
print
(obj)
f
=
open
(obj.name,
'wb'
)
for
i
in
obj:
f.write(i)
return
render(req,
'post.html'
)
|
Form上传文件
#-----------------------------------------------------------urls.py urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^postfile/', views.postfile), ] #-----------------------------------------------------------models.py from django.db import models # Create your models here. from django.db import models class UploadFile(models.Model): file = models.FileField(upload_to = './upload/') date = models.DateTimeField(auto_now_add=True) #-----------------------------------------------------------views.py from django.shortcuts import render,HttpResponse # Create your views here. from django import forms from app01 import models class FileForm(forms.Form): PostFile = forms.FileField() def postfile(req): if req.method=="POST": obj = FileForm(req.POST,req.FILES) if obj.is_valid(): upload = models.UploadFile() upload.file = obj.cleaned_data['PostFile'] upload.save() print (upload.file) return HttpResponse('上传成功!') obj=FileForm() return render(req,'post.html',{'obj':obj}) #-----------------------------------------------------------post.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <form action="/postfile/" method="post" enctype="multipart/form-data"> {{ obj.PostFile }} {% csrf_token %} <input type="submit" value="提交"> </form> </body> </html>