1

2-django的模型

概述:Django对各种数据库都提供了很好的支持,Django为这些数据库提供了统一的调用接口API,程序员可以根据自己的业务需求选择不同的数据库

ORM简介

概述:对象-关系-映射

作用: 根据类生成表结构;将对象、列表的操作转换成对应的SQL语句;将SQL语句查询到的结果转换为对象或者列表

优点:极大的减轻开发人员的工作量,不需要面对因数据库的变更而导致代码无效在修改代码, 防止sql注入攻击

图解:

clipboard.png定义模型

模型、属性、表、字段之间的关系:一个模型在数据库中对应一张表,在模型中定义的属性对应该模型对照表中的一个字段

创建模型类

班级类

class Grade(models.Model):
    name     = models.CharField(max_length=20)
    boyNum   = models.IntegerField()
    girlNum  = models.IntegerField()
    isDelete = models.BooleanField(default=False)
    def __str__(self):
        return self.name

学生类

class Student(models.Model):
    name     = models.CharField(max_length=20)
    sex      = models.BooleanField()
    age      = models.IntegerField()
    contend = models.CharField(max_length=40)
    #  关联类名的小写或直接类名 Grade
    grade    = models.ForeignKey("grade")
    isDelete = models.BooleanField(default=False)
    def __str__(self):
        return self.name

字段类型和字段选项

字段类型        
        ·AutoField
            ·一个根据实际ID自动增长的IntegerField,通常不指定如果不指定,一个主键字段将自动添加到模型中

        ·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实例来表示的浮点数

        ·BooleanField
            ·true/false 字段,此字段的默认表单控制是CheckboxInput

        ·NullBooleanField
            ·支持null、true、false三种值

        ·DateField(auto_now=False, auto_now_add=False)
            ·使用Python的datetime.date实例表示的日期
            ·参数说明
                ·DateField.auto_now
                    ·每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它总是使用当前日期,                        默认为false
                ·DateField.auto_now_add
                    ·当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当前日期,默认为false
            ·说明
                ·该字段默认对应的表单控件是一个TextInput. 在管理员站点添加了一个JavaScript写的日历控件,和一                    个“Today"的快捷按钮,包含了一个额外的invalid_date错误消息键
            ·注意
                ·auto_now_add, auto_now, and default 这些设置是相互排斥的,他们之间的任何组合会发生错误的结果

        ·TimeField
            ·使用Python的datetime.time实例表示的时间,参数同DateField

        ·DateTimeField
            ·使用Python的datetime.datetime实例表示的日期和时间,参数同DateField

        ·FileField
            ·一个上传文件的字段

        ·ImageField
            ·继承了FileField的所有属性和方法,但对上传的对象进行校验,确保它是个有效的image
字段选项
        ·概述
            ·通过字段选项,可以实现对字段的约束
            ·在字段对象时通过关键字参数指定

        ·null
            ·如果为True,Django 将空值以NULL存储到数据库中,默认值是 False

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

        ·注意
            ·null是数据库范畴的概念,blank是表单验证证范畴的

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

        ·db_index
            ·若值为 True, 则在表中会为此字段创建索引

        ·default
            ·默认值

        ·primary_key
            ·若为 True, 则该字段会成为模型的主键字段

        ·unique
            ·如果为 True, 这个字段在表中必须有唯一值

模型之间的关系

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

        ·用一访问多
            ·格式
                对象.模型类小写_set
            ·示例
                grade.students_set

        ·用一访问一
            ·格式
                ·对象.模型类小写
            ·示例
                ·grade.students

        ·访问id
            ·格式
                ·对象.属性_id
            ·示例
                ·student.sgrade_id

元选项

在模型类中定义一个Meta类,用于设置元信息

class Student(models.Model):
    name     = models.CharField(max_length=20)
    sex      = models.BooleanField()
    age      = models.IntegerField()
    contend = models.CharField(max_length=40)
    #                            关联类名的小写
    grade    = models.ForeignKey("grade")
    isDelete = models.BooleanField(default=False)
    def __str__(self):
        return self.name
    class Meta():
        db_table = "students"
        ordering = ["-id"]

属性

  • db_table: 定义数据表名,推荐使用类名小写,并且添加复数
  • ordering: 规定对象的默认排序字段 ; ordering = ["id"]: 正序; ordering = ["-id"]: 倒序;

注意:排序会增加数据库的开销

模型成员

objects对象

