django-- Models
数据库配置
django默认支持sqlite,MySQL,Oracle,postgresql数据库
如何将数据库设置为MySQL
将setting中的database修改:
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'books', #你的数据库名称 'USER': 'root', #你的数据库用户名 'PASSWORD': '', #你的数据库密码 'HOST': '', #你的数据库主机,留空默认为localhost 'PORT': '3306', #你的数据库端口 } }
在项目中的init文件中:
import pymysql pymysql.install_as_MySQLdb()
ORM(对象关系映射)
用于实现面向对象编程语言里不同类型系统的数据之间的转换,换言之,就是用面向对象的方式去操作数据库的创建表以及增删改查等操作。
优点: 1 ORM使得我们的通用数据库交互变得简单易行,而且完全不用考虑该死的SQL语句。快速开发,由此而来。
2 可以避免一些新手程序猿写sql语句带来的性能问题。
缺点: 1 性能有所牺牲,不过现在的各种ORM框架都在尝试各种方法,比如缓存,延迟加载登来减轻这个问题。效果很显著。
2 对于个别复杂查询,ORM仍然力不从心,为了解决这个问题,ORM一般也支持写raw sql。
3 通过QuerySet的query属性查询对应操作的sql语句
表--模型的创建
实例:书,作者,出版社。
书和出版社是多对一的关系,和作者是多对多的关系。
在models中创建这三个表。
from django.db import models # Create your models here. class Publish(models.Model): name = models.CharField(max_length=64) def __str__(self): return self.name class Book(models.Model): title = models.CharField(max_length=64) price = models.IntegerField() color = models.CharField(max_length=64) publisher = models.ForeignKey(Publish) author = models.ManyToManyField("Author") def __str__(self): return self.title class Author(models.Model): name = models.CharField(max_length=64) def __str__(self): return self.name
代码的分析:
1 每一个表都是Model的一个子类,它继承了父类的很多方法以便于和数据库的交流。
2 每一个表的属性都是表的一个列,并且得定义好这些属性(字段)的数据类型和大小。
3 关于一对多,如book表中的publisher,django会默认给publisher加上一个_id,并且存在于表的一列。后面有参数unique=true使其唯一,所以变成了一对一。
4 关于多对多,author不会存在于book表中而是像MySQL一样多出来一张表来放对应的关系。
5 Publish 是类名,所以book表上面必须有这个类。但是author是字符,通过反射来取到。
6 这些关系对应会默认对应类的主键。
其他字段类型与参数:
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 的文件系统路径. 134 135 more parametre
其他:
多对多的关系表除了django帮忙创建外,也可以自己创建一个用两个foreignkey绑定。
class zan_news_user(models.Model): userId = models.ForeignKey(to=UserMain,to_field="userId") newsId = models.ForeignKey(to=News_main, to_field="uuid") class Meta(): # 大写 unique_together = ( ("userId", "newsId"), )
表的操作
-------------------------------------增:
1 create 方式1 2 Book.objects.create( 3 title = "Python红宝书", 4 price = 15, 5 color = "pink" 6 ) 7 create方式2 8 dic = {"title":"Python","price":15,"color":"pink"} 9 Book.objects.create(**dic) 10 save 方法 必须有save() 11 obj = Book() 12 obj.title="Python" 13 obj.price=15 14 obj.color="pink" 15 obj.save()
常用create方式二,字典用的多。
注意:多对一,多对多的关系的创建。
#pub = Publish.objects.all().filter(id=2)#这里可以看出外 # 键绑定的是一个对象 # Book.objects.create( # title = "Python", # price = 15, # color = "pink", # publisher = pub[0] # 这里是多对一 所以必须是一个对象 # 方式2
# publisher_id=2
)
多对多:可以添加多个对象。注意 *
1 authors = Author.objects.all().filter(id__gt=1) 2 book = Book.objects.all().get(id=5) 3 book.author.add(authors[0],authors[1]) == book.author.add(*authors) 同理也有remove方法
反向查找:
book = Book.objects.filter(id__gt=2)
author = Author.objects.get(id=2)
author.book_set.add(*book)
-------------------------------------删:
>>> Book.objects.filter(id=1).delete()
(3, {'app01.Book_authors': 2, 'app01.Book': 1})
我们表面上删除了一条信息,实际却删除了三条,因为我们删除的这本书在Book_authors表中有两条相关信息,这种删除方式就是django默认的级联删除。
-------------------------------------改:
Publish.objects.all().filter(id=2).update(name="gaga")
需要注意的是get是返回的一个对象或者false,并且改的时候因为效率的原因不推荐使用object.save()的方法。
setting日志记录
1 LOGGING = { 2 'version': 1, 3 'disable_existing_loggers': False, 4 'handlers': { 5 'console':{ 6 'level':'DEBUG', 7 'class':'logging.StreamHandler', 8 }, 9 }, 10 'loggers': { 11 'django.db.backends': { 12 'handlers': ['console'], 13 'propagate': True, 14 'level':'DEBUG', 15 }, 16 } 17 } 18 19 LOGGING
关于查
查一直是数据库操作中最重要最频繁的。
# 查询相关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。
1 #--------------------对象形式的查找-------------------------- 2 # 正向查找 3 ret1=models.Book.objects.first() 4 print(ret1.title) 5 print(ret1.price) 6 print(ret1.publisher) 7 print(ret1.publisher.name) #因为一对多的关系所以ret1.publisher是一个对象,而不是一个queryset集合 8 9 # 反向查找 10 ret2=models.Publish.objects.last() 11 print(ret2.name) 12 print(ret2.city) 13 #如何拿到与它绑定的Book对象呢? 14 print(ret2.book_set.all()) #ret2.book_set是一个queryset集合 15 16 #---------------了不起的双下划线(__)之单表条件查询---------------- 17 18 # models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值 19 # 20 # models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据 21 # models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in 22 # 23 # models.Tb1.objects.filter(name__contains="ven") 24 # models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感 25 # 26 # models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and 27 # 28 # startswith,istartswith, endswith, iendswith, 29 30 #----------------了不起的双下划线(__)之多表条件关联查询--------------- 31 32 # 正向查找(条件) 33 34 # ret3=models.Book.objects.filter(title='Python').values('id') 35 # print(ret3)#[{'id': 1}] 36 37 #正向查找(条件)之一对多 38 39 ret4=models.Book.objects.filter(title='Python').values('publisher__city') 40 print(ret4) #[{'publisher__city': '北京'}] 41 ret5=models.Book.objects.filter(publisher__address='北京').values('publisher__name') 42 print(ret5) #[{'publisher__name': '人大出版社'}, {'publisher__name': '人大出版社'}] 43 44 #正向查找(条件)之多对多 45 ret6=models.Book.objects.filter(title='Python').values('author__name') 46 print(ret6) 47 ret7=models.Book.objects.filter(author__name="alex").values('title') 48 print(ret7) 49 50 # 反向查找(条件) 51 52 #反向查找之一对多: 53 ret8=models.Publisher.objects.filter(book__title='Python').values('name') 54 print(ret8)#[{'name': '人大出版社'}] 注意,book__title中的book就是Publisher的关联表名 55 56 ret9=models.Publisher.objects.filter(book__title='Python').values('book__authors') 57 print(ret9)#[{'book__authors': 1}, {'book__authors': 2}] 58 59 #反向查找之多对多: 60 ret10=models.Author.objects.filter(book__title='Python').values('name') 61 print(ret10)#[{'name': 'alex'}, {'name': 'alvin'}]
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')}
可以通过计算查询结果中每一个对象所关联的对象集合,从而得出总计值(也可以是平均值或总和),即为查询集的每一项生成聚合。
查询alex出的书总价格

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

