Python框架Django -- 02 模型

一、完善案例


1、完成根据图书显示图书下所有英雄的功能


(1)、在 booktest/views.py 中编写根据图书id查询英雄信息的函数




(2)、在 booktest/urls.py 中添加url和函数的映射




(3)、编写模版文件


<CTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>显示英雄人物信息</title>
    </head>
    <body>
        <h1>{{ book.btitle }}</h1>
        英雄人物信息如下:<br/>
        <ul>
            {% for hero in heros %}
                <li>{{ hero.hname }}--{{ hero.hcomment }}</li>
            {% empty %}  <!-- 该标签的作用是如果列表中没有数据,则显式该标签之后的数据 -->
                <li>没有英雄信息</li>
            {% endfor %}
        </ul>
    </body>
</html>


(4)、浏览器测试




2、切换到MySQL数据库


(1)、在虚拟环境中安装MySQLdb的包


安装pymysql包




在 test1/__init__.py 中加入以下配置


import pymysql
pymysql.install_as_MySQLdb()


(2)、在test1/settings.py中配置MySQL的链接




(3)、执行迁移操作




(4)、进入数据库查看数据库表




3、添加案例删除功能前的准备操作


(1)、在模型类文件 models.py 中添加属性




(2)、删除之前的迁移文件,并重新生成新的迁移文件




(3)、删除mysql数据库的重复表以及django_migrations表中之前的迁移记录数据






(4)、执行迁移操作




(5)、向数据库的表中插入数据


insert into booktest_bookinfo(bittle,bpud_date,bread,bcomment,isDelete) values
('射雕英雄传','1980-5-1',12,34,0),
('天龙八部','1986-7-24',36,40,0),
('笑傲江湖','1995-12-24',20,80,0),
('雪山飞狐','1987-11-11',58,24,0);

insert into booktest_heroinfo(hname,hgender,hbook_id,hcoment,isDelete) 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);

4、完成图书信息展示页的功能


(1)、在views.py中重新定义index函数




(2)、修改 index.html 模版页面




(3)、配置urls.py




二、模型类详解


1、定义属性


属性定义的语法:


属性 = models.字段类型(选项)

Django根据属性的类型确定以下信息


        当前选择的数据库支持字段的类型。


        渲染管理表单时使用的默认html控件。


        在管理站点最低限度的验证。


django会为表创建自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后django不会再创建自动增长的主键列。


默认创建的主键列属性为id,可以使用pk代替,pk全拼为primary key。


注意:pk是主键的别名,若主键名为id2,那么pk是id2的别名。



属性命名限制


        不能是python的保留关键字。


        不允许使用连续的下划线,这是由django的查询方式决定的。


        定义属性时需要指定字段类型,通过字段类型的参数指定选项,语法如下:


2、字段类型详解


注意:使用时需要引入django.db.models包。


from django.db import models

        AutoField:自动增长的IntegerField,通常不用指定,不指定时Django会自动创建属性名为id的自动增长属性。


        BooleanField:布尔字段,值为True或False。


        NullBooleanField:支持Null、True、False三种值。


        CharField(max_length=字符长度):字符串。


                参数max_length表示最大字符个数。


        TextField:大文本字段,一般超过4000个字符时使用。


        IntegerField:整数。


        DecimalField(max_digits=None, decimal_places=None):十进制浮点数。


                参数max_digits表示总位数。


                参数decimal_places表示小数位数。


        FloatField:浮点数。


        DateField[auto_now=False, auto_now_add=False]):日期。


                参数auto_now表示每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,默认为false。


                参数auto_now_add表示当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为false。


                参数auto_now_add和auto_now是相互排斥的,组合将会发生错误。


        TimeField:时间,参数同DateField。


        DateTimeField:日期时间,参数同DateField。


        FileField:上传文件字段。


        ImageField:继承于FileField,对上传的内容进行校验,确保是有效的图片。


3、选项详解


通过选项实现对字段的约束,选项如下:


        null:如果为True,表示允许为空,默认值是False。


        blank:如果为True,则该字段允许为空白,默认值是False。


                对比:null是数据库范畴的概念,blank是表单验证范畴的。


        db_column:字段的名称,如果未指定,则使用属性的名称。


        db_index:若值为True, 则在表中会为此字段创建索引,默认值是False。


        default:默认值。


        primary_key:若为True,则该字段会成为模型的主键字段,默认值是False,一般作为AutoField的选项使用。


        unique:如果为True, 这个字段在表中必须有唯一值,默认值是False。


