• Django之ORM


    ORM对象关系映射,Django中用于操作数据库的Python语句,并且每一条ORM语句,都可以翻译成一条sql语句,而且是符合sql语句优化原则的语句

     

    0 创建表之前的准备工作

    (1) 先在终端下的mysql中创建一个数据库。

    create database db_book charset=utf8mb4;

    (2) pycharm界面中连接数据库

    Database中的+-> Data Source -> MySQL -> 填写Name(@localhost), User(root), Password(xxx), Database(db_book) -> Test Connection -> 下面出现对勾,就代表连接成功

    (3) 在主应用drfdemo中的__init__.py中设置使用pymysql作为数据库驱动,写下如下代码

    import pymysql
    pymysql.install_as_MySQLdb()

    (4) settings.py配置文件中设置mysql的账号密码

    DATABASES = {
    
        # 'default': {
    
        #     'ENGINE': 'django.db.backends.sqlite3',
    
        #     'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    
        # }
    
        'default': {
    
            'ENGINE': 'django.db.backends.mysql',
    
            'NAME': "db_book", #db_book是我们用的数据库的库名
    
            "HOST": "127.0.0.1",
    
            "PORT": 3306,
    
            "USER": "root",
    
            "PASSWORD": "xxx",
    
        },
    
    }

     

    下面进入正题

      针对单表的创建,增删改查

    1.1 models.py创建单表

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

    1.2

    Python终端Terminal中执行命令

    迁移数据库

    python manage.py makemigration

    在数据库中创建表

    python manage.py migrate

    这个时候就可以在pycharm中的Database中看到models.py中我们要创建的表了

    添加数据

    (1) 手动在数据库里插入就不说了,实际项目中一般是前端提交过来的数据调用views.py中的方法执行增删改查操作

    (2) view.py文件中写插入表的语句

    # 添加记录方式一 (实例化对象)  一条记录就对应一个对象
    
    此方法分两步,第一步对Book模型类进行实例化得到book_obj模型类对象,第二步执行book_obj对象.save()方法进行数据保存
    
    book_obj=Book(title="java",pub_data="2012-12-12",prict=120,publish="西瓜出版社")
    
    book_obj.save()
    
    # 添加记录方式二 (objects.create方法)
    
    book=Book.objects.create(title="python",pub_data="2019-12-30",prict=666,publish="香蕉出版社")
    
    # 这里的book就对应着一条记录的模型类对象, book都能点出来表中字段的值 就是对象 . 属性

    QuerySet数据类型的概念

    QuerySet数据类型是针对Django创建的一种序列数据类型,是类似列表的数据类型,不过列表里面可以放任意数据类型(包括放对象,函数等等),QuerySet数据类型有两种形式

    第一种 类似列表里面装模型类对象的形式

    所以这种QuerySet序列可索引取值(取出的值就是一个个的模型类对象),可切片,for i in遍历等等

    (1)以下列出几种返回第一种QuerySet序列的查询方法
    
    ret1=Book.objects.all()
    
    print(ret1)
    
    # 打印结果 <QuerySet [<Book: Book object (16)>, <Book: Book object (17)>,...
    
    ret2=Book.objects.filter(title="小黄书")
    
    print(ret2) # 注意就算filter过滤出的数据只有一条,这一条也是装在QuerySet序列里的
    
    # 打印结果 <QuerySet [<Book: Book object (19)>, <Book: Book object (20)>]>
    (2)下列几种返回模型类对象的查询方法
    
    book_obj=Book.objects.get(title="红楼梦")
    
    print(book_obj) # 打印结果Book object (22)
    
    ret2=Book.objects.filter(title="小黄书").first()
    
    # 上面一句话等同于Book.objects.filter(title="小黄书")[0]
    
    print(ret2) # 打印结果Book object (19)
    
    可以看出,查询多条数据的查询方式一般就返回的是QuerySet序列,而查询一条数据的查询方式,返回的就是模型类对象.
    
    模型类就是models.py中建表的类,比如Book类中有title属性,对应的就是book表中有title字段,并且由模型类查询得到的模型类对象,都可以点出其中的属性,比如book_obj=Book.objects.get(id=16)
    
    print(book_obj.title) # 打印出书名,异形
    
    print(book_obj.price) # 打印出价格,32
    
    ...

    第二种 类似列表里面套字典的形式

    这种QuerySet序列是通过.values()方法查询出来的返回的数据类型,能够使用字典的相关操作方法
    
    只有QuerySet序列才有.values()方法,用于取到自己想要的字段,模型类对象没有.values()方法
    
    使用.values()方法,你需要传参字段,返回一个特殊的QuerySet序列,这个序列中装着的是字典,字典中的键值对是 字段:字段值的形式,比如说values(“name”,”age”)中的参数相当于sql语句select name,age from table1 中的name和age,所以这个QuerySet序列通过遍历之后,能通过键取出name和age值,例如:
    
    ret=Book.objects.filter(title="小黄书").values("pub_date","price")
    
    print(ret)
    
    # <QuerySet [{'pub_date': datetime.date(2019, 12, 7), 'price': Decimal('666.00')}, {'pub_date': datetime.date(2019, 12, 28), 'price': Decimal('666.00')}]>
    
    for i in ret:
    
    print(“%s,%s”%(i["price"],i[“pub_date”]) )
    
    # 666.00, 2019-12-07
    
    # 666.00, 2019-12-28

    3 单表查询

    除了添加表记录,删和改都得通过查询到这条表记录,再执行对应的删或改方法就行

    3.1 直接查询

    Book.objects.可以.出来以下一些方法(Book就是models.py中的模型类)

    <1> all():                  查询所有结果
    
    <2> filter(**kwargs):       它包含了与所给筛选条件相匹配的对象(其中**kwargs代表关键字形参,比如说有个字段名叫name,那么查询name是xiaoming的参数就传name=”xiaoming”),可以传多个关键字形参,代表同时满足多个条件,用逗号隔开,返回QuerySet序列
    
    <3> get(**kwargs):          返回与所给筛选条件相匹配的模型类对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
    
    <4> exclude(**kwargs):      它包含了与所给筛选条件不匹配的对象
    
    <5> order_by(*field):       对查询结果排序
    
    <6> reverse():              对查询结果反向排序
    
    <8> count():                返回数据库中匹配查询(QuerySet)的对象数量。
    
    <9> first():                返回第一条记录
    
    <10> last():                返回最后一条记录
    
    <11> exists():              如果QuerySet包含数据,就返回True,否则返回False
    
    <12> values(*field):        返回一个特殊的QuerySet序列,详情见QuerySet数据类型的介绍
    
    <13> values_list(*field):   它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
    
    <14> distinct():            从返回结果中剔除重复纪录

    3.2 基于双下划线的模糊查询

    Book.objects.filter(price__in=[100,200,300])
    
    Book.objects.filter(price__gt=100)
    
    Book.objects.filter(price__lt=100)
    
    Book.objects.filter(price__range=[100,200])
    
    Book.objects.filter(title__contains="python")
    
    Book.objects.filter(title__icontains="python")
    
    Book.objects.filter(title__startswith="py")
    
    Book.objects.filter(pub_date__year=2012)
    

    4 单表删除数据

    book=Book.objects.get(pk=1)
    
    book.delete()
    
    合起来写就是Book.objects.get(pk=1).delete()

     delete() 方法是 QuerySet 上的方法,但并不适用于 Manager 本身。这是一种保护机制,是为了避免意外地调用 Entry.objects.delete() 方法导致 所有的 记录被误删除。如果你确认要删除所有的对象,那么你必须显式地调用:

    Entry.objects.all().delete()  

    如果不想级联删除,可以设置为:

    pubHouse = models.ForeignKey(to='Publisher', on_delete=models.SET_NULL, blank=True, null=True)

    5 单表修改数据

    Book.objects.filter(title__startswith="py").update(price=120)

    此外,update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录,update()方法会返回一个整型数值,表示受影响的记录条数。

    针对多表的创建,增删改查

    1.1 models.py文件中写建表语句 也就是写一个Book类 使用命令迁移后就是一个book,代码如下

    from django.db import models
    
     
    
    # Create your models here.
    
    class Book(models.Model):
    
        id = models.AutoField(primary_key=True)
    
        title = models.CharField(max_length=32)
    
        pub_date = models.DateField()
    
        price = models.DecimalField(max_digits=8, decimal_places=2)
    
        publisher = models.ForeignKey(to="Publish",to_field="pid", on_delete=models.CASCADE)
    
        authors=models.ManyToManyField("Author")
    
        def __str__(self):
    
            return self.title
    
     
    
    class Publish(models.Model):
    
    pid=models.AutoField(primary_key=True)
    
        name = models.CharField(max_length=32)
    
        email = models.CharField(max_length=32)
    
        addr = models.CharField(max_length=32)
    
     
    
        def __str__(self):
    
            return self.name
    
     
    
    class Author(models.Model):
    
        name=models.CharField(max_length=32)
    
        age=models.IntegerField()
    
        ad=models.OneToOneField("AuthorDetail",on_delete=models.CASCADE)
    
     
    
    class AuthorDetail(models.Model):
    
        tel=models.CharField(max_length=32)
    
        email=models.CharField(max_length=32)
    
     
    
    class User(models.Model):
    
        name=models.CharField(max_length=32)
    
    pwd=models.CharField(max_length=32)

    其中

    to_field="pid" 表示这个外键关联另一张表的pid,对应的,另一张表建表时需要将pid作为主键;如果不加这句话,表示这个外键将自动关联另一张表的主键id,默认自动创建,建表时不用写这个id字段
    
    on_delete=models.CASCADE 联级删除
    
    ManyToManyField 多对多关系,一个字段主动关联多对多关系后,Django自动生成一张多对多关系表.比如上述book表和author表为多对多关联,Django默认创建一张book_author表,用来关联两张表的主键,形成多对多关系
    
    OneToOneField 一对一关系
    
    ForeignKey 一对多直接使用外键
    
    AutoField 自动设置主键
    
    booleanField 布尔型
    
    DecimalField 浮点型

    # 表格详情图(进行数据查询时看着图写比较清晰)

     

    1.2 创建3类关联表

    pycharm中每创建一张表都会有一个主键,如果你不加,他会默认加一个id字段作为主键

    models中创建多张关系表

    1) 多对一:(在多的那张表中设置外键 ForeignKey 进行多对一关联)

    publisher=models.ForeignKey(to="Publish", on_delete=models.CASCADE)

    # 在多的那个表中的字段publisher,设置为外键关联Publish表(也就是Publish类)

    # 级联删除:models.CASCADE 当关联表中的数据删除时,该外键也删除

    # 置空:models.SET_NULL 当关联表中的数据删除时,该外键置空,当然,你的这个外键字段得允许为空,null=True

    # 设置默认值:models.SET_DEFAULT 删除的时候,外键字段设置为默认值,所以定义外键的时候注意加上一个默认值。

    2) 一对一:(OneToOneField)

    models中创建一对一外键关系表,就是在多对一的基础上,在原来多的那个表添加一个unique唯一约束,就是一对一了.models中简化了操作,OneToOneField方法

    class Author(models.Model):
    
    ...
    
        ad=models.OneToOneField("AuthorDetail",on_delete=models.CASCADE)

    # 使用OneToOneField之后,会将此表中的ad字段自动设置成unique唯一约束,并作为外键(并改名为ad_id作为数据库中的字段)关联AuthorDetail这张表的主键,这样形成一对一关联

    3) 多对多:(ManyToManyField)

    在两张表里分别设置两个外键,然后分别关联(新创建的)第三张表的两个字段,ORM中自动创建第三张关系表,第三张表的表名是  子应用名_这个类名_这个字段名,也就是home_book_authors,这张表中的两个字段book_idauthor_id多对多一一对应

    class Book(models.Model):
    
    ...
    
    authors=models.ManyToManyField("Author")

    # Author是多对多的另一张表

    2 多表添加记录

    2.1 一对多添加

    方式1:

    # 先在publish表里利用出版社id获取出版社对象
    
    publish_obj=Publish.objects.get(nid=1)
    
    # 再在book表中添加一条数据,book_obj.publishr正向查询按字段,按照book表中的publishr字段,也就是给publish表添加了一个模型类对象publish_obj,即添加了一条数据(因为book_obj.publishr可以点出来publish表中的各个字段)
    
    book_obj=Book.objects.create(title="西游记,price=100,
    
    publishDate="2012-12-12",publishr=publish_obj)
    
    # 多个book对应一个publish(出版社)

    方式2:(更简便)

       book_obj=Book.objects.create(title="西游记",publishDate="2012-12-12"
    
    ,price=100,publishr_id=1) 
    
    # 直接给主动关联多对一的那个字段赋值,完成添加操作

    2.2 一对一添加

    同上一对多,一模一样

    2.3 多对多添加

        # 当前生成的书籍对象
    
    book_obj=Book.objects.create(title="追风筝的人",price=200,
    
    publishDate="2012-11-12",publishr_id=1)
    
    # 为书籍绑定的做作者对象
    
    listvar1=[“yuan”,”alex”] #前端传过来的两个作者
    
    listvar2=[]
    
    for i in listvar1:
    
         j=Author.objects.filter(name=i).first() # 在Author表中作者叫yuan的纪录
    
    listvar2.append(j)
    
        # 绑定多对多关系,即向关系表book_authors中添加纪录
    
    book_obj.authors.add(*listvar2)    #  将某些特定的 model 对象添加到被关联对象集合中 book_obj.authors.add(*[])
    
    这里好像也可以这样写
    
    listvar2=Author.objects.filter(name_in=listvar1)
    
    book_obj.authors.add(*listvar2)
    
     
    
    (当然现在前端提交过来的是名字,如果你让前端提交过来的直接是作者对应的author_id),这样简单的操作就添加完成了)
    
    authors_id = request.POST.getlist("authors") #前端传过来作者id列表
    
    book=Book.objects.create(title=title, price=price, pub_date=pub_date, publisher_id=publish_id)
    
    book.authors.add(*authors_id)
    
    # *author_id解包多个普通形参列表,相当于传入多个普通形参

    3 多表修改记录

    # 先添加其他字段
    
    Book.objects.filter(id=pk).update(title=title, price=price, pub_date=pub_date, publisher_id=publish_id)
    
    # 再添加多对多字段
    
    book_obj=Book.objects.filter(id=pk).first()
    
    book_obj.authors.clear()
    
    book_obj.authors.add(*authors_id)
    
    # 上面两行相当于 book_obj.authors.set(authors_id) #set后面需要传参列表

    4 多表查询

    参考博客园:

    cnblogs.com/yuanchenqi/articles/8963244.html

    口诀:  正向查询按字段,反向查询按表名(的小写)

    正反向查询都是在两张表是关联表的基础上应用的,models.py中可以看出,比如说A表和B表关联,A表为主动关联的一方(从建表语句就可以看出),则由A表查B表为正向查询,BA则为反向查询

      

    图示:

                         正向查询
    
    Book(多) ----------------------------> Publish(一)
    
             <----------------------------
    
                         反向查询
    
     
    
                         正向查询
    
    Author(一) ---------------------------> AuthorDetail(一)
    
               <---------------------------
    
                         反向查询
    
     
    
                         正向查询
    
    Book(多) -----------------------------> Author(多)
    
             <-----------------------------
    
                         反向查询
    

    3.1 基于对象的跨表查询(子查询)  

    用法:

      模型类对象.字段 /表名.属性值

    正向查询按字段 (publishr)

    例1 查询主键为1的书籍所对应的出版社名字

    book_obj=Book.objects.filter(pk=1).first()
    print(book_obj.publishr.name)  # book_obj.publishr 是主键为1的书籍对象关联的出版社对象,然后获取到publish表中的name字段对应的值
    例2 查出红楼梦的所有作者的名字和年龄

    # 正向查询按字段,那应该是book表的哪个字段呢,看book表的建表语句authors=models.ManyToManyField("Author") # 这个就是Book类中主动多对多关联的字段,用的就是这个字段 book=Book.objects.filter(title="红楼梦").first() print(book.authors.all().values("name","age")) # <QuerySet [{'name': 'alex', 'age': 345}, {'name': 'egon', 'age': 324}]>

    反向查询按表名 (book)

    例1 查询苹果出版社出版的所有书籍的名称

    publish=Publish.objects.get(name="苹果出版社") # publish.book.all() 与苹果出版社关联的所有书籍对象集合 book_list=publish.book.all() for book_obj in book_list: print(book_obj.title)

    例2 查询人民出版社出版社所有书籍的名称和价格

    pub_obj=Publish.objects.filter(name="人民出版社").first() print(pub_obj.book_set.all().values("title","price")) # <QuerySet [{'title': '红楼梦', 'price': Decimal('122.00')}, {'title': '水浒传', 'price': Decimal('333.00')}]> # 反向查询应该按表名Book的小写也就是book,那为什么要用book_set,因为这里一个出版社对应多本书,所以由出版社查书,查询的时候会出现多个book对象符合条件,所以这里规定要用book_set,而不是book,当你这里非要写book的时候会报错 AttributeError: 'Author' object has no attribute 'book' 所以,当你通过一对一关系查询的时候,直接用表名/字段名;当你通过一对多或者多对多,查多的这个表时,要用表名/字段名加上”_set”

    例3 alex出版过得所有书籍的名称和价格

    alex_obj=Author.objects.filter(name="alex").first() print(alex_obj.book_set.all().values("title","price"))

    3.2 基于双下划线的跨表查询 (jion联表查询),联表之后随便写,遵循规则就行

    同样遵循正向查询按字段,反向查询按表名

    # 例一 三国演义这本书籍的出版社的邮箱

    方法一
    # 查的是Book,然后正向查询按字段publisher,然后publish__email跨到publish表拿到email ret=Book.objects.filter(title="三国演义").values("publisher__email") print(ret) # <QuerySet [{'publisher__email': '234'}]> 方法二 # 查的是Publish,然后反向查询按表名book,然后book__title跨到book表拿title,将book__title="三国演义"作为筛选条件获取到email # 实际就是查的时候,查自己表的字段,直接写字段,查别的表的字段,就要按正/反向查询写 字段/表名__字段,注意:作为values()的参数,必须要用引号包起来,也就是”字段/表名__字段”,当然这两张表不能是没关系的两张表,必须是关联的表,否则也谈不上正反向查询 ret=Publish.objects.filter(book__title="三国演义").values("email") print(ret) # <QuerySet [{'email': '234'}]> 方法三(基于对象的查询,有点麻烦) # 正向查询按字段,利用.publisher得到publish表记录(模型类对象),然后对象.email取到邮箱 ret = Book.objects.filter(title='三国演义').first().publisher.email print(ret) # 直接打印出邮箱 234
    # 例二 查询人民出版社出版社所有书籍的名称和价格

    #
    方法一 ret=Publish.objects.filter(name="人民出版社") .values("book__title","book__price") # 方法二 ret=Book.objects.filter(publisher__name="人民出版社") .values("title","price")
    # 例三 查询alex的手机号

    ret=Author.objects.filter(name="alex").values("ad__tel") ret=AuthorDetail.objects.filter(author__name="alex").values("tel") # 例四 红楼梦的所有作者的名字和年龄 Book.objects.filter(title="红楼梦").values("authors__name", "authors__age") Author.objects.filter(book__title="红楼梦").values("name","age")
    # 例五 连续跨表: 手机号以123开头的作者出版过的所有书籍名称以及出版社名称

    #
    连续跨表一般挑选关系表中最中间的表作为起点,跨表更容易 # 方法一,以Book表为起点 Book.objects.filter(authors__ad__tel__startswith="123").values("title", "publisher__name") # 方法二,以Author表为起点 Author.objects.filter(ad__tel__startswith="123").values("book__title", "book__publisher__name"))

     

  • 相关阅读:
    ql语句中left join和inner join中的on与where的区别分析
    C#如何通过属性名称反射出属性本身
    mvc中查询字符串请求过长
    SQL Server 中 EXEC 与 SP_EXECUTESQL 的区别
    后台二进制文件 在前台下载方法总结
    C#中try catch finally的执行顺序
    python生成随机密码
    Bridged(桥接模式)
    NAT(地址转换模式)
    TCP协议详解
  • 原文地址:https://www.cnblogs.com/banbosuiyue/p/12350559.html
Copyright © 2020-2023  润新知