django之模型层ORM操作

1.orm中常用命令

# models.Book.objects

# 增
create()

# 删
delete()

# 改
update()

# 查
filter()
exclude() # 与filter()取反,除条件之外的记录。
all()
get()----># 此命令可直接获取对象本身,但如果查找的记录不存在,则会报错。

# 获取queryset中的对象
first()
last()

# 其他
values() # 获取queryset中指定字段的数据,以字典形式
values_list() # 和values一样,以元组形式
count() # 计数 不接受任何参数
order_by() # 按指定字段进行排序

exists() # 判断queryset是否有数据
reverse() #  将queryset的顺序倒置,一般在order_by后使用。
distinct()  #  将queryset去重(每个字段的值都必须完全一样才会被去重)

#################数据库优化查询###############################
#  only和defer(only只取部分字段,defer时only的取反)只获取部分字段与可与filter配合使用
res = models.Book.objects.only('title', 'price')
for obj in res:
    print(obj.title, obj.price)
res = models.Book.objects.defer('title', 'price')
for obj in res:
    print(obj.title, obj.price)
    
# selected_related与prefetch_related(selected_related返回的queryset的obj中有两表所有的内容,联表查询,一次sql)
res = models.Book.objects.select_related('publish')
for obj in res:
    print(obj.title, obj.price, obj.publish.name)
# prefetch_related实际上进行两次sql查询,先从Book表获取,再基于Book获取的对象从publish查询(子查询)
res = models.Book.objects.prefetch_related('publish')
print(res)
# selected_related与prefetch_related在外部看来返回的queryset是一样的,可根据表的实际情况来选择优化。

2.双下划线查询

# 查询id>2的数据
res = models.Book.objects.filter(id__gt=2)
# 查询id>=2的数据
res = models.Book.objects.filter(id__gte=2)
# __lt、__lte 同理

# 查询在每个范围内的数据
res = models.Book.objects.filter(id__in=[1,2,3])
res = models.Book.objects.filter(id__in=range(1,6,2))
# 查询在100-1000价格的数据,包括10和100
res = models.Book.objects.filter(price__range=(100,1000))


# 模糊查询
res = models.Book.objects.filter(title__contains='三')
# 不区分大小写icontains
res = models.Book.objects.filter(title__icontains='三')

# 查询2020年1月出版的电影
res = models.Book.objects.filter(publish_date_year=2020,publish_date_month=1)

3.外键字段的增删改查

# 一对多 在book表增加一条数据
# 方式1
models.Book.objects.create(title='三国演义',price=333,publish_id=1)
# 方式2:通过增加或查找先得到一个obj对象
publish_obj = models.Publish.objects.create(name='东方出版社',addr='东京')
models.Book.objects.create(title='三国演义',price=333,publish=publish_obj)

# 对book表中的外键字段进行修改
models.Book.objects.filter(pk=1).update(publish_id=2)

# 将id为1的book出版社改为东方出版社
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
models.Book.objects.filter(pk=1).update(publish=publish_obj)

在为多对多创建关联记录时,由于django的orm是自动为我们创建的第三张表,我们的models类里也不存在这个类。所以我们不能直接以第三张表为对象去添加book和author的关联记录。

只能通过book _obj和author_obj下的方法来添加关联记录。

# 多对多    add、remove、set、clear
# 1.给书籍绑定作者关系
book_obj = models.Book.objects.filter(pk=1).first()
res = book_obj.author.all() #  可获得book_obj这条记录对应的作者对象。本质上在book_obj.author时进行了与book_author的连表操作,并获取到了结果。

# 为书籍与作者id为2和3进行绑定。
book_obj.author.append(2,3)
# 为某一书籍与作者david4和david3进行绑定
book_obj = models.Book.objects.filter(pk=1).first()
res = book_obj.author.all()
print(res)
print(res.query)
author_obj1 = models.Author.objects.filter(name='David4').first()
author_obj2 = models.Author.objects.filter(name='David3').first()
book_obj.author.add(author_obj1,author_obj2)
#remove方法与add方法同理
#set方法:接收一个元组或列表,有则删除、无则添加。
book_obj.author.set((1,))

# clear方法无需参数
book_obj.author.clear()

4.跨表查询

跨表查询分为子查询和联表查询。

子查询:将一张表的查询结果当做另外一张表的查询条件。

联表查询:先将两张有关联的表合并,然后再进行查询。

子查询和联表查询都有正向查询和反向查询的概念。

正向查询:跨表查询时外键字段在当前数据对象中则是正向,查询时按外键字段.

反向查询:跨表查询时外键字段不在当前数据对象中则是反向,查询时按表名小写_set.

注意:!!!外键字段在不在当前数据对象中以models中为准!!!一对一时无论正反都不需要加_set。