概述: 是Manager类型的对象,用于与数据库进行交互; 当定义模型时没有指定管理器,则Django会为模型提供一个名为objects的管理器

自定义模型管理器

class Student(models.Model):
    #自定义模型管理器
    myobject = models.Manager()
    
    name     = models.CharField(max_length=20)
    sex      = models.BooleanField()
    age      = models.IntegerField()
    contend = models.CharField(max_length=40)
    #                            关联类名的小写
    grade    = models.ForeignKey("grade")
    isDelete = models.BooleanField(default=False)
    def __str__(self):
        return self.name
    class Meta():
        db_table = "students"
        ordering = ["-id"]

注意: 如果为模型指定了管理器,那么Django不再为模型类提供名为objects的管理器

自定义模型管理器类

class StudentManager(models.Manager):
    def get_queryset(self):
        return super(StudentManager, self).get_queryset().filter(isDelete=False)
class Student(models.Model):
    #自定义模型管理器
    objects = StudentManager()

get_queryset获取查询集(数据集),自定义管理器类调用父类中的get_queryset方法后在进行过滤

创建对象

当创建对象时,Django不会对数据库进行读写操作。调用save()方法才与数据库进行交互,将对象存储到数据库中。__init__方法已经在基类models.Model中使用,在自定义模型类中无法使用

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

class Student(models.Model):
    #自定义模型管理器
    objects = StudentManager()

    name     = models.CharField(max_length=20)
    sex      = models.BooleanField()
    age      = models.IntegerField()
    contend = models.CharField(max_length=40)                        
    grade    = models.ForeignKey(Grade)
    isDelete = models.BooleanField(default=False)
    def __str__(self):
        return self.name
    class Meta():
        db_table = "students"
        ordering = ["-id"]
   
    #类方法,类可以直接调用  创建对象可以直接写 Student.create('ray',1,18,....)
    @classmethod 
    def create(cls, name, sex, age, contend, grade):
        return cls(name=name, sex=sex, age=age, contend=contend, grade=grade)

在自定义模型器类中增加一个方法

class StudentManager(models.Manager):
    def get_queryset(self):
        return super(StudentManager, self).get_queryset().filter(isDelete=False)
    def create(self, name, sex, age, contend, grade):
        obj = self.model()
        obj.name = name
        obj.sex = sex
        obj.age = age
        obj.contend = contend
        obj.grade = grade
        return obj

查询数据

查询集:表示从数据库中获取的对象集合,查询集可以含有一个或者多个过滤器

过滤器:基于所给的参数限制查询集的结果,得到新的查询集

从SQL的角度,查询集合select语句等价,过滤器就像where和limit子句相似

查询集

在管理器上调用过滤器方法或者是其他数据查询集上调用过滤器方法得到新的查询集

查询集经过过滤器筛选得到新的查询集,因此可以使用链式语法结构

惰性查询:创建查询集不会带来任何的数据库访问,直到调用数据时,才会访问数据库

什么时候对查询集求值? 迭代、序列化、与if合用

返回查询集的方法

all()

返回查询集的方法

def students(request):
    stus = Student.objects.all()
    return render(request, 'students.html', {"stus":stus})
filter()

作用:将符合条件的数据过滤进来

使用:filter(键1=值1, 键2=值2);filter(键1=值1, 键2=值2)

def students(request):
    stus = Student.objects.filter(age=20)
    return render(request, 'students.html', {"stus":stus})
exclude()

作用: 将符合条件的数据过滤出去

def students(request):
    stus = Student.objects.exclude(age=20)
    return render(request, 'students.html', {"stus":stus})
order_by()

作用:排序

def students(request):
    stus = Student.objects.order_by("age")
    return render(request, 'students.html', {"stus":stus})
def students(request):
    stus = Student.objects.order_by("-age")
    return render(request, 'students.html', {"stus":stus})
values()

作用: 一个对象构成一个字典,然后构成一个列表返回

def students(request):
    stus = Student.objects.values()
    print(stus)
    return render(request, 'students.html', {"stus":stus})

返回单个值的方法

get()

作用: 返回单个满足条件的对象

def students(request):
    #   pk代表主键
    try:
        stus = [Student.objects.get(age=20)]
    except Student.DoesNotExist as e:
        pass
    except Student.MultipleObjectsReturned as e:
        pass
    return render(request, 'students.html', {"stus":stus})

