Django~数据结构表
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': 'books', #你的数据库名称 'USER': 'root', #你的数据库用户名 'PASSWORD': '', #你的数据库密码 'HOST': '', #你的数据库主机,留空默认为localhost 'PORT': '3306', #你的数据库端口 } }
注意:NAME即数据库的名字,在mysql连接前该数据库必须已经创建,而上面的sqlite数据库下的db.sqlite3则是项目自动创建
USER和PASSWORD分别是数据库的用户名和密码。
设置完后,再启动我们的Django项目前,我们需要激活我们的mysql。
然后,启动项目,会报错:no module named MySQLdb
这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb对于py3有很大问题,所以我们需要的驱动是PyMySQL
所以,我们只需要找到项目名文件下的__init__,在里面写入:
import pymysql pymysql.install_as_MySQLdb()
问题解决!!!!!!!
ORM对象映射关系
用于实现面向对象编程语言里不同类型系统的数据之间的转换,换言之,就是用面向对象的方式去操作数据库的创建表以及增删改查等操作。
优点: 1 ORM使得我们的通用数据库交互变得简单易行,而且完全不用考虑该死的SQL语句。快速开发,由此而来。
2 可以避免一些新手程序猿写sql语句带来的性能问题。
比如 我们查询User表中的所有字段:

新手可能会用select * from auth_user,这样会因为多了一个匹配动作而影响效率的。
缺点:1 性能有所牺牲,不过现在的各种ORM框架都在尝试各种方法,比如缓存,延迟加载登来减轻这个问题。效果很显著。
2 对于个别复杂查询,ORM仍然力不从心,为了解决这个问题,ORM一般也支持写raw sql。
3 通过QuerySet的query属性查询对应操作的sql语句
author_obj=models.Author.objects.filter(id=2) print(author_obj.query)
下面要开始学习Django ORM语法了,为了更好的理解,我们来做一个基本的 书籍/作者/出版商 数据库结构。 我们这样做是因为 这是一个众所周知的例子,很多SQL有关的书籍也常用这个举例。
表(模型)的创建
实例:我们来假定下面这些概念,字段和关系
作者模型:一个作者有姓名。
作者详细模型:把作者的详情放到详情表,包含性别,email地址和出生日期,作者详情模型和作者模型之间是一对一的关系(one-to-one)(类似于每个人和他的身份证之间的关系),在大多数情况下我们没有必要将他们拆分成两张表,这里只是引出一对一的概念。
出版商模型:出版商有名称,地址,所在城市,省,国家和网站。
书籍模型:书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many),一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many),也被称作外键。
1 from django.db import models<br> 2 class Publisher(models.Model): 3 name = models.CharField(max_length=30, verbose_name="名称") 4 address = models.CharField("地址", max_length=50) 5 city = models.CharField('城市',max_length=60) 6 state_province = models.CharField(max_length=30) 7 country = models.CharField(max_length=50) 8 website = models.URLField() 9 10 class Meta: 11 verbose_name = '出版商' 12 verbose_name_plural = verbose_name 13 14 def __str__(self): 15 return self.name 16 17 class Author(models.Model): 18 name = models.CharField(max_length=30) 19 def __str__(self): 20 return self.name 21 22 class AuthorDetail(models.Model): 23 sex = models.BooleanField(max_length=1, choices=((0, '男'),(1, '女'),)) 24 email = models.EmailField() 25 address = models.CharField(max_length=50) 26 birthday = models.DateField() 27 author = models.OneToOneField(Author) 28 29 class Book(models.Model): 30 title = models.CharField(max_length=100) 31 authors = models.ManyToManyField(Author) 32 publisher = models.ForeignKey(Publisher) 33 publication_date = models.DateField() 34 price=models.DecimalField(max_digits=5,decimal_places=2,default=10) 35 def __str__(self): 36 return self.title
注意:记得在settings里的INSTALLED_APPS中加入“app01”,然后再 同步数据库。
初始化数据库:在命令行下输入以下命令:
1:
分析代码:
1 每个数据模型都是django.db.models.Model的子类,它的父类Model包含了所有必要的和数据库交互的方法。并提供了一个简介漂亮的定义数据库字段的语法。
2 每个模型相当于单个数据库表(多对多关系例外,会多生成一张关系表),每个属性也是这个表中的字段。属性名就是字段名,它的类型(例如CharField)相当于数据库的字段类型(例如varchar)。大家可以留意下其它的类型都和数据库里的什么字段对应。
3 模型之间的三种关系:一对一,一对多,多对多。
一对一:实质就是在主外键(author_id就是foreign key)的关系基础上,给外键加了一个UNIQUE=True的属性;
一对多:就是主外键关系;(foreign key)
多对多:彼此一对多,自动创建第三张表(当然我们也可以自己创建第三张表)
4 模型常用的字段类型以及参数
1 # AutoField 2 # 一个 IntegerField, 添加记录时它会自动增长. 你通常不需要直接使用这个字段; 如果你不指定主键的话,系统会自动添加一个主键字段到你的 model.(参阅 _自动主键字段) 3 4 # BooleanField 5 # A true/false field. admin 用 checkbox 来表示此类字段. 6 7 # CharField 8 # 字符串字段, 用于较短的字符串. 9 # 10 # 如果要保存大量文本, 使用 TextField.admin 用一个 <input type="text"> 来表示此类字段 (单行输入). 11 # CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数. 12 # 13 # CommaSeparatedIntegerField 14 # 用于存放逗号分隔的整数值. 类似 CharField, 必须要有 maxlength 参数. 15 # DateField 16 # 一个日期字段. 共有下列额外的可选参数: 17 # 18 # Argument 描述 19 # auto_now 当对象被保存时,自动将该字段的值设置为当前时间.通常用于表示 "last-modified" 时间戳. 20 # auto_now_add 当对象首次被创建时,自动将该字段的值设置为当前时间.通常用于表示对象创建时间. 21 # admin 用一个文本框 <input type="text"> 来表示该字段数据(附带一个 JavaScript 日历和一个"Today"快键. 22 # 23 # DateTimeField 24 # 一个日期时间字段. 类似 DateField 支持同样的附加选项. 25 # admin 用两上文本框 <input type="text"> 表示该字段顺序(附带JavaScript shortcuts). 26 # 27 # EmailField 28 # 一个带有检查 Email 合法性的 CharField,不接受 maxlength 参数. 29 # FileField 30 # 一个文件上传字段. 31 # 32 # 要求一个必须有的参数: upload_to, 一个用于保存上载文件的本地文件系统路径. 这个路径必须包含 strftime formatting, 该格式将被上载文件的 date/time 替换(so that uploaded files don't fill up the given directory). 33 # 34 # admin 用一个``<input type="file">``部件表示该字段保存的数据(一个文件上传部件) . 35 # 36 # 在一个 model 中使用 FileField 或 ImageField 需要以下步骤: 37 # 38 # 在你的 settings 文件中, 定义一个完整路径给 MEDIA_ROOT 以便让 Django在此处保存上传文件. (出于性能考虑,这些文件并不保存到数据库.) 定义 MEDIA_URL 作为该目录的公共 URL. 要确保该目录对 WEB 服务器用户帐号是可写的. 39 # 在你的 model 中添加 FileField 或 ImageField, 并确保定义了 upload_to 选项,以告诉 Django 使用 MEDIA_ROOT 的哪个子目录保存上传文件. 40 # 你的数据库中要保存的只是文件的路径(相对于 MEDIA_ROOT). 出于习惯你一定很想使用 Django 提供的 get_<fieldname>_url 函数.举例来说,如果你的 ImageField 叫作 mug_shot, 你就可以在模板中以 {{ object.get_mug_shot_url }} 这样的方式得到图像的绝对路径. 41 # FilePathField 42 # 可选项目为某个特定目录下的文件名. 支持三个特殊的参数, 其中第一个是必须提供的. 43 # 44 # 参数 描述 45 # path 必需参数. 一个目录的绝对文件系统路径. FilePathField 据此得到可选项目. Example: "/home/images". 46 # match 可选参数. 一个正则表达式, 作为一个字符串, FilePathField 将使用它过滤文件名. 注意这个正则表达式只会应用到 base filename 而不是路径全名. Example: "foo.*\.txt^", 将匹配文件 foo23.txt 却不匹配 bar.txt 或 foo23.gif. 47 # recursive 可选参数.要么 True 要么 False. 默认值是 False. 是否包括 path 下面的全部子目录. 48 # 这三个参数可以同时使用. 49 # 50 # 我已经告诉过你 match 仅应用于 base filename, 而不是路径全名. 那么,这个例子: 51 # 52 # FilePathField(path="/home/images", match="foo.*", recursive=True) 53 # ...会匹配 /home/images/foo.gif 而不匹配 /home/images/foo/bar.gif 54 # 55 # FloatField 56 # 一个浮点数. 必须 提供两个 参数: 57 # 58 # 参数 描述 59 # max_digits 总位数(不包括小数点和符号) 60 # decimal_places 小数位数 61 # 举例来说, 要保存最大值为 999 (小数点后保存2位),你要这样定义字段: 62 # 63 # models.FloatField(..., max_digits=5, decimal_places=2) 64 # 要保存最大值一百万(小数点后保存10位)的话,你要这样定义: 65 # 66 # models.FloatField(..., max_digits=19, decimal_places=10) 67 # admin 用一个文本框(<input type="text">)表示该字段保存的数据. 68 # 69 # ImageField 70 # 类似 FileField, 不过要校验上传对象是否是一个合法图片.它有两个可选参数:height_field 和 width_field,如果提供这两个参数,则图片将按提供的高度和宽度规格保存. 71 # 72 # 该字段要求 Python Imaging Library. 73 # 74 # IntegerField 75 # 用于保存一个整数. 76 # 77 # admin 用一个``<input type="text">``表示该字段保存的数据(一个单行编辑框) 78 # 79 # IPAddressField 80 # 一个字符串形式的 IP 地址, (i.e. "24.124.1.30"). 81 # 82 # admin 用一个``<input type="text">``表示该字段保存的数据(一个单行编辑框) 83 # 84 # NullBooleanField 85 # 类似 BooleanField, 不过允许 NULL 作为其中一个选项. 推荐使用这个字段而不要用 BooleanField 加 null=True 选项. 86 # 87 # admin 用一个选择框 <select> (三个可选择的值: "Unknown", "Yes" 和 "No" ) 来表示这种字段数据. 88 # 89 # PhoneNumberField 90 # 一个带有合法美国风格电话号码校验的 CharField``(格式: ``XXX-XXX-XXXX). 91 # PositiveIntegerField 92 # 类似 IntegerField, 但取值范围为非负整数(这个字段应该是允许0值的....所以字段名字取得不太好,无符号整数就对了嘛). 93 # PositiveSmallIntegerField 94 # 类似 PositiveIntegerField, 取值范围较小(数据库相关) 95 # SlugField 96 # "Slug" 是一个报纸术语. slug 是某个东西的小小标记(短签), 只包含字母,数字,下划线和连字符.它们通常用于URLs. 97 # 98 # 若你使用 Django 开发版本,你可以指定 maxlength. 若 maxlength 未指定, Django 会使用默认长度: 50. 在以前的 Django 版本,没有任何办法改变 50 这个长度. 99 # 100 # 这暗示了 db_index=True. 101 # 102 # 它接受一个额外的参数: prepopulate_from, which is a list of fields from which to auto-populate the slug, via JavaScript, in the object's admin form: 103 # 104 # models.SlugField(prepopulate_from=("pre_name", "name")) 105 # prepopulate_from 不接受 DateTimeFields. 106 # 107 # admin 用一个``<input type="text">``表示 SlugField 字段数据(一个单行编辑框) 108 # 109 # SmallIntegerField 110 # 类似 IntegerField, 不过只允许某个取值范围内的整数.(依赖数据库) 111 # 112 # TextField 113 # 一个容量很大的文本字段. 114 # 115 # admin 用一个 <textarea> (文本区域)表示该字段数据.(一个多行编辑框). 116 # 117 # TimeField 118 # A time. Accepts the same auto-population options as DateField 和 DateTimeField. 119 # 120 # admin 用一个 <input type="text"> 文本框表示该字段保存的数据(附加一些JavaScript shortcuts). 121 # 122 # URLField 123 # 用于保存 URL. 若 verify_exists 参数为 True (默认), 给定的 URL 会预先检查是否存在(即URL是否被有效装入且没有返回404响应). 124 # 125 # admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框) 126 # 127 # USStateField 128 # 一个两字母的美国州名缩写. 129 # 130 # admin 用一个 <input type="text"> 文本框表示该字段保存的数据(一个单行编辑框) 131 # 132 # XMLField 133 # 一个校验值是否为合法XML的 TextField,必须提供参数: schema_path, 它是一个用来校验文本的 RelaxNG schema 的文件系统路径.
表的操作(增删改查)
-------------------------------------增(create , save) -------------------------------
from app01.models import * #create方式一: Author.objects.create(name='Alvin') #create方式二: Author.objects.create(**{"name":"alex"}) #save方式一: author=Author(name="alvin") author.save() #save方式二: author=Author() author.name="alvin" author.save()
重点来了------->那么如何创建存在一对多或多对多关系的一本书的信息呢?(如何处理外键关系的字段如一对多的publisher和多对多的authors)
1 #一对多(ForeignKey): 2 3 #方式一: 由于绑定一对多的字段,比如publish,存到数据库中的字段名叫publish_id,所以我们可以直接给这个 4 # 字段设定对应值: 5 Book.objects.create(title='php', 6 publisher_id=2, #这里的2是指为该book对象绑定了Publisher表中id=2的行对象 7 publication_date='2017-7-7', 8 price=99) 9 10 11 #方式二: 12 # <1> 先获取要绑定的Publisher对象: 13 pub_obj=Publisher(name='河大出版社',address='保定',city='保定', 14 state_province='河北',country='China',website='http://www.hbu.com') 15 OR pub_obj=Publisher.objects.get(id=1) 16 17 # <2>将 publisher_id=2 改为 publisher=pub_obj 18 19 #多对多(ManyToManyField()): 20 21 author1=Author.objects.get(id=1) 22 author2=Author.objects.filter(name='alvin')[0] 23 book=Book.objects.get(id=1) 24 book.authors.add(author1,author2) 25 #等同于: 26 book.authors.add(*[author1,author2]) 27 book.authors.remove(*[author1,author2]) 28 #------------------- 29 book=models.Book.objects.filter(id__gt=1) 30 authors=models.Author.objects.filter(id=1)[0] 31 authors.book_set.add(*book) 32 authors.book_set.remove(*book) 33 #------------------- 34 book.authors.add(1) 35 book.authors.remove(1) 36 authors.book_set.add(1) 37 authors.book_set.remove(1) 38 39 #注意: 如果第三张表是通过models.ManyToManyField()自动创建的,那么绑定关系只有上面一种方式 40 # 如果第三张表是自己创建的: 41 class Book2Author(models.Model): 42 author=models.ForeignKey("Author") 43 Book= models.ForeignKey("Book") 44 # 那么就还有一种方式: 45 author_obj=models.Author.objects.filter(id=2)[0] 46 book_obj =models.Book.objects.filter(id=3)[0] 47 48 s=models.Book2Author.objects.create(author_id=1,Book_id=2) 49 s.save() 50 s=models.Book2Author(author=author_obj,Book_id=1) 51 s.save()
-----------------------------------------删(delete) ---------------------------------------------
>>> Book.objects.filter(id=1).delete()
(3, {'app01.Book_authors': 2, 'app01.Book': 1})
我们表面上删除了一条信息,实际却删除了三条,因为我们删除的这本书在Book_authors表中有两条相关信息,这种删除方式就是django默认的级联删除。
-----------------------------------------改(update和save) ----------------------------------------
实例:

注意:
<1> 第二种方式修改不能用get的原因是:update是QuerySet对象的方法,get返回的是一个model对象,它没有update方法,而filter返回的是一个QuerySet对象(filter里面的条件可能有多个条件符合,比如name='alvin',可能有两个name='alvin'的行数据)。
<2>在“插入和更新数据”小节中,我们有提到模型的save()方法,这个方法会更新一行里的所有列。 而某些情况下,我们只需要更新行里的某几列。
#---------------- update方法直接设定对应属性---------------- models.Book.objects.filter(id=3).update(title="PHP") ##sql: ##UPDATE "app01_book" SET "title" = 'PHP' WHERE "app01_book"."id" = 3; args=('PHP', 3) #--------------- save方法会将所有属性重新设定一遍,效率低----------- obj=models.Book.objects.filter(id=3)[0] obj.title="Python" obj.save() # SELECT "app01_book"."id", "app01_book"."title", "app01_book"."price", # "app01_book"."color", "app01_book"."page_num", # "app01_book"."publisher_id" FROM "app01_book" WHERE "app01_book"."id" = 3 LIMIT 1; # # UPDATE "app01_book" SET "title" = 'Python', "price" = 3333, "color" = 'red', "page_num" = 556, # "publisher_id" = 1 WHERE "app01_book"."id" = 3;
在这个例子里我们可以看到Django的save()方法更新了不仅仅是title列的值,还有更新了所有的列。 若title以外的列有可能会被其他的进程所改动的情况下,只更改title列显然是更加明智的。更改某一指定的列,我们可以调用结果集(QuerySet)对象的update()方法,与之等同的SQL语句变得更高效,并且不会引起竞态条件。
此外,update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录update()方法会返回一个整型数值,表示受影响的记录条数。
注意,这里因为update返回的是一个整形,所以没法用query属性;对于每次创建一个对象,想显示对应的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', }, } }
---------------------------------------查(filter,value等) -------------------------------------
---------->查询API:
# 查询相关API: # <1>filter(**kwargs): 它包含了与所给筛选条件相匹配的对象 # <2>all(): 查询所有结果 # <3>get(**kwargs): 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。 #-----------下面的方法都是对查询的结果再进行处理:比如 objects.filter.values()-------- # <4>values(*field): 返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列 # <5>exclude(**kwargs): 它包含了与所给筛选条件不匹配的对象 # <6>order_by(*field): 对查询结果排序 # <7>reverse(): 对查询结果反向排序 # <8>distinct(): 从返回结果中剔除重复纪录 # <9>values_list(*field): 它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列 # <10>count(): 返回数据库中匹配查询(QuerySet)的对象数量。 # <11>first(): 返回第一条记录 # <12>last(): 返回最后一条记录 # <13>exists(): 如果QuerySet包含数据,就返回True,否则返回False。
补充:
#扩展查询,有时候DJANGO的查询API不能方便的设置查询条件,提供了另外的扩展查询方法extra: #extra(select=None, where=None, params=None, tables=None,order_by=None, select_params=None (1) Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"}) (2) Blog.objects.extra( select=SortedDict([('a', '%s'), ('b', '%s')]), select_params=('one', 'two')) (3) q = Entry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"}) q = q.extra(order_by = ['-is_recent']) (4) Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
---------->惰性机制:
所谓惰性机制:Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会马上执行sql,而是当调用QuerySet的时候才执行。
QuerySet特点:
<1> 可迭代的
<2> 可切片
#objs=models.Book.objects.all()#[obj1,obj2,ob3...]
#QuerySet: 可迭代
# for obj in objs:#每一obj就是一个行对象
# print("obj:",obj)
# QuerySet: 可切片
# print(objs[1])
# print(objs[1:4])
# print(objs[::-1])
QuerySet的高效使用:
1 #<1>Django的queryset是惰性的 2 3 Django的queryset对应于数据库的若干记录(row),通过可选的查询来过滤。例如,下面的代码会得到数据库中 4 名字为‘Dave’的所有的人: 5 person_set = Person.objects.filter(first_name="Dave") 6 7 上面的代码并没有运行任何的数据库查询。你可以使用person_set,给它加上一些过滤条件,或者将它传给某个函数, 8 这些操作都不会发送给数据库。这是对的,因为数据库查询是显著影响web应用性能的因素之一。 9 10 #<2>要真正从数据库获得数据,你可以遍历queryset或者使用if queryset,总之你用到数据时就会执行sql. 11 为了验证这些,需要在settings里加入 LOGGING(验证方式) 12 13 obj=models.Book.objects.filter(id=3) 14 15 # for i in obj: 16 # print(i) 17 18 # if obj: 19 # print("ok") 20 21 #<3>queryset是具有cache的 22 当你遍历queryset时,所有匹配的记录会从数据库获取,然后转换成Django的model。这被称为执行(evaluation). 23 这些model会保存在queryset内置的cache中,这样如果你再次遍历这个queryset,你不需要重复运行通用的查询。 24 obj=models.Book.objects.filter(id=3) 25 26 # for i in obj: 27 # print(i) 28 29 # for i in obj: 30 # print(i) #LOGGING只会打印一次 31 32 33 #<4> 34 简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,虽然你并不需要这些数据! 35 为了避免这个,可以用exists()方法来检查是否有数据: 36 37 obj = Book.objects.filter(id=4) 38 # exists()的检查可以避免数据放入queryset的cache。 39 if obj.exists(): 40 print("hello world!") 41 42 #<5>当queryset非常巨大时,cache会成为问题 43 44 处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统进程,让你的 45 程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法来获取数据,处理完数据就 46 将其丢弃。 47 objs = Book.objects.all() 48 # iterator()可以一次只从数据库获取少量数据,这样可以节省内存 49 for obj in objs.iterator(): 50 print(obj.name) 51 当然,使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。所以使用iterator() 52 的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询 53 54 总结: 55 queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。 56 使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能 57 会造成额外的数据库查询。
---------->对象查询,单表条件查询,多表条件关联查询
#--------------------对象形式的查找-------------------------- # 正向查找 ret1=models.Book.objects.first() print(ret1.title) print(ret1.price) print(ret1.publisher) print(ret1.publisher.name) #因为一对多的关系所以ret1.publisher是一个对象,而不是一个queryset集合 # 反向查找 ret2=models.Publish.objects.last() print(ret2.name) print(ret2.city) #如何拿到与它绑定的Book对象呢? print(ret2.book_set.all()) #ret2.book_set是一个queryset集合 #---------------了不起的双下划线(__)之单表条件查询---------------- # 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, #----------------了不起的双下划线(__)之多表条件关联查询--------------- # 正向查找(条件) # ret3=models.Book.objects.filter(title='Python').values('id') # print(ret3)#[{'id': 1}] #正向查找(条件)之一对多 ret4=models.Book.objects.filter(title='Python').values('publisher__city') print(ret4) #[{'publisher__city': '北京'}] ret5=models.Book.objects.filter(publisher__address='北京').values('publisher__name') print(ret5) #[{'publisher__name': '人大出版社'}, {'publisher__name': '人大出版社'}] #正向查找(条件)之多对多 ret6=models.Book.objects.filter(title='Python').values('author__name') print(ret6) ret7=models.Book.objects.filter(author__name="alex").values('title') print(ret7) # 反向查找(条件) #反向查找之一对多: ret8=models.Publisher.objects.filter(book__title='Python').values('name') print(ret8)#[{'name': '人大出版社'}] 注意,book__title中的book就是Publisher的关联表名 ret9=models.Publisher.objects.filter(book__title='Python').values('book__authors') print(ret9)#[{'book__authors': 1}, {'book__authors': 2}] #反向查找之多对多: ret10=models.Author.objects.filter(book__title='Python').values('name') print(ret10)#[{'name': 'alex'}, {'name': 'alvin'}]
注意:条件查询即与对象查询对应,是指在filter,values等方法中的通过__来明确查询条件。
---------->聚合查询和分组查询
<1> aggregate(*args,**kwargs):
通过对QuerySet进行计算,返回一个聚合值的字典。aggregate()中每一个参数都指定一个包含在字典中的返回值。即在查询集上生成聚合。
from django.db.models import Avg,Min,Sum,Max 从整个查询集生成统计值。比如,你想要计算所有在售书的平均价钱。Django的查询语法提供了一种方式描述所有 图书的集合。 >>> Book.objects.all().aggregate(Avg('price')) {'price__avg': 34.35} aggregate()子句的参数描述了我们想要计算的聚合值,在这个例子中,是Book模型中price字段的平均值 aggregate()是QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的 标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定 一个名称,可以向聚合子句提供它: >>> Book.objects.aggregate(average_price=Avg('price')) {'average_price': 34.35} 如果你也想知道所有图书价格的最大值和最小值,可以这样查询: >>> Book.objects.aggregate(Avg('price'), Max('price'), Min('price')) {'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
<2> annotate(*args,**kwargs):
可以通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),即为查询集的每一项生成聚合。
查询alex出的书总价格