# 基于对象的跨表查询(子查询)
# 1.查询书籍id为1的出版社名称
book_obj = models.Book.objects.filter(pk=1).first()
# res = models.Publish.objects.filter(pk=book_obj.publish_id)
# print(res.first().name)
res = book_obj.publish.name
print(res)

#2.查询书籍id为2的所有作者
book_obj = models.Book.objects.filter(pk=2).first()
res = book_obj.author.all()
print(res)

#3.查询作者id为1的电话号码
author_obj = models.Author.objects.filter(id=1).first()
res = author_obj.author_detail
print(res.phone)

#4.查询出版社名称为东方出版社的书籍
publish_obj = models.Publish.objects.filter(name='东方出版社').first()
res = publish_obj.book_set.all()
print(res)

#5.查询作者为david1的书籍
author_obj = models.Author.objects.filter(name='David1').first()
res = author_obj.book_set.all()
print(res)

#6.查询手机号为110的作者
author_detail_obj = models.AuthorDetails.objects.filter(phone=110).first()
res = author_detail_obj.author
print(res)
# 基于双下划线跨表查询(联表查询)
# django会将__自动识别为联表
# 1、查询书籍id为1的出版社名称
# 正向查询 
res = models.Book.objects.filter(id=1).values('publish__name')
print(res)
# 反向查询
res = models.Publish.objects.filter(book__id=1)
print(res)

# 2.查询书籍id为1的作者姓名和年龄
# 正向查询
res = models.Book.objects.filter(id=1).values('author__name','author__age')
# 反向查询
res = models.Author.objects.filter(book__id=1)
print(res)

# 3.查询作者是David1的年龄和手机号
# 正向查询
res = models.Author.objects.filter(name='David1').values('name','age','author_detail__phone')
print(res)
# 反向查询
res = models.AuthorDetails.objects.filter(author__name='David1')
print(res.first().phone)

# 4.查询书籍id为1的作者的手机号
# 正向查询
res = models.Book.objects.filter(pk=1).values('author__author_detail__phone')
print(res)

# 反向查询
res = models.AuthorDetails.objects.filter(author__book__id=1)
print(res.first().phone)

#!!!!! 只要表之间有关系,就可以一直用__进行联表操作。!!!!!!!

5.聚合查询

关键字aggregate

from django.db.models import Max, Min, Avg, Count, Sum
# 查询所有书的平均价格
res = models.Book.objects.aggregate(avg_num=Avg('price'))
print(res)
# 查询最贵的书的价格
res = models.Book.objects.aggregate(max_price=Max('price'))
print(res)
res = models.Book.objects.aggregate(Avg("price"),Max("price"), Min("price"),Count("pk"),Sum('price'))
print(res)

6.分组查询

关键字annotate

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

# 1.统计每一本数的作者的个数
res = models.Book.objects.all().annotate(author_num=Count('author')).values('title','author_num')
print(res)

# 2.统计出每个出版社卖的最便宜的书的价格
res = models.Publish.objects.all().annotate(min_price=Min('book__price')).values('name','min_price')
print(res)

#3.统计出不止一个作者的书
res =models.Book.objects.annotate(author_num=Count('author__id')).filter(author_num__gt=1)
print(res)

# 4.查询各个作者出的书总价格
res = models.Author.objects.annotate(total_price=Sum('book__price')).values('name','total_price')
print(res)


# 按照表中的一个字段分组
res = models.Book.objects.values('price').annotate()

7.F查询和Q查询

from django.db.models import F,Q

# F可以获取到每条记录对应的字段值
# 1.查询库存数大于销售数的书籍
res = models.Book.objects.filter(storage__gt=F('sales'))
print(res)
# 2. 为所有书籍价格增加100
res = models.Book.objects.update(price=F('price') + 100)
print(res)

# Q的用法
q = Q()
q.connector = 'or'  # q对象默认也是and关系  可以通过connector改变or
q.children.append(('title','python入门'))
q.children.append(('price',1000))
res = models.Book.objects.filter(q)
print(res)

8.models中常用字段

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

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

        注:当model中如果没有自增列,则自动会创建一个列名为id的列
        from django.db import models

        class UserInfo(models.Model):
            # 自动创建一个列名为id的且为自增的整数列
            username = models.CharField(max_length=32)

        class Group(models.Model):
            # 自定义自增列
            nid = models.AutoField(primary_key=True)
            name = models.CharField(max_length=32)

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

    PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
        - 正小整数 0 ~ 32767
    IntegerField(Field)
        - 整数列(有符号的) -2147483648 ~ 2147483647

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

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

    BooleanField(Field)
        - 布尔值类型

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

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

    TextField(Field)
        - 文本类型

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

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

    GenericIPAddressField(Field)
        - 字符串类型,Django Admin以及ModelForm中提供验证 Ipv4和Ipv6
        - 参数:
            protocol,用于指定Ipv4或Ipv6, 'both',"ipv4","ipv6"
            unpack_ipv4, 如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"

    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)
        - 二进制类型