F 和 Q 查询:
1 # F 使用查询条件的值,专门取对象中某列值的操作 2 3 # from django.db.models import F 4 # models.Tb1.objects.update(num=F('num')+1) 5 6 7 # Q 构建搜索条件 8 from django.db.models import Q 9 10 #1 Q对象(django.db.models.Q)可以对关键字参数进行封装,从而更好地应用多个查询 11 q1=models.Book.objects.filter(Q(title__startswith='P')).all() 12 print(q1)#[<Book: Python>, <Book: Perl>] 13 14 # 2、可以组合使用&,|操作符,当一个操作符是用于两个Q的对象,它产生一个新的Q对象。 15 Q(title__startswith='P') | Q(title__startswith='J') 16 17 # 3、Q对象可以用~操作符放在前面表示否定,也可允许否定与不否定形式的组合 18 Q(title__startswith='P') | ~Q(pub_date__year=2005) 19 20 # 4、应用范围: 21 22 # Each lookup function that takes keyword-arguments (e.g. filter(), 23 # exclude(), get()) can also be passed one or more Q objects as 24 # positional (not-named) arguments. If you provide multiple Q object 25 # arguments to a lookup function, the arguments will be “AND”ed 26 # together. For example: 27 28 Book.objects.get( 29 Q(title__startswith='P'), 30 Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) 31 ) 32 33 #sql: 34 # SELECT * from polls WHERE question LIKE 'P%' 35 # AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06') 36 37 # import datetime 38 # e=datetime.date(2005,5,6) #2005-05-06 39 40 # 5、Q对象可以与关键字参数查询一起使用,不过一定要把Q对象放在关键字参数查询的前面。 41 # 正确: 42 Book.objects.get( 43 Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)), 44 title__startswith='P') 45 # 错误: 46 Book.objects.get( 47 question__startswith='P', 48 Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
Queryset:
所谓惰性机制:Publisher.objects.all()或者.filter()等都只是返回了一个QuerySet(查询结果集对象),它并不会马上执行sql,而是当调用QuerySet的时候才执行。
queryset是可迭代可切片的。
queryset的高效使用:
#<1>Django的queryset是惰性的 Django的queryset对应于数据库的若干记录(row),通过可选的查询来过滤。例如,下面的代码会得到数据库中 名字为‘Dave’的所有的人: person_set = Person.objects.filter(first_name="Dave") 上面的代码并没有运行任何的数据库查询。你可以使用person_set,给它加上一些过滤条件,或者将它传给某个函数, 这些操作都不会发送给数据库。这是对的,因为数据库查询是显著影响web应用性能的因素之一。 #<2>要真正从数据库获得数据,你可以遍历queryset或者使用if queryset,总之你用到数据时就会执行sql. 为了验证这些,需要在settings里加入 LOGGING(验证方式) obj=models.Book.objects.filter(id=3) # for i in obj: # print(i) # if obj: # print("ok") #<3>queryset是具有cache的 当你遍历queryset时,所有匹配的记录会从数据库获取,然后转换成Django的model。这被称为执行(evaluation). 这些model会保存在queryset内置的cache中,这样如果你再次遍历这个queryset,你不需要重复运行通用的查询。 obj=models.Book.objects.filter(id=3) # for i in obj: # print(i) # for i in obj: # print(i) #LOGGING只会打印一次 #<4> 简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,虽然你并不需要这些数据! 为了避免这个,可以用exists()方法来检查是否有数据: obj = Book.objects.filter(id=4) # exists()的检查可以避免数据放入queryset的cache。 if obj.exists(): print("hello world!") #<5>当queryset非常巨大时,cache会成为问题 处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统进程,让你的 程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法来获取数据,处理完数据就 将其丢弃。 objs = Book.objects.all() # iterator()可以一次只从数据库获取少量数据,这样可以节省内存 for obj in objs.iterator(): print(obj.name) 当然,使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。所以使用iterator() 的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询 总结: queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。 使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能 会造成额外的数据库查询。
admin的配置
在使用之前确保已经创建好了一个数据库,并且在命令行中输入python manage.py createsuperuser来设置一个超级用户。
django admin是django自带的一个后台app,提供了后台的管理功能。
一 认识ModelAdmin
管理界面的定制类,如需扩展特定的model界面需从该类继承
二 注册medel类到admin的两种方式:
<1> 使用register的方法
<2> 使用register的装饰器
三 掌握一些常用的设置技巧
list_display: 指定要显示的字段
search_fields:指定搜索的字段
list_filter: 指定列表过滤器
ordering:指定排序字段


浙公网安备 33010602011771号