三、查询集


        查询集表示从数据库中获取的对象集合,在管理器上调用某些过滤器方法会返回查询集,查询集可以含有零个、一个或多个过滤器。过滤器基于所给的参数限制查询的结果,从Sql的角度,查询集和select语句等价,过滤器像where和limit子句。


1、返回查询集的过滤器如下:


        all():返回所有数据。


        filter():返回满足条件的数据。


        exclude():返回满足条件之外的数据,相当于sql语句中where部分的not关键字。


        order_by():对结果进行排序。

2、返回单个值的过滤器如下:


        get():返回单个满足条件的对象


                如果未找到会引发"模型类.DoesNotExist"异常。


                如果多条被返回,会引发"模型类.MultipleObjectsReturned"异常。


        count():返回当前查询结果的总条数。


        aggregate():聚合,返回一个字典。


3、判断某一个查询集中是否有数据:


        exists():判断查询集中是否有数据,如果有则返回True,没有则返回False。


4、两大特性


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


        缓存:使用同一个查询集,第一次使用时会发生数据库的查询,然后把结果缓存下来,再次使用这个查询集时会使用缓存的数据。


5、查询集的缓存


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

6、限制查询集


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


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


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


四、查询


1、查看MySQL日志文件


Django框架默认帮我们生成可以在MySQL中执行的sql语句,可以在MySQL的日志文件中查看sql语句


(1)、修改MySQL数据库的配置文件


打开配置文件中MySQL日志相关的注释




(2)、重新启动MySQL服务


service mysql restart


(3)、查看MySQL日志文件




2、查询语法


get():返回表中满足条件的一条且只能有一条数据。


        如果查到多条数据,则抛异常:MultipleObjectsReturned


        查询不到数据,则抛异常:DoesNotExist


all():返回模型类对应表格中的所有数据。返回值是QuerySet类型,查询集


filter(模型类属性名__条件名=值):参数写查询条件,返回满足条件的数据。返回值是QuerySet类型


3、条件运算符


exact:表示判等


>>> from booktest.models import *
>>> from datetime import date
>>> 
>>> # 查询id为1的图书信息
>>> book = BookInfo.objects.filter(id=1)
>>> book
[<BookInfo: 射雕英雄传>]
>>> book = BookInfo.objects.filter(id__exact=1)
>>> book
[<BookInfo: 射雕英雄传>]


contains:表示包含,包含%时无需转义,可直接写%


>>> # 查询包含‘雕’的图书信息
>>> list = BookInfo.objects.filter(bittle__contains='雕')
>>> list
[<BookInfo: 射雕英雄传>]


startswith、endswith:以指定的字符开头或者结尾


>>> # 查询以'射'开头的图书信息
>>> list = BookInfo.objects.filter(bittle__startswith='射')
>>> list
[<BookInfo: 射雕英雄传>]
>>> 
>>> 
>>> # 查询以'传'结尾的图书信息
>>> list = BookInfo.objects.filter(bittle__endswith='传')
>>> list
[<BookInfo: 射雕英雄传>]


isnull:是否为null


>>> # 查询书名不为空的图书信息
>>> list = BookInfo.objects.filter(bittle__isnull=False)
>>> list
[<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>, <BookInfo: 笑傲江湖>, <BookInfo: 雪山飞狐>, <BookInfo: 鹿鼎记>]
>>> 


in:是否包含在范围内


>>> # 查询图书id为1或3或5的图书信息
>>> list = BookInfo.objects.filter(id__in=[1,3,5])
>>> list
[<BookInfo: 射雕英雄传>, <BookInfo: 笑傲江湖>, <BookInfo: 鹿鼎记>]
>>> 


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


>>> # 查询编号大于3的图书信息    
>>> list = BookInfo.objects.filter(id__gt=3)
>>> list
[<BookInfo: 雪山飞狐>, <BookInfo: 鹿鼎记>]
>>> 


exclude:不包括(不等于)


>>> # 查询编号大于3的图书信息    
>>> list = BookInfo.objects.filter(id__gt=3)
>>> list
[<BookInfo: 雪山飞狐>, <BookInfo: 鹿鼎记>]
>>> 


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