注意: 如果多条数据被返回,会引发 “类名.MultipleObjectsReturned” 异常;数据不存在会报 "类名.DoesNotExist" 异常

count()

返回当前查询的总条数

first()

返回查询集中的第一个对象

last()

返回查询集中的最后一个对象

exists()

判断查询集中是否有数据,如果有返回True,否则返回False

def students(request):
    stus = Student.objects.filter(age=20)
    if stus.exists():
        return render(request, 'students.html', {"stus":stus})
    else:
        return HttpResponse("没有学生数据")

限制查询集

查询集返回列表,可以使用下标的方式进行限制,等同于SQL中的limit和offset子句

注意: 不支持负数索引; 使用下标后返回一个新的查询集,不会立即执行查询字段查询

def students(request):
    stus = Student.objects.all()[5:10]#[0:5)
    return render(request, 'students.html', {"stus":stus})

字段查询

概述

实现where语句,作为filter()、exclude()、get()的参数

语法 : 属性名称__比较运算符=值 (注意:里面有两个下划线)

外键: 使用 “属性名_id” 表示原始的外键值

转义: like语句中使用%,可以直接使用

比较运算符

exact: 表示判断相当,大小写敏感

contains: 是否包含,大小写敏感

# 找描述中带有"ray"的所有学生
def students(request):
    stus = Student.objects.filter(contend__icontains="sunck")
    return render(request, 'students.html', {"stus":stus})

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

# 找描述以ray开头的所有学生
def students(request):
    stus = Student.objects.filter(contend__startswith="ray")
    return render(request, 'students.html', {"stus":stus})

iexact、icontains、istartswith、iendswith: 不区分大小写

isnull, isnotnull : 是否为空, 是否为非空

in : 包含在范围之内

# 找id为2,5,8,9的学生
def students(request):
    stus = Student.objects.filter(pk__in=[2,5,8,9])
    return render(request, 'students.html', {"stus":stus})

gt, gte, lt, lte

# 找年龄大于等于20的所有学生
def students(request):
    stus = Student.objects.filter(age__gte=20)
    return render(request, 'students.html', {"stus":stus})

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

跨关联关系的查询

处理join查询:

语法:关联的模型类名小写__属性名__比较运算符=值

注意: __比较运算符 没有,表示等于; 可以反向使用,即关联的两个模型中都可以使用

# 描述中带有ray的学生属于那些班级
def students(request):
    grades = Grade.objects.filter(student__contend__contains="ray")
    print(grades)
    stus = Student.objects.all()
    return render(request, 'students.html', {"stus":stus})

查询的快捷方式: pk pk表示主键,默认主键是id

聚合函数

注意: 使用aggregate()函数返回聚合函数的值

函数: Avg、Count、Max、Min、Sum

# 求所有人的年龄的和
from django.db.models import Sum
def students(request):
    age = Student.objects.aggregate(Sum("age"))
    print("***********", age)
    stus = Student.objects.all()
    return render(request, 'students.html', {"stus":stus})

F对象

作用: 可以使模型中的A字段与B字段进行比较,如果A字段出现在等号的左侧,那么B字段使用F对象写在等号右侧

# 找男生个数多于女生个数的班级
from django.db.models import F
def students(request):
    grades = Grade.objects.filter(boyNum__gt=F("girlNum"))
    print(grades)
    stus = Student.objects.all()
    return render(request, 'students.html', {"stus":stus})

F对象可以进行数学运算

from django.db.models import F
def students(request):
    grades = Grade.objects.filter(boyNum__gt=F("girlNum")*2)
    print(grades)
    stus = Student.objects.all()
    return render(request, 'students.html', {"stus":stus})

F对象还可以进行关联查询 模型类小写__属性名

# 没有被删除的学生的班级
from django.db.models import F
def students(request):
    grades = Grade.objects.filter(isDelete=F("student__isDelete"))
    print(grades)
    stus = Student.objects.all()
    return render(request, 'students.html', {"stus":stus})

对于data/time字段,可以进行timedelta()进行运算

Q对象

逻辑与

# 年龄大于等于20且小于等于50的学生
def students(request):
    stus = Student.objects.filter(age__gte=20,age__lte=50)
    return render(request, 'students.html', {"stus":stus})
def students(request):
    stus = Student.objects.filter(age__gte=20).filter(age__lte=50)
    return render(request, 'students.html', {"stus":stus})

from django.db.models import Q
def students(request):
    stus = Student.objects.filter(Q(age__gte=20) & Q(age__lte=50))
    return render(request, 'students.html', {"stus":stus})

