其他分享
首页 > 其他分享> > django之模型层1

django之模型层1

作者:互联网


# 模型层就是跟数据库打交道的 数据库就是如何查询数据

一、表查询数据准备以及测试环境搭建

'''1.django自带一个数据库sqlite3小型数据库 
这个数据库功能少 而且对日期类型不兼容 只适合做本地测试'''

# 2.django链接mysql数据库
'''链接上之后需要一些配置才能运行
django1.x  
    需要在应用或在与项目同名的文件夹下的__init__.py文件中写固定的代码
    import pymysql
    pymysql.install_as_MYSQLDB()
django2.x、3.x
    只需要下载一个模块即可
    pip3 install mysqlclient'''

# 3.定义模型类
class User(models.Model):
    uid = models.AutoField(primary_key=True, verbose_name='编号')
    name = models.CharField(max_length=32, verbose_name='姓名')
    age = models.IntegerField(verbose_name='年龄')
    join_time = models.DateField(auto_now_add=True)
'''日期类型有两个参数:
auto_now_add: 只有在创建数据库的时候会自动获取时间 之后不在人为改动的情况下不会在修改
auto_now: 每次操作数据并保存都会自动更新时间'''

# 4.执行数据库迁移命令(模型类>>>表)   
    migrations
        migrate

# 5.模型层测试环境准备
    '''因为编写orm代码需要通过编写路由层和视图层代码 这会很麻烦 这个时候就需要一个测试环境就可以直接操作数据库了'''
    # 方式一:这需要在一个空的py文件下编写固定代码即可
        import os
        def main():
            os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'day59.settings')
            import django
            django.setup()

            from app01 import models
            print(models.User.objects.filter())
    # 然后在main函数下编写orm代码即可
        if __name__ == '__main__':
            main()
    # 方式2:pycharm提供测试环境
        python console命令行测试环境    

二、ORM常见查询关键字

# 1.filter()
    <QuerySet [<User: User object (1)>, <User: User object (2)>, <User: User object (3)>]>
    '''筛选条件  返回值是一个QuerySet对象(可以看成是一个列表套数据对象)
        1.括号内不写条件就是查询全部
        2.括号内可以写条件 而且可以是多个 逗号隔开 是and关系 '''

# 2.all()
    '''查询所有数据 返回值是一个QuerySet对象(可以看成是一个列表套数据对象)'''

# 3.first()
    '''当查询出来的数据对象可以使用索引获取
       res = models.User.objects.filter()[0] 就可以获取到第一个数据对象
        然后就可以点出该对象的值 print(res.name)  # jason  
       不支持负数索引
        但是不推荐使用索引 推荐使用first 获取第一个
        res = models.User.objects.filter().first()
        也是获取QuerySet第一个数据对象 print(res.name)  # jason  
    两者的区别 如果该索引没有就会报错  而first如果没有值不会报错返回None'''

# 4.last()
    获取QuerySet最后一个数据对象

# 5.get()
    直接根据条件条件查询具体的数据对象 但是没有的话就会报错 不推荐使用
    res = models.User.objects.get(pk=2)
        print(res.name)  # kevin

# 6.values()
    指定查询字段 结果是一个Queryset (可以看成是一个列表套字典数据)
res = models.User.objects.filter().values('name')  # 条件不写就是查询全部
    print(res)  # <QuerySet [{'name': 'jason'}, {'name': 'kevin'}, {'name': 'tony'}]>

# 7.value_list()
    也是指定查询字段 结果是一个Queryset(可以看成是一个列表套元祖数据)
    res = models.User.objects.filter().values_list('name')  # 没写条件就是查询全部
        print(res)  # <QuerySet [('jason',), ('kevin',), ('tony',)]>

# 8.order_by()
    指定字段排序 默认升序 在字段前面加符号就是降序 支持多个字段排序
    res = models.User.objects.order_by('age')
    res1 = models.User.objects.order_by('-age')
    print(res,'\n',res1) 
 <QuerySet [<User: 对象:jason>, <User: 对象:kevin>, <User: 对象:tony>]> 
 <QuerySet [<User: 对象:tony>, <User: 对象:kevin>, <User: 对象:jason>]>

# 9.count()
    统计orm查询之后结果集中的数据格式
    res = models.User.objects.filter().count()
    print(res)  # 3

# 10.distinct()
    去重 需要数据要完全一模一样 不能有主键

# 11.exclude()
    其实就是取反 根据括号内的条件查询不同的值
    res = models.User.objects.exclude(pk=1)
    print(res)  # <QuerySet [<User: 对象:kevin>, <User: 对象:tony>]>