>>> # 查询1980年发表的图书信息
>>> list = BookInfo.objects.filter(bpud_date__year=1980)
>>> list
[<BookInfo: 射雕英雄传>]
>>> 
>>> # 查询1980年1月1日以后发表的图书信息
>>> list = BookInfo.objects.filter(bpud_date__gt=date(1980, 1, 1))
>>> list
[<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>, <BookInfo: 笑傲江湖>, <BookInfo: 雪山飞狐>, <BookInfo: 鹿鼎记>]
>>> 


4、F对象


        之前的查询都是对象的属性与常量值比较,两个属性怎么比较呢? 答:使用F对象,被定义在django.db.models中。语法如下:


F(属性名)


示例:


>>> from django.db.models import F
>>> 
>>> # 查询阅读量大于评论量的图书信息
>>> list = BookInfo.objects.filter(bread__gt=F('bcomment'))
>>> list
[<BookInfo: 雪山飞狐>]
>>> 
>>> # 查询阅读量大于等于2倍评论量的图书信息
>>> list = BookInfo.objects.filter(bread__gte=F('bcomment') * 2)
>>> list
[<BookInfo: 雪山飞狐>, <BookInfo: 鹿鼎记>]
>>> 


5、Q对象


多个过滤器逐个调用表示逻辑与关系,等同于sql语句中where部分的and关键字。


>>> # 查询阅读量大于20并且编号小于3的图书信息
>>> list = BookInfo.objects.filter(bread__gt=20,id__lt=3)
>>> list
[<BookInfo: 天龙八部>]
>>> 
>>> # 或者如下写法
>>> list = BookInfo.objects.filter(bread__gt=20).filter(id__lt=3)
>>> list
[<BookInfo: 天龙八部>]
>>>

如果需要实现逻辑或or的查询,需要使用Q()对象结合|运算符,Q对象被义在django.db.models中。语法如下:


Q(属性名__运算符=值)


示例:


from django.db.models import Q
 
# 使用Q对象查询阅读量大于20的图书
list = BookInfo.objects.filter(Q(bread__gt=20))
print(list)
# 结果:[<BookInfo: 天龙八部>, <BookInfo: 雪山飞狐>]


Q对象可以使用&、|连接,&表示逻辑与,|表示逻辑或。


# 查询阅读量大于20或者id小于3的图书信息
list = BookInfo.objects.filter(Q(bread__gt=20) | Q(id__lt=3))
print(list)
# 结果:[<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>, <BookInfo: 雪山飞狐>]
 


Q对象前可以使用~操作符,表示非not。


# 查询编号不为3的图书信息
list = BookInfo.objects.filter(~Q(pk=3))
print(list)
# 结果:[<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>, <BookInfo: 雪山飞狐>, <BookInfo: 鹿鼎记>]


6、对查询结果进行排序


from booktest.models import *

# 查询所有图书信息,按照id从小到大排列
BookInfo.objects.all().order_by('id')
# 结果:[<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>, <BookInfo: 笑傲江湖>, <BookInfo: 雪山飞狐>, <BookInfo: 鹿鼎记>]
BookInfo.objects.order_by('id')
# 结果:[<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>, <BookInfo: 笑傲江湖>, <BookInfo: 雪山飞狐>, <BookInfo: 鹿鼎记>]


# 查询所有图书信息,按照id从大到小排列
BookInfo.objects.order_by('-id')
# 结果:[<BookInfo: 鹿鼎记>, <BookInfo: 雪山飞狐>, <BookInfo: 笑傲江湖>, <BookInfo: 天龙八部>, <BookInfo: 射雕英雄传>]


# 查询所有id大于3的图书,并按照阅读量从大到校的顺序排列
BookInfo.objects.filter(id__gt=3).order_by('-bread')
# 结果:[<BookInfo: 雪山飞狐>, <BookInfo: 鹿鼎记>]


7、聚合函数


        使用aggregate( )过滤器调用聚合函数。聚合函数包括:AvgCountMaxMinSum,被定义在django.db.models中。注意aggregate的返回值是一个字典类型,格式如下:


 {'聚合类小写__属性名':值}
  如:{'sum__bread':3}


>>> from django.db.models import *
>>> 
>>> # 查询图书的总阅读量
>>> dic = BookInfo.objects.aggregate(Sum('bread'))
>>> dic
{'bread__sum': 126}
>>> dic['bread__sum']
126
>>> 


