• 第6天:数据库配置与模型


    •  数据库配置
    •  Django模型类定义
    •  字段类型与选项
    •  模型迁移
    •  Django交互环境
    •  增
    •  删
    •  改
    •  基本查询
    •  过滤查询
    •  F对象
    •  Q对象
    •  排序
    •  聚合
    •  关联查询
    •  查询集QuerySet
    •  自定义模型管理器

    数据库配置

    ORM Object relational mapping 对象关系映射,把类和数据库表对应,把对象和表记录对应,通过类和对象操作数据库表中的数据,而不需要编写SQL语句

    1、使用MySQL数据库首先需要安装驱动程序

    pip install PyMySQL

    2、在Django的工程同名子目录的__init__.py中添加如下语句,作用是让Django的ORM能以mysqldb的方式来调用PyMySQL

    from pymysql import install_as_MySQLdb
    
    install_as_MySQLdb()

    3、编辑settings.py,修改DATABASE配置信息

    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'HOST': '127.0.0.1',#主机 
            'PORT': 3306,       #端口
            'USER': 'root',     #数据库用户名
            'PASSWORD': 'root', #数据库用户密码
            'NAME': 'demo'   #数据名字
        }
    }
    settings.py

    4、在MySQL中创建数据库

    create database demo default charset=utf8;

    Django模型类的定义

    • 模型类被定义到‘应用/models.py文件中’
    • 模型类必须继承Model类,位于包django.db.models中

    创建应用book,在models.py文件中定义模型

    from django.db import models
    
    
    class Book(models.Model):
        title = models.CharField(max_length=20, verbose_name='名称')
        pub_date = models.DateField(verbose_name='发布日期')
        read_num = models.IntegerField(default=0, verbose_name='阅读量')
        comment_num = models.IntegerField(default=0, verbose_name='评论量')
        is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
    
        class Meta:
            db_table = 'book'   #指明数据库表名
            verbose_name = '图书'  #在admin站点中显示的名称
            verbose_name_plural = verbose_name #显示复数的名称
    
        def __str__(self):
            """定义每个数据对象的显示信息"""
            return self.title
    
    
    class Hero(models.Model):
        GENDER_CHOICES = (
            (0, 'male'),
            (1, 'female')
        )
        name = models.CharField(max_length=20, verbose_name='名称')
        gender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')
        comment = models.CharField(max_length=200, null=True, verbose_name='描述信息')
        book = models.ForeignKey(Book, on_delete=models.CASCADE, verbose_name='图书') #CASCADE级联,删除主表数据时连同一起删除外键表中的数据
        is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
    
        class Meta:
            db_table = 'hero'
            verbose_name = '英雄'
            verbose_name_plural = verbose_name
    
        def __str__(self):
            return self.name
    book.models

     关于主键

    • 主键:primary key,简写 pk
    • 不需要主动定义,django会自动生成自增长的主键,属性名叫 id,
    • 如果开发者自己定义了主键,则django不会再生成默认的主键

    字段类型与选项

    类型说明
    AutoField 自动增长的IntegerField,通常不用指定,不指定时Django会自动创建名为id的自动增长属性
    BooleanField 布尔字段,值为True或False
    NullBooleanField 支持Null、True、False三种值
    CharField 字符串,必须指定:max_length,表示最大字符个数
    TextField 大文本字段,一般超过4000个字符时使用
    IntegerField 整数
    DecimalField 十进制浮点数,用python中的Decimal实例来表示
    必须指定: max_digits总位数,decimal_places小数位数。 
    FloatField 浮点数
    DateField 日期 
    1) 参数auto_now表示每次修改保存对象时,自动设置该字段为当前时间,用于保存"最后一次修改"时间,默认为False; 
    2) 参数auto_now_add表示当对象第一次被创建时自动设置保存当前时间,用于保存"创建时间"时间,默认为值为False; 
    3) 参数auto_now_addauto_now是相互排斥的,不能同时用到一个属性中
    TimeField 时间,参数同DateField
    DateTimeField 日期时间,参数同DateField
    FileField 上传文件字段
    ImageField 继承于FileField,对上传的内容进行校验,确保是有效的图片
    选项默认值描述是否要迁移修改表结构
    null False True表示表字段允许为空
    unique False True表示表字段不能重复
    db_column 属性名称 表字段名称
    primary_key False True表示字段设置为了主键,一般作为AutoField的选项使用
    default - 默认值
    blank False 在django管理后台新增或编辑一条表数据时,该字段是否允许为空;
    null是数据库范畴的概念,blank是表单验证范畴的
    choices - 在django管理后台新增或编辑一条表数据时,该字段显示为下拉框,默认为编辑框
    • choices: 性别属性使用了choices选项后,在录入一条数据时,会以下拉框显示
    • blank:blank属性默认值为false, 表示录入一条数据时,当前字段必须填写,不能为空,否则js端js校验不通过,例如:下图的comment员工备注信息字段。

    外键

    • ForeignKey: 一对多,将 关联属性 定义在多的一端中
    • ManyToManyField: 多对多,将 关联属性 定义任意一方中
    • OneToOneField: 一对一,将 关联属性 定义在任意一方中

    模型迁移

    定义完模型类以后,我们需要把模型生成到数据库里面去

    python manage.py mikemigrations
    python manage.py migrate

    Django交互环境

    在Django交互环境中,可以直接执行django项目代码,类似ipython交换环境

    通过shell命令进入Django交换环境

    insert into book(name,pub_date,read_num,comment_num,is_delete) values
    ('射雕英雄传','1980-5-1',12,34,0),
    ('天龙八部','1986-7-24',36,40,0),
    ('笑傲江湖','1995-12-24',28,18,0),
    ('雪山飞狐','1987-11-11',58,24,0);
    
    insert into hero(name,gender,book_id,comment,is_delete) values
    ('郭靖',1,1,'降龙十八掌',0),
    ('黄蓉',0,1,'打狗棍法',0),
    ('黄药师',1,1,'弹指神通',0),
    ('欧阳锋',1,1,'蛤蟆功',0),
    ('梅超风',0,1,'九阴白骨爪',0),
    ('乔峰',1,2,'降龙十八掌',0),
    ('段誉',1,2,'六脉神剑',0),
    ('虚竹',1,2,'天山六阳掌',0),
    ('王语嫣',0,2,'神仙姐姐',0),
    ('令狐冲',1,3,'独孤九剑',0),
    ('任盈盈',0,3,'弹琴',0),
    ('岳不群',1,3,'华山剑法',0),
    ('东方不败',0,3,'葵花宝典',0),
    ('胡斐',1,4,'胡家刀法',0),
    ('苗若兰',0,4,'黄衣',0),
    ('程灵素',0,4,'医术',0),
    ('袁紫衣',0,4,'六合拳',0);
    给数据库添加测试数据

    现在我们可以在Django交互环境测试数据库操作,首先我们来对数据库进行增加操作,有两种方式

    1)模型类对象.save()

    from book.models import Book, Hero
    from datetime import date
    
    
    book = Book(
      title='西游记',
      pu_date=date(1988,1,1),
      read_num=10,
      comment_num=10
    )
    
    book.save
    增加一个图书

    2)通过模型类.objects.creare()保存

    from book.models import Book, Hero 
    
    b = Book.objects.get(title='西游记')
    
    Hero.objects.create(
      name='沙悟净',
      gender=0,
      book=book
    )
    
    或者
    Hero.objects.create(
      name='沙悟净',
      gender=0,
      book_id=book.id
    )
    增加一个英雄

    删除记录有两种方式

    1)模型类对象.delete()

    book = Book.objects.get(id=1)
    book.delete()
    删除id为1的图书

    2)模型类.filter(条件).delete()

    Book.objects.filter(id=1).delete()
    删除id为1的图书

    注意事项: on_delete选项

    • 默认值为models.CASCADE,当删除图书时,会删除相关联的英雄
    • 如果不想删除关联数据,可设置on_delete为 PROTECT

    修改记录有以下两种方式

    1)模型类对象.save()

    h = Hero.objects.get(name='沙悟净')
    h.name = '沙僧'
    h.save()
    把沙悟净的名字改为沙僧

    2)模型类.filter()条件.update(属性1=值1, 属性2=值2, ...)

    Hero.objetcs.filter(name='猪八戒').update(name='猪悟能')
    把猪八戒的名字改为猪悟能

    基本查询

    get 查询单一结果,如果不存在或抛出模型类.DoesNotExist异常,通过存在多条记录会抛出MultipleObjectsReturned异常

    #查询id为3的图书
    In [7]: Book.objects.get(id=3)
    Out[7]: <Book: 笑傲江湖>
    
    #查询id为100的图书,该图书其实不存在
    In [8]: Book.objects.get(id=100)
    ---------------------------------------------------------------------------
    DoesNotExist                              Traceback (most recent call last)
    ...
    
    #查询is_delete为false的图书,有多条
    In [9]: Book.objects.get(is_delete=0)
    ---------------------------------------------------------------------------
    MultipleObjectsReturned                   Traceback (most recent call last)
    示例

    all 查询多个结果

    In [6]: Book.objects.all()
    Out[6]: <QuerySet [<Book: 射雕英雄传>, <Book: 天龙八部>, <Book: 笑傲江湖>, <Book: 雪山飞狐>, <Book: 西游记>]>
    查询出所有的图书

    count 查询结果数量

    In [10]: Hero.objects.count()
    Out[10]: 20
    查询英雄的总数

    过滤查询

    •  调用filter方法:模型类.objects.filter(模型属性名__条件名=值)
    •  返回包含查询结果数据的QuerySet对象

    1)判等: exact

    In [11]: Book.objects.filter(id__exact=1)
    Out[11]: <QuerySet [<Book: 射雕英雄传>]>
    
    可简写为
    In [12]: Book.objects.filter(id=1)
    Out[12]: <QuerySet [<Book: 射雕英雄传>]>
    查询id为1的图书

    2)模糊查询: contains / endswith / startswith

    In [13]: Hero.objects.filter(name__contains='')
    Out[13]: <QuerySet [<Hero: 岳不群>, <Hero: 东方不败>]>
    查询名字包含"不"的英雄
    In [14]: Hero.objects.filter(name__startswith='')
    Out[14]: <QuerySet [<Hero: 黄蓉>, <Hero: 黄药师>]>
    查询名字以'黄'开头的英雄
    • 上面查询都是区分大小写的,如果不想区分大小写,只需要加个i,如 iexact、icontains、iendstartswith、istartswith
    • 如何想查询包含%的字符,%不需要转义

    3)空查询: isnull

    In [17]: Book.objects.filter(title__isnull=False)
    Out[17]: <QuerySet [<Book: 射雕英雄传>, <Book: 天龙八部>, <Book: 笑傲江湖>, <Book: 雪山飞狐>, <Book: 西游记>]>
    查看图书名不为空的图书

    4)范围查询: in

    In [20]: Book.objects.filter(id__in=[1,3,5])
    Out[20]: <QuerySet [<Book: 射雕英雄传>, <Book: 笑傲江湖>, <Book: 西游记>]>
    查询id编号为1,3,5的图书

    5)比较查询: gt、lt、gte、lte

    In [21]: Book.objects.filter(read_num__gt=20)
    Out[21]: <QuerySet [<Book: 天龙八部>, <Book: 雪山飞狐>]>
    查询阅读量大于20的图书

    6)日期查询: year、month、day、week_day、hour、minute、second

    In [22]: Book.objects.filter(pub_date__year=1980)                                                                                                               
    Out[22]: <QuerySet [<Book: 射雕英雄传>]>
    查询1980年发表的图书
    In [23]: Book.objects.filter(pub_date__gt=date(1980,1,1))
    Out[23]: <QuerySet [<Book: 射雕英雄传>, <Book: 西游记>]>
    查询1980年1月1日后发表的图书

    F对象

    之前查询都是对象的属性与常量值比较,两个属性怎么比较?这就需要用到F对象

    需要导包: from django.db.models import F

    语法: F(属性名)

    In [25]: from django.db.models import F
    
    In [26]: Book.objects.filter(read_num__gte=F('comment_num'))
    Out[26]: <QuerySet [<Book: 雪山飞狐>, <Book: 西游记>]>
    查询阅读量大于等于评论量的图书

    可以在F对象上使用算数运算

    In [27]: from django.db.models import F
    
    In [28]: Book.objects.filter(read_num__gt=F('comment_num') * 2)
    Out[28]: <QuerySet [<Book: 雪山飞狐>]>
    查询阅读量大于2倍评论量的图书

    Q对象

    作用: 对查询条件进行与 或 非(& | ~)的逻辑操作

    需要导包: from django.db.models import Q

    与: Q(查询条件1) & Q(查询条件2)

    In [29]: from django.db.models import Q
    
    In [30]: Hero.objects.filter(Q(id__gt=2) & Q(name__contains=''))
    Out[30]: <QuerySet [<Hero: 黄药师>]>
    
    可以简写为
    In [31]: Hero.objects.filter(id__gt=2, name__contains='')
    Out[31]: <QuerySet [<Hero: 黄药师>]>
    查询id大于2且名字包含'黄'的英雄

    或: Q(查询条件1) | Q(查询条件2)

    In [34]: Hero.objects.filter(Q(id__lt=2) | Q(name__contains=''))
    Out[34]: <QuerySet [<Hero: 郭靖>, <Hero: 岳不群>, <Hero: 东方不败>]>
    查询id小于2或名字包含'不'的英雄

    非: ~Q(查询条件)

    Hero.objects.filter(~Q(id=3))
    查询id不等于3的英雄

     排序

    作用: 对查询结果进行排序,默认为升序

    用法:

    • 升序: 模型类.objects.order_by('属性名')
    • 降序: 模型类.objects.order_by('-属性名')
    In [37]: Book.objects.all().order_by('-id')
    Out[37]: <QuerySet [<Book: 西游记>, <Book: 雪山飞狐>, <Book: 笑傲江湖>, <Book: 天龙八部>, <Book: 射雕英雄传>]>
    查询所有图书,按照id从到小进行排序

    聚合

    作用: 聚合操作,对多行查询结果中的一列进行操作,返回一个值
    用法: 模型类.objects.aggregate(聚合类('属性名'))

    • 常用聚合类有:Sum, Count, Max, Min, Avg等
    • 返回值是一个字典, 格式: {'属性名__聚合函数': 值}
    • 使用时需要先导入聚合类: from django.db.models import Sum, Count, Max, Min, Avg
    In [38]: from django.db.models import Avg
    
    In [39]: Book.objects.aggregate(Avg('read_num'))
    Out[39]: {'read_num__avg': 25.2}
    查询所有图书的平均阅读量

    关联查询

    1)、由 一类对象 查询 多类对象: 一类对象.多类名小写_set.all()

    In [42]: b = Book.objects.get(title='西游记')
    
    In [43]: b.hero_set.all()
    Out[43]: <QuerySet [<Hero: 孙悟空>, <Hero: 猪悟能>, <Hero: 沙僧>]>
    查询图书'西游记'的所有英雄

    2)、由 多类对象 查询 一类对象: 多类对象.关联属性

    In [44]: h = Hero.objects.get(name='郭靖')
    
    In [45]: h.book
    Out[45]: <Book: 射雕英雄传>
    查询"郭靖"所属图书信息

     通过模型类实现上述两个案例

    • 一类名.objects.filter(多类名小写__多类属性名__条件名=值)
    • 多类名.objects.filter(关联属性__一类属性名__条件名=值)
    In [48]: Hero.objects.filter(book__title='西游记')
    Out[48]: <QuerySet [<Hero: 孙悟空>, <Hero: 猪悟能>, <Hero: 沙僧>]>
    
    In [49]: Book.objects.filter(hero__name='郭靖')
    Out[49]: <QuerySet [<Book: 射雕英雄传>]>

    查询集 QuerySet

    查询集,也称查询结果集、QuerySet,表示从数据库中获取的对象集合,当调用一下过滤器方法时,Django会返回查询集(而不是简单的列表)

    • all(): 返回所有数据
    • filter(): 返回满足条件的数据
    • exclude(): 返回满足条件之外的数据
    • order_by(): 对结果进行排序

    对查询集可以再次调用过滤器进行过滤

    Book.objects.filter(read_num__gt=30).order_by('pub_date')

    查询集两大特性

    1)惰性执行

    创建查询集不会访问数据库,直到调用数据时,才会访问数据库,调用数据的情况包括迭代、序列化、与if合用

    例如,当执行如下语句时,并未进行数据库查询,只是创建了一个查询集qs

    qs = Book.objects.all()

    继续执行遍历迭代操作后,才真正的进行数据库查询

    for book in qs:
        print(book.title)

    2)缓存

    • 第一次查询数据后,Django会将查询集缓存起来,并返回请求的结果
    • 再次查询相同数据时将重用缓存的结果
      from Book.models import Book
    
      [book.id for book in Book.objects.all()]
      [book.id for book in Book.objects.all()]
    无缓存:查询集有两个
     from Book.models import Book
    
      list=Book.objects.all()
      [book.id for objects in list]
      [book.id for objectsin list]
    有缓存:查询集只有一个

    限制查询集

    可以对查询集进行取下标或切片操作,等同于sql中的limit和offset子句(注意,不支持负数索引)

    对查询集进行切片后返回一个新的查询集,不会立即执行查询

    如果获取一个对象,直接使用[0],等同于[0:1].get(),但是如果没有数据,[0]引发IndexError异常,[0:1].get()引发DoesNotExist异常

    qs = Book.objects.all()[0:2]
    获取第1、2项

    自定义模型管理器

    可以自定义模型管理器,比如以下两种场景:

    •  需要重写模型管理器中现有的方法
      • 需求:调用Book.objects.all()时,返回的是is_delete等于False的图书
    • 封装增删改查的方法
      • 需求:在管理器类中,封装一个创建图书的方法,方便直接调用

    注意:自定义后模型管理器后, Django 将不再自动生成默认的 objects

     # 在book.models.py文件添加以下代码, 自定义模型管理器
     class BookManager(Manager):
    
         def all(self):
             """重写all方法:只返回没有删除的部门"""
             return super().all().filter(is_delete=False)
    
         def create_book(self, title, pub_date):
             """封装新增图书的方法,方便调用"""
    
             book = Book()
             book.title = title
             book.pub_date = pub_date
             book.save()
             return book
    
    
    
    # 自定义模型管理器
     class Book(models.Model):
         """部门类"""
         ...
    
         # 自定义模型管理器
         objects = BookManager()
    实现参考
  • 相关阅读:
    SPOJ1043 GSS1(线段树)
    cf1028C. Rectangles(前缀和)
    cf559C. Gerald and Giant Chess(容斥原理)
    五校联考解题报告
    BZOJ1853: [Scoi2010]幸运数字(容斥原理)
    使用spring-amqp结合使用rabbitmq
    instanceof关键字
    qml学习:对象和属性
    [置顶] 推荐12款很棒的HTML5开发框架和开发工具
    linux系统开机过程描述
  • 原文地址:https://www.cnblogs.com/sellsa/p/10804755.html
Copyright © 2020-2023  润新知