model数据库模型介绍

Django默认支持的数据库

Django默认支持sqlite、mysql、oracle、postgresql数据库

  • sqlite
    • Django默认使用sqlite数据库,默认自带sqlite的数据库驱动,引擎名称:django.db.backends.sqlite3
  • mysql
    • 引擎名称:django.db.backends.mysql

在settings.py中配置数据库

配置使用其他数据库

如果有的时候不希望使用sqlite而想使用mysql或者其他数据库,这时候需要先在setting.py文件中进行相关配置,找到settings.py中的DATABASE字典,将内容更改为如下形式:

DATABASES = {

    'default': {

        'ENGINE': 'django.db.backends.mysql',   # 数据库引擎,上面有提到,根据数据库选择对应的引擎

        'NAME': 'books',    # 数据库名称

        'USER': 'root',   # 数据库用户名

        'PASSWORD': '', # 数据库密码

        'HOST': '', # 数据库主机,留空默认为localhost

        'PORT': '3306', # 数据库端口

    }

}

然后,启动项目,可能会报错:no module named MySQLdb

这是因为django默认导入的驱动是MySQLdb,可是MySQLdb对于py3有很大问题,所以需要的驱动是PyMySQL,它对python3的支持更好

所以,只需要找到项目名文件下的__init__,在里面写入:

import pymysql
pymysql.install_as_MySQLdb()

即可

输出sql语句

model的相关方法其实都是在跟数据库打交道,因此可以将每一个方法都翻译成相对应的sql语句,那么怎样把这些sql语句打印到控制台让开发者看到呢?

只需要在settings.py文件中进行相关的配置即可,在settings.py的末尾添加上如下列表

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

添加完成后,就可以将sql语句打印到控制台了

在models.py中创建一个类对应一张表

说了这么多,终于把配置都搞定了,那么开始上主菜,要想操作表,首先要有表可以操作,但是并不需要手动建表,只需要在models.py文件中建立一个类就表示数据库中的一个表,利用这个类可以去操作数据库中相关的表,类中的每一个字段就表示表中的每一列,首先先建立一个Book类试试

class Book(models.Model):
    name = models.CharField(max_length=20)
    price = models.IntegerField()
    pub_date = models.DateField()

向上面这样光是写了一个类可还不够,还需要在项目下执行两条命令才能在数据库中建立相应的表,这两条命令便是:

python manage.py makemigrations

python manage.py migrate

执行过后就会在数据库中生成一张名为  应用名_类名小写  的表,例如假设应用的名字是app,那么此时建立的表名就是 app_book

单表查询之增删改查

配置弄完了,表也生成了,那么就下来就是操作表的时间了,当然,在Django中操作表不再需要写sql语句了(如果sql写的特别6的可以自己写,甚至连整个Django的ORM都不用学),而对应的是一个个方法,下面就来一一介绍

向数据库表中添加一条数据的方式有两种

方式一

b = Book(name="python基础", price=90, pub_date="2017-12-12")   # 实例化一个Book对象
b.save()    # 将数据添加到数据库

方式二

Book.objects.create(name="python基础", price=90, pub_date="2017-12-12")

注意:有的时候获得了用户的输入内容,可以将内容封装成一个字典,一起存进数据库

dic = {"name": "python基础", "price": 90, "pub_date": "2017-12-12"}
Book.objects.create(**dic) # 必须保证字典的键的名字跟类的字段名相同才能一一对应

自古删的操作就很简单,在Django的ORM中也是,找到删除之就可以了

# 先找到要删除的对象,然后执行delete()方法删除
Book.objects.filter(name="python基础").delete()

改的方式也分为两种,可以直接使用update()方法,也获取要修改的对象,在将对象修改,然后执行save()方法保存即可

方式一

# 先查到收条数据,然后修改
Book.objects.filter(name="python基础").update(price=999)

方式二

# 找到要修改的对象
b = Book.objects.get(name="python基础")   
b.price = 100     # 修改数据
b.save()		# 将数据保存到数据库

注意:使用update修改数据时,只会更新修改的内容,但是使用save()方法修改数据,会把所有的数据都更新一遍,因此效率很低,所以用update()更好

自古查询就很复杂,方式杂多,可惜在Django的ORM中也不例外

查询的API

方法 说明 演示 演示说明
all             查询所有的记录 Book.objects.all() 可以查出书籍表中的每一列数据
first 拿到第一条记录 Book.objects.first() 拿到书籍表中的第一条记录
last 拿到最后一条记录 Book.objects.last() 拿到书籍表中的最后一条记录
get

