zoukankan      html  css  js  c++  java
  • django模型层之多表操作 增删改查

    多表操作之创建模型

    这边以书为中心创建一个模型

    作者模型:一个作者有姓名和年龄.

    作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)

    出版商模型:出版商有名称,所在城市以及email。

    书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。

    模型建立如下:  models.py

    from django.db import models
    
    # Create your models here.
    
    
    class Author(models.Model):
        nid = models.AutoField(primary_key=True)
        name=models.CharField( max_length=32)
        age=models.IntegerField()
    
        # 与AuthorDetail建立一对一的关系
        authorDetail=models.OneToOneField(to="AuthorDetail",on_delete=models.CASCADE)
    
    class AuthorDetail(models.Model):
    
        nid = models.AutoField(primary_key=True)
        birthday=models.DateField()
        telephone=models.BigIntegerField()
        addr=models.CharField( max_length=64)
    
    class Publish(models.Model):
        nid = models.AutoField(primary_key=True)
        name=models.CharField( max_length=32)
        city=models.CharField( max_length=32)
        email=models.EmailField()
    
    
    class Book(models.Model):
    
        nid = models.AutoField(primary_key=True)
        title = models.CharField( max_length=32)
        publishDate=models.DateField()
        price=models.DecimalField(max_digits=5,decimal_places=2)
    
        # 与Publish建立一对多的关系,外键字段建立在多的一方
        publish=models.ForeignKey(to="Publish",to_field="nid",on_delete=models.CASCADE)
        # 与Author表建立多对多的关系,ManyToManyField可以建在两个模型中的任意一个,自动创建第三张表
        authors=models.ManyToManyField(to='Author',)
    会有如下的结构图,并不完全按上面的去创建,有略微差别
    这里可以看到,自己为命名,表格命名是以 应用名_类名 作为表名:book_author
    多对多的表格创建是以 应用名_第一张表名_第二张表名 作为表名: book_book_author
     








    
    

      这边要注意的是:

    • 表的名称myapp_modelName,是根据 模型中的元数据自动生成的,也可以覆写为别的名称------在models.py文件class Meta: db_table='XXX'
    • id字段是自动添加的(如果没有设置id字段的话)
    • 对于外键字段,Django默认的会以字段_id作为数据表的表头 : 上图book_book表中中pubulish,自动添加为publish_id
    • 这个例子中的CREATE TABLE SQL 语句使用PostgreSQL 语法格式,要注意的是Django 会根据settings 中指定的数据库类型来使用相应的SQL 语句。
    • 定义好模型之后,你需要告诉Django _使用_这些模型。你要做的就是修改配置文件中的INSTALL_APPSZ中设置,在其中添加models.py所在应用的名称。
    • 外键字段 ForeignKey 有一个 null=True 的设置(它允许外键接受空值 NULL),你可以赋给它空值 None 。(就是说一张表的数据,有些可以没有外键)

       在这整个模型中,你应该明确的是:

    关于表之间的关系:


    1.  一对多:双方一个一对多,一个一对一(一本书由一个出版出版,出版社可以出版多本书)
      关联字段在'多'的一方

    book <-----------> pulish
      
      book
      id title pulish
      

      pulish
      id name city

    2. 多对多:双方都是一对多(一本书可以有多个作者,一个作者可以写多本书)
      
      book <------------> author

      book
      id  title

      aothor  
      id name

      book2author
      id  book_id authorid

    3. 一对一:双方都是一对一 (一个作者只有对应唯一一个作者详情,一个作者详情也只能对应一个作者)
      关联字段在双方任意一方,但是必须unique约束

      auhor <------------> authordetail

      author
      id name

      aothordetail
      id addr email author_id

       建立外键的约束的常用语句

    字段名=models.ForeignKey(to='Publish',to_field='id',on_delete=models.CASCADE)
    字段名=models.OneToOneField('Author',on_delete=models.CASCADE)
    字段名=models.ManyToManyField("Author",db_table='book2author')

    #Djang会自动以字段名_id命名来作为那个表头
    #关于ForeignKey和oneTooneField的差别:只有当ForeignKey有Unique时,两者效果是一样的

    多表操作之增删改查

      添加数据

    #一对多:书和出版社的关系
      1.定义添加的路径,分发视图函数:
        方式1:
        models.Book.objects.create(title='xx',price=100,publishDate='2012-12-22',publish_id=1)
        #这边要注意的是:外键字段我们命名是publish,而Django会自动添加_id,所以我们在添加参数也应该这么写
        #还有就是这是一对多,这边book是一,他外键关联到publish,所以publish里面应该要先有参数让他关联,要不然会报错
        #就是说publish表格里面必须先有相应的id值
      
        #方式2
        2.pub_obj=models.objects.filter(title='深圳出版社').first()
        models.Book.objects.create(title='xx',price=100,publishDate='2012-12-22',publish_id=pub_obj)

    #多对多:书和作者的关系
      #其实在在关系表中添加记录(第三张表)
      #这边要注意的是,数据库中虽然有创建第三表,但是,我们去而不能引用,因为第三张并不是我们自己创建的,
      #就是说models里面没有我们的第三张表的模型,我们没办法直接从medels直接拿到第三张表

      #要给第三张表加数据,首先得保证,两张要关联的表必须有要添加的数据


      #拿到书籍对象
      linux=models.Book.objects.filter(title='linux').first()
      #拿到作者对象

      alex=models.Author.objects.filter(name='alex').first()

      egon=models.Author.objects.filter(name='sb').first()  
      #操作,添加,删除等
      linux.author.add(alex,egon)  #会直接找到第三张关系表,添加数据
       linux.author.add(1,2)         #根据作者id(主键)添加,同时添加两个

      linux.author.clear() #清空
      
      
    linux.author.remove(1,2) #删除
      linux.author.set([2,])       #先清空再增加
      linux.author.add(*[1,2,3,4,5])#从前端拿过来的数据就是列表,这样打散后传进去,就不需要一个个去拿值了(常用)

      #key:
      #拿到关联属性author,会自己拿到第三张表
      #第一个表名对象.关联属性.操作(第二张表名对象)
      
      # 正向操作按字段,反向操作按表名小写
    
    
      #给作者绑定两本书
      #拿到书对象
      go=models.Book.objects.filter(title='go').first()
      linux=models.Book.objects.filter(title='linux').first()
      #拿到数对象

      alex=models.Author.objects.filter(id=1).first()

      egon=models.Author.objects.filter(name='sb').first()

      alex.book_set.add(linux,go) #这边需要注意的是,每个作者有可以多本书,是一个集合,所以需要用_set来表示

      跨表查询

      基于对象查询(先找到对象,再查找他的相连属性)

    #先记住这一句,正向查询按字段,反向查询按表名小写+选择性的_set,具体是不是_set看他查询出来的个数
    #基于对象的查询,所以首先必须先拿到相应的对象才可以进行查询
    
    def query(request):
    
        #基于对象的跨表查询
        #(1)一对多  书籍和出版社的关系
    
        #       正向按字段
        #Book ----------->Publish  正向  从关联属性所在的表到另一张表
        #           反向
        #Publish <---------Book    反向  从关联字段所在的表到另一张表
        #          反向按表名小写_set   pubish查找出来的可能多个,所以要加_set
    
        # 查询linux这本书出版社的地址(正向)
        # book=models.Book.objects.filter(title="linux").first()
        # print(book.publish.city)
    
        #查询深圳出版社出版的所有书籍(反向)
        # shenzhen=models.Publish.objects.filter(name='深圳').first()
        # shenzhen.book_set.all()
    
        #(2)多对多查询
        #      正向查询按字段 Book.Author.all()
        #  Book对象 -------------------> Author对象
    
        #  Author对象  <---------------- Book
    对象 
    
        #     反向查询按小写表名_set.all()
    
        #查询linux书籍的所有作者(正向)
        # linux=models.Book.objects.filter(title='linux').first()
        # queryset=linux.author.all()             #(这边是一个queryset,所以不用加set)
        # print(queryset)
    
        #查询alex出版过的所有书 (反向)
        # alex=models.Author.objects.filter(name='alex').first()
        # queryset=alex.book_set.all()
        # print(queryset)
    
    
        #(3)一对一  作者和作者详情的关系
        #     反向 小写表名_set.all()
        #   -------------> AuthorDetail
        #           正向 按字段
        #  Author  <--------> AuthorDetail
        #查询alex的手机号  (正向)
        # alex=models.Author.objects.filter(id=1).first()
        # print(alex.authordetail.telephone)
        # print(alex.authordetail.addr)
    
        #查询手机号为456的作者的名字
        authord=models.AuthorDetail.objects.filter(telephone=456).first()
        print(authord.ad.name)
        return HttpResponse('查询成功')

     关于orm转换成sql语句的过程 想要看他的转换,需要到settings.py配置

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

      基于双下划线的跨表查询(join)使用频率更高,更使用

    ##记住这两句话,遇到跨表,正向用  字段__,反向用 小写表名__
    #要看清基表是那一个

    #一对多
    #查询linux对应出版社的地址
    # linux=models.Book.objects.filter(titile="linux").first()
    #sql语句
    ''' #select后面要查询的相当于value
    SELECT "book_book"."price", "book_publish"."city" FROM "book_book" INNER JOIN #字段名__相当于inner
    "book_publish" ON ("book_book"."publish_id" = "book_publish"."id")
    WHERE "book_book"."title" = 'linux' LIMIT 21; args=('linux',) #顾虑条件,对应filter
    '''
    #正向,按字段连接
    # queryset= models.Book.objects.filter(title='linux').values__list('price','publish__city') #price是Book(基表)中的,不用加字段
    # print(queryset)

    #反向 小写表名__
    '''
    "book_publish"."city" FROM "book_publish" INNER JOIN "book_book"
    ON ("book_publish"."id" = "book_book"."publish_id")
    WHERE "book_book"."title" = 'linux'
    '''
    # queryset=models.Publish.objects.filter(book__title='linux').values('city')
    # print(queryset)


    #多对多
    #查询linux的所有作者 正向
    # queryset=models.Book.objects.filter(title='linux').values('author__name')
    # queryset=models.Book.objects.filter(title__startswith='l').values('author__name')
    # print(queryset)

    #反向 小写表名__ 过滤条件是不变的,正常先写格式再慢慢去补全
    # queryset=models.Author.objects.filter(book__title='linux').values('name')
    # print(queryset)

    #查询alex手机号
    #反向
    # queryset=models.Author.objects.filter(name='alex').values('authordetail__telephone')
    #正向
    # queryset=models.AuthorDetail.objects.filter(ad__name='alex').values('telephone')
    # print(queryset)
     

       基于双下划线的连续跨表查询

    #上面的跨表查询都是有直接关系的(外键约束,而基于没有直接关联的怎么库表查询呢)




    #查询深圳人出版社出版过得的所有书籍名字以及作者名字(Author表和pubish没有直接关联,这就需要用到连续跨表了) # queryset
    =models.Book.objects.filter(publish__name='深圳出版社').values_list('title','author__name') # queryset=models.Author.objects.filter(book__publish__name='深圳出版社').values_list('book__title','name') # print(queryset) #上面两种写法效果是一样的,推荐第一种,

    #手机号以12开头的作者出版过的书籍名称以及出版社名称 # queryset
    =models.Book.objects.filter(author__authordetail__telephone__startwith='12').values_list('title',"publish__name") queryset = models.Book.objects.filter(author__authordetail__telephone__contains='12').values_list('title',"publish__name") print(queryset)

    聚合查询和分组查询

       聚合查询

    aggregate(*args,**kargs)
    #计算所有的图书平均价格 (把所有的图书聚合成一组) ###在控制台操作
    from books.models import Book
    from  django.db.models import Avg
    from  django.db.models import Max,Min
    Book.objects.all().aggregate(Avg('price'))
    {'price__avg': 999.99}                 #结果是一个字典

      aggregate()QuerySet 的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值。键的名称是按照字段和聚合函数的名称自动生成出来的。如果你想要为聚合值指定一个名称,可以向聚合子句提供它。
      
    >>> Book.objects.aggregate(average_price=Avg('price'))
      >>>{'avg__price': 999.99}
      

    如果你希望生成不止一个聚合,你可以向aggregate()子句中添加另一个参数。所以,如果你也想知道所有图书价格的最大值和最小值,可以这样查询:
    Book.objects.all().aggregate(Max('price'),Min('price'),Avg('price'))
    {'price__max': Decimal('999.990000000000'),
    'price__min': Decimal('999.990000000000'),
    'price__avg': 999.99}

       分组查询

      单表分组

    ###################################--单表分组查询--#######################################################
    
    查询每一个部门名称以及对应的员工数
    
    emp:
    
    id  name age   salary    dep
    1   alex  12   2000     销售部 
    2   egon  22   3000     人事部
    3   wen   22   5000     人事部
    
    
    sql语句:
    select dep,Count(*) from emp group by dep;
    
    ORM:
    emp.objects.values("dep").annotate(c=Count("id")          ##values的作用相当于group by ,就是以values里面的参数进行分组,annotate里面的参数W为我们要查询的内容
    

      多表分组

    ###把多张相关的表进行join后再进行单表查询

    ############### 多表分组查询

    emp:
    
    id  name age   salary    dep
    1   alex  12   2000     销售部 
    2   egon  22   3000     人事部
    3   wen   22   5000     人事部
    class Emp(models.Model):
        name=models.CharField(max_length=32)
        age=models.IntegerField()
        salary=models.DecimalField(max_digits=8,decimal_places=2)
        dep=models.CharField(max_length=32)
        province=models.CharField(max_length=32)


    # 1 查询每一个出版社的名字和出版过的书籍的平均价格
    '''

    -- sql语句:
    SELECT app01_publish.name,AVG(app01_book.price) from app01_book LEFT JOIN app01_publish on
    app01_book.publish_id = app01_publish.id
    group by app01_publish.id,app01_publish.name
    '''

    #方法1

    # queryset=models.Publish.objects.values("id","name").annotate(avg_price=Avg("book__price"))
    # queryset=models.Publish.objects.values("id","name","email","city").annotate(avg_price=Avg("book__price"))
    # [{"id":1,"name":"苹果出版社","eamil":"123","city":"beijing",'avg_price': 119.0},{"id":1,"name":"橘子出版社","eamil":"123","city":"beijing",'avg_price': 155.333333.0}]

    #方法2

    # queryset=models.Publish.objects.all().annotate(avg_price=Avg("book__price"))
    # print(queryset) #<QuerySet [<Publish: 苹果出版社>, <Publish: 橘子出版社>]>
    # for obj in queryset:
    # print(obj.name,obj.avg_price)

    #上面两种写法的效果是等价的,只不过第一种有指定分组的字段,字段就会显示分组用的字段,以及要查询的字段

    #第二种没有指定分组的字段,就会返回对象(对象的具体个数就是all当中的对象个数),对象的属性会增加,就是annotate里面的参数

    #所以需要for循环把值拿出来我们要的

    # 2 查询每一个作者的名字以及出版书籍的个数

      queryset=models.Author.objects.annotate(c=Count('book')).value('name',c)

      print(queryset)

    # 3 查询每一个书籍的名称以及作者的个数

      querysetmodels.Book.objects.annotate(c=Count('authors')).values('title',c)

      print(queryset)

    # 4 查询作者个数大于1 的每一本书籍的名称和作者个数

      queryset=models.Book.objects.annotate(c=Count("authors")).filter(c__gt=1).values('title','c')

      print(queryset)

     # 5 查询书籍名称包含"h"的书籍名称和作者个数

      queryset=models.Book.objects.filter(title__contains='h').annotate(c=Count('authors')).values('title','c')

      print(queryset)

    F查询和Q查询

      F查询

    ###########models

    class Article(models.Model):
      title=models.CharField(max_length=32)
      comment_num=models.IntegerField()
      poll_num=models.IntegerField()

      price=models.IntegerField()  
      def __str__(self):

        return self.title

    #######

    #1. 查询评论数大于100的文章

      models.Article.objects.filter(comment_num>100)

    #2. 查询评论数大于点赞数的文章

      models.Article.objects.filter(comment_num>Poll_num)  #会报错,涉及到名称空间,不知道poll_num是什么

      #正确写法

      from django.db.models import F,Q,Avg

      models.Article.objects.filter(comment_num>F('poll_num'))

    3. #查询点赞数大于两倍评论数

      models.Article.objects.filter(poll_num__gt=F('comment_num')*2)

    4.将所有的文章提高100元

      model.Article.objects.all().update(price=F('price')+100)

     

      Q查询

    ##  基本语法 & | ~  (与或非)
    #
    ######
    class Book(models.Model):
    
        nid = models.AutoField(primary_key=True)
        title = models.CharField( max_length=32)
        publishDate=models.DateField()
        price=models.DecimalField(max_digits=5,decimal_places=2)
    #1 查询价格大于200 或者名称以p开头

      models.Book.objects.filter(Q(price__gt=200)|Q(title__startwith='p'))


    #2 查询价格大约300 或者2019年1月出版的书籍
      models.Book.objects.filter(Q(price__gt=200)|Q(pub_date__year=2019)& Q(pub_date__month=1))


     

  • 相关阅读:
    [LeetCode] 1898. Maximum Number of Removable Characters
    [LeetCode] 1897. Redistribute Characters to Make All Strings Equal
    [LeetCode] 1400. Construct K Palindrome Strings
    235. 二叉搜索树的最近公共祖先
    349. 两个数组的交集
    海量数据TOPK 问题
    121. 买卖股票的最佳时机
    删除数组中为0元素
    这行字符串中出现频率最高的字符
    50. Pow(x, n)
  • 原文地址:https://www.cnblogs.com/tjp40922/p/10220090.html
Copyright © 2011-2022 走看看