python---django中orm的使用(1)

首先推荐两篇文章:Django之ORM操作http://www.cnblogs.com/yuanchenqi/articles/6083427.html十分全面。

另外和python---ORM之SQLAlchemy(1)等前面几篇文章相结合

1.ORM对象关系映射(Object Relational Mapping)

优点:

使用orm,对于数据库迁移十分有用,只需要修改相关引擎即可,
不用考虑是sqlite还是mysql,oracle等,
而且设计好类后,会自动生成相关表,数据。
但是对于一些简单的表可能会稍微有点多余,但是毕竟优点多余这些

orm类大概了解:

类名对应      数据库表名
类成员属性对应   数据库字段
类实例对应      数据库表中的一行数据

注意:使用orm时对应的数据库是在settings文件中红设置的,必须提前建库

create database 库名 default character set utf8 collate utf8_general_ci

相关配置请看python---django中models配置

2.模型常用字段类型介绍

   models.CharField
    #字符串字段,用于可变长字符串(varchar)
    #要求必须有一个参数maxlength,用于设置其所允许的最大字符串长度

    models.IntegerField
    #用于保存一个整数

    choice=(
        (1,''),
        (2,""),
        (3,"其他"),
    )
    models.IntegerField(choices=choice)
    # 枚举类型

    models.FloatField
    #浮点数保存,必须提供两个参数,max_digits总位数,decimal_places小数位数

    models.AutoField
    #一个IntegerField,添加记录时,自动增长
    #一般主键设置为他,models.AutoField(primary_key=True)
    #若是不指定主键,系统会自动添加一个主键字段到该表

    models.TextField
    #一个容量很大的文本字段
models.DateField #一个日期字段,可以带以下可选参数 #Argument 描述 #auto_now 当对象被保存时自动将该字段的值设为当前时间,通常表示“last
-modified”时间戳,最后一次修改的时间 #auto_new_add 当对象被创建时设置,即首次添加时的创建时间 models.DateTimeField #一个日期时间字段,和DateField类似

以下在数据库中本质都是字符串数据类型,此类字段只是在Django自带的admin中有用
    models.EmailField
    #检测email合法性的CharField,不需要maxlength
  models.FileField 
  #一个文件上传字段
  
  
  models.ImageField
  #图片上传字段,类似FileField
  
  
  models.IPAddressField
  #ip检验字段
  .....

其他字段:

db_index = True 表示设置索引
unique(唯一的意思) = True 设置唯一索引

联合唯一索引
class Meta:
unique_together = (
 ('email','ctime'),
)
联合索引(不做限制)
index_together = (
('email','ctime'),
)

ManyToManyField(RelatedField)  #多对多操作

3.字段参数:

null                 :    为空
default              :    设置默认值
primary_key          :    主键    若是自己没有设置主键,Django会自动添加上id = meta.AutoField('ID', primary_key=True)
unique               :    数据唯一
verbose_name         :    admin中字段的显示名称
db_column,
db_index             :    如果为真,将为这个字段创建索引
choices              :    一个用于选择的二维元组,枚举
.....

4.数据表的增删改查

表创建:

from django.db import models<br>
class Publisher(models.Model):
    name = models.CharField(max_length=30, verbose_name="名称")
    address = models.CharField("地址", max_length=50)
    city = models.CharField('城市',max_length=60)
    state_province = models.CharField(max_length=30)
    country = models.CharField(max_length=50)
    website = models.URLField()
 
    class Meta:
        verbose_name = '出版商'
        verbose_name_plural = verbose_name
 
    def __str__(self):
        return self.name
 
class Author(models.Model):
    name = models.CharField(max_length=30)
    def __str__(self):
        return self.name
 
class AuthorDetail(models.Model):
    sex = models.BooleanField(max_length=1, choices=((0, ''),(1, ''),))
    email = models.EmailField()
    address = models.CharField(max_length=50)
    birthday = models.DateField()
    author = models.OneToOneField(Author)
 
class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher)
    publication_date = models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2,default=10)
    def __str__(self):
        return self.title
View Code

 

from 应用APP.models import *

增(create, save):

方式一:

Author.objects.create(name='das')

方法二:

Author.objects.create(**{"name":"dsaf"})  推荐
方法三:

author = Author(name="dsa")

author.save()

方法四:

author = Author()

author.name="dsa"
author.save()

删delete:

Book.objects.filter(id=1).delete() #先查找到这条数据,再删除

默认是级联删除,含有相关的这条记录的数据,也会被删除

对于外键中的级联删除,我们可以使用on_delete参数进行修改

改update,save:

author = Author.objects.get(id=5)
author.name="gae"
author.save()

Author.objects.filter(id=5).update(name="geag")  