拿到符合条件的数据,只有一条数据,而且只有结果是一条数据时才没问题,

如果查询结果是多条数据或者是0的话就会报错

Book.objects.get(**kwargs)

这里拿到的是一个book对象,查询的内容也可以是某个单一的字段,

例如:Book.objects.get(id=3)

values 获取查询对象中的某个或某几个字段(以字典的形式展示) Book.objects.filter(name="python基础").values("price", "pub_date") 拿到的是所有名字为python基础的书籍的价格和出版日期
values_list 获取查询对象中的某个或某几个字段(以列表的形式展示) Book.objects.filter(name="python基础").values_list("price", "pub_date") 与上面相同,只是将数据都存到了列表中,每一列的数据单独以一个元组储存
count 计数 Book.objects.all().count() 计算查到的QuerySet集合的长度
filter 按照条件进行查找 Book.objects.filter(name="python基础") 与get()方法不同,这个可以查询多条记录
exclude 查询出条件以外的数据 Book.objects.exclude(name="python基础") 查询出名字不为 python基础的书籍
defer 不取某几列数据
models.UserInfo.objects.filter(...).defer('username','id')
 不取username跟id列
only 只取某几列数据
models.UserInfo.objects.only('username','id')
只取username跟id列,相当于SELECT username, id FROM UserInfo
dates 根据时间进行某一部分的去重查找并截取指定内容
models.DatePlus.objects.dates('ctime','day','DESC')

参数说明:

field_name:要筛选的字段名

kind:只能是"year" "month" "day"

order只能是"ASC" "DESC"

并获取转换后的时间

year:年-01-01

month:年-月-01

day:年-月-日

datetimes 根据时间进行某一部分的去重查找并截取指定内容,将时间转换成指定时区的时间
models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))

pip3 install pytz
import pytz
pytz.all_timezones
pytz.timezone(‘Asia/Shanghai’)
 

参数说明:

field_name:要筛选的字段名

kind:只能是"year" "month" "day" "hour" "minute" "second"

order只能是"ASC" "DESC"

tzinfo:时区对象

 none  空QuerySet对象    
get_or_create 如果存在则获取,否则创建
obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})
 如果存在username等于root1则取出,否则创建root1,并且其他字段是defaults里面的内容
in_bulk 根据主键ID进行查找
id_list = [11,21,31]
models.DDD.objects.in_bulk(id_list)
 

 

万能的双下划线 __

在filter()、get()、exclude()、values()和values_list()等方法中,除了可以将字段或者字典作为查询条件以外,还可以通过双下划线去筛选出符合某一范围的条件

方法 说明 演示 演示说明
gt         大于 Book.objects.filter(price__gt = 50) 筛选出价格大于50的书籍
lt 小于 Book.objects.filter(price__lt = 50) 筛选出价格小于50的书籍
contains 名字中包含指定内容的字段 Book.objects.filter(name__contains="p") 筛选出名字中带有p的书籍
icontains 名字中包含指定内容的字段(忽略大小写) Book.objects.filter(name__icontains="a") 筛选出名字中带有a或A的书籍
in 筛选出符合指定集合的数据 Book.objects.filter(price__in=[11, 22, 33])  筛选出价格是等于11或22或33的数据
range 筛选出某个范围内的数据 Book.objects.filter(price__range(0, 20)) 筛选出价格在0到20之间的书籍  也就是数据库中的between and
startswith 以某某开头    
istartswith 以某某开头(忽略大小写)    
endswith 以某某结尾    
iendswith 以某某结尾(忽略大小写)    
regex   正则匹配

Entry.objects.get(title__regex=r'^(An?|The) +')

筛选出title中匹配了r'^(An?|The) +'的
iregex 忽略大小写

Entry.objects.get(title__iregex=r'^(an?|the) +')

 
date 匹配日期

Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))

 
year 匹配年份

Entry.objects.filter(pub_date__year=2005)

Entry.objects.filter(pub_date__year__gte=2005)

 
month 匹配月份

 Entry.objects.filter(pub_date__month=12)

 
day 匹配天

Entry.objects.filter(pub_date__day=3)

 
week_day 匹配周

Entry.objects.filter(pub_date__week_day=2)

 
hour 匹配小时

Event.objects.filter(timestamp__hour=23)

 
minute 匹配分钟

Event.objects.filter(timestamp__minute=29)

 
second 匹配秒钟

Event.objects.filter(timestamp__second=31)

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

多表查询之一对多

一对多就是两张表通过外键关联起来的,那么首先需要有两张表

建表