逻辑或

# 获取年龄小于20或者年龄大于50的学生
from django.db.models import Q
def students(request):
    stus = Student.objects.filter(Q(age__lt=20) | Q(age__gt=50))
    return render(request, 'students.html', {"stus":stus})

逻辑非

# 获取年龄不大于等于20的学生
def students(request):
    stus = Student.objects.filter(age__lt=20)
    return render(request, 'students.html', {"stus":stus})

from django.db.models import Q
def students(request):
    stus = Student.objects.filter(~Q(age__gte=20))
    return render(request, 'students.html', {"stus":stus})

注意: 过滤器函数中可以混合使用Q对象和关键字参数,所有参数条件都将and在一起,Q对象必须位于关键字参数的前面

def students(request):
    stus = Student.objects.all()[5:10]#[0:5)
    return render(request, 'students.html', {"stus":stus})

模型关系

1-1

使用场景: 表的字段太多,需要拆分

# 人
class Person(models.Model):
    name = models.CharField(max_length=20)
    age  = models.IntegerField()
    # 关系存放在哪张表都可以
    # 绑定身份证与人的一对一关系,默认情况下当删身份证时绑定的人也可以删除,通过on_delete属性设置
    idCard = models.OneToOneField(IDCard)
# 身份证
class IDCard(models.Model):
    sex = models.BooleanField()
    num = models.CharField(max_length=20)

主表从表: 声明关系的表为从表

级联数据获取:

从获取主:

  • 关系是直接声明的,是一个显性的属性
  • 获取pk为1的人的身份证号
  • per = Person.objects.get(pk=1) print(per.idCard.num)

主获取从:

  • 关系字段是隐性属性,对象.关系模型类名的小写
  • 获取身份证号是1的人的姓名
  • card = IDCard.objects.get(pk=1) print(card.person.name)

img

1-n

一对多,也就是外键。

主表从表: 声明关系的表为从表(ForeignKey)。

级联数据获取:

  • 从获取主: 关系是直接声明的,是一个显性的属性
  • 主获取从: 关系字段是隐性属性,对象.模型类小写_set
#找到1班的所有学生
grade = Grade.objects.get(pk=1)
print(grade.student_set.all())

m-n

原理: 底层是通过两个外键实现,两个外键存在于另一张关系表中

#多对多
class Buyer(models.Model):
    name = models.CharField(max_length=20)
    level = models.IntegerField()
class Product(models.Model):
    name = models.CharField(max_length=20)
    price = models.IntegerField()
    #关系形成
    buyers = models.ManyToManyField(Buyer)

形成关系

def ptob(reqeust, p, b):
    product = Product.objects.get(pk=p)
    buyer   = Buyer.objects.get(pk=b)
    #产生关联
    product.buyers.add(buyer)
    return HttpResponse("购买成功")

数据级联获取

从获取主

  • 对象.显性属性,得到的是一个数据集合,可以进行过滤
  • 已知一件商品,获取该商品所有的购买者
  • product = Product.objects.get(pk=1) print(product.buyers.all())

主获取从:

  • 隐性属性 对象.关联类名小写_set 得到的是一个数据集合,可以进行过滤
  • 已知一个购买者,获取他所买的所有商品
  • buyer = Buyer.objects.get(pk=1) print(buyer.product_set.all())

模型继承

使用最原始的python类的继承方式

class Animal(models.Model):
    name = models.CharField(max_length=20)
    age  = models.IntegerField()
class Cat(Animal):
    weight = models.IntegerField()
class Dog(Animal):
    height = models.IntegerField()

注意: 默认继承方式不是很合理

  • 父类也会对应一张表
  • 默认在父类中定义的字段会存在父类表中,子类的数据通过外键关联父表中数据,子类只有特殊的数据在子表中
  • 效率比较低

Django中的数据库模块提供了一个非常不错的功能,就是支持models的面向对象,可以在Meta类中指定是否抽象,然后继承

class Animal(models.Model):
    name = models.CharField(max_length=20)
    age  = models.IntegerField()
    class Meta():
        # 让该类抽象,抽象的父类不会再生产数据表
        # 子类会继承父类中的通用数据,复制到子表中
        abstract = True
class Cat(Animal):
    weight = models.IntegerField()
class Dog(Animal):
    height = models.IntegerField()

rayzz
145 声望13 粉丝