python2.0_day18_django_ORM

Django_ORMday15\16我们学到的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())
    book_set是什么?我们在Publisher表里没有定义吧,而是自己生成的.只要你有反向关联,它就会自动的生成 返向关联表名_set,我们models文件中定义的是Book,但是在数据库中生成的却是app01_book,所以这里生成的是book_set,而不是Book_set.    #而select_related() 就是把所有的跟我这个publisher实例关联的记录查出来的方法.

如果我们想查每一个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‘)                                                ))
    
时间: 2024-11-01 13:58:49

python2.0_day18_django_ORM的相关文章

ubuntu下卸载python2和升级python3.5

卸载python只需一条语句就可以实现 sudu apt-get remove python ubuntu下安装python3 sudo apt-get install python3 但这样只安装了python3.4 要想使用python3.5,则必须升级python3.4 sudo add-apt-repository ppa:fkrull/deadsnakes sudo apt-get update sudo apt-get install python3.5 使用以上三行命令便可升级py

windows下同时安装Python2.7.10和3.5.0

1. 在Python官网下载两个最新版本的Python安装包:www.python.org Windows x86-64 executable installer Windows x86-64 MSI installer 分别安装在C:\Python35和C:\Python27 2. 在Path环境变量中检查以下4个变量(Path中的环境变量是以分号隔开的): 1) c:\Python27 2) c:\Python27\Scripts 3) c:\Python33 4) c:\Python33\

python2和python3中的编码问题

开始拾起python,准备使用python3, 造轮子的过程中遇到了编码的问题,又看了一下python3和python2相比变化的部分. 首先说个概念: unicode:在本文中表示用4byte表示的unicode编码,也是python内部使用的字符串编码方式. utf-8:在本文中指最少1byte表示的unicode编码方式 我在使用 if isinstance(key,unicode): key= key.encode('utf-8') 的时候,发现key值被转成了b'foo',b'bar'

Centos6.4上python2.6.6升级到2.7.6

好久不写博文了,本文没什么含金量,只是做一个记录.便于日后翻阅. 0.操作系统信息 [[email protected] ~]$ cat /etc/issue CentOS release 6.4 (Final) Kernel \r on an \m 1.当前python版本信息 [[email protected] bin]$ pwd /usr/bin [[email protected] bin]$ ll -a python -rwxr-xr-x. 2 root root 4864 2月 

zg手册 之 python2.7.7源码分析(4)-- pyc字节码文件

什么是字节码 python解释器在执行python脚本文件时,对文件中的python源代码进行编译,编译的结果就是byte code(字节码) python虚拟机执行编译好的字节码,完成程序的运行 python会为导入的模块创建字节码文件 字节码文件的创建过程 当a.py依赖b.py时,如在a.py中import b python先检查是否有b.pyc文件(字节码文件),如果有,并且修改时间比b.py晚,就直接调用b.pyc 否则编译b.py生成b.pyc,然后加载新生成的字节码文件 字节码对象

如何在CentOS6上安装Python2.7和Python3.3

原文来自http://toomuchdata.com/2014/02/16/how-to-install-python-on-centos/,个人觉得对在linux安装新版本Python是很有参考意义,因而转载,原文是英文的,本人简单翻译下,大家看懂即可,有不妥的地方请留言. 如何在CentOS 6上同时安装Python 2.7和Python 3.3 本文将介绍如何在CentOS 6上安装Python 2.7和3.3.下面以Python 2.7.6和Python 3.3.5为例进行说明,但本人实

CentOS 7安装Python3.5,并与Python2.7兼容并存

CentOS7默认安装了python2.7.5,当需要使用python3的时候,可以手动下载Python源码后编译安装. 1.安装python3.5可能使用的依赖 1 yum install openssl-devel bzip2-devel expat-devel gdbm-devel readline-devel sqlite-devel 2.下载python wget "https://www.python.org/ftp/python/3.5.0/Python-3.5.0.tgz&quo

Python2和Python3的一些语法区别

Python2和Python3的一些语法区别 python Python2和Python3的一些语法区别 1.print 2.input 3. python3版本相对2版本的部分其他区别 问题:为何会出现乱码的情况 问题:如何获取编码方式的信息? 问题:在控制台上看到的到底是什么? 1.print 在版本2的使用方法是: print 'this is version 2 也可以是 print('this is version 2') 但到了3,就只能加上括号,像一个函数一样来使用 print:

python2 与 python3的区别总结

python2 与 python3的区别总结 几乎所有的Python 2程序都需要一些修改才能正常地运行在Python 3的环境下.为了简化这个转换过程,Python 3自带了一个叫做2to3的实用脚本(Utility Script),这个脚本会将你的Python 2程序源文件作为输入,然后自动将其转换到Python 3的形式. 案例研究:将chardet移植到Python 3(porting chardet to Python 3)描述了如何运行这个脚本,然后展示了一些它不能自动修复的情况.这