zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    禁用网络连接后无法访问本机数据库的问题
    DevExpress使用笔记
    DEV控件 皮肤问题
    SQLServer2008导入Excel遇到的问题
    InnoSetup使用笔记
    SQLServer清空数据库中所有表的数据
    MS SQL Server2000转换成MySQL
    由MySQL登录不了引发的一些问题
    onerror事件
    DIV+CSS一些小小的技巧
  • 原文地址:https://www.cnblogs.com/ssyfj/p/8647352.html
Copyright © 2011-2022 走看看