class Book(models.Model):
    name = models.CharField(max_length=20)
    price = models.IntegerField()
    pub_date = models.DateField() 
    # 这里的Publish用引号引起来了,也可以不用引号引起来,但是必须把Publish类放到Book类的上面,这样才能找到
    # 而用引号将Publish引起来后则不需要关心顺序,因为是在加载完后再去找的Publish,所以无所谓先后顺序
    publish = models.ForeignKey("Pubish")    # 创建Publish的外键,自动关联主键

    def __str__(self):
        return self.name

class Publisher(models.Model):
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)

之后执行 python manage.py makemigrations 跟 python manage.py migrate 即可

一对多添加关联

将一张表中的某一列与另一张表中的某一列关联有两种方式,第一种是直接为外键指定要关联的列的id,第二种是把要关联的对象赋值给类中的外键字段

方式一

# 对数据库中的publish_id进行赋值
Book.objects.create(name="linux", price=49, pub_date="2017-12-12", publish_id=2)

方式二

# 对类中的publish进行赋值
publish_obj = Publish.objects.filter(name="人民出版社")[0]
Book.objects.create(name="linux", price=49, pub_date="2017-12-12", publish=publish_obj)

一对多查询(通过对象查询)

通过多的一方查出一的一方(正向查询)

b = Book.objects.get(name="linux")
b.publish        # 拿到的是与该对象对应的一的一方的对象,一定是一个对象

通过一的一方找多的一方(反向查询)

反向查询的方式有两种,一种是通过外键字段查找,两一种是通过 类名小写_set的方式查找

方式一
pub_obj = Publish.objects.filter(name='人民出版社')[0]
book_obj = Book.objects.filter(publish=pub_obj)   # 拿到的是所有与pub_obj关联的book对象
方式二
pub_obj = Publish.objects.filter(name='人民出版社')[0]
pub_obj.book_set.all()   # 获取与该对象关联的Book对象
pub_obj.book_set.all().values('name', 'price')

注意:这里默认使用的是表名小写加set,即book_set,但是也可以自己定义,就是在models.py中在外键关联的字段中加上related_name字段即可

class Book(models.Model):
    name = models.CharField(max_length=20)
    price = models.IntegerField()
    pub_date = models.DateField() 
    publish = models.ForeignKey("Pubish", related_name="bbbb")    # 下次反向查询的时候就可以使用bbbb进行查询了 

进行反向查询

pub_obj = Publish.objects.get(name='人民出版社')
pub_obj.bbbb.all()     # 获取所有该出版社出版的书籍

一对多查询(通过双下划线 __ 查询)

在经历了上面的对象查询后发现,用对象查询真的太麻烦了,那么能不能有一条语句就解决问题的方法呢,自然是有的,那就是通过万能的双下划线查询,而且对于 filter()跟values()都可以使用

通过多的一方查询一的一方(正向查找)

方式一
Publish.objects.filter(book__name="python基础").values("name")  # 这里的book是book对象 
方式二(通过values查找)
Book.objects.filter(name="python基础").values("publish__name")    # 这里的publish是外键

通过一的一方查多的一方(反向查找)

Book.objects.filter(publish__name="人民出版社").values("name")  # 这里的publish是外键

多表查询之多对多

多对多查询是通过建立第三张表然后通过两个外键关联其他两张表,我们可以建立三个类然后自己设定外键关系,也可以创建两个类,通过ManyToMany让Django为我们建立第三张表

建表

class Book(models.Model):
    name = models.CharField(max_length=20)
    price = models.IntegerField()
    pub_date = models.DateField() 
    author = models.ManyToManyField("Author")   # 与Author建立多对多关系

    def __str__(self):
        return self.name

class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField(default=20)

之后执行 python manage.py makemigrations 跟 python manage.py migrate 即可

添加关联信息

book_obj = Book.objects.get(id=4)
author_obj = Author.objects.all()
author_obj2 = Author.objects.get(id=1)


# 给一本书籍添加多个作者
book_obj.author.add(*author_obj)   # 将所有的作者对该书籍关联
# 给一本书籍添加一个作者
book_obj.author.add(author_obj2)

解除关联关系

book_obj = Book.objects.get(id=4)
author_obj = Author.objects.all()
author_obj2 = Author.objects.get(id=1)


# 解除多个作者与书籍的绑定关系
book_obj.author.remove(*author_obj)
# 解除一个作者与书籍的绑定关系
book_obj.author.remove(author_obj2)
# 通过作者的id解除关系
book_obj.author.remove(1) # 解除id为1的作者与书籍的关系

多对多查询(通过对象查询)

通过有ManyToMany字段的一方查询没有的(正向查找)

