美文网首页
3.5Django再说Modle

3.5Django再说Modle

作者: 昵称又重复 | 来源:发表于2018-04-26 15:00 被阅读9次

    初闻不识曲中意,在听已是曲中人

    先写目录

    不要被目录吓到哦!这只是给读者的一个导航而已,为了给大家使用,文章的美观就不太能照顾到了!!!谅解哦!不过内容还是挺有趣的!!!

    —————————————————————————

    1.ORM简介
    2.开发流程
    3.定义模型
    4.定义属性
    5.字段类型
    6.字段选项
    7.关系
    7.1关系之:一对一
    7.2关系之:一对多
    8.元选项
    9.管理器Manage
    9.1管理器之:显示定义管理器
    10.模型对象创建
    11.模型方法
    12.查询
    12.1查询之过滤器
    12.2限制查询集,查询集的缓存
    12.3条件查询
    12.4比较运算符
    12.4.1判等 exact
    12.4.2包含 contains
    12.4.3开头或结尾
    12.4.4是否为None
    12.4.5大小写不敏感
    12.4.6不连续查询
    12.4.7日期和时间查询
    12.4.8跨关联关系的查询
    12.4.9查询的快捷方式: pk
    12.5聚合函数
    12.6F对象
    12.7Q对象

    —————————————————————————

    1.ORM简介

     前面说过MVC了,MVC 框架中包括一个重要的部分,就是 ORM,它实现了数据模型与数据库的解耦
     即数据模型的设计不需要依赖于特定的数据库,通过简单的配置就可以轻松更换数据库
     ORM 即 Object-Relational Mapping 是“对象-关系-映射” 的简称,主要任务是:
    根据对象的类型生成表结构;
    将对象、列表的操作,转换为 sql 语句;
    将 sql 查询到的结果转换为对象、列表;
    Django 中的模型包含存储数据的字段和约束,对应着数据库中唯一的表。

    ORM 分为两种:

     DB First 数据库里先创建数据库表结构,根据表结构生成类,根据类操作数据库
     Code First 先写代码,执行代码创建数据库表结构
    主流的 orm 都是 code first。 django 的 orm 也是 code first,所以学的时候,本质就分为两块:
     根据类自动创建数据库表
     根据类对数据库表中的数据进行各种操作
    优点:
    摆脱复杂的 SQL 操作,适应快速开发;
    让数据结构变得简洁;
    数据库迁移成本更低
    注意:数据库 django 不能创建,需要自己提前创建

    —————————————————————————

    2.开发流程

    1. 在 models.py 中定义模型类,要求继承自 models.Model
    2. 把应用加入 settings.py 文件的 installed_app 项
    3. 生成迁移文件
    4. 执行迁移生成表
    5. 使用模型类进行 crud 操作

    —————————————————————————

    3.定义模型

    在模型中定义属性,会生成表中的字段
    模型类要求继承自models.Modle
    属性命名限制
     不能是 python 的保留关键字
     由于 django 的查询方式,不允许使用连续的下划线

    —————————————————————————

    4.定义属性

    1. 导入 from django.db import models
    2. 通过 models.Field 创建字段类型的对象,赋值给属性
       对于重要数据都做逻辑删除,不做物理删除,实现方法是定义 isDelete 属性,类型为
      BooleanField,默认值为 False
    btitle = models.CharField(max_length=20)
    

    —————————————————————————

    5.字段类型

    AutoField:一个根据实际 ID 自动增长的 IntegerField
    django 会为表增加自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后,则 django 不会再生成默认的主键列

    id = models.AutoField(primary_key=True)
    
    BooleanField: true/false 字段,此字段的默认表单控制是CheckboxInput
    NullBooleanField:支持 null、 true、 false 三种值
    CharField(max_length=字符长度):字符串,默认的表单样式是 TextInput
    TextField:大文本字段,一般超过 4000 使用,默认的表单控件是 Textarea
    IntegerField:整数
    DecimalField(max_digits=None, decimal_places=None):使用 python 的 Decimal
    
    实例表示的十进制浮点数
    DecimalField.max_digits:位数总数
    DecimalField.decimal_places:小数点后的数字位数
    FloatField:用 Python 的 float 实例来表示的浮点数
    DateField[auto_now=False, auto_now_add=False]):使用 Python 的 datetime.date
    
    实例表示的日期
    参数 DateField.auto_now:每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为 false
    参数 DateField.auto_now_add:当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为 false
    auto_now_add, auto_now, and default 这些设置是相互排斥的,他们之间的任何组合将会发生错误的结果
    TimeField:使用 Python 的 datetime.time 实例表示的时间,参数同 DateField
    DateTimeField:使用 Python 的 datetime.datetime 实例表示的日期和时间,参数同DateField
    

    FileField:一个上传文件的字段
    ImageField:继承了 FileField 的所有属性和方法,但对上传的对象进行校验,确保它是个有效的 image
    上传路径设置:

    字段选项 STATICFILES_DIRS=[
    os.path.join(BASE_DIR,'static')
    ]
    MEDIA_ROOT=os.path.join(BASE_DIR,"static/media")
    

    —————————————————————————

    6.字段选项

     通过字段选项,可以实现对字段的约束
     在字段对象时通过关键字参数指定
     null:如果为 True, Django 将空值以 NULL 存储到数据库中,默认值是 False
     blank:如果为 True,则该字段允许为空白,默认值是 False
     对比: null 是数据库范畴的概念, blank 是表单验证证范畴的
     如果一个字段设置为 blank=True,表单验证时允许输入一个空值。而 blank=False,则该项必需输入数据。
     db_column:字段的名称,如果未指定,则使用属性的名称
     db_index:若值为 True, 则在表中会为此字段创建索引
     default:默认值
     primary_key:若为 True, 则该字段会成为模型的主键字段
     unique:如果为 True,该字段在表中必须有唯一的值
     help_text:会在 form 表单控件中显示 help 文本

    —————————————————————————

    7.关系

     关系的类型包括:
    ForeignKey:一对多,将字段定义在多的端中
    ManyToManyField:多对多,将字段定义在两端中
    OneToOneField:一对一,将字段定义在任意一端中

     用一访问多:对象.模型类小写_set

    bookinfo.heroinfo_set
    

     django 默认每个主表的对象都有一个是外键的属性,可以通过它来查询到子表的信息。
     这个属性的名称默认是以子表的名称小写加上_set 来表示
     默认返回的是一个 querydict 对象
     自定义名称:

    person = models.ForeignKey(Person, related_name='person_set')
    hBook = models.ForeignKey('BookInfo', \related_name='person_set',\
    on_delete=models.CASCADE)
    

     用一访问一:对象.模型类小写

    heroinfo.bookinfo
    

     访问 id:对象.属性_id

    heroinfo.book_id
    

    django 2.0,外键(ForeignKey)和一对一(OneToOneField),必须要写 on_delete 参数否则会报异常:

    TypeError: __init__() missing 1 required positional argument: 'on_delete'
    

    on_delete 参数的各个值的含义:

    on_delete=None, # 删除关联表中的数据时,当前表与其关联的 field 的行为
    on_delete=models.CASCADE, # 删除关联数据,与之关联也删除
    on_delete=models.DO_NOTHING, # 删除关联数据,什么也不做
    on_delete=models.PROTECT, # 删除关联数据,引发错误 ProtectedError
    on_delete=models.SET_NULL, # 删除关联数据,与之关联的值设置为 null(前提 FK 字段需要设置为可空,一对一同理)
    on_delete=models.SET_DEFAULT, # 删除关联数据,与之关联的值设置为默认值(前提 FK字段需要设置默认值,一对一同理)
    

    —————————————————————————

    7.1关系之:范例:一对一的关系

    需求: 一个账户对应着一个联系人,而一个联系人有一个账户
    1、 定义模型类

    class Account(models.Model):
    username = models.CharField(max_length=20, null=True, blank=True,
    verbose_name=u'用户名')
    password = models.CharField(max_length=40, null=True, blank=True,
    verbose_name=u'密码')
    register_date = models.DateField(auto_now_add=True, null=True, blank=True,
    verbose_name=u'注册时间')
    class Contact(models.Model):
    # 在 Contact 中,关联 Account 表,让两个表的数据产生联系
    # 第一个参数:是被关联的模型名称
    # 第二个参数:当 Account 中的一条数据被删除的时候,与之对应的 Contact 数据也
    会被删除
    account = models.OneToOneField(Account, on_delete=models.CASCADE,
    primary_key=True)
    address = models.CharField(max_length=100, null=True)
    code = models.CharField(max_length=20, null=True)
    mobile = models.CharField(max_length=20, null=True)
    

    2、 生成迁移文件
    3、 执行迁移
    4、 数据测试

    python manage.py shell
    

    一对一的正向查询和反向查询

    a1 = Account(username='Thompson',password='333')
    a1.save()
    c1 = Contact(account=a1,address='zz',code='475000',mobile='15555555555')
    c1.save()
    print a1.contact# 正向查询,通过账户查询该账户对应的详细信息
    print c1.account# 反向查询,通过详细信息查询信息对应的账
    

    —————————————————————————

    7.2关系之:范例: 多对多的关系

    1、 定义模型类

    class Host(models.Model):
    id = models.AutoField(primary_key=True)
    hostname = models.CharField(max_length=32)
    ip = models.GenericIPAddressField(protocol='ipv4')
    port = models.IntegerField()
    def __str__(self):
    return self.hostname
    class Application(models.Model):
    name = models.CharField(max_length=32)
    r = models.ManyToManyField(to='Host') # 这个字段在表里不存在。
    

    2、 数据迁移
    3、 数据测试

    from booktest.models import Host,Application
    

    添加

    a=Application('qq')
    a.save()
    h1=Host(hostname='host1',ip='192.168.1.20',port='8000')
    h1.save()
    h2=Host(hostname='host2',ip='192.168.1.21',port='8001')
    h2.save()
    h3=Host(hostname='host3',ip='192.168.1.22',port='8002')
    h3.save()
    a.r.add(1) #对第三张表添加 application_id=1,host_id=1
    #对第三张表添加 application_id=1, host_id=2;application_id=1, host_id=3
    a.r.add(2,3)
    #对第三张表添加 application_id=1,host_id=1;application_id=1,host_id=2;
    #application_id=1,host_id=3
    a.r.add(*[1,2,3])
    #查询
    a.r.all()
    反向查询(在没有 ManyToManyField 字段的表中查)
    h1.application_set.all()
    #删除
    a.r.remove(1) #删除第三张表 application_id=1,host_id=1
    #删除第三张表 application_id=1,host_id=2;application_id=1,host_id=3
    a.r.remove(2,3)
    #删除第三张表 application_id=1,host_id=1;application_id=1,host_id=2;
    #application_id=1,host_id=3
    a.r.remove(*[1,2,3])
    a.r.clear() #删除第三张表的 application_id=1 的所有数据
    #更新
    a.name = 'new_name'
    a.save()
    #设置第三张表的 application_id=1 的数据为 application_id=1,host_id=1;
    #application_id=1,host_id=2;application_id=1,host_id=3 其它数据删除
    a.r.set([1,2,3])
    

    —————————————————————————

    8.元选项

    在模型类中定义类 Meta,用于设置元信息
    元信息 db_table:定义数据表名称,推荐使用小写字母,数据表的默认名称
    <app_name>_<model_name>
    ordering:对象的默认排序字段,获取对象的列表时使用,接收属性构成的列表

    class BookInfo(models.Model):
    ...
    class Meta():
    ordering = ['id']
    

    字符串前加-表示倒序,不加-表示正序

    class BookInfo(models.Model):
    ...
    class Meta():
    ordering = ['-id']
    

    排序会增加数据库的开销

    —————————————————————————

    9.管理器Manage

    管理器是 Django 的模型进行数据库的查询操作的接口,用于与数据库进行交互,Django应用的每个模型都拥有至少一个管理器
    当定义模型类时没有指定管理器,则 Django 会为模型类提供一个名为 objects 的管理器

    —————————————————————————

    9.1管理器之:显示定义管理器

    继承 models.Manager
    自定义管理器类主要用于两种情况:
    情况一:向管理器类中添加额外的方法:见下面“创建对象” 中的方式二
    情况二:修改管理器返回的原始查询集:重写 get_queryset()方法
    当为模型类指定管理器后, django 不再为模型类生成名为 objects 的默认管理器

    class BookInfoManager(models.Manager):
    def get_queryset(self):
    return super(BookInfoManager, self).get_queryset().filter(isDelete=False)
    class BookInfo(models.Model):
    ...
    books = BookInfoManager()
    

    —————————————————————————

    10.模型对象创建

    方式一:在模型类中增加一个类方法

    class BookInfo(models.Model):
    ...
    @classmethod
    def create(cls, title, pub_date):
    book = cls(btitle=title, bpub_date=pub_date)
    book.bread=0
    book.bcommet=0
    book.isDelete = False
    return book
    

    引入时间包: from datetime import *
    调用: book=BookInfo.create("hello",datetime(1980,10,11));
    保存: book.save()

    方式二:在自定义管理器中添加一个方法
    在管理器的方法中,可以通过 self.model 来得到它所属的模型类

    class BookInfoManager(models.Manager):
    def create_book(self, title, pub_date):
    book = self.model()
    book.btitle = title
    book.bpub_date = pub_date
    book.bread=0
    book.bcommet=0
    book.isDelete = False
    return book
    

    调用: book=BookInfo.books.create_book("abc",datetime(1980,1,1))
    保存: book.save()

    —————————————————————————

    11.模型方法

     str (self):重写 object 方法,此方法在将对象转换成字符串时会被调用
     save():将模型对象保存到数据表中
     delete():将模型对象从数据表中删除

    —————————————————————————

    12.查询

    查询集表示从数据库中获取的对象集合
    返回查询集的方法,称为过滤器
    过滤器基于所给的参数限制查询的结果
    从 Sql 的角度,查询集和 select 语句等价,过滤器像 where 和 limit 子句
    查询集是可迭代的对象

    —————————————————————————

    12.1查询之过滤器

    返回查询集的方法,称为过滤器
    all() : 检索所有的对象
    filter(**kwargs) : 检索特定的对象 返回一个与参数匹配的 QuerySet
    exclude(): 返回一个与参数不匹配的 QuerySet
    order_by(column_name): 检索排序后的对象
    column_name:排序的列,默认升序, 列名前加- 降序排序
    get():返回单个满足条件的对象
    如果未找到会引发"模型类.DoesNotExist"异常
    如果多条被返回,会引发"模型类.MultipleObjectsReturned"异常
    count():返回当前查询的总条数
    first():返回第一个对象
    last():返回最后一个对象
    exists():判断查询集中是否有数据,如果有则返回 True

    —————————————————————————

    12.2限制查询集

     查询集返回列表,可以使用下标的方式进行限制,等同于 sql 中的 limit 和 offset 子句
     注意:不支持负数索引
     使用下标后返回一个新的查询集,不会立即执行查询
     如果获取一个对象,直接使用[0],等同于[0:1].get()
     但是如果没有数据, [0]引发 IndexError 异常, [0:1].get()引发 DoesNotExist 异常

    BookInfo.objects.all()[:5]
    查找前 5 个 entry 表里的数据
    BookInfo.objects.all()[5:10]
    查找从第 5 个到第 10 个之间的数据。
    BookInfo.objects.all()[:10:2]
    查询从第 0 个开始到第 10 个,步长为 2 的数据。
    

    —————————————————————————

    查询集的缓存

     每个查询集都包含一个缓存来最小化对数据库的访问
     首次对查询集求值时,会发生数据库查询, django 会将查询的结果存在查询集的缓存中,并返回请求的结果,接下来对查询集求值将重用缓存的结果

     情况一:这构成了两个查询集,无法重用缓存,每次查询都会与数据库进行一次交互,
    增加了数据库的负载

    print([e for e in BookInfo.books.all()])
    print([e.btitle+":"+e.bpub_date.strftime("%Y-%m-%d") for e in
    BookInfo.books.all()])
    

     情况二:两次循环使用同一个查询集,第二次使用缓存中的数据

    b = BookInfo.books.all()
    print([e.btitle for e in b])
    print([e.bpub_date.strftime("%Y-%m-%d") for e in b])
    

    何时查询集不会被缓存:当只对查询集的部分进行求值时会检查缓存,但是如
    果这部分不在缓存中,那么接下来查询返回的记录将不会被缓存,这意味着使
    用索引来限制查询集将不会填充缓存,如果这部分数据已经被缓存,则直接使
    用缓存中的数据

    —————————————————————————

    12.3条件查询

     实现 where 子名,作为方法 filter()、 exclude()、 get()的参数
     语法:属性名称__比较运算符=值
     表示两个下划线,左侧是属性名称,右侧是比较类型
     对于外键,使用“属性名_id” 表示外键的原始值
     转义: like 语句中使用了%与,匹配数据中的%与,在过滤器中直接写,例如:

    filter(title__contains="%")=>where title like '%\%%',
    

    表示查找标题中包含%的

    —————————————————————————

    12.4比较运算符

    12.4.1判等 exact

     表示判等,大小写敏感;如果没有写“比较运算符” ,表示判等
    filter(isDelete=False)
    范例

    BookInfo.books.filter(btitle__exact='射雕英雄传')
    BookInfo.books.get(btitle__exact='射雕英雄传')
    BookInfo.books.filter(btitle='射雕英雄传')
    BookInfo.books.get(btitle='射雕英雄传')
    BookInfo.books.get(btitle__exact='射雕英雄传')
    

    —————————————————————————

    12.4.2包含 contains

    包含 contains
     是否包含,大小写敏感
    范例

    BookInfo.books.exclude(btitle__contains='传')
    BookInfo.books.filter(btitle__contains='雕')
    BookInfo.books.filter(btitle__contains='龙')
    BookInfo.books.exclude(btitle__contains='龙')
    

    —————————————————————————

    12.4.3开头或结尾

    startswith、 endswith
     以 value 开头或结尾,大小写敏感

    BookInfo.books.exclude(btitle__endswith='传')
    BookInfo.books.filter(btitle__endswith='传')
    BookInfo.books.get(btitle__startswith='倚',btitle__endswith='记')
    

    —————————————————————————

    12.4.4是否为None

     isnull

    BookInfo.books.filter(btitle__isnull=False)
    

    —————————————————————————

    12.4.5大小写不敏感

     在前面加个 i 表示不区分大小写,如 iexact、 icontains、 istarswith、 iendswith,

    —————————————————————————

    12.4.6不连续查询

     in:是否包含在范围内

    BookInfo.books.filter(pk__in=[1, 2, 3, 4, 5])
    

     gt、 gte、 lt、 lte:大于、大于等于、小于、小于等于

    BookInfo.books.filter(id__gt=3)
    

    —————————————————————————

    12.4.7日期和时间查询

     year、 month、 day、 week_day、 hour、 minute、 second
     对日期时间类型的属性进行运算

    BookInfo.books.filter(bpub_date__year=1980)
    BookInfo.books.filter(bpub_date__gt=date(1980, 12, 31))
    BookInfo.books.filter(bpub_date__gt=date(1990, 12, 31))
    

    注意:
    为避免查询 month 和 day 结果为空,请在 setting.py 中设置:
    USE_TZ = False
    跨关联关系的查询

    —————————————————————————

    12.4.8跨关联关系的查询

    处理 join 查询
    语法:模型类名<属性名><比较>
    注:可以没有__<比较>部分,表示等于,结果同 inner join
    可反向使用,即在关联的两个模型中都可以使用
    BookInfo.books.filter(heroinfo__hcontent__contains='八')
    BookInfo.books.filter(heroinfo__hname__contains='靖')

    —————————————————————————

    12.4.9查询的快捷方式: pk

     pk 表示 primary key,默认的主键是 id

    BookInfo.books.filter(pk__lt=3)
    BookInfo.books.filter(id__gt=3)
    

    —————————————————————————

    12.5聚合函数

     使用 aggregate()函数返回聚合函数的值
     函数: Avg, Count, Max, Min, Sum

    from django.db.models import Max
    maxDate = BookInfo.books.all().aggregate(Max('bpub_date'))
    

     count 的一般用法:

    count = BookInfo.books.all().count()
    

    —————————————————————————

    12.6F对象

    F()允许 Django 在未实际链接数据的情况下具有对数据库字段的值的引用。
    Python 操作方式
    通常情况下我们在更新数据时需要先从数据库里将原数据取出后方在内存里,然后编辑某些属性,最后提交.

    book = BookInfo.books.get(pk=3)
    print(book.bpub_date)
    delta = timedelta(days=3)
    book.bpub_date = book.bpub_date + delta
    book.save()
    book = BookInfo.books.get(pk=3)
    print(book.bpub_date)
    

    当我们使用了 F()之后呢?

    book = BookInfo.books.get(pk=3)
    print(book.bpub_date)
    delta = timedelta(days=3)
    book.bpub_date = F('bpub_date') + delta
    book.save()
    book = BookInfo.books.get(pk=3)
    print(book.bpub_date)
    

    结论
    当 Django 程序中出现 F()时,Django 会使用 SQL 语句的方式取代标准的 Python 操作。
    上述代码中不管 book.bpub_date 的值是什么,Python 都不曾获取过其值, python 做的唯一的事情就是通过 Django 的 F()函数创建了一条 SQL 语句然后执行而已。
    特别是并发的情况,效率也变高了,减少了多线程同时操作带来的隐患。 但是不支持字
    符串相加的操作
     可以使用模型的字段 A 与字段 B 进行比较,如果 A 写在了等号的左边,则 B 出现在等号
    的右边,需要通过 F 对象构造

    list.filter(bread__gte=F('bcommet'))
    

     django 支持对 F()对象使用算数运算

    list.filter(bread__gte=F('bcommet') * 2)
    

     F()对象中还可以写作“模型类__列名” 进行关联查询

    list.filter(isDelete=F('heroinfo__isDelete'))
    

     对于 date/time 字段,可与 timedelta()进行运算

    list.filter(bpub_date__lt=F('bpub_date') + timedelta(days=1))
    

    —————————————————————————

    12.7Q对象

     应用场景
     一般我们在 Django 程序中查询数据库操作都是在 QuerySet 里进行进行,例如下面
    代码:

    q1 = BookInfo.books.filter(bpub_date__gt=date(1992, 1, 1))
    q2 = q1.exclude(btitle__startswith="笑")
    q3 = q2.filter(bpub_date__year=1992)
    

     将其组合起来:

    q = BookInfo.books.filter(bpub_date__gt=date(1992, 1,
    1)).exclude(btitle__startswith="笑").filter(bpub_date__year=1992)
    

    问题
     复杂的查询条件,导致查询越来越长。
     引入 Q 对象
     Q()对象就是为了将这些条件组合起来。
     当我们在查询的条件中需要组合条件时(例如两个条件“且”或者“或” )时。我们可
    以使用 Q()查询对象。

    fromdjango.db.modelsimports Q
    Q(bpub_date__gt=date(1992, 1, 1))
    Q(btitle__startswith="笑")
    Q(bpub_date__year=1992)
    

     &(与)或者|(或) ~(非)
     可以将多个 Q()对象组合起来传递给 filter(), exclude(), get()等函数。
     当操作符应用在两个 Q 对象时,会产生一个新的 Q 对象
     结合括号进行分组,构造复杂的 Q 对象

    Q(bpub_date__gt=date(1992, 1, 1)) & ~Q(btitle__startswith="笑") &
    Q(bpub_date__year=1992)
    

     对应 SQL 语句可以理解为:

    WHERE bpub_date__gt=date(1992, 1, 1) and not btitle like "笑%" and
    year(bpub_date) =1992
    q = BookInfo.books.filter(Q(bpub_date__gt=date(1992, 1, 1)) &
    ~Q(btitle__startswith="笑") & Q(bpub_date__year=1992))
    

     改写一下

    c1 = Q(bpub_date__gt=date(1992, 1, 1))
    c2 = Q(btitle__startswith="笑")
    c3 = Q(bpub_date__year=1992)
    q= BookInfo.books.filter(c1 & ~c2 & c3)
    

     使用~(not)操作符在 Q 对象前表示取反

    list.filter(~Q(pk__lt=6))
    

    —————————————————————————

    感谢语

    胡咧咧乐这么长时间,我都觉的耽误大家时间了,我知道没有人会一口气看完这么多东西,不过你要是觉得有用的话,就收藏起来吧,或者转载啊什么的,反正我这个也不会收费的,总之可以帮到你就好!也不枉我这额头上留下来的汗水!!! 谢谢默默给我鼓励的大佬!!!

    相关文章

      网友评论

          本文标题:3.5Django再说Modle

          本文链接:https://www.haomeiwen.com/subject/bweplftx.html