使用count时一般不使用aggregate()过滤器。count返回值是一个int类型的数字


>>> # 查询图书总数
>>> count = BookInfo.objects.count()
>>> type(count)
<class 'int'>
>>> count
5
>>> 


五、关联


1、模型类之间的关系


(1)、关系字段类型


关系型数据库的关系包括三种类型:


        ForeignKey:一对多,将字段定义在多的一端中。


        ManyToManyField:多对多,将字段定义在任意一端中。


        OneToOneField:一对一,将字段定义在任意一端中。


(2)、一对多模型类


"""
    定义一对多类:图书类和英雄类

    使用关键字:ForeignKey('一的一方的类名')    定义在多方中,关联一方的类名
"""
# 定义一个图书类,继承自models.Model
class BookInfo(models.Model):
    """图书模型类"""
    # 定义属性

    # CharField :表明该属性对应字段为字符串类型,max_length : 设置字段长度
    bittle = models.CharField(max_length=20)    # 图书名

    # DateField : 表明该字段为Date类型 
    bpud_date = models.DateField()  # 出版日期

    bread = models.IntegerField(default=0)  # 阅读量
    bcomment = models.IntegerField(default=0)   # 图书评论量
    isDelete = models.BooleanField(default=False)   # 图书删除标记,False代表未删除

    # 添加__str__方法
    def __str__(self):
        return self.bittle

# 定义一个英雄类,继承自models.Model
class HeroInfo(models.Model):
    """英雄信息类"""
    # 定义属性

    hname = models.CharField(max_length=20)     # 英雄名称
    hgender = models.BooleanField(default=False)        # 英雄性别,Boolean类型的值,默认为False(男)
    hcoment = models.CharField(max_length=20)   # 英雄备注
    """
            BookInfo和HeroInfo是一对多的关系,需要在多的一方中这只外键用来关联一的一方的主键
    """
    hbook = models.ForeignKey('BookInfo')       # 关系属性,建立BookInfo和HeroInfo的一对多的关系
    isDelete = models.BooleanField(default=False)   # 英雄删除标记


(3)、多对多模型类


"""
    定义多对多类:新闻类和新闻类型类
    
    使用关键字:ManyToManyField('另一方的类名')     定义在任意一方即可
    
"""
# 定义新闻类型类
class TypesInfo(models.Model):
    """新闻类型类"""
    type_title = models.CharField(max_length=20)    # 新闻类型

# 定义新闻类
class NewsInfo(models.Model):
    """新闻类"""
    news_title = models.CharField(max_length=128)   # 新闻标题
    pub_date = models.DateTimeField(auto_now_add=True)  # 发布时间,默认是创建对象的时间
    news_content = models.TextField()   # 新闻内容,大文本类型
    new_type = models.ManyToManyField('TypesInfo')  # 建立多对多关系


(4)、一对一模型类


"""
    定义一对一类:员工基本信息类和员工详细信息类

    使用关键字:OneToOneField('另一方的类名')   定义在任意一方即可
"""
# 定义员工基本信息类
class EmployeeBasicInfo(models.Model):
    """员工基本信息类"""
    employee_id = models.CharField(max_length=40,primary_key=True)  # 员工编号,设置为主键
    employee_name = models.CharField(max_length=24) # 员工姓名
    employee_age = models.IntegerField()    # 员工年龄
    employee_detail = models.OneToOneField('EmployeeDetailInfo')    # 设置员工基本信息类和员工详细信息类的一对一关系


# 定义员工详细信息类
class EmployeeDeatilInfo(models.Model):
    """员工详细信息类"""
    employee_addr = models.CharField(max_length=256)    # 地址
    employee_comment = models.CharField(max_length=256) # 备注信


2、一对多的关联查询


(1)、通过对象执行关联查询


由一到多的访问语法:


from booktest.models import *
 
# 由一查询多方集合:一方对象.多方模型类小写_set

# 查询id为1的图书下的所有英雄信息
book = BookInfo.objects.get(id=1)
book.heroinfo__set.all()

# 结果:[<HeroInfo: HeroInfo object>, <HeroInfo: HeroInfo object>, <HeroInfo: HeroInfo object>, <HeroInfo: HeroInfo object>, <HeroInfo: HeroInfo object>]
>>>


由多到一的查询语法:


# 由多查一:多方对象.多方模型类中关系类属性名
 
# 查询id为1的英雄对象的图书信息
hero = HeroInfo.objects.get(id=1)
hero.hbook    # hbook是英雄类中图书类的关系字段
# 结果:<BookInfo: 射雕英雄传>


由多查询一对方对象的id语法:


# 由多查一的id:多对应的模型类对象.关联类属性_id

# 查询id为1的英雄对应的图书的id
hero = HeroInfo.objects.get(id=1)
hero.hbook_id

# 结果:1


(2)、通过模型类执行关联查询


由多方通过条件查询一方数据语法:


# 由多方条件查询一方数据:关联模型类名小写__属性名__条件运算符 = 值

# 查询描述信息中含有'八'的英雄对应的图书信息  
list = BookInfo.objects.filter(heroinfo__hcoment__contains = '八') 

print(list)

# 结果:[<BookInfo: 射雕英雄传>, <BookInfo: 天龙八部>]
 


由一方通过条件查询多方数据语法:


# 由一方条件查询多方数据:一模型类关联属性名__一模型类属性名__条件运算符=值
 
# 查询书名为'天龙八部'的图书下的所有英雄
list = HeroInfo.objects.filter(hbook__bittle = '天龙八部')
print(list)

# 结果:[<HeroInfo: HeroInfo object>, <HeroInfo: HeroInfo object>, <HeroInfo: HeroInfo object>, <HeroInfo: HeroInfo object>]


3、自关联


模型类


"""
    一对多中的自关联:省市县自关联类
"""
class AreaInfo(models.Model):
    """地区类"""
    aTitle = models.CharField(max_length=20)    # 地区名称
    # 自关联字段,代表当前地区的父级地区
    aParent = models.ForeignKey('self', null=True, blank=True)


六、模型类扩展


1、模型类实例方法


        str():在将对象转换成字符串时会被调用。


        save():将模型对象保存到数据表中,ORM框架会转换成对应的insert或update语句。


        delete():将模型对象从数据表中删除,ORM框架会转换成对应的delete语句


2、模型类的属性


objects:管理器,是models.Manager类型的对象,用于与数据库进行交互。


当没有为模型类定义管理器时,Django会为每一个模型类生成一个名为objects的管理器,自定义管理器后,Django不再生成默认管理器objects。


为模型类BookInfo定义管理器books语法如下:


class BookInfo(models.Model):
    ...
    books = models.Manager()


3、管理器Manager


管理器是Django的模型进行数据库操作的接口,Django应用的每个模型类都拥有至少一个管理器。Django支持自定义管理器类,继承自models.Manager。


自定义管理器类主要用于两种情况:


        ①、修改原始查询集,重写all()方法


        ②、向管理器类中添加额外的方法,如向数据库中插入数据。


(1)、修改原始查询集,修改all( ),自定义创建对象的方法


打开booktest/models.py文件,定义类BookInfoManager


"""
    定义BookInfoManager
    
    图书管理器

"""
class BookInfoManager(models.Manager): 
    """图书管理器"""

    # 定义查询所有的方法
    def all(self):
        # 默认查询为删除的图书信息
        # 调用父类的成员方法为:super().方法名
        return super().all().filter(isDelete=False)

    # 定义创建模型类的方法,参数为属性的值
    def create_book(self, title, pud_date):
        # 创建模型类对象,self.model可以获得模型类
        book = self.model()
        book.bittle = title
        book.bpud_date = pud_date
        book.bread = 0 
        book.bcomment = 0 
        book.isDelete = False
        # 将数据插入进数据表
        book.save()
        return book


(2)、在模型类BookInfo中定义管理器




(3)、调用方法


from booktest.models import *
 
from datetime import date

book = BookInfo.books.create_book('abc', date(1991, 10, 1))
print(book)

# 结果:<BookInfo: abc>
 


4、元类


在模型类中定义类Meta,用于设置元信息,如使用db_table自定义表的名字。


例:指定BookInfo模型类生成的数据库表名为:bookinfo


在BookInfo模型类中添加如下内容,代码如下:


# 定义图书模型类BookInfo
class BookInfo(models.Model):
    ...

    # 定义元选项
    class Meta:
      db_table='bookinfo' #指定BookInfo生成的数据表名为bookinfo



猜你喜欢

转载自blog.csdn.net/wingzhezhe/article/details/79771594