book_obj = Book.objects.get(id=3)
book_obj.author.all()

通过没有ManyToMany字段查询有的(反向查找)

author_obj = Author.objects.get(id=2)
author_obj.book_set.all()    # 拿到所有与author_obj关联的书籍对象

注意:在ManyToMany字段中也有related_name字段,设置该字段后再进行反向查找也可以通过设置的名字进行查找

多对多查询(通过filter,values配合双下划线查询)

通过有ManyToMany字段的一方查询没有的(正向查找)

Book.objects.filter(author__name="alex").values("name")

Author.objects.filter(name="alex").values("book__name")

通过没有ManyToMany字段查询有的(反向查找)

Author.objects.filter(book__name="python基础")

聚合函数跟分组查询

聚合函数:aggregate()

引入文件

from django.db.models import Avg, Min, Sum, Max, Count

使用

# 求平均值
ret = Book.objects.all().aggregate(Avg("price"))

# 求总和
ret = Book.objects.filter(author__name='alex').aggregate(Sum("price"))

分组:annotate()

# 求每个作者出的书的总价格
ret = Book.objects.values("author__name").annotate(Sum("price"))

# 拿到每个出版社出的价格最低的书
ret = Publish.objects.values("name").annotate(Min("book__price"))

F查询跟Q查询

F查询:拿到查询的结果

# 将所有的记录的某列都加10
Book.objects.all().update(price=F("price")+10)  # 将每本书的价格都加10

Q查询:组合多个查询条件

# 或
Book.objects.filter(Q(name="python基础")|Q(price__gt=40)) # 拿到python基础或者价格大于40的书籍

# 非
Book.objects.filter(~Q(name="python基础"))  # 拿到除了python基础之外的书籍

# Q查询联合普通关键字查询,Q查询一定要在前面
Book.objects.filter(Q(name="Go"), price=40)

除了像上面那样直接在filter中写Q查询语句以外,还可以事先准备好查询对象,给查询对象添加查询条件后,将这个对象传到filter中即可

from django.db.models import Q

q = Q()    # 创建一个Q对象
q.connector = 'OR'    # 将多个条件之间设置为或,相当于在filter中直接写 |,默认取值是 'AND'

q.children.append(("contact__contains", "33"))  # 设置查询条件,可以通过dir(q.children)查看所有的方法,使用q.children.pop()可以删除最后一个条件
q.children.append(("consultant__name__contains", "33"))  # 设置另一个查询条件,与上面的天剑通过 ‘OR’ 相连

models.CustomerInfo.objects.filter(q)   # 将条件置入filter中进行查询

QuerySet

QuerySet的特点

  • 可迭代
  • 可切片

QuerySet的高效使用

1. Django的queryset是惰性的

在执行 Book.objects.filter(price=40) 该条语句的时候,并不会查询数据库,只是把该条代码翻译成了sql,只有真正用到的时候才会去查询

2. queryset具有cache

何为cache? cache即缓存,在Django执行第一次查询的时候就已经把查询的数据保存到了queryset内置的cache中了,如果程序再次遍历这个queryset会直接到cache中拿,不会再走数据库

3. 不必要的时候不让数据存入cache

上面说过,在执行查询命令的时候并不会真正去查询,只是把查询的代码翻译成了sql语句,用到时再查询,那么即使在用if对这个queryset集合进行判断也会执行sql并且把结果放到cache中,如果仅仅只是想判断某个queryet中是否有数据而不想把数据放到cache中,可以使用exists()方法

obj = Book.objects.filter(price__lt=40)
if obj.exists():    # 这样不会把数据存到cache中,但当然会想向数据库发送sql
    print("exists")

5. 当queryset非常巨大时,cache会成为问题

处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法来获取数据,处理完数据就将其丢弃。

obj = Book.objects.filter(name__contain = 'p')
obj = obj.iterator()    # obj现在是一个迭代对象
# iterator()可以一次只从数据库中去取量的数据,这样恶意节省内存
for i in obj:
    print(i.name)

# 但是再次遍历就没有数据了,因为迭代器已经在上一个for循环中遍历完了,再次遍历已经没有了
for i in obj:
    print(i.name)

总结:在使用itrator()防止生成cache时,会有两个问题

  • 第一个就是由于迭代器的性质,只能遍历一次,所以不能重复使用
  • 第二个就是会造成额外的数据库查询

因此什么时候用iterator()应该根据情况而定,真正到了查询上万级别记录的时候,还是用iterator(),别把内存玩儿坏了

 

更多Models介绍:见本文

posted @ 2018-07-08 11:34  Jin同学  阅读(536)  评论(0)    收藏  举报