Django之ORM

Posted yinhaiping

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django之ORM相关的知识,希望对你有一定的参考价值。

Django之ORM

对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。在业务逻辑层和数据库层之间充当了桥梁的作用.

Django项目使用mysql数据库

  1. 在Django项目的settings.py文件中,配置数据库连接信息:
    DATABASES = 
        "default": 
            "ENGINE": "django.db.backends.mysql",
            "NAME": "你的数据库名称",  # 需要自己手动创建数据库
            "USER": "数据库用户名",
            "PASSWORD": "数据库密码",
            "HOST": "数据库IP",
            "POST": 3306
        
    
  2. 在与Django项目同名的目录下的__init__.py文件中写如下代码,告诉Django使用pymysql模块连接MySQL数据库:
    import pymysql
    pymysql.install_as_MySQLdb()

    注:数据库迁移的时候出现一个警告

    WARNINGS:
    ?: (mysql.W002) MySQL Strict Mode is not set for database connection ‘default‘
    HINT: MySQL‘s Strict Mode fixes many data integrity problems in MySQL, such as data truncation upon insertion, by escalating warnings into errors. It is strongly recommended you activate it.

    在配置中多加一个OPTIONS参数:Django官网解释

    ‘OPTIONS‘: 
       ‘init_command‘: "SET sql_mode=‘STRICT_TRANS_TABLES‘",

Model

在Django中model是你数据的单一、明确的信息来源。它包含了你存储的数据的重要字段和行为。通常,一个模型(model)映射到一个数据库表。

基本情况:

  • 每个模型都是一个Python类,它是django.db.models.Model的子类。
  • 模型的每个属性都代表一个数据库字段。
  • 综上所述,Django为您提供了一个自动生成的数据库访问API,详询官方文档链接

技术图片

快速入门

下面这个例子定义了一个 Person 模型,包含 first_namelast_name

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

first_namelast_name 是模型的字段。每个字段被指定为一个类属性,每个属性映射到一个数据库列。

上面的 Person 模型将会像这样创建一个数据库表:

CREATE TABLE myapp_person (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
);

说明:

  • 表明myapp_person是自动生成的,app名_类名,如果你要自定义表名,需要在model的Meta类中指定 db_table 参数.
  • id字段是自动添加的,如果你想要指定自定义主键,只需在其中一个字段中指定 primary_key=True 即可。如果Django发现你已经明确地设置了Field.primary_key,它将不会添加自动ID列。
  • Django支持MySQL5.5及更高版本。

ORM的字段和参数

字段

# AutoField    主键
自增的整形字段,必填参数primary_key=True,则成为数据库的主键。无该字段时,django自动创建。
一个model不能有两个AutoField字段。
    # 自定义自增列
    nid = models.AutoField(primary_key=True)
# IntegerField  整数,数值的范围是 -2147483648 ~ 2147483647。

# CharField    字符串
字符类型,必须提供max_length参数。max_length表示字符的长度。
BooleanField  布尔值
TextField    文本
DateTimeField DateField  日期时间
    auto_now_add=True    # 新增数据的时候会自动保存当前的时间
    auto_now=True        # 新增、修改数据的时候会自动保存当前的时间
DecimalField   十进制的小数
    max_digits       小数总长度   5
    decimal_places   小数位长度   2
  • AutoField 主键
    自增的整形字段,必填参数primary_key=True,则成为数据库的主键。无该字段时,django自动创建。
    一个model不能有两个AutoField字段。
    
    # 自定义自增列
    
      nid = models.AutoField(primary_key=True)
  • IntegerField
    整数列(有符号的) -2147483648 ~ 2147483647
    PositiveIntegerField正整数
    BigIntegerField长整型(有符号的)
  • BooleanField
    布尔值类型
    gender = models.BooleanField(‘性别‘, choices=((0, ‘女‘), (1, ‘男‘)))
  • CharField
    字符类型
    必须提供max_length参数, max_length表示字符长度
  • TextField
    文本类型
  • EmailField
    字符串类型,Django Admin以及ModelForm中提供验证机制
  • IPAddressField
    字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
  • DateTimeField DateField 日期时间
    auto_now_add=True    # 新增数据的时候会自动保存当前的时间
    auto_now=True        # 新增、修改数据的时候会自动保存当前的时间
    default
  • DecimalField 十进制的小数
    max_digits       小数总长度   5
    decimal_places   小数位长度   2

自定义一个char类型字段:

class MyCharField(models.Field):#继承models.Field,不会生成表
    """
    自定义的char类型的字段类
    """
    def __init__(self, max_length, *args, **kwargs):
        self.max_length = max_length
        super(MyCharField, self).__init__(max_length=max_length, *args, **kwargs)

    def db_type(self, connection):
        """
        限定生成数据库表的字段类型为char,长度为max_length指定的值
        """
        return ‘char(%s)‘ % self.max_length

使用自定义char类型字段:

class Class(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=25)
    # 使用自定义的char类型的字段
    cname = MyCharField(max_length=25)

参数

    null                数据库中字段是否可以为空
    db_column           数据库中字段的列名
    default             数据库中字段的默认值
    primary_key         数据库中字段是否为主键
    db_index            数据库中字段是否可以建立索引
    unique              数据库中字段是否可以建立唯一索引

    unique_for_date     数据库中字段【日期】部分是否可以建立唯一索引
    unique_for_month    数据库中字段【月】部分是否可以建立唯一索引
    unique_for_year     数据库中字段【年】部分是否可以建立唯一索引

    verbose_name        Admin中显示的字段名称
    blank               Admin中是否允许用户输入为空
    editable            Admin中是否可以编辑
    help_text           Admin中该字段的提示信息
    choices             Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作
                        如:gf = models.IntegerField(choices=[(0, ‘何穗‘),(1, ‘大表                            姐‘),],default=1)

    error_messages      自定义错误信息(字典类型),从而定制想要显示的错误信息;
                        字典健:null, blank, invalid, invalid_choice, unique, and                             unique_for_date
                        如:‘null‘: "不能为空.", ‘invalid‘: ‘格式错误‘

Model Meta 表参数

不常用

class UserInfo(models.Model):
     def __str__(self):#定义打印该对象显示内容
        return self.name
    nid = models.AutoField(primary_key=True)
    username = models.CharField(max_length=32)

    class Meta:
        # 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
        db_table = "table_name"

        # admin中显示的表名称
        verbose_name = ‘个人信息‘

        # verbose_name加s
        verbose_name_plural = ‘所有用户信息‘

        # 联合索引
        index_together = [
            ("pub_date", "deadline"),   # 应为两个存在的字段
        ]

        # 联合唯一索引
        unique_together = (("driver", "restaurant"),)   # 应为两个存在的字段

ORM查询

  • all() 获取所有的数据 ——QuerySet对象列表
    ret = models.Person.objects.all()
  • get() 获取满足条件的一个数据 ——对象

    获取不到或者多个都报错

    ret = models.Person.objects.get(pk=1)
  • filter() 获取满足条件的所有数据 —— QuerySet 对象列表
    ret = models.Person.objects.filter(pk=1)
  • exclude() 获取不满足条件的所有数据 —— QuerySet 对象列表
    ret = models.Person.objects.exclude(pk=1)
  • values() 拿到对象所有的字段和字段的值 QuerySet [ , ]
    拿到对象指定的字段和字段的值    QuerySet  [  , ]
    ret = models.Person.objects.values(‘pid‘,‘name‘)
    <QuerySet [‘pid‘: 1, ‘name‘: ‘bbb‘]>
  • values_list() 拿到对象所有的字段的值 QuerySet [ () ,() ]
    values(‘字段‘)   拿到对象指定的字段的值      QuerySet  [  , ]
    ret = models.Person.objects.values_list(‘name‘,‘pid‘)
  • order_by 排序 -表示降序 ——》 QuerySet [ () ,() ]
    ret = models.Person.objects.all().order_by(‘age‘,‘-pid‘)
  • reverse 反向排序 只能对已经排序的QuerySet进行反转
    ret = models.Person.objects.all().order_by(‘age‘,‘-pid‘).reverse()
  • distinct 去重 完全相同的内容才能去重
    ret = models.Person.objects.values(‘age‘).distinct()
  • count() 计数
    ret = models.Person.objects.all().count()
  • first 取第一元素 没有元素 None
    ret = models.Person.objects.filter(pk=1).values().first()
  • last 取最后一元素 没有元素 None
  • exists 查询的数据是否存在
    ret = models.Person.objects.filter(pk=1000).exists()

总结:

返回queryset 返回对象 返回数字 返回布尔值的
filter() get() count() exists()
exclude() first()
values() last()
values_list()
order_by()
reverse()
distinct()
all()

单表双下划线

ret = models.Person.objects.filter(pk__gt=1)   # gt  greater than   大于
ret = models.Person.objects.filter(pk__lt=3)   # lt  less than   小于
ret = models.Person.objects.filter(pk__gte=1)   # gte  greater than   equal    大于等于
ret = models.Person.objects.filter(pk__lte=3)   # lte  less than  equal  小于等于
ret = models.Person.objects.filter(pk__range=[2,3])   # range  范围
ret = models.Person.objects.filter(pk__in=[1,3,10,100])   # in  成员判断
ret = models.Person.objects.filter(name__contains=‘A‘)
ret = models.Person.objects.filter(name__icontains=‘A‘)   # 忽略大小写
ret = models.Person.objects.filter(name__startswith=‘a‘)  # 以什么开头
ret = models.Person.objects.filter(name__istartswith=‘A‘)
ret = models.Person.objects.filter(name__endswith=‘a‘)  # 以什么结尾
ret = models.Person.objects.filter(name__iendswith=‘I‘)
date字段可以:
ret  = models.Person.objects.filter(birth__year=‘2019‘)
ret  = models.Person.objects.filter(birth__contains=‘2018-06-24‘)
ret  = models.Person.objects.filter(phone__isnull=False)

多对一操作

ForeignKey外键

class Publisher(models.Model):
    name = models.CharField(max_length=32, verbose_name="名称")

    def __str__(self):
        return self.name


class Book(models.Model):
    title = models.CharField(max_length=32)
    pub = models.ForeignKey(Publisher, related_name=‘books‘,related_query_name=‘xxx‘,on_delete=models.CASCADE)

    def __str__(self):
        return self.title

基于对象查询

正向

从多的一方查询,查询到单个对象

语法:对象.关联字段.字段

book_obj.pub ——》 所关联的对象

book_obj.pub_id ——》 所关联的对象id

book_obj.pub.name

反向

从少的一方查询,查询到多个结果

语法:obj.表名_set

  • 没有指定related_name

    pub_obj.book_set ——》 关系管理对象 (类名小写_set)

    pub_obj.book_set.all() ——》 所关联的所有对象

  • 指定related_name=‘books‘

    pub_obj.books ——》 关系管理对象

    pub_obj.books.all() ——》 所关联的所有对象

关系管理对象方法

print(pub_obj.books.all())
pub_obj.books.set(models.Book.objects.filter(pk__in=[4,5]))  # 不能用id  只能用对象
pub_obj.books.add(*models.Book.objects.filter(pk__in=[1,2]))# 不能用id  只能用对象
pub = models.ForeignKey(Publisher, related_name=‘books‘, related_query_name=‘xxx‘,null=True, on_delete=models.CASCADE)
# 只有外键可为空null=True时有remove  clear
pub_obj.books.remove(*models.Book.objects.filter(pk__in=[1,2]))
pub_obj.books.clear()
obj = pub_obj.books.create(title=‘用python养猪‘)

基于字段查询

正向

语法:关联字段__字段

models.Book.objects.filter(pub__name=‘xxxxx‘) 外键+双下划线+跨表字段

反向

语法:表名__字段

  • 没有指定related_name

    models.Publisher.objects.filter(book__title=‘xxxxx‘)

  • 指定related_name=‘books’

    models.Publisher.objects.filter(books__title=‘xxxxx‘)

  • 指定related_query_name=‘book‘,专门用于字段查询,优先级高于related_name

    models.Publisher.objects.filter(book__title=‘xxxxx‘)

多对多操作

ManyToManyField

实例解析:

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=6, decimal_places=2)  # 9999.99
    sale = models.IntegerField()
    kucun = models.IntegerField()
    pub = models.ForeignKey(Publisher, null=True,on_delete=models.CASCADE)

    def __str__(self):
        return self.title


class Author(models.Model):
    name = models.CharField(max_length=32, )
    books = models.ManyToManyField(Book)    #django自建,共三种创建方式.也可加related_name

    def __str__(self):
        return self.name

基于对象:

  1. 正向:
    author_obj = models.Author.objects.get(pk=1)
    author_obj.books    #关系管理对象
    author_obj.books.all()
  2. 反向:
    book_obj = models.Book.objects.get(pk=1)
    
    # 不指定related_name
    
    print(book_obj.author_set)  #  ——》  关系管理对象
    print(book_obj.author_set.all())
    
    # related_name=‘authors‘
    
    print(book_obj.authors)#  ——》  关系管理对象
    print(book_obj.authors.all())

基于字段:

  1. 正向:
    ret  =  models.Author.objects.filter(books__title=‘Django‘)
  2. 反向:
    # 不指定related_name
    
    ret = models.Book.objects.filter(author__name=‘yhp‘)    #类名字__字段
    
    # related_name=‘authors‘
    
    ret = models.Book.objects.filter(authors__name=‘yhp‘)  #用其替换类名
    
    # related_query_name=‘xxx‘
    
    ret = models.Book.objects.filter(xxx__name=‘yhp‘)

关系管理对象方法

author_obj = models.Author.objects.get(pk=1)

# all()  所关联的所有的对象

# set  重置多对多的关系    [id,id]    [ 对象,对象 ]清空关系,再重新设置
author_obj.books.set([1,2])
author_obj.books.set(models.Book.objects.filter(pk__in=[1,2,3]))

# add  添加多对多的关系   (id,id)   (对象,对象),再原有关系基础上添加
author_obj.books.add(4,5)
author_obj.books.add(*models.Book.objects.filter(pk__in=[4,5]))#打散添加

# remove 删除多对多的关系  (id,id)   (对象,对象)
author_obj.books.remove(4,5)
author_obj.books.remove(*models.Book.objects.filter(pk__in=[4,5]))

# clear()   清除所有的多对多关系
author_obj.books.clear()

# create()    创建对象并设置关系
obj = author_obj.books.create(title=‘书名‘,pub_id=1)
book_obj = models.Book.objects.get(pk=1)
obj = book_obj.author_set.create(name=‘烧饼‘)

对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。

关系管理对象详解:

"关联管理对象"是在一对多或者多对多的关联上下文中使用的管理器。

它存在于下面两种情况:

  1. 外键关系的反向查询
  2. 多对多关联关系

简单来说就是当 点后面的对象 可能存在多个的时候就可以使用以下的方法。

create()

创建一个新的对象,保存对象,并将它添加到关联对象集之中,返回新创建的对象。

>>> import datetime
>>> models.Author.objects.first().book_set.create(title="番茄物语", publish_date=datetime.date.today())

add()

把指定的model对象添加到关联对象集中。

添加对象

>>> author_objs = models.Author.objects.filter(id__lt=3)
>>> models.Book.objects.first().authors.add(*author_objs)

添加id

>>> models.Book.objects.first().authors.add(*[1, 2])

set()

更新model对象的关联对象。

>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.set([2, 3])

remove()

从关联对象集中移除执行的model对象

>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.remove(3)

clear()

从关联对象集中移除一切对象。

>>> book_obj = models.Book.objects.first()
>>> book_obj.authors.clear()

注意:

对于ForeignKey对象,clear()和remove()方法仅在null=True时存在。

举个例子:

ForeignKey字段没设置null=True时,

class Book(models.Model):
    title = models.CharField(max_length=32)
    publisher = models.ForeignKey(to=Publisher)

没有clear()和remove()方法:

>>> models.Publisher.objects.first().book_set.clear()
Traceback (most recent call last):
  File "<input>", line 1, in <module>
AttributeError: ‘RelatedManager‘ object has no attribute ‘clear‘

当ForeignKey字段设置null=True时,

class Book(models.Model):
    name = models.CharField(max_length=32)
    publisher = models.ForeignKey(to=Class, null=True)

此时就有clear()和remove()方法:

>>> models.Publisher.objects.first().book_set.clear()

注意:

  1. 对于所有类型的关联字段,add()、create()、remove()和clear(),set()都会马上更新数据库。换句话说,在关联的任何一端,都不需要再调用save()方法。

聚合分组

from app01 import models

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

# 聚合
ret = models.Book.objects.filter(pk__gt=3).aggregate(Max(‘price‘),avg=Avg(‘price‘))
print(ret)
运行结果:‘avg‘:160.0,‘price__max‘:Decimal(‘190.0‘)
    数据结构为字典,终止语句.关键字传参可改变字典的key,默认为:字段__聚合函数名
说明:   
    models.Book.objects.filter(pk__gt=3) #表示对哪些数据进行聚合
    aggregate(Max(‘price‘),avg=Avg(‘price‘))#对哪些字段进行聚合,

# 分组
# 统计每一本书的作者个数
ret = models.Book.objects.annotate(count=Count(‘author‘)) # annotate 注释
# 统计出每个出版社的最便宜的书的价格
# 方式一
ret = models.Publisher.objects.annotate(Min(‘book__price‘)).values()
# 方式二
ret = models.Book.objects.values(‘pub_id‘).annotate(min=Min(‘price‘))

aggregate()QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。

键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。

F和Q

from django.db.models import F

# 比较两个字段的值
ret=models.Book.objects.filter(sale__gt=F(‘kucun‘))

# 只更新sale字段
models.Book.objects.all().update(sale=100)

# 取某个字段的值进行操作
models.Book.objects.all().update(sale=F(‘sale‘)*2+10)

Q(条件)

| 或

& 与

~ 非

from django.db.models import Q

ret = models.Book.objects.filter(Q(Q(pk__gt=3) | Q(pk__lt=2)) & Q(price__gt=50))
print(ret)

事务

from django.db import transaction

try:
    with transaction.atomic():
        # 进行一系列的ORM操作

        models.Publisher.objects.create(name=‘xxxxx‘)
        models.Publisher.objects.create(name=‘xxx22‘)

except Exception as e :
    print(e)




以上是关于Django之ORM的主要内容,如果未能解决你的问题,请参考以下文章

Django

Django基础--3

ORM增删改查

ORM Django 多对多

使用 ORM Django 过滤新更新的查询集返回空查询集

与ORM django和sqlite3数据库相关的问题