python2.0_day18_django_ORM
Django_ORM
day15\16我们学到的ORM都是最基本的增删改查.下面我们稍作升级,学习下高级点的增删改查.
先创建一个新的APP项目
python3. manage.py startapp blog
1.编辑blog/models.py
from django.db import models # Create your models here. class Blog(models.Model):
name = models.CharField(max_length=)
tagline = models.TextField() def __str__(self): # __unicode__ on Python
return self.name class Author(models.Model):
name = models.CharField(max_length=)
email = models.EmailField() def __str__(self): # __unicode__ on Python
return self.name class Entry(models.Model):
blog = models.ForeignKey(Blog)
headline = models.CharField(max_length=)
body_text = models.TextField()
pub_date = models.DateField()
mod_date = models.DateField()
authors = models.ManyToManyField(Author)
n_comments = models.IntegerField()
n_pingbacks = models.IntegerField()
rating = models.IntegerField() def __str__(self): # __unicode__ on Python
return self.headline
2. 在配置文件中添加blog项目,这样blog/models.py文件才可以被引用
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01',
'blog',
]
3. 初始化数据库
python3. manage.py makemigrations 生成配置文件
python3. 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代码可见:
#!/usr/bin/env python
import os
import sys if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day18_site.settings") #这里设置了环境变量,相当于Linux中用户环境变量一样
from django.core.management import execute_from_command_line
execute_from_command_line(sys.argv)
也就是说我们如果想让自己的脚本也可以调用数据库,就必须把settings.py文件引入到脚本里.
#!/usr/bin/evn python3.
# -*- coding:utf- -*-
# Author:Zhou Ming
import os
import sys
base = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # 这里要加上否则找不到day18_site目录
sys.path.append(base) os.environ['DJANGO_SETTINGS_MODULE'] = 'day18_site.settings'
import django
django.setup()
from blog import models
entry = models.Entry.objects.get(pk=)
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=) # 先获取一个记录对象
>>> 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)
查询的例子:
all_entries = Entry.objects.all() #查询所有
Entry.objects.filter(pub_date__year=) #查询所有pub_date为2006年的纪录
Entry.objects.all().filter(pub_date__year=) #与上面那句一样,有的同学会以为这条是先查出来在过滤,不是,最终它和上面的那条语句会产生一摸一样的原生sql语句.所以是一样的
>>> Entry.objects.filter( #链式查询
... headline__startswith='What'
... ).exclude(
... pub_date__gte=datetime.date.today #表示大于等于今天的的意思
... ).filter(
... pub_date__gte=datetime(, , ) # 然后在取时间大于等于2005//
... ) one_entry = Entry.objects.get(pk=) #单条查询 Entry.objects.all()[:] #查询前5条 ,好多同学以为查出所有的记录然后在截取前5条,其实不是的,最终转换的sql语句,就是limit
Entry.objects.all()[:] #你猜 limit5, Entry.objects.order_by('headline')[] #按headline排序取第一条 Entry.objects.filter(pub_date__lte='2006-01-01') #相当于sql语句SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01'; Entry.objects.get(headline__exact="Cat bites dog") #相当于SELECT ... WHERE headline = 'Cat bites dog';
Blog.objects.get(name__iexact="beatles blog") #与上面相同,只是大小写不敏感 Entry.objects.get(headline__contains='Lennon') #相当 于SELECT ... WHERE headline LIKE '%Lennon%';
单表内查询语句
#This example retrieves all Entry objects with a Blog whose name is 'Beatles Blog':
Entry.objects.filter(blog__name='Beatles Blog') Blog.objects.filter(entry__headline__contains='Lennon') 反过来找
关联查询
接下来学习一个有点意思的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个至多个都行,如下:
>>> 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=))
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[] # Queries the database 只取一个值的时候
>>> print queryset[] # Queries the database again >>> queryset = Entry.objects.all()
>>> [entry for entry in queryset] # Queries the database
>>> print queryset[] # Uses cache
>>> print queryset[] # Uses cache
Complex lookups with Q objects(复杂查询)
我们在filter中实现sql原声语句中的and条件,如(接下来的语句都是在orm_test.py中书写的):
Entry.objects.filter(n_comments__gt=F('n_pingbacks'),
pub_date__lte='2006-01-01')
那如果我们要实现原生语句中的OR关系条件,应该如何书写呢?就不能使用filter了,要使用Q了
具体使用如下:
from django.db.models import Q
Q(question__startswith='What')
你可以使用"|"."|"表示or关系,","表示and的关系
Q(question__startswith='Who') | Q(question__startswith='What')
#上面的语句的意思就是:
# WHERE question LIKE 'Who%' OR question LIKE 'What%'
还可以使用~,表示非
Q(question__startswith='Who') | ~Q(pub_date__year=)
orm代码中的写法举例:
Poll.objects.get(
Q(question__startswith='Who'),
Q(pub_date=date(, , )) | Q(pub_date=date(, , ))
)
#这句就如同:
# SELECT * from polls WHERE question LIKE 'Who%'
# AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06')
那我们想,在不使用Q的时候,我们直接些某一个字段=值,那这种普通的写法和Q()能在一起使用吗?可以,不过要保证Q()的方式在前面,而普通方式在后面的顺序.
如:
Poll.objects.get(
Q(pub_date=date(, , )) | Q(pub_date=date(, , )),
question__startswith='Who')
下面这种写法就错了:
# INVALID QUERY
Poll.objects.get(
question__startswith='Who',
Q(pub_date=date(, , )) | Q(pub_date=date(, , )))
更新
Updating multiple objects at once
# Update all the headlines with pub_date in 2007.
Entry.objects.filter(pub_date__year=).update(headline='Everything is the same')
在原有数据的基础上批量自增
>>> Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + )
注释:批量更新有一个要注意的点,就是只能对本表内的字段进行自增,不能对外键和manytomany进行自增
另外付值时也不能用外键或者manytomany
# THIS WILL RAISE A FieldError
>>> Entry.objects.update(headline=F('blog__name'))
反向关联查询:
我们知道整个Blog系统有如下几张表,
class Publisher(models.Model):
name = models.CharField(max_length=,unique=True) # CharField后必须有(max_length=?)
address = models.CharField(max_length=)
city = models.CharField(max_length=)
state_province = models.CharField(max_length=)
country = models.CharField(max_length=)
website = models.URLField()
def __str__(self):
return "<%s>"%(self.name) class Author(models.Model):
first_name = models.CharField(max_length=)
last_name = models.CharField(max_length=)
email = models.EmailField()
def __str__(self):
return "<%s %s>"%(self.first_name,self.last_name) class Book(models.Model):
title = models.CharField(max_length=)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
其中Book表中publisher字段外键关联Publisher表类.所以我们可以在查询Book表publisher字段获得Publisher实例.
那么在Django的ORM中能不能在被外键关联的表中获取,一个Publisher实例被几个Book记录关联.答案是能!!
from app01 import models as book_models
pub_obj = book_models.Publisher.objects.last()
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实例,被关联多少次
pub_objs = book_models.Publisher.objects.annotate(book_nums=Count('book'))
for publisher in pub_ojbs: #分类聚合
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')
))
python2.0_day18_django_ORM的更多相关文章
- 让VIM支持Python2 by update-alternatives
前言 Ubuntu 16+中$ sudo apt install vim所安装的vim只支持Python3,但很多插件如YCM和powerline均需要Python2,那就来场"生命贵在折 ...
- 烂泥:python2.7和python3.5源码安装
本文由ilanniweb提供友情赞助,首发于烂泥行天下 想要获得更多的文章,可以关注我的微信ilanniweb 前几天在centos6.6安装ansible时,一直提示python版本不对,导致不能安 ...
- python版本随意切换之python2.7+django1.8.7+uwsgi+nginx源码包部署。
资源准备: wget https://www.python.org/ftp/python/2.7.13/Python-2.7.13.tgz wget https://www.djangoproject ...
- windows XP上实现python2.7.5和python3.4.3共存
windows XP上实现python2.7.5和python3.4.3共存过程记录: 1. 首先安装python2.7.5和python3.4.3,两个版本安装顺序不分前后; 2. 检查系统环境变量 ...
- Python2 基本数据结构源码解析
Python2 基本数据结构源码解析 Contents 0x00. Preface 0x01. PyObject 0x01. PyIntObject 0x02. PyFloatObject 0x04. ...
- Ubuntu安装Python2.7,nodejs
安装Python2.7 sudo add-apt-repository ppa:fkrull/deadsnakes-python2.7sudo apt-get update sudo apt-get ...
- Windows下安装python2和python3双版本
现在大家常用的桌面操作系统有:Windows.Mac OS.ubuntu,其中Mac OS 和 ubuntu上都会自带python.这里我们只介绍下Windows(我用的Win10)环境下的pytho ...
- [python]CentOS 6下安装Python2.7
安装方法 如果在CentOS上自己编译安装过python2.7,使用过程中会发现有些标准库没有安装之类的问题. 逛别人博客的时候发现,一个便捷的方法:使用RHSCL的全称是Red Hat Softwa ...
- 在CentOS 6.5上安装python2.7
1.yum groupinstall “Development tools” 2.安装编译Python需要的组件 yum install zlib-devel bzip2-devel openssl- ...
随机推荐
- mysql linux 区分大小写
查看大小写区分 mysql> show variables like "%case%"; +------------------------+-------+ | Varia ...
- IE11不支持Selenium 2.0的解决方法
题前话(Pre-words) 希望使用Selenium 2.0的人看到这篇文章能够收藏此文,以后遇到该问题,再也不用花费多余的时间进行research了!本文就是对网上所有千奇百怪各种各样的searc ...
- SVN导入maven项目
在项目中,曾今遇到过这种问题,用eclipse将项目从svn下载下来,maven去自动下载jar包怎么都报错,本来时间就很紧张, 还特么遇到这种坑爹的问题.不过,整了我一天,最后终于在同事的帮助下,搞 ...
- POJ 3253-Fence Repair(堆)
Fence Repair Time Limit: 2000MS Memory Limit: 65536K Total Submissions: 27055 Accepted: 8800 Des ...
- 查看linux硬件信息
more /proc/cpuinfo more /proc/meminfo more /proc/*info lspci 查看主板信息等cat /proc/cpuinfo CPU信息cat /proc ...
- DOS建立带点的文件夹
md 表示make directory 创建文件夹 rd 表示remove directory 删除文件夹 我们用一个实例来说明一切吧. 1.按 键盘Windows徽标键+R 来打开 运行 对话框 2 ...
- skynet1.0阅读笔记_skynet的启动
首先看skynet的启动,函数入口在 skynet_main.c 的main(),其中最重要的是: skynet_start(&config); 在skynet_start中做了两个启动: / ...
- CSS圆角框,圆角提示框
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- oracle权限详解
一.权限分类:系统权限:系统规定用户使用数据库的权限.(系统权限是对用户而言). 实体权限:某种权限用户对其它用户的表或视图的存取权限.(是针对表或视图而言的). 二.系统权限管理:1.系统权限分类: ...
- 关于Cocos2d-x头文件的引用
cocos2d-x 3.10的G:\cocoshome\Cocos2d-x\cocos2d-x-3.10\extensions\GUI\CCControlExtension\CCScale9Sprit ...