查询各个作者出的书的总价格,这里就涉及到分组了,分组条件是authors__name
查询各个出版社最便宜的书价是多少

---------->F查询和Q查询
仅仅靠单一的关键字参数查询已经很难满足查询要求。此时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 #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)) ) #sql: # SELECT * from polls WHERE question LIKE 'P%' # AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06') # import datetime # e=datetime.date(2005,5,6) #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)))
八admin的配置
django admin是django自带的一个后台app,提供了后台的管理功能。
一 认识ModelAdmin
管理界面的定制类,如需扩展特定的model界面需从该类继承
二 注册medel类到admin的两种方式:
<1> 使用register的方法
<2> 使用register的装饰器
三 掌握一些常用的设置技巧
list_display: 指定要显示的字段
search_fields:指定搜索的字段
list_filter: 指定列表过滤器
ordering:指定排序字段

九 知识补充
1.常用过滤器
1 add : 给变量加上相应的值
2 addslashes: 给变量中的引号前加上斜线
3 capfirst : 首字母大写
4 cut : 从字符串中移除指定的字符
5 date : 格式化日期字符串
6 default : 如果值是False,就替换成设置的默认值,否则就是用本来的值
7 default_if_none:如果值是None,就替换成设置的默认值,否则就使用本来的值
{#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>

{# value6='<a href="#">跳转</a>'#}
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>
FORM
一 什么是Form?什么是DjangoForm?
Django表单系统中,所有的表单类都作为django.forms.Form的子类创建,包括ModelForm
关于django的表单系统,主要分两种
基于django.forms.Form:所有表单类的父类
基于django.forms.ModelForm:可以和模型类绑定的Form
需求:向数据库的Info表中添加一些新的个人信息
二 不使用Django Form的情况
1 #***************urls.py 2 3 urlpatterns = [ 4 url(r'^admin/', admin.site.urls), 5 url(r'^add_info/', views.add_info,name='add_info'), 6 7 ] 8 9 ##************************view.py 10 from django.shortcuts import render,HttpResponse 11 12 # Create your views here. 13 14 from app01.models import * 15 16 def add_info(req): 17 if req.method=='POST': 18 name=req.POST.get('name') 19 age=req.POST.get('age') 20 sex=req.POST.get('sex') 21 birthday=req.POST.get('birthday') 22 qualification=req.POST.get('qualification') 23 job=req.POST.get('job') 24 25 Info.objects.create(name=name, 26 age=age, 27 sex=sex, 28 birthday=birthday, 29 qualification=qualification, 30 job=job 31 ) 32 return HttpResponse('添加成功!') 33 34 return render(req,'add_info.html') 35 36 37 #*****************************add_info.html 38 <!DOCTYPE html> 39 <html lang="en"> 40 <head> 41 <meta charset="UTF-8"> 42 <title>添加个人信息</title> 43 </head> 44 <body> 45 <form action="{% url 'add_info' %}" method="post"> 46 47 姓名<input type="text" name="name"><br> 48 性别<input type="text" name="sex"><br> 49 年龄<input type="text" name="age"><br> 50 生日<input type="text" name="birthday"><br> 51 学历<input type="text" name="qualification"><br> 52 工作<input type="text" name="job"><br> 53 54 <input type="submit" value="提交"><br> 55 56 {% csrf_token %} 57 </form> 58 </body> 59 </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来做就非常好,因为前两种都只是针对某一个字段进行验证,而确认密码则需要将两个字段信息都拿来进行匹配。
文件上传
自定义文件上传
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>
参考文献:http://www.admin10000.com/document/2220.html

浙公网安备 33010602011771号