update效率高于save,save会重新保存所有字段,update只会修改你修改的相关字段

注意:第二种方式修改不能用get的原因是:update是QuerySet对象的方法,get返回的是一个model对象,它没有update方法,而filter返回的是一个QuerySe集合对象(filter里面的条件可能有多个条件符合,比如name='alvin',可能有两个name='alvin'的行数据),get返回的是一个记录对象

查:

filter(**kwargs)
all()
get(**kwargs)

下面方法都是对查询的结果进行操作
values(
*field) #返回字典序列 exclude(**kwargs) #它包含了与所给筛选条件不匹配的对象
order_by(*field)
reverse()
distinct()#剔除重复
values_list(
*field) #元组序列
count()
first()
last()
exists()  #
如果QuerySet包含数据,就返回True,否则返回False。判断对象是否存在数据,不会去取出数据(关于惰性机制)

 更多方法查看:http://www.cnblogs.com/yuanchenqi/articles/6083427.html

queryset惰性机制了解:

惰性机制:当使用filter()或者all()等都是返回一个QuerySet()查询结果集对象,
他不会马上执行SQL语句,只有当调用QuerySet的时候才会被执行

可以查看SQL语句来进行分析:可以使用queryset.query()  { queryset是获取的queryset结果集 }获取该对象这一条数据的SQL语句

  queryset = MyModel.objects.all()
  print 'query sql: ' + str(queryset .query)

也可以通过日志来查看,需要先配置settings文件

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console':{
            'level':'DEBUG',
            'class':'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'propagate': True,
            'level':'DEBUG',
        },
    }
}
settings.py中SQL日志配置
class Handler(Filterer)   #基类

有下面两种方式:
class StreamHandler(Handler)   #流输出

class FileHandler(StreamHandler)  #文件形式
日志类型

然后可以开始测试:

from blog import models

def data_oper(req):
    obj_set = models.Book.objects.filter(id=2)  #获取queryset结果集
    
    return HttpResponse("<h1>ok</h1>")

执行后发现,并没有SQL日志产生,所有SQL语句没有执行,只有当使用queryset集合是才会开始执行。

def data_oper(req):
    obj_set = models.Book.objects.filter(id=2)  #获取queryset结果集
    
    for obj in obj_set :
        print(obj.title)
    return HttpResponse("<h1>ok</h1>")    

此时会发现执行了查询数据的SQL语句。

缺点分析:

def data_oper(req):
    obj_set = models.Book.objects.filter(id=2)       
    if obj_set:    #if 逻辑判断只需要知道其中是否包含数据,需要快速执行,但是在这里会去执行SQL语句,将逻辑语句效率降低
        print("ok")
    return HttpResponse("<h1>ok</h1>")     
#高效判断,可以使用exists方法

#取数据时,也可以不用一次性取完,可以使用迭代器,一条一条数据取出
obj_set.iterator()

缓存:

def data_oper(req):
    obj_set = models.Book.objects.filter(id=2)  #获取queryset结果集
    
    for obj in obj_set :    #去数据库中取数据,第一次执行后会将queryset数据存放在缓存中,后面获取数据时,则不需要在进行SQL语句
        print(obj.title)
  
   #若是在这里修改    #obj_set.update(title="dsaf") #会修改数据库数据,也会修改缓存数据
for obj in obj_set : #不需要在进行SQL语句,缓存中取数据 print(obj.title) return HttpResponse("<h1>ok</h1>")

补充:QuerySet

QuerySet特点:可迭代,可切片

objs=models.Book.objects.all()#[obj1,obj2,ob3...]

    #QuerySet:   可迭代

    for obj in objs:#每一obj就是一个行对象  或者:obj_set.iterator()获取数据,可用while
    print("obj:",obj)
    # QuerySet:  可切片

    # print(objs[1])
    # print(objs[1:4])
    # print(objs[::-1])

高效使用:

<1>Django的queryset是惰性的

     Django的queryset对应于数据库的若干记录(row),通过可选的查询来过滤。例如,下面的代码会得
     到数据库中名字为‘Dave’的所有的人:person_set = Person.objects.filter(first_name="Dave")
     上面的代码并没有运行任何的数据库查询。你可以使用person_set,给它加上一些过滤条件,或者将它传给某个函数,
     这些操作都不会发送给数据库。这是对的,因为数据库查询是显著影响web应用性能的因素之一。

<2>要真正从数据库获得数据,你可以遍历queryset或者使用if queryset,总之你用到数据时就会执行sql.
   为了验证这些,需要在settings里加入 LOGGING(验证方式)
        obj=models.Book.objects.filter(id=3)
        # for i in obj:
        #     print(i)

        # if obj:
        #     print("ok")