常用和非常用字段合集
对应关系:
    '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)',

ORM字段与数据库实际字段的对应关系
null                数据库中字段是否可以为空
    db_column           数据库中字段的列名
    db_tablespace
    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': '格式错误'}

    validators          自定义错误验证(列表类型),从而定制想要的验证规则
    
from django.core.validators import RegexValidator
from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
如:
test = models.CharField(
    max_length=32,
    error_messages={
        'c1': '优先错信息1',
        'c2': '优先错信息2',
        'c3': '优先错信息3',
    },
    validators=[
        RegexValidator(regex='root_\d+', message='错误了', code='c1'),
        RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
        EmailValidator(message='又错误了', code='c3'), ]
)
# chocies参数
choices参数
用户的性别
学历
婚否
在职状态
客户来源
能够被枚举的

class Userinfo(models.Model):
    username = models.CharField(max_length=32)
    gender_choices = (
        (1,'男'),
        (2,'女'),
        (3,'其他'),
    )
    gender = models.IntegerField(choices=gender_choices)
    # 该字段还是存数字 并且可以匹配关系之外的数字
    record_choices = (('checked', "已签到"),
                      ('vacate', "请假"),
                      ('late', "迟到"),
                      ('noshow', "缺勤"),
                      ('leave_early', "早退"),
                     )
    
record = models.CharField("上课纪录", choices=record_choices, default="checked", max_length=64)
user_obj = models.Userinfo.objects.get(pk=1)
print(user_obj.username)
print(user_obj.gender)
# 针对choices参数字段 取值的时候   get_xxx_display()
print(user_obj.get_gender_display())
# 针对没有注释信息的数据  get_xxx_display()获取到的还是数字本身
user_obj = models.Userinfo.objects.get(pk=4)
print(user_obj.gender)
print(user_obj.get_gender_display())

9.自定义char字段

from django.db.models import Field


class RealCharField(Field):
    def __init__(self,max_length,*args,**kwargs):
        self.max_length = max_length
        super().__init__(max_length=max_length,*args,**kwargs)


        def db_type(self, connection):
            return 'char(%s)'%self.max_length

        class Movie(models.Model):
            textField = RealCharField(max_length=64)

10.orm中开启事务

事务的四大特性:ACID

原子性、一致性、隔离性、持久性

from django.db import transaction
from django.db.models import F

with transaction.atomic():
    models.Book.objects.filter(pk=1).update(storage=F('storage') - 10,sales=F('sales')+10)

11.MTV与MVC模型

MTV  django号称是MTV框架
	M:models
	T:templates
	V:views
MVC
	M:models
	V:views
	C:contronnar  控制器(路由分发 urls.py)
本质:MTV本质也是MVC

12.django多对多表的三种创建方式

# 1.全自动(较为常用)
	class Book(models.Model):
		title = models.CharField(max_length=32)
		authors = models.ManyToManyField(to='Author')
		# orm就会自动帮你创建第三张表
		
	class Author(models.Model):
		name = models.CharField(max_length=32)
	"""
	好处:第三张表自动创建
	不足之处:第三张表无法扩展额外的字段
	"""
# 2.纯手动(了解)
class Book(models.Model):
    title = models.CharField(max_length=32)

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

class Book2Author(models.Model):
	book = models.ForeignKey(to='Book')
	author = models.ForeignKey(to='Author')
	create_time = models.DateField(auto_now_add=True)
"""
	好处在于第三表可以扩展额外的字段
	不足之处:orm查询的时候会带来不便
"""

# 3.半自动(推荐)
class Book(models.Model):
    title = models.CharField(max_length=32)
    authors = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author'))

class Author(models.Model):
    name = models.CharField(max_length=32)
    books = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('author','book'))

class Book2Author(models.Model):
    book = models.ForeignKey(to='Book')
    author = models.ForeignKey(to='Author')
    create_time = models.DateField(auto_now_add=True)
    
"""
好处在于第三步可以扩展任意的额外字段 还可以利用orm 正反向查询
	不足之处:无法利用
	add
	set
	remove
	clear
虽然无法使用了 但是你还可以自己直接操作第三表
"""

添加以下设置到settings.py中将自动打印sql语句

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

配置测试环境

tests.py
-------------------
import os

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "BMS.settings")
    import django
    django.setup()
posted @ 2020-01-09 09:39  GhostAnt  阅读(163)  评论(0编辑  收藏  举报