zoukankan      html  css  js  c++  java
  • python2.0_day18_django_ORM

    Django_ORM
    day1516我们学到的ORM都是最基本的增删改查.下面我们稍作升级,学习下高级点的增删改查.
    先创建一个新的APP项目
    1  python3.5 manage.py startapp blog
    1.编辑blog/models.py
     1 from django.db import models
     2 
     3 # Create your models here.
     4 
     5 class Blog(models.Model):
     6     name = models.CharField(max_length=100)
     7     tagline = models.TextField()
     8 
     9     def __str__(self):              # __unicode__ on Python 2
    10         return self.name
    11 
    12 class Author(models.Model):
    13     name = models.CharField(max_length=50)
    14     email = models.EmailField()
    15 
    16     def __str__(self):              # __unicode__ on Python 2
    17         return self.name
    18 
    19 class Entry(models.Model):
    20     blog = models.ForeignKey(Blog)
    21     headline = models.CharField(max_length=255)
    22     body_text = models.TextField()
    23     pub_date = models.DateField()
    24     mod_date = models.DateField()
    25     authors = models.ManyToManyField(Author)
    26     n_comments = models.IntegerField()
    27     n_pingbacks = models.IntegerField()
    28     rating = models.IntegerField()
    29 
    30     def __str__(self):              # __unicode__ on Python 2
    31         return self.headline
    2. 在配置文件中添加blog项目,这样blog/models.py文件才可以被引用
     1 INSTALLED_APPS = [
     2     'django.contrib.admin',
     3     'django.contrib.auth',
     4     'django.contrib.contenttypes',
     5     'django.contrib.sessions',
     6     'django.contrib.messages',
     7     'django.contrib.staticfiles',
     8     'app01',
     9     'blog',
    10 ]
    3. 初始化数据库
    1  python3.5 manage.py makemigrations  生成配置文件
    2  python3.5 manage.py migrate    初始化数据库

    在自己写的脚本里调用Django models


    我们直接在pycharm中运行这个脚本文件.发现报错:
        % (desc, ENVIRONMENT_VARIABLE))
    django.core.exceptions.ImproperlyConfigured: Requested setting DEFAULT_INDEX_TABLESPACE, but settings are not configured. You must either define the environment variable DJANGO_SETTINGS_MODULE or call settings.configure() before accessing settings.
    为什么会报错? 说白了,你现在无法调用数据库的东西.如果你想在自己的脚本调用数据库的东西,你需要导入settings.
    我们之前在做命令行测试的时候,使用python3.5 manger.py shell进入到python命令行模式.
    因为maneger.py文件在执行时引入了系统环境变量.我们通过查看maneger.py代码可见:
    1    #!/usr/bin/env python
    2     import os
    3     import sys
    4 
    5     if __name__ == "__main__":
    6         os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day18_site.settings")  #这里设置了环境变量,相当于Linux中用户环境变量一样
    7         from django.core.management import execute_from_command_line
    8         execute_from_command_line(sys.argv)
    也就是说我们如果想让自己的脚本也可以调用数据库,就必须把settings.py文件引入到脚本里.
     1 #!/usr/bin/evn python3.5
     2 # -*- coding:utf-8 -*-
     3 # Author:Zhou Ming
     4 import os
     5 import sys
     6 base = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))  # 这里要加上否则找不到day18_site目录
     7 sys.path.append(base)
     8 
     9 os.environ['DJANGO_SETTINGS_MODULE'] = 'day18_site.settings'
    10 import django
    11 django.setup()
    12 from blog import models
    13 entry = models.Entry.objects.get(pk=1)
    14 print(entry)
    完成后,我们在直接运行此脚本程序就不会有报错了.
    我这里想,我们为什么要自己写orm_test.py文件呢?是因为我们在实际开发中,都是在视图文件views.py中直接写orm代码的,这样当访问进来后调用,但是在正式调用之前,我们是需要在一个.py文件里测试这些orm代码是否符合要求,所以要自己创建一个orm_test.py文件。

    接下来看下ORM的一些基本语法
    创建
    普通表的创建
        >>> from blog.models import Blog
        >>> b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
        >>> b.save()
        处理带外键关联或多对多关联的对象
    ForeignKey的关联
        >>> from blog.models import Entry
        >>> entry = Entry.objects.get(pk=1)   # 先获取一个记录对象
        >>> cheese_blog = Blog.objects.get(name="Cheddar Talk") # 获得一个blog对象实例
        >>> entry.blog = cheese_blog        #设置外键
        #>>> entry.blog_id = cheese_blog.id       #或者设置id设置外键,两种设置外键的方式结果是一样的
        >>> entry.save()            # 改完之后要保存
        ManyToManyField关联
        >>> from blog.models import Author
        >>> joe = Author.objects.create(name="Joe") # 创建一个Author对象
        >>> entry.authors.add(joe)                  # 添加,而不是=,这个是多对多方式
        # 添加完成后,不需要进行保存
        添加多个ManyToMany对象
        >>> john = Author.objects.create(name="John")
        >>> paul = Author.objects.create(name="Paul")
        >>> george = Author.objects.create(name="George")
        >>> ringo = Author.objects.create(name="Ringo")
        >>> entry.authors.add(john, paul, george, ringo)
    查询的例子:
     1     all_entries = Entry.objects.all() #查询所有
     2     Entry.objects.filter(pub_date__year=2006) #查询所有pub_date为2006年的纪录
     3     Entry.objects.all().filter(pub_date__year=2006) #与上面那句一样,有的同学会以为这条是先查出来在过滤,不是,最终它和上面的那条语句会产生一摸一样的原生sql语句.所以是一样的
     4     >>> Entry.objects.filter(   #链式查询
     5     ...     headline__startswith='What'
     6     ... ).exclude(
     7     ...     pub_date__gte=datetime.date.today   #表示大于等于今天的的意思
     8     ... ).filter(
     9     ...     pub_date__gte=datetime(2005, 1, 30) # 然后在取时间大于等于2005/1/30
    10     ... )
    11 
    12     one_entry = Entry.objects.get(pk=1) #单条查询
    13 
    14     Entry.objects.all()[:5] #查询前5条  ,好多同学以为查出所有的记录然后在截取前5条,其实不是的,最终转换的sql语句,就是limit 5
    15     Entry.objects.all()[5:10] #你猜 limit5,10
    16 
    17     Entry.objects.order_by('headline')[0] #按headline排序取第一条
    18 
    19     Entry.objects.filter(pub_date__lte='2006-01-01') #相当于sql语句SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01';
    20 
    21     Entry.objects.get(headline__exact="Cat bites dog") #相当于SELECT ... WHERE headline = 'Cat bites dog';
    22     Blog.objects.get(name__iexact="beatles blog") #与上面相同,只是大小写不敏感
    23 
    24     Entry.objects.get(headline__contains='Lennon') #相当 于SELECT ... WHERE headline LIKE '%Lennon%';
    单表内查询语句
    1     #This example retrieves all Entry objects with a Blog whose name is 'Beatles Blog':
    2     Entry.objects.filter(blog__name='Beatles Blog')
    3 
    4     Blog.objects.filter(entry__headline__contains='Lennon')  反过来找
    5 
    6  
    关联查询

    接下来学习一个有点意思的F_expressions
    对于同一表中不同字段进行对比查询,我们上面的例子中,我们建立的查询过滤条件或对比条件给到的都是一个常规的值,比如Entry.objects.all().filter(pub_date__year=2006) 2006就是一个常规的值
    现在我们有一个需求,拿上面的Entry表举例:Entry表里有两个字段注释数 N comments 和 评论数N pingbacks
    我现在想找,所有 N comments <= N pingbacks的记录条目.
    我们看这里面的条件过滤就是使用同一个表中的两个字段.我们使用sql原生语句很好实现.但是在Django中的ORM如何实现呢?
    Django提供一个F()表达式,允许同表的不同字段进行比较.F()是一个实例.具体用法如下:
            >>> from django.db.models import F
            >>> Entry.objects.filter(n_comments__gt=F('n_pingbacks'))
    
        原生sql语句 :select n_comments,n_pingbacks from Entry where n_comments <= n_pingbacks

    不仅可以直接比较,还可以对后面的条件进行运算后在进行比较,如:
            >>> Entry.objects.filter(n_comments__gt=F('n_pingbacks') * 2)
        甚至可以多个字段进行运算,2个至多个都行,如下:
            >>> Entry.objects.filter(rating__lt=F('n_comments') + F('n_pingbacks'))
        原生sql语句: select n_comments,n_pingbacks,rating from blog_entry where rating > (n_comments + n_pingbacks)
        找出在发布后三天后进行修改的条目
            >>> from datetime import timedelta
            >>> Entry.objects.filter(mod_date__gt=F('pub_date') + timedelta(days=3))

    Caching and QuerySets
    缓存 和 结果集
    为什么把缓存和结果集放到一起说呢?
    每一个结果集都会包含一个缓存,来降低对数据库的访问.我们要理解这个,有助于我们写一个搞笑的orm的代码.
    在一个刚创建的一个结果集.(什么是刚创建的?)假如我们执行一个orm代码使用filter()获得了的记录会存到QuerySets这个结果集里,紧接着我们要循环这些记录,这个时候我们就不需要去数据库中取了,而是循环结果集.
    使用QuerySets 就非常的高效的使你的数据库查询结果提高利用率了.
            >>> print([e.headline for e in Entry.objects.all()])
            >>> print([e.pub_date for e in Entry.objects.all()])
        上面的代码做了两次for循环,这两个for循环将会产生两次对数据库的查询.而不是查询一次存入QuerySets ,然后在对QuerySets进行循环.
    并且两次的循环结果很可能不一样,因为每一次查询的结果集可能不一样.
    要是用缓存和结果集,就要写成:
            >>> queryset = Entry.objects.all() #这段代码并未真正的将数据从数据库读到内存中,它只是读了一小部分,这个只有当大数据查询能看到.比如你有100W条数据进行select,你会发现,你执行后立刻就返回了.你在print(queryset),只会打印前几十条,后面省略了只显示前几十条和总共的数量.其实是没取,不是省略.
            # 在什么时候会去数据库中取呢?当你第一次真正去遍历它的时候,它才会去把数据从数据库中取出来.第二次就可以直接用了
            >>> print([p.headline for p in queryset]) # Evaluate the query set.
            >>> print([p.pub_date for p in queryset]) # Re-use the cache from the evaluation.

    什么情况下不会被缓存?
    下面例子的时候:
            >>> queryset = Entry.objects.all()      前查出一部分
            >>> print queryset[5] # Queries the database  只取一个值的时候
            >>> print queryset[5] # Queries the database again
    
            >>> queryset = Entry.objects.all()
            >>> [entry for entry in queryset] # Queries the database
            >>> print queryset[5] # Uses cache
            >>> print queryset[5] # Uses cache
    Complex lookups with Q objects(复杂查询)
    我们在filter中实现sql原声语句中的and条件,如(接下来的语句都是在orm_test.py中书写的):
    1         Entry.objects.filter(n_comments__gt=F('n_pingbacks'),
    2                             pub_date__lte='2006-01-01')
        那如果我们要实现原生语句中的OR关系条件,应该如何书写呢?就不能使用filter了,要使用Q了
    具体使用如下:
    1         from django.db.models import Q
    2         Q(question__startswith='What')
        你可以使用"|"."|"表示or关系,","表示and的关系
    1         Q(question__startswith='Who') | Q(question__startswith='What')
    2     #上面的语句的意思就是:
    3     #  WHERE question LIKE 'Who%' OR question LIKE 'What%'
        还可以使用~,表示非
    1         Q(question__startswith='Who') | ~Q(pub_date__year=2005)
       orm代码中的写法举例:
    1         Poll.objects.get(
    2             Q(question__startswith='Who'),
    3             Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6))
    4         )
    5     #这句就如同:
    6     #    SELECT * from polls WHERE question LIKE 'Who%'
    7     #        AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
      那我们想,在不使用Q的时候,我们直接些某一个字段=值,那这种普通的写法和Q()能在一起使用吗?可以,不过要保证Q()的方式在前面,而普通方式在后面的顺序.
    如:
    1         Poll.objects.get(
    2             Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)),
    3             question__startswith='Who')
    4     下面这种写法就错了:
    5     # INVALID QUERY
    6         Poll.objects.get(
    7             question__startswith='Who',
    8             Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)))
    更新 
    Updating multiple objects at once
    # Update all the headlines with pub_date in 2007.
    1         Entry.objects.filter(pub_date__year=2007).update(headline='Everything is the same')
        在原有数据的基础上批量自增
    1         >>> Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)
        注释:批量更新有一个要注意的点,就是只能对本表内的字段进行自增,不能对外键和manytomany进行自增
    另外付值时也不能用外键或者manytomany
    1        # THIS WILL RAISE A FieldError
    2         >>> Entry.objects.update(headline=F('blog__name'))

    反向关联查询:
    我们知道整个Blog系统有如下几张表,
     1     class Publisher(models.Model):
     2         name = models.CharField(max_length=30,unique=True)   # CharField后必须有(max_length=?)
     3         address = models.CharField(max_length=50)
     4         city = models.CharField(max_length=60)
     5         state_province = models.CharField(max_length=30)
     6         country = models.CharField(max_length=50)
     7         website = models.URLField()
     8         def __str__(self):
     9             return "<%s>"%(self.name)
    10 
    11     class Author(models.Model):
    12         first_name = models.CharField(max_length=30)
    13         last_name = models.CharField(max_length=40)
    14         email = models.EmailField()
    15         def __str__(self):
    16             return "<%s %s>"%(self.first_name,self.last_name)
    17 
    18     class Book(models.Model):
    19         title = models.CharField(max_length=100)
    20         authors = models.ManyToManyField(Author)
    21         publisher = models.ForeignKey(Publisher)
    22         publication_date = models.DateField()
    其中Book表中publisher字段外键关联Publisher表类.所以我们可以在查询Book表publisher字段获得Publisher实例.
    那么在Django的ORM中能不能在被外键关联的表中获取,一个Publisher实例被几个Book记录关联.答案是能!!
    1     from app01 import models as book_models
    2     pub_obj = book_models.Publisher.objects.last()
    3     print(pub_obj.name,pub_obj.book_set.select_related())  打印所有关联pub_obj对象的book实例,结果是关联对象实例列表
        book_set是什么?我们在Publisher表里没有定义吧,而是自己生成的.只要你有反向关联,它就会自动的生成 返向关联表名_set,我们models文件中定义的是Book,但是在数据库中生成的却是app01_book,所以这里生成的是book_set,而不是Book_set.
     #而select_related() 就是把所有的跟我这个publisher实例关联的记录查出来的方法.
    book_set是一个被关来类的实例的方法。而在外键类的反向查询中用的是:
         Blog.objects.filter(entry__headline__contains='Lennon')  反过来找,这里是通过类来找被关联对象为headline like 'lennon'的blog实例。结果是被关联对象列表
    总结:这两个都是具有反向查找的功能,
    当为某一个具体的实例查找时:pub_obj.book_set.select_related()
    当从类的角度反向查找时:Blog.objects.filter(entry__headline__contains='Lennon')
    如果我们想查每一个publisher实例,被关联多少次
    1     pub_objs = book_models.Publisher.objects.annotate(book_nums=Count('book'))
    2     for publisher in pub_ojbs: #分类聚合
    3         print(publisher.book_nums)
        上面的写法实际上是分类的聚合.我们会问.annotate()是什么意思?

    Aggregation(聚合)
    聚合是什么意思?比如说我想查出来我们班级里所有人的平均成绩.
    from django.db.models import Avg,Sum,Min,Max
    print(models.Entry.objects.all().aggregate(Avg('n_pingbacks'),Sum('n_pingbacks'),
    Min('n_pingbacks')
    ))

  • 相关阅读:
    Python 学习日记 第七天
    Python 学习日记 第六天
    Python 学习日记 第五天
    Python 学习日记 第四天
    Redis 中的数据类型及基本操作
    Asp.net mvc 中View 的呈现(二)
    Asp.net mvc 中View的呈现(一)
    Asp.net mvc 中Action 方法的执行(三)
    Asp.net mvc 中Action 方法的执行(二)
    Asp.net mvc 中Action 方法的执行(一)
  • 原文地址:https://www.cnblogs.com/zhming26/p/5780913.html
Copyright © 2011-2022 走看看