<3>queryset是具有cache的
     当你遍历queryset时,所有匹配的记录会从数据库获取,然后转换成Django的model。这被称为执行
    (evaluation).这些model会保存在queryset内置的cache中,这样如果你再次遍历这个queryset,
     你不需要重复运行通用的查询。
        obj=models.Book.objects.filter(id=3)

        # for i in obj:
        #     print(i)
                          ## models.Book.objects.filter(id=3).update(title="GO")
                          ## obj_new=models.Book.objects.filter(id=3)
        # for i in obj:
        #     print(i)   #LOGGING只会打印一次

<4>
     简单的使用if语句进行判断也会完全执行整个queryset并且把数据放入cache,虽然你并不需要这些
     数据!为了避免这个,可以用exists()方法来检查是否有数据:

            obj = Book.objects.filter(id=4)
            #  exists()的检查可以避免数据放入queryset的cache。
            if obj.exists():
                print("hello world!")

<5>当queryset非常巨大时,cache会成为问题

     处理成千上万的记录时,将它们一次装入内存是很浪费的。更糟糕的是,巨大的queryset可能会锁住系统
     进程,让你的程序濒临崩溃。要避免在遍历数据的同时产生queryset cache,可以使用iterator()方法
     来获取数据,处理完数据就将其丢弃。
        objs = Book.objects.all().iterator()
        # iterator()可以一次只从数据库获取少量数据,这样可以节省内存
        for obj in objs:
            print(obj.name)
        #BUT,再次遍历没有打印,因为迭代器已经在上一次遍历(next)到最后一次了,没得遍历了
        for obj in objs:
            print(obj.name)

     #当然,使用iterator()方法来防止生成cache,意味着遍历同一个queryset时会重复执行查询。所以使
     #用iterator()的时候要当心,确保你的代码在操作一个大的queryset时没有重复执行查询

总结:
    queryset的cache是用于减少程序对数据库的查询,在通常的使用下会保证只有在需要的时候才会查询数据库。
使用exists()和iterator()方法可以优化程序对内存的使用。不过,由于它们并不会生成queryset cache,可能
会造成额外的数据库查询。

5.一对多,多对多

一对多,外键foreign_key

class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher)
    publication_date = models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2,default=10)
    def __str__(self):
        return self.title
Book
class Publisher(models.Model):
    name = models.CharField(max_length=30, verbose_name="名称")
    address = models.CharField("地址", max_length=50)
    city = models.CharField('城市',max_length=60)
    state_province = models.CharField(max_length=30)
    country = models.CharField(max_length=50)
    website = models.URLField()
 
    class Meta:
        verbose_name = '出版商'
        verbose_name_plural = verbose_name
 
    def __str__(self):
        return self.name
Publisher

增加:

一对多:

方法一:直接添加外键值

Book.objects.create(title='php',
                    publisher_id=2,   #这里的2是指为该book对象绑定了Publisher表中id=2的行对象
                    publication_date='2017-7-7',
                    price=99)
注意:

publisher = models.ForeignKey(Publisher)
外键就是在foreign基础上,加上一个_id
例如:publisher外键,在数据库存在为publisher_id,所以调用时也应该使用publisher_id
方法二:直接添加对象,则不需要管外键_id了

对象创建或查找:

pub_obj=Publisher(name='河大出版社',address='保定',city='保定',state_province='河北',country='China',website='http://www.hbu.com')
#直接新建
或者
pub_obj=Publisher.objects.get(id=1)
#查找
添加加入:将 publisher_id=2 改为 publisher=pub_obj

Book.objects.create(title='php',
                    publisher=pub_obj,
                    publication_date='2017-7-7',
                    price=99)

 

补充:连续跨多张表:

 

class School(models.Model):
    title = models.CharField(max_length=32)

class Classes(models.Model):
    sc = models.ForeignKey(School)
    caption = models.CharField(max_length=32)
    def __str__(self):
        return self.caption

class Student(models.Model):
    username = models.CharField(max_length=32)
    age = models.IntegerField()
    gender = models.BooleanField()
    cls = models.ForeignKey("Classes")

#跨多表查询
#跨一表查询班级信息
stu_list = Student.objects.all().values('username',"cls__caption")
#跨两张表查询学校
stu_list = Student.objects.all().values('username',"cls__caption","cls__sc__title")

 

补充:related_name:相当于在Classes表中加入了一个ccc字段,是指向Student表,反向查询(有这个就可以直接使用这个字段进行反向查询,不需要字段_set来进行操作)

 

class Student(models.Model):
    username = models.CharField(max_length=32)
    age = models.IntegerField()
    gender = models.BooleanField()
    cls = models.ForeignKey("Classes",related_name="ccc")

obj = Classes.objects.filter(caption="python").first()
Stu_set =  obj.ccc.all()  #关于python班级下的所有学生的query_set集合

 

就是tornado中的relationship中的backref参数python---ORM之SQLAlchemy(3)外键与relationship的关系

 

注:尽量使用正向查询

 

补充:一对多---->一对一  对应关系

只需要在外键中设置为unique=true

一对一:反向查找只需要在后面加上反向的表名即可

一对多:反向查找需要在后面跟上反向的小写表名+_set

多对多:像书籍和作者

class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher)
    publication_date = models.DateField()
    price=models.DecimalField(max_digits=5,decimal_places=2,default=10)

    def __str__(self):
        return self.title
class Author(models.Model):
    name = models.CharField(max_length=30)
    def __str__(self):
        return self.name

这个多对多键也可以设在Author中,最好应该是在数据较多的一侧,去查找少的

多对多一般会创建第三张表来保存数据,第三张表是通过models.ManyToManyField()自动创建,默认是  应用APP_表一名_表二名,会自动生成主键,将两张表的主键作为记录,字段名为:表名_id,并且是联合唯一。  

但是:

对于Django自动创建的这张表,我们无法自己直接使用,使用方法如下:add   remove

     因为author字段放在book表中,所以下面是正向查询 
   author1=Author.objects.get(id=1) author2=Author.objects.filter(name='alvin')[0] book=Book.objects.get(id=1) book.authors.add(author1,author2)

  #上面可以写为
book.authors.add(*[author1,author2])

   对应删除操作:
   book.authors.remove(*[author1,author2])

或者:

    author表中并没有book字段,所以为反向查询  ---》  反向查询直接找author中book对象是找不到的,
需要使用_set才可以找到

   book=models.Book.objects.filter(id__gt=1) authors=models.Author.objects.filter(id=1)[0] authors.book_set.add(*book)

对应删除操作:
   authors.book_set.remove(*book)

或者:

book.authors.add(1)
authors.book_set.add(1)

对应删除操作:
book.authors.remove(1)
authors.book_set.remove(1)

查询方法:

# Create your models here.
class Classes(models.Model):
    caption = models.CharField(max_length=32)

class Teacher(models.Model):
    name = models.CharField(max_length=32)
    cls = models.ManyToManyField('Classes',related_name="ssss")

Teacher.objects.all().values('id','name','cls','cls__caption')
#默认直接访问cls是获取其id,使用__连表获取信息
Classes.objects.all().values('id','caption','ssss','ssss_name')

要想直接使用第三张表进行操作,需要我们自己创建该表(使用方便)

当不使用ManyToManyField自动创建表时,自己手动创建第三张表:


from django.db import models

class
Book2Author(models.Model) book=models.ForeignKey("Book") author=models.ForeignKey("Author")  #最好使用引号,若是想不加引号,那么必须将那个类放在前面
  
   #联合唯一
   class Meta:  #在元类中添加联合唯一,建联合唯一要根据需求来看
     unique_together = ['book','author']

 此时可以直接使用第三张表去添加数据:

from 应用APP import models

models.Book2Author.objects.create( book_id
=2, author_id=3 )

注:上面两类models是不一样的

 补充:正向操作也可以操作关联表(咂filter字段操作时)

class Role(models.Model):
    caption = models.CharField(max_length=32)
    class Meta:
        verbose_name_plural = "角色表"
    def __str__(self):
        return self.caption

class User2Role(models.Model):
    u = models.ForeignKey("User")
    r = models.ForeignKey("Role")

    class Meta:
        verbose_name_plural = "用户角色分配表"
    def __str__(self):
        return "%s:%s"%(self.u.username,self.r.caption)

class Permission2Action2Role(models.Model):
p2a = models.ForeignKey("Permission2Action")
r = models.ForeignKey("Role")

class Meta:
verbose_name_plural = "角色权限分配表"
def __str__(self):
return "%s:%s"%(self.r.caption,self.p2a)

使用时:

    role_list = models.Role.objects.filter(user2role__u__id=id).all()


    user = models.User.objects.filter(id=int(id)).get()
    role_list2 = models.Role.objects.filter(user2role__u = user)

测试可用字段:

role_list2 = models.Role.objects.filter(aaa = user)    #aaa不存在,错误提示中会显示我们所能调用的字段

django.core.exceptions.FieldError: Cannot resolve keyword 'aaa' into field.
Choices are: caption, id, permission2action2role, user2role

filter使用时可以正向操作字段,对象使用时需要加上_set

posted @ 2018-03-25 22:14  山上有风景  阅读(557)  评论(0编辑  收藏  举报