1. 数据库配置
- Django默认支持sqlite, mysql, oracle, postgresql 数据库:
- Django默认使用sqlite数据库,引擎名称:
django.db.backends.sqlite3
- MySQL 引擎名称:
django.db.backends.mysql
- Django默认使用sqlite数据库,引擎名称:
- MySQL 驱动程序:
MySQLdb(mysql python)
mysqlclient
MySQL
PyMySQL(纯python的mysql驱动程序)
# 示例: 更改数据库# settings.pyDATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'books', # 数据库名称 'USER': 'root', # 数据库用户名 'PASSWORD': 'root', # 数据库密码 'HOST': '', # 数据库主机,留空默认为localhost 'PORT': '3306', # 数据库端口 }}# 注意:# 启动项目时,会报错: no module named MySQLdb# 这是因为django默认导入的驱动是 MySQLdb,可是MySQLdb对于py3有很大问题,所以我们需要的驱动是# PyMySQL#解决方法:# __init__.py 中加入以下代码:# import pymysql# pymysql.install_as_MySQLdb()
2. ORM(对象关系映射)
- 表的创建:
- 作者模型: 一个作者有姓名;
- 作者详细模型: 把作者的详情放到详情表,包含性别, email地址和出生日期。作者详情模型和作者模型之间 是一对一的关系(one-to-noe),在大多数情况下,我们没有必要将他们拆分成两张表,这里只是引出一对一的概念。
- 出版商模型: 出版商有名称,地址,所在城市,省,国家和网站;
- 书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和数据的关系 就是多对多的关联关系(many-to-many),一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系 (one-to-many),也被称作外键。
# 实体类# 项目名(orm)/app01/models.pyfrom django.db import modelsclass 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.nameclass Author(models.Model): name = models.CharField(max_length=30) def __str__(self): return self.nameclass AuthroDetail(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, on_delete=models.CASCADE)class Book(models.Model): title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher, null=True, on_delete=models.SET_NULL) publication_date = models.DateField() price = models.DecimalField(max_digits=5, decimal_places=2, default=10) def __str__(self): return self.title# 创建表# iTerm 中进入 项目名(orm) 下python3 manage.py makemigrationspython3 manage.py migrate# 备注:# 1. on_delete 属性说明# CASCADE: 级联删除# PROTECT: 保护模式,如果采用该选项,删除的时候,会抛出 ProtectedError 错误;# SET_NULL: 置空模式,删除的时候, 外键自动被设置为空, 前提就是 blank=True, null=True;# SET_DEFAULT: 删除的时候,外键自动设置为默认值。定义外键的时候,需要加上一个默认值;# SET(): 自定义一个值,该值当然只能是对应的实体;# 2. 每个数据模型都是 django.db.models.Model 的子类, 它的父类Model包含了所有必要的和数据库交互的方法,# 并提供了一个简洁漂亮的定义数据库字段的语法;
2.1 模型常用的字段类型参数
# CharField # 字符串字段,用于较短的字符串 # CharField 要求必须有一个参数 maxlength, 用于从数据库层和Django校验层限制该字段所允许的最大字符数# InterField: 用于保存一个整数# FloatField: 一个浮点数, 必须提供两个参数: # max_digits: 总位数(不包括小数点和符号) # decimal_places: 小数点位数 # 例如,要保存最大值为 999(小数点后保存2位) # models.FloatField(..., max_digits=5, decimal_palces=2)# AutoField # 一个 IntegerField, 添加记录时,会自动增长,通常不需要直接使用这个字段 # 自定义一个主键: my_id=models.AutoField(primary_key=True) # 如果不指定主键的话,系统会自动添加一个主键字段到你的 model# BooleanField # admin 用 checkbox 来表示此类字段# TextField # 一个容量很大的文本字段 # admin 用一个
2.2 Field 重要参数
null: 数据库中字段是否可以是空blank: django 的 Admin 中添加数据时,是否允许空值default: 设定缺省值editable: 如果为假, admin 模式下将不能改写, 缺省为真;primary_key: 设置主键, 如果没有设置,Django 创建表时,会自动加上;unique: 数据唯一;verbose_name: Admin 中字段的显示名称;validator_list: 有效性检查, 非有效产生 django.core.validators.ValidationError 错误db_column, db_index: 如果为真,将为此字段创建索引choices: 一个用来选择值的二维数组,第一个值是实际存储的值, 第二个用来备选;
3. 表的操作
3.1 增(create, save)
from app01.models import * # create 方式一: Author.objects.create(name='Jame') # create 方式二: Author.objects.create(**{'name':'Jame'}) # 以字典的方式插入,前面需要添加两个星号 # save 方式一: author = Author(name='Jame') author.save() # save 方式二: author=Author() author.name='Jame' author.save()# 如何处理外键关系的字段,如一对多的publisher和多对多的authors# 一对多(ForeignKey): # 方式一: 由于绑定一对多的字段,例如publisher,存到数据库中的字段为 publisher_id, # 所以我们可以直接给这个字段设定对应值: Book.objects.create(title='Python 入门', publisher_id=2, publication_date='2015-4-18', price=98) # 方式二: # 首先,获取绑定的 Publisher对象 pub_obj = Publisher(name='河北教育出版社',address='保定',city='保定', state_province='河北',country='China',website='http://www.edu.com') 或 pub_obj=Publisher.objects.get(id=1) # 将 publisher_id=2 改为 publisher=pub_obj# 多对多(ManyToManyField): author1 = models.Author.objects.get(id=1) author2 = models.Author.objects.filter(name='zhangsan')[0] book = models.Book.objects.get(id=1) book.author.add(author1, author2) # book.author 表示book绑定的 author 对象集合 # 等同于 ----------- 正向查询(从拥有外键的表开始查找) book.author.add(*[author1, author2]) book.author.remove(*[author1, author2]) ----------- 反向查询 book=models.Book.objects.filter(id__gt=1) # id__gt 表示 id大于 authors=models.Author.objects.filter(id=1)[0] authors.book_set.add(*book) # Author 类里面没有与 book 相关的属性 authors.book_set.remove(*book)# 注意: 如果第三张表是通过 models.ManyToManyField() 自动创建的, 那么绑定关系只有上面一种方式# 如果第三张表是自己创建的: class Book2Author(models.Model): author = models.ForeignKey("Author") book = models.ForeignKey("Book") # 绑定联合唯一索引 class Meta: unique_together=["author", "book"] # 保存数据 s = models.Book2Author.objects.create(author_id = 1, Book_id = 2) s.save()
3.2 删除(delete)
# 单表:Book.objects.filter(id=1).delete()# 备注: 表面上删除了一条信息,实际却删除了三条,因为我们删除的这本书在 Book_authors 表中有两条# 相关信息,这种删除方式就是 django 默认的级联删除。# 多对多关系: remove() 和 clear() 方法# 正向book = models.Book.objects.filter(id=1)# 删除第三张表中和id=1关联的所有关联信息book.author.clear() # 清除与book中 id=1 关联的所有数据book.author.remove(*[1, 2, 3, 4])# 反向author = models.Author.objects.filter(id=1)author.book_set.clear() # 清除与book中 id=1 关联的所有数据
3.3 修改(update 和 save)
# 单表:# 方式一: update 方法直接设定对应属性 Book.objects.filter(id=2).update(title='Python') # 不能用 get(id=2) # SQL # UPDATE 'app01_book' SET 'title' = 'Python' WHERE 'app01_book'.'id'=2; args=('Python', 2) # 不能用get的原因是: update 是 QuerySet对象的方法, get返回的是一个model对象,它没有 update 方法, # 而 filter 返回的是一个 QuerySet 对象;# 方式二: save 方法会将所有属性重新设定一遍, 效率低 obj=models.Book.objects.filter(id=2)[0] obj.title='Python' obj.save()# SQL:# 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'=22,'color'='red','page_num'=223,# 'publisher_id'=1 WHERE 'app01_book'.'id'=3;
3.3.1 控制台输出SQL语句
# setting 加上日志记录部分LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level': 'DEBUG', 'class': 'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level': 'DEBUG', }, }}
3.4 查询(filter, valued等)
- 查询相关API:
filter(**kwargs)
: 包含了与所给筛选条件相匹配的对象;all()
: 查询所有结果;get(**kwargs)
: 返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的 对象超过一个或者没有都会抛出错误。
- 对查询的结果进行再处理(例如,
objects.filter.values()
)values(*field)
: 返回一个ValueQuerySet,一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列;exclude(**kwargs)
: 包含了与所给筛选条件不匹配的对象;order_by(*field)
: 对查询结果排序;reverse()
: 对查询结果反向排序;distinct()
: 从返回结果中剔除重复记录;values_list(*field)
: 返回一个元组序列;count()
: 返回数据库中匹配查询(QuerySet)的对象数量;first()
: 返回第一条记录;last()
: 返回最后一条记录;exists()
: 如果QuerySet包含数据,就返回 True, 否则返回 False。
# 补充:# 扩展查询,有时候Django的查询API不能方便的设置查询条件,提供了另外的扩展查询方法extra:# extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None)1) q = Entry.objects.extra(select={'is_recent': "pub_date > '2006-12-29'"}) q = q.extra(order_by=['-is_recent']) # 负号表示逆序2) Blog.objects.extra( select = SortedDict([('a', '%s'), ('b', '%s')]), select_params=('one', 'two'))3) Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
3.4.1 QuerySet
- 惰性机制:
Publisher.objects.all()
或者.filter()
等都只是返回一个QuerySet(查询结果集对象), 它并不会马上执行SQL, 而是当调用QuerySet的时候才执行。 - QuerySet的特点:
- 可迭代的
- 可切片
# 示例:objs = models.Book.objects.all() #[obj1, obj2, obj3...]# 可迭代 for obj in objs: # 每一个obj,就是一个行对象 print('obj:',obj)# 可切片 print(objs[1]) print(objs[1:4]) print(objs[::-1])# QuerySet 是具有cache的 # 当遍历QuerySet时,所有匹配的记录会从数据库获取,然后转换成Django的model,这被称为执行(evaluation) # 这些model会保存在QuerySet内置的cache中,如果再次遍历这个QuerySet,不需要再去数据库查询 objs=models.Book.Objects.filter(id=3) for i in objs: prin(i) # SQL 语句只运行一次 for i in objs: print(i)# 当QuerySet非常巨大时,cache会成为问题 # 处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的QuerySet可能会锁住系统 # 进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生QuerySet cache, 可以使用 iterator() # 方法来获取数据,处理完数据就将其丢弃 objs=Book.objects.all().iterator() # 迭代器对象 for obj in objs: print(obj.name) # 当然,使用iterator()方法来防止生成cache, 意味着遍历同一个QuerySet时,会重复执行查询。所以使用 # iterator()的时候要当心,确保代码在操作一个大的QuerySet时,没有重复执行查询。
3.4.2 单表条件查询 多表条件关联查询
# 示例:了不起的双下划线(__)之单表条件查询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 inmodels.Tb1.objects.filter(name__contains="ven")models.Tb1.objects.filter(name__icontains="ven") # icontains 表示大小写不敏感models.Tb1.objects.filter(id__range=[1, 2]) # 范围 between andstartswith, istartswith, endswith, iendswith# 示例: 多表条件关联查询# 正向条件查询models.Book.objects.filter(title="Python").values("id")# 一对多models.Book.objects.filter(title="Python").values("publisher__city")# 多对多models.Book.objects.filter(title="Python").values("author__name")models.Book.objects.filter(author__name="zhangsan").values("title")
3.4.3 聚合查询和分组查询
# aggregate(*args, **kwargs):from django.db.models import Avg, Min, Sum, Max# 查询图书价格的平均值Book.objects.all().aggregate(Avg('price')) # {'price__avg': 20}# 查询图书价格的最大值,最小值Book.objects.all().aggregate(Avg('price'), Max('price'), Min('price'))# 输出: {'price__avg': 20, 'price__max':Decimal('81.20'), 'price__min': Decimal('12.99')}# aggregate() 是 QuerySet 的一个终止子句,意思是说, 它返回一个包含一些键值对的字典。键的名称是聚合值# 的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定# 一个名称,可以向聚合子句提供Book.objects.all().aggregate(average_price=Avg('price'))# annotate(*args, **kwargs) 分组查询# 查询各个作者出的书的总价格,这里就涉及到分组了, 分组条件是 authors__nameBook.objects.values("authors__name").annotte(Sum("price"))# 查询各个出版社最便宜的书价是多少Book.objects.values("publisher__name").annotate(Min("price"))
3.4.4 F 查询和 Q 查询
# 示例一: F 查询# F 使用查询条件的值,专门取对象中某列值的操作, 例如,将书的价格增加 10from django.db.models import Fmodels.Book.objects.update(price=F('price')+10)# 示例二: Q 构建搜索条件from django.db.models import Q# Q 对象可以对关键字参数进行封装,从而更好地应用多个查询models.Book.objects.filter(Q(title__startswith='P')).all()# 可以组合使用 &, | 操作符,当一个操作符用于两个Q的对象,它产生一个新的Q对象Q(title__startswith='P') | Q(title__startswith='J')# Q 对象可以用 ~ 操作符放在前面表示否定, 也可允许否定与肯定形式的组合Q(title__startswith='P') | ~Q(pub_update__year=2005)models.Book.objects.get( Q(title__startswith='P'), Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 10, 1)))# Q对象可以与关键字参数查询一起使用,不过,一定要把Q对象放在关键字参数查询的前面:# 正确:models.Book.objects.get( Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 10, 1)), title__startswith='P')# 错误:models.Book.objects.get( title__startswith='P', Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 10, 1)))
4. admin 的配置
- admin 是 Django 强大功能之一,它能从数据库中读取数据,呈现在页面上,进行管理。
- 注册model类到admin的两种方式:
- 使用register方法:
admin.site.register(Book, MyAdmin)
- 使用register的装饰器:
@admin.register(Book)
- 使用register方法:
# 示例:# 一些常用的设置: # list_display: 指定要显示的字段 # search_fields: 指定搜索的字段 # list_filter: 指定列表过滤器 # ordering: 指定排序字段from django.contrib import adminfrom app01.models import *# 注册modelclass MyAdmin(admin.ModelAdmin): list_display = ("title", "price", "publisher") search_fields = ("title", "publisher") list_filter = ("publisher",) ordering = ("price",) fieldsets = [ (None, {'fields': ['title']}), ('price information', {'fields': ['price', 'publisher'], 'classes': ['collapse']}), ]admin.site.register(Book, Admin)admin.site.register(Publish)admin.site.register(Author)
参考资料: