Django ORM

文章目录

      • ORM
      • Django中的ORM
        • Django使用MySQL数据库配置
      • ORM对应关系
      • Django ORM常用字段参数
        • 常用字段
        • 字段参数
        • 自定义字段
        • ORM字段与数据库字段的对应关系
      • ForeignKey、ManyToManyField 和 OneToOneField的区别
      • 查询操作返回值
        • 常用方法
          • create
          • add
          • remove
          • clear
      • 单表操作
        • 一般操作
        • 双下划线操作
      • 多表操作
        • OneToOneField 一对一
        • ForeignKey 一对多
        • ManyToManyField 多对多
          • 1. ORM生成第三张表
          • 2. 自己创建第三张表
          • 3. ManyToManyField指定第三张表
      • 聚合查询
      • 分组查询
      • F 查询
      • Q 查询
      • 执行原生查询
      • Django终端打印SQL语句
      • 在Python脚本中调用Django环境

ORM

对象关系映射(Object Relational Mapping,简称ORM)模式是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。

ORM解决的主要问题是对象和关系的映射。它通常把一个类和一个表一一对应,类的每个实例对应表中的一条记录,类的每个属性对应表中的每个字段。

Django中的ORM

官方文档

Django使用MySQL数据库配置

一、在Django项目的settings.py中配置连接信息:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': '数据库名', 
        'USER': '用户名',
        'PASSWORD': '数据库密码',
        'HOST': '数据库IP',
        'POST': 3306
    }
}

二、在Django项目的__init__.py配置,告诉Django使用pymysql连接MySQL数据库:

import pymysql

pymysql.install_as_MySQLdb()

注意:Django1.x默认的还是使用MySQLdb:执行会报:ImportError: No module named ‘MySQLdb’

报错:django migrate django.db.utils.InternalError:,一般是数据库编码不为utf8导致

三、执行命令

python manage.py makemigrations [appname]

–> 将models.py文件中的改动记录在(app/migrations/00xx_****.py)上

python manage.py migrate [appname]

–> 将改动翻译成SQL语句,去数据库中执行

ORM对应关系

Django ORM_第1张图片

Django ORM常用字段参数

常用字段

AutoField(Field):int自增列;必须填入参数primary_key=True

BigAutoField(AutoField):bigint自增列;必须填入参数primary_key=True

PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField):正小整数;0 ~ 32767

IntegerField(Field):整数列(有符号的);2147483648 ~ 2147483647

SmallIntegerField(IntegerField):小整数;32768 ~ 32767

BigIntegerField(IntegerField):长整型(有符号的);9223372036854775808 ~ 9223372036854775807

PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField):正整数;0~2147483647

BooleanField(Field):布尔值类型

NullBooleanField(Field):可以为空的布尔值

CharField(Field):字符类型;必须提供max_length参数,max_length表示字符长度

TextField(Field):文本类型

EmailField(CharField):字符串类型;Django Admin以及ModelForm中提供验证机制

IPAddressField(Field):字符串类型;Django Admin以及ModelForm中提供验证 IPV4 机制

URLField(CharField):字符串类型;Django Admin以及ModelForm中提供验证 URL

SlugField(CharField):字符串类型;Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)

CommaSeparatedIntegerField(CharField):字符串类型;格式必须为逗号分割的数字

UUIDField(Field):字符串类型;Django Admin以及ModelForm中提供对UUID格式的验证

FilePathField(Field):字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能;
				参数:
                path,                      文件夹路径
                match=None,                正则匹配
                recursive=False,           递归下面的文件夹
                allow_files=True,          允许文件
                allow_folders=False,       允许文件夹

FileField(Field):字符串,路径保存在数据库,文件上传到指定目录;
                参数:
                upload_to = ""      上传文件的保存路径
                storage = None      存储组件,默认django.core.files.storage.FileSystemStorage


ImageField(FileField):字符串,路径保存在数据库,文件上传到指定目录;
                参数:
                upload_to = ""      上传文件的保存路径
                storage = None      存储组件,默认django.core.files.storage.FileSystemStorage
                width_field=None,   上传图片的高度保存的数据库字段名(字符串)
                height_field=None   上传图片的宽度保存的数据库字段名(字符串)

DateTimeField(DateField):日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]

DateField(DateTimeCheckMixin, Field):日期格式      YYYY-MM-DD

TimeField(DateTimeCheckMixin, Field):时间格式      HH:MM[:ss[.uuuuuu

DurationField(Field):长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型

FloatField(Field):浮点型

DecimalField(Field):10进制小数
                参数:
                max_digits,小数总长度
                decimal_places,小数位长度

BinaryField(Field):二进制类型

字段参数

null:表示这个字段可以为空

unique:表示该字段必须唯一

db_index:表示设置数据库索引

default:设置默认值

auto_now_add:创建数据记录的时候会把当前时间添加到数据库

auto_now:每次更新数据记录的时候会更新该字段

自定义字段

class FixedChardField(models.Field):
    """
    自定义char类型
    """

    def __init__(self, max_length, *args, **kwargs):
        super().__init__(max_length=max_length, *args, **kwargs)
        self.length = max_length

    def db_type(self, connection):
        """
        生成数据表的字段类型:(char,长度为length的值)
        """
        return 'char(%s)' % self.length


class Person(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=25)
    cname = FixedChardField(max_length=25)
+-------+-------------+------+-----+---------+----------------+
| Field | Type        | Null | Key | Default | Extra          |
+-------+-------------+------+-----+---------+----------------+
| id    | int(11)     | NO   | PRI | NULL    | auto_increment |
| title | varchar(25) | NO   |     | NULL    |                |
| cname | char(25)    | NO   |     | NULL    |                |
+-------+-------------+------+-----+---------+----------------+

ORM字段与数据库字段的对应关系

'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',

ForeignKey、ManyToManyField 和 OneToOneField的区别

item 关系 举个栗子 在数据库中的体现
ForeignKey 一对多 每个用户可能有多个评论,但某一个评论中只对应到一个用户 建立个字段,保存对方的主键
ManyToManyField 多对多 每个文章有多个作者,每个用户可以发表多个文章 建立一个表。维护两个model数据的关系
OneToOneField 一对一 每个用户只能有一个“安全邮箱" 建立个字段,保存对方的主键,并建立约束

查询操作返回值

返回QuerySet对象

  • all()
  • filter()
  • exclude()
  • order_by()
  • reverse()
  • distinct()

返回特殊QuerySet

  • values():返回一个可迭代的字典序列
  • values_list():返回一个可迭代的元祖序列

返回具体对象

  • get()
  • first()
  • last()

返回布尔值

  • exists()

返回数字

  • count()

常用方法

create

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

# 作者创建一本书,并指定出版社
models.Author.objects.first().books.create(title='python', publisher_id=2)
add

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

# 作者添加一本书
author_obj = models.Author.objects.get(id=1)
book_obj = models.Book.objects.get(id=3)
author_obj.books.add(book_obj)

# 作者添加多本书
books_objs = models.Book.objects.filter(id__gt=4)
author_obj = models.Author.objects.get(id=2)
author_obj.books.add(*books_objs)

# 直接添加id
author_obj = models.Author.objects.get(id=2)
author_obj.books.add(9)
remove

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

# 删除title='java'的书 
author_obj = models.Author.objects.get(id=2)
book_obj = models.Book.objects.get(title='java')
author_obj.books.remove(book_obj)

# 删除id=7的书
author_obj = models.Author.objects.get(id=2)
author_obj.books.remove(7)
clear

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

# 清除所有关联对象
author_obj = models.Author.objects.get(id=2)
author_obj.books.clear()

单表操作

一般操作

创建一个学生表:

class Student(models.Model):
    name = models.CharField(max_length=20)
    gender = models.BooleanField(default=True)
    age = models.IntegerField(default=0)
    content = models.TextField(max_length=200)
    email = models.EmailField()

    class Meta:
        db_table = 'student'
# 增
models.Student.objects.create(name='学生A', gender=False, age=122, content='学习优异', email='[email protected]')

student_obj = models.Student(name='学生B', gender=True, age=13, content='学习良好', email='[email protected]')
student_obj.save()

# 批量增加数据
student_objs = [models.Student(name='学生', gender=True, age=11, content="学生的描述") for i in range(10)]
models.Student.objects.bulk_create(student_objs)


# 删
models.Student.objects.get(id=1).delete()

# 改
obj = models.Student.objects.get(name='学生A')
obj.name = '学生AAA'
obj.save()

# 查
# all():查询所有结果;QuerySet
student_all = models.Student.objects.all()

# filter(**kwargs):查询条件匹配的对象;QuerySet
student_list = models.Student.objects.filter(name='学生B')

# get(**kwargs):查询条件相匹配的对象,对象类型,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
student_obj = models.Student.objects.get(name='学生C')

# exclude(**kwargs):筛选条件不匹配的对象
student_list = models.Student.objects.exclude(name='学生D')

# values(*field):返回指定列名的数据,每个对象是一个字典
student_list = models.Student.objects.values('id', 'name')

# value_list(*field):返回指定列名的数据,每个对象是一个元组
student_list = models.Student.objects.values_list('id', 'name')

# order_by(*field):排序;'+':升序,'-':降序
student_list = models.Student.objects.all().order_by('age','-id')

# reverse():顺序反转

# distince():去重

# count():统计对象数量
c = models.Student.objects.all().count()

# first():返回第一条数据;对象类型

# last():返回最后一条数据;对象类型

# exists():表里是否包含数据
has = models.Student.objects.exists()

双下划线操作

# 查询id大于5小于10的数据
student_list = models.Student.objects.filter(id__gt=5, id__lt=10)

# 查询id为2,5,7的数据
student_list = models.Student.objects.filter(id__in=[2, 5, 7])

# 查询名字中包含'A'的数据
student_list = models.Student.objects.filter(name__contains='A')

# 不区分大小写
student_list = models.Student.objects.filter(name__icontains='a')

# 查询id范围从7到10
student_list = models.Student.objects.filter(id__range=[7, 10])

# 查询指定字符串开头
student_list = models.Student.objects.filter(name__startswith='学生')

多表操作

OneToOneField 一对一

通常一对一字段用来扩展已有字段

class Employee(models.Model):
    name = models.CharField(max_length=32)

    class Meta:
        db_table = 'employee'


class EmployeeDetail(models.Model):
    phone = models.CharField(max_length=11)
    email = models.EmailField()
    addr = models.CharField(max_length=108)
    employee = models.OneToOneField(to='Employee')

    class Meta:
        db_table = 'employee_detail'
# 正向查找:EmployeeDetail->Employee
employee_detail = models.EmployeeDetail.objects.get(pk=1)
employee_detail.employee

# 反向查找:Employee->EmployeeDetail
employee = models.Employee.objects.get(pk=1)
employee.employeedetail

ForeignKey 一对多

外键类型在ORM中用来表示外键关联关系,一般把ForeignKey字段设置在 一对多的一方

class Publisher(models.Model):
    """
    出版
    """
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32, unique=True, db_index=True)

    def __str__(self):
        return self.name

    class Meta:
        db_table = 'publisher'


class Book(models.Model):
    """
    书籍
    """
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=32)
    # 外键联系
    publisher = models.ForeignKey(to='Publisher', on_delete=models.CASCADE)

    def __str__(self):
        return self.title

    class Meta:
        db_table = 'book'

字段参数

to:设置关联的表
to_field:设置关联的表的字段
related_name:反向操作时,使用该字段名替代'表名_set'
on_delete:
	models.CASCADE:删除关联数据,与之关联也删除
	models.DO_NOTHING:删除关联数据,引发错误IntegrityError
	models.PROTECT:删除关联数据,引发错误ProtectedError
	models.SET_NULL:删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
	models.SET_DEFAULT:删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)

正向查询

通过外键查询

# 1.使用对象查询
book_obj = models.Book.objects.first()  # 获取书对象
book_obj.publisher  # 获取出版社对象
book_obj.publisher.name  # 获取出版社名

# 2.使用双下划线
publishers = models.Book.objects.filter(id=1).values_list('publisher__name')

反向查询

# 1.通过对象查询
publisher_obj = models.Publisher.objects.first() # 获取出版社对象
books = publisher_obj.book_set.all() # 获取所有书

# 2.使用双下划线
books = models.Publisher.objects.filter(id=1).values_list('book__title')

ManyToManyField 多对多

1. ORM生成第三张表
class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    books = models.ManyToManyField(to='Book')

class Book(models.Model):
    title = models.CharField(max_length=32)
    publish_date = models.DateField(auto_now_add=True)

正向查询

# 1.通过对象查询
book_list = models.Author.objects.get(id=1).books.all()

# 2.使用双下划线
book_list = models.Author.objects.filter(id=1).values_list('books__title')

反向查询

# 1.通过对象查询
author_list = models.Book.objects.get(id=2).author_set.all()

# 2.使用双下划线
author_list = models.Book.objects.filter(id=2).values_list('author__name')
2. 自己创建第三张表
class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()

    def __str__(self):
        return self.name


class Book(models.Model):
    title = models.CharField(max_length=32)
    publish_date = models.DateField(auto_now_add=True)

    def __str__(self):
        return self.title