# 12.reverse()
    针对已经排过序的结果集做颠倒  没有排过序会报错

# 13.exists()
    判断查询结果集是否有数据  返回布尔值 基本不用因为所有的数据都自带布尔值

# 14.编写SQL语句
    1.可以使用RAW()
    res = models.User.objects.raw('select * from app01_user')
    print(list(res))
    2.还可以使用模块
 from django.db import connection
    cursor = connection.cursor()
    cursor.execute("insert into app01_user(name,age,join_time) VALUES ('oscar',18)")  # 添加数据
    cursor.execute("update app01_user set name='oscar' WHERE name='jason'")  # 修改数据
    cursor.execute("delete from app01_user where name='oscar'")  # 删除数据
    cursor.execute("select * from app01_user")  # 查看数据
    res = cursor.fetchone()
    res1 = cursor.fetchall()
    print(res, res1)

"""
1.当需要查询数据主键字段值的时候 可以使用pk忽略掉数据字段真正的名字
2.在模型类中可以定义一个__str__方法 便于后续数据对象被打印展示的是查看方便
3.Queryset中如果是列表套对象那么直接for循环和索引取值但是索引不支持负数
4.虽然queryset支持索引但是当queryset没有数据的时候索引会报错 推荐使用first
    1.create()
        创建数据 返回值就是当前创建的数据对象
        ps:还可以利用类实例化对象然后调用save方法创建
      2.update()
             更新数据
    3.delete()
         删除数据
5.first和all、exclude获取的都是列表套数据对象
6.values和values_list 分别获取的是列表套字典数据和列表套元祖数据
"""

三、神奇的双下划线查询

# 当我们查询年龄大于20的数据的时候怎么查询呢?
# res = models.User.objects.filter(age > 18)  这样是会报错的 不能使用特殊符号

# 这个时候就需要双下划线查询了 
    res = models.User.objects.filter(age__gt=18)  # 查询年龄大于18的数据
    print(res)  # <QuerySet [<User: 对象:kevin>, <User: 对象:tony>]>

# 只需要在字段名后面加上双下划线再写上特定符号即可

# 1.比较运算符
    字段__gt       大于
    字段__lt        小于
    字段__gte      大于等于
    字段__lte       小于等于

# 2.成员运算符
    字段__on        是否在什么什么之内

# 3.范围查询
    字段__range  # 左右包含
 res = models.User.objects.filter(age__range=(18, 38))
 print(res)  # <QuerySet [<User: 对象:jason>, <User: 对象:kevin>, <User: 对象:tony>, <User: 对象:oscar>]>

# 4.模糊查询
    在mysql中使用like
    在orm中使用
    字段__contains   不忽略大小写
    字段__icontains    忽略大小写
 res = models.User.objects.filter(name__contains='j')
 print(res)  # <QuerySet [<User: 对象:jason>, <User: 对象:jerry>]>

# 5.日期处理
    # 就是获取日期中含有的年月日
    字段__year       # 年
    字段__month     # 月
    字段__day         # 日
  res = models.User.objects.filter(join_time__year=2018)
    print(res)  # <QuerySet [<User: 对象:jason>, <User: 对象:kevin>]>

四、查看ORM底层SQL语句

# 方式1:
    如果结果是Queryset对象 那么直接点query即可
    res = models.User.objects.filter(join_time__year=2018)
    print(res.query)  
# SELECT `app01_user`.`uid`, `app01_user`.`name`, `app01_user`.`age`, `app01_user`.`join_time` FROM `app01_user` WHERE `app01_user`.`join_time` BETWEEN 2018-01-01 AND 2018-12-31 # 方式2: 如果不是Queryset对象那么直接在配置文件中配置下面代码即可 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }

五、ORM创建外键字段

'''
一对多
    ORM中外键字段建在多的一方 models.ForeignKey()
           会自动在外键字段名称添加_id后缀
多对多
    ORM中有三种创建多对多字段的方式 models.ManyToManyField()
        方式1:直接在查询频率较高的表中填写字段即可 自动创建第三张关系表
       方式2:自己创建第三张关系表
       方式3:自己创建第三张关系表 但是还是要orm多对多字段做关联
一对一
    ORM中外键字段建在查询频率较高的表中 models.OneToOneField()
        会自动在外键字段名称添加_id后缀
 
django1.X 针对 models.ForeignKey() models.OneToOneField()不需要on_delete
django2.X 3.X 则需要添加on_delete参数
'''

六、外键字段数据操作

1.数据准备