class AuthorToBook(models.Model):
    author = models.ForeignKey(to='Author')
    book = models.ForeignKey(to='Book')
    remark = models.CharField(max_length=300)

    class Meta:
        # 联合唯一约束
        unique_together = ('author', 'book')

查询

# 查询author_id=1的书籍id
book_id_list = models.AuthorToBook.objects.filter(author_id=1).values_list('book_id')
# 查询指定书籍
book_list = models.Book.objects.filter(id__in=book_id_list)
3. ManyToManyField指定第三张表
class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    books = models.ManyToManyField(to='Book', through='Author2Book', through_fields=('author', 'book'))
    # through:指定第三张表
    # through_fields:有顺序要求,field1关联ManyToManyField的外键,field2关联book外键

    def __str__(self):
        return self.name


class Book(models.Model):
    title = models.CharField(max_length=32)
    publish_date = models.DateField(auto_now_add=True)

    def __str__(self):
        return self.title


class Author2Book(models.Model):
    author = models.ForeignKey(to='Author')
    book = models.ForeignKey(to='Book')
    remark = models.CharField(max_length=300)

    class Meta:
        # 联合唯一约束
        unique_together = ('author', 'book')

查询

# 通过作者查找书籍
book_list = models.Author.objects.get(id=1).books.all()
# 通过书籍查找作者
author_list = models.Book.objects.get(id=3).author_set.all()

注意:

  1. 第一种适合关系表中没有额外的字段,第三种适合关系表中有额外的字段,推荐使用第一种和第三种
  2. 我们使用第三种方式创建多对多关联关系时,就无法使用set、add、remove、clear方法来管理多对多的关系了,需要通过第三张表的model来管理多对多关系。

聚合查询

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

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

# 获取平均数
models.Book.objects.all().aggregate(Avg('price'))
# 指定命名
models.Book.objects.all().aggregate(price_avg=Avg('price'))
# 获取最大值
models.Book.objects.all().aggregate(Max('price'))
# 获取最小值
models.Book.objects.all().aggregate(Min('price'))
# 获取总价格
models.Book.objects.all().aggregate(Sum('price'))
# 获取价格出现的次数
models.Book.objects.all().aggregate(Count('price'))

分组查询

class Employee(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    salary = models.DecimalField(max_digits=8, decimal_places=2)
    province = models.CharField(max_length=32)
    dept = models.CharField(max_length=32)

    class Meta:
        db_table = 'employee'

根据部门和平均工资分组:

使用原生SQL查询

select dept,avg(salary) from employee group by dept;

使用Django的ORM

models.Employee.objects.values('dept').annotate(avg=Avg('salary')).values('dept', 'avg')

说明:annotate前面是什么字段,就按什么分组

F 查询

Django 提供 F() 来做这样的比较。F() 的实例可以在查询中引用字段,用于比较同一个 model 实例中两个不同字段的值

# 查询工资大于年龄的员工
from django.db.models import F

emploee_list = models.Employee.objects.filter(salary__gt=F('age'))

Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取模的操作。

models.Employee.objects.filter(salary__gt=F('age')+6000)

修改char字段

from django.db.models import Value
from django.db.models.functions import Concat
models.Employee.objects.all().update(dept=Concat(F('dept'),Value('部门')))

Q 查询

filter() 等方法中的关键字参数查询都是一起进行“AND” 的。 如果你需要执行更复杂的查询(例如OR语句),你可以使用Q对象

# 查询id或age
employee_list = models.Employee.objects.filter(Q(id=1) | Q(age=30))

你可以组合&| 操作符以及使用括号进行分组来编写任意复杂的Q 对象。同时,Q 对象可以使用~ 操作符取反,这允许组合正常的查询和取反(NOT) 查询

执行原生查询

from django.db import connection

# 查询id=1的数据
cursor = connection.cursor()
cursor.execute('select * from person where id=%s', [1])
ret = cursor.fetchone()
print(ret)
# 查询salary>1000的数据
ret = models.Person.objects.all().extra(
    select={'gt': '200'}
)
print(ret)

Django终端打印SQL语句

在Django项目的settings.py文件中,在最后复制粘贴如下代码:

[复制代码](javascript:void(0)

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}

即为你的Django项目配置上一个名为django.db.backends的logger实例即可查看翻译后的SQL语句。

在Python脚本中调用Django环境

import os

if __name__ == '__main__':
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
    import django
    django.setup()

    from app01 import models

    books = models.Book.objects.all()
    print(books)

你可能感兴趣的:(Django)