class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)  # 总共八位 小数点后面站两位
    publish_time = models.DateTimeField(auto_now=True)

    publish = models.ForeignKey(to='Publish', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')

    def __str__(self):
        return '书籍对象:%s' % self.title


class Publish(models.Model):
    name = models.CharField(max_length=32)
    email = models.EmailField()

    def __str__(self):
        return '出版社对象:%s' % self.name


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_detail = models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)

    def __str__(self):
        return '作者对象:%s' % self.name


class AuthorDetail(models.Model):
    phone = models.BigIntegerField()
    addr = models.CharField(max_length=255)

    def __str__(self):
        return '作者详情对象:%s' % str(self.phone)

2.外键字段操作数据

'''
因为书和作者的关系是多对多关系所以ORM会帮我们创建一张虚拟的表
而我们现在需要往这张表添加书与作者的对应关系的时候
models点点不出来这张表  该怎么添加数据呢'''

# 多对多
models.ManyToManyField(to='Author')
# 我们可以先创建一个数的对象通过这个对象添加数据
     book_obj = models.Book.objects.filter(pk=1).first()
     book_obj.authors.add(1)  # 朝第三张关系表添加数据 就是将主键为1的书籍与主键为1的作者绑定
    # 可以添加多个
    book_obj.authors.add(2,3)  # 也是朝第三张关系表添加数据
    # 也可以吧作者对象先创建出来也可以
    author_obj1 = models.Author.objects.filter(pk=1).first()
    author_obj2 = models.Author.objects.filter(pk=2).first()
    book_obj.authors.add(author_obj1)  # 将主键是1的作者跟主键为1的书籍绑定
    book_obj.authors.add(author_obj1,author_obj2)

# 删除
    book_obj.authors.remove(1)  # 删除主键为1的书籍与主键为1的作者的绑定关系
    book_obj.authors.remove(2, 3)
    book_obj.authors.remove(author_obj1)
    book_obj.authors.remove(author_obj1,author_obj2)

# 修改
     # book_obj.authors.set([2,])  # 把主键为1的书籍遂改为与主键为2的作者绑定
    book_obj.authors.set([2,3])
    book_obj.authors.set([author_obj1, ])
    book_obj.authors.set([author_obj1, author_obj2])
    # 其实就是把原来的删除 然后在添加 set后必须要是可迭代对象

# 清空
    book_obj.authors.clear()  # 将主键为1的书籍与绑定的作者清空

#一对多
models.ForeignKey(to='Publish', on_delete=models.CASCADE)
      # 方式1直接给实际字段添加关联数据值     publish_id = 1
models.Book.objects.create(title='python从入门到放弃',price=29800.88,publish_id=1)  # 创建的时候直接写上即可
      # 方式2间接使用外键虚拟字段添加数据对象 publish=publish_obj

# 一对一
models.OneToOneField(to='AuthorDetail', on_delete=models.CASCADE)
      # 方式1直接给实际字段添加关联数据值     author_detail_id = 1
      # 方式2间接使用外键虚拟字段添加数据对象 author_detail=authorDetail_obj

七、正反向概念

# 当我们用ORM进行跨表查询的时候需要知道什么是正向和反向

'''
正向与反向的核心就是 外键字段在哪个表上面
正向查询
    通过书查询出版社 书与出版社的外键字段在书表上 所以是正向查询 

反向查询
    通过出版社查询书  出版社与书的外键字段不在出版社上 所以是反向查询
一对多、多对多、一对一都是看外键字段在谁手上
'''

# 然后ORM跨表查询口诀:
    '''
正向查询按外键字段 反向查询按表名小写
    '''

八、基于对象跨表查询(子查询)

'''我们可以做几道题目理解'''

'''基于对象的正向跨表查询'''
# 1.查询主键为1的书籍对应的出版社(书>>>出版社)
    book_obj = models.Book.objects.filter(pk=1).first()
    # 1.1 因为外键字段在书表上 所以是正向
    print(book_obj.publish)  # 出版社对象:北方出版社

# 2.查询主键为3的书籍对应的作者(书>>>作者)
    book_obj = models.Book.objects.filter(pk=3).first()
    # 2.1 因为外键字段在书表上 所以是正向
    # print(book_obj.authors)  # app01.Author.None
    print(book_obj.authors.all())  # <QuerySet [<Author: 作者对象:jason>, <Author: 作者对象:tony>]>

# 3.查询jason的作者详情(作者>>>作者详情)
    author_obj = models.Author.objects.filter(name='jason').first()
    # 3.1 因为外键字段在作者表上  所以是正向
    print(author_obj.author_detail)  # 作者详情对象:111
'''如果查询的结果是多个 那么必须要加上.all
查询结果是一个 那么不需要加'''

'''基于对象的反向跨表查询'''
    # 4.查询南方出版社出版的书籍(出版社>>>书)
    publish_obj = models.Publish.objects.filter(name='南方出版社').first()
    # 4.1因为外键字段在书籍表上 所以是反向查询
    print(publish_obj.book_set)  # app01.Book.None
    print(publish_obj.book_set.all())  # <QuerySet [<Book: 书籍对象:java>]>

    # 5.查询jason写过的书(作者>>>书)
    author_obj = models.Author.objects.filter(name='jason').first()
    # 5.1 因为外键字段在书籍表上 所以是反向查询
    # print(author_obj.book_set)  # app01.Book.None
    print(author_obj.book_set.all())  # <QuerySet [<Book: 书籍对象:java>, <Book: 书籍对象:golang>]>

    # 6.查询电话是111的作者(作者详情>>>作者)
    author_detail_obj = models.AuthorDetail.objects.filter(phone=111).first()
    # 6.1 因为外键字段在作者表上  所以是反向查询
    print(author_detail_obj.author)  # 作者对象:jason
'''
如果查询结果为多个那么必须要加上_set.all()
如果查询结果为一个 那么不需要加
'''

九、基于双下划线跨表查询(链表操作)

'''题目还是一样只不过现在需要一条ORM代码就要得出结果'''

    '''基于双下划线的正向跨表查询'''
    # 1.查询主键为1的书籍对应的出版社名称及书名
    # 因为外键字段在书籍表中 所以是正向
    res = models.Book.objects.filter(pk=1).values('publish__name', 'title')  # publish是书籍表中的外键字段名称
    print(res)  # <QuerySet [{'publish__name': '北方出版社', 'title': 'python'}]>
    '''当我们写上外键字段就已经跳到出版社表了 在双下划线就可以拿到出版社表的字段了 因为前面是书籍对象所以只要写字段名即可'''

    # 2.查询主键为3的书籍对应的作者姓名及书名
    # 因为外键字段在书籍表中 所以是正向
    res = models.Book.objects.filter(pk=3).values('authors__name', 'title')  # authors是书籍表中的外键字段名称
    print(res)  # <QuerySet [{'authors__name': 'jason', 'title': 'golang'}, {'authors__name': 'tony', 'title': 'golang'}]>

     # 3.查询jason的作者的电话号码和地址
    # 因为外键字段在作者表中 所以是正向
    res = models.Author.objects.filter(name='jason').values('author_detail__phone','author_detail__addr')  # author_detail是作者表中的外键字段
    print(res)

    '''基于双下划线的反向跨表查询'''
    # 4.查询南方出版社出版的书籍名称和价格
    # 因为外键字段在书籍表中  所以是反向 
    res = models.Publish.objects.filter(name='南方出版社').values('book__title','book__price')  # book就是表Book的小写
    print(res)  # <QuerySet [{'book__title': 'java', 'book__price': Decimal('333333.00')}]>
    '''当我们写上小写表名的时候其实就已经跳到了书籍表 然后在双下划线就能拿到书籍表中的字段了'''

    # 5.查询jason写过的书的名称和日期
    # 因为外键字段在书籍表中  所以是反向
    res = models.Author.objects.filter(name='jason').values('book__title', 'book__publish_time')
    print(res)

    # 6.查询电话是111的作者姓名和年龄
    # 因为外键字段在作者表 所以是反向 
    res = models.AuthorDetail.objects.filter(phone=111).values('author__name', 'author__age')
    print(res)  # <QuerySet [{'author__name': 'jason', 'author__age': 18}]>

'''

双下划线其实是可以一直点的只要可以点

    # 7.查询主键为1的书籍对应的作者电话号码
    res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone')  authors是书籍表中的外键字段     author_detail是作者表中的外键字段
    print(res)  # <QuerySet [{'authors__author_detail__phone': 222}]>

    因为书籍和作者表的外键字段在书籍表中 所以是正向所以只要写外键字段名称即可 
    写下外键字段名称其实就是跳到了作者表中 然后作者表和作者详情表的外键字段在作者表所以是正向
    所以只要写外键字段名称即可  然后写下外键字段其实就是跳转到了作者详情表中 
    然后在双下划线就可以拿到作者详情表对应的值
'''

 

标签:__,models,res,模型,外键,查询,objects,django
来源: https://www.cnblogs.com/stephenwzh/p/16659219.html