zoukankan      html  css  js  c++  java
  • Django基础(ORM)

    数据库与ORM

    数据库的配置

    1    django默认支持sqlite,mysql, oracle,postgresql数据库。

         <1> sqlite

                django默认使用sqlite的数据库,默认自带sqlite的数据库驱动 , 引擎名称:django.db.backends.sqlite3

         <2> mysql

                引擎名称:django.db.backends.mysql

    2    mysql驱动程序

    •    MySQLdb(mysql python)
    •    mysqlclient
    •    MySQL
    •    PyMySQL(纯python的mysql驱动程序)

    3     在django的项目中会默认使用sqlite数据库,在settings里有如下设置:

    如果我们想要更改数据库,需要修改如下:

    复制代码
    DATABASES = {
    
        'default': {
    
            'ENGINE': 'django.db.backends.mysql', 
    
            'NAME': 'books',    #你的数据库名称
    
            'USER': 'root',   #你的数据库用户名
    
            'PASSWORD': '', #你的数据库密码
    
            'HOST': '', #你的数据库主机,留空默认为localhost
    
            'PORT': '3306', #你的数据库端口
    
        }
    
    }
    复制代码

    注意:

    复制代码
    NAME即数据库的名字,在mysql连接前该数据库必须已经创建,而上面的sqlite数据库下的db.sqlite3则是项目自动创建
    
    USER和PASSWORD分别是数据库的用户名和密码。
    
    设置完后,再启动我们的Django项目前,我们需要激活我们的mysql。
    
    然后,启动项目,会报错:no module named MySQLdb
    
    这是因为django默认你导入的驱动是MySQLdb,可是MySQLdb对于py3有很大问题,所以我们需要的驱动是PyMySQL
    
    所以,我们只需要找到项目名文件下的__init__,在里面写入:
    
    import pymysql
    pymysql.install_as_MySQLdb()
    
    问题解决!
    复制代码

    总结详细步骤:
    1. 手动创建数据库
    2. 在app/models.py里面写上一个类,必须继承models.Model这个类 (注意启动Django项目)
    3. 在Django 项目的settings.py 里面 配置上数据库的相关信息
    4. 在Django项目里的__init__.py里面写上 两句话 import pymysql pymysql.install_as_MySQLdb()
    5. 给Django发布命令 
    1. python manage.py makemigrations # 相当于去你的models.py 里面看一下有没有改动
    2. python manage.py migrate # 把改动翻译成SQL语句,然后去数据库执行

    ORM表模型

    表(模型)的创建:

    实例:我们来假定下面这些概念,字段和关系

    作者模型:一个作者有姓名。

    作者详细模型:把作者的详情放到详情表,包含性别,email地址和出生日期,作者详情模型和作者模型之间是一对一的关系(one-to-one)(类似于每个人和他的身份证之间的关系),在大多数情况下我们没有必要将他们拆分成两张表,这里只是引出一对一的概念。

    出版商模型:出版商有名称,地址,所在城市,省,国家和网站。

    书籍模型:书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many),一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many),也被称作外键

    复制代码
    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
    复制代码

    分析代码:

           <1>  每个数据模型都是django.db.models.Model的子类,它的父类Model包含了所有必要的和数据库交互的方法。并提供了一个简介漂亮的定义数据库字段的语法。

           <2>  每个模型相当于单个数据库表(多对多关系例外,会多生成一张关系表),每个属性也是这个表中的字段。属性名就是字段名,它的类型(例如CharField)相当于数据库的字段类型(例如varchar)。大家可以留意下其它的类型都和数据库里的什么字段对应。

           <3>  模型之间的三种关系:一对一,一对多,多对多。

                 一对一:实质就是在主外键(author_id就是foreign key)的关系基础上,给外键加了一个UNIQUE=True的属性;

                 一对多:就是主外键关系;(foreign key)

                 多对多:(ManyToManyField) 自动创建第三张表(当然我们也可以自己创建第三张表:两个foreign key)

    简单的增删改查

    查询数据时需要注意查询结果为单个对象还是数据对象的列表

    复制代码
    # 增
    models.Tb1.objects.create(c1='xx', c2='oo')   # 增加一条数据,可以接受字典类型数据 **kwargs
    obj = models.Tb1(c1='xx', c2='oo')
    obj.save()
    
    
    # 查
    models.Tb1.objects.get(id=123)  # 获取单条数据,不存在则报错(不建议)
    models.Tb1.objects.all()  # 获取全部
    models.Tb1.objects.filter(name='seven')  # 获取指定条件的数据
    models.Tb1.objects.exclude(name='seven')  # 去除指定条件的数据
    
    
    # 删
    # models.Tb1.objects.filter(name='seven').delete()  # 删除指定条件的数据
    
    
    # 改
    models.Tb1.objects.filter(name='seven').update(gender='0')   # 将指定条件的数据更新,均支持 **kwargs
    obj = models.Tb1.objects.get(id=1)
    obj.c1 = '111'
    obj.save()   # 修改单条数据
    复制代码

    ORM之增(create,save)

    复制代码
    from app01.models import *
    
        #create方式一:   Author.objects.create(name='Alvin')
    
        #create方式二:   Author.objects.create(**{"name":"alex"})
    
        #save方式一:     author=Author(name="alvin")
                                author.save()
    
        #save方式二:     author=Author()
                                author.name="alvin"
                                author.save()
    复制代码

    重点来了------->那么如何创建存在一对多或多对多关系的一本书的信息呢?(如何处理外键关系的字段如一对多的publisher和多对多的authors)

    复制代码
    #一对多(ForeignKey):
    
        #方式一: 由于绑定一对多的字段,比如publish,存到数据库中的字段名叫publish_id,所以我们可以直接给这个
        #       字段设定对应值:
               Book.objects.create(title='php',
                                   publisher_id=2,   #这里的2是指为该book对象绑定了Publisher表中id=2的行对象
                                   publication_date='2017-7-7',
                                   price=99)
    
    
        #方式二:
        #       <1> 先获取要绑定的Publisher对象:
            pub_obj=Publisher(name='河大出版社',address='保定',city='保定',
                    state_province='河北',country='China',website='http://www.hbu.com')
        OR  pub_obj=Publisher.objects.get(id=1)
    
        #       <2>将 publisher_id=2 改为  publisher=pub_obj
    
    #多对多(ManyToManyField()):
    
        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])
        #-------------------
        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)
        book.authors.remove(1)
        authors.book_set.add(1)
        authors.book_set.remove(1)
    
    #注意: 如果第三张表是通过models.ManyToManyField()自动创建的,那么绑定关系只有上面一种方式
    #     如果第三张表是自己创建的:
         class Book2Author(models.Model):
                author=models.ForeignKey("Author")
                Book=  models.ForeignKey("Book")
    #     那么就还有一种方式:
                author_obj=models.Author.objects.filter(id=2)[0]
                book_obj  =models.Book.objects.filter(id=3)[0]
    
                s=models.Book2Author.objects.create(author_id=1,Book_id=2)
                s.save()
                s=models.Book2Author(author=author_obj,Book_id=1)
                s.save()
    复制代码

    ORM之删(delete)

    >>> Book.objects.filter(id=1).delete()
    (3, {'app01.Book_authors': 2, 'app01.Book': 1})

    我们表面上删除了一条信息,实际却删除了三条,因为我们删除的这本书在Book_authors表中有两条相关信息,这种删除方式就是django默认的级联删除

    ORM之改(update和save)

    实例:

    注意:

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

    <2>在“插入和更新数据”小节中,我们有提到模型的save()方法,这个方法会更新一行里的所有列。 而某些情况下,我们只需要更新行里的某几列

    复制代码
    #---------------- update方法直接设定对应属性----------------
        models.Book.objects.filter(id=3).update(title="PHP")
        ##sql:
        ##UPDATE "app01_book" SET "title" = 'PHP' WHERE "app01_book"."id" = 3; args=('PHP', 3)
    
    
    #--------------- save方法会将所有属性重新设定一遍,效率低-----------
        obj=models.Book.objects.filter(id=3)[0]
        obj.title="Python"
        obj.save()
    # SELECT "app01_book"."id", "app01_book"."title", "app01_book"."price", 
    # "app01_book"."color", "app01_book"."page_num", 
    # "app01_book"."publisher_id" FROM "app01_book" WHERE "app01_book"."id" = 3 LIMIT 1; 
    # 
    # UPDATE "app01_book" SET "title" = 'Python', "price" = 3333, "color" = 'red', "page_num" = 556,
    # "publisher_id" = 1 WHERE "app01_book"."id" = 3; 
    复制代码

    此外,update()方法对于任何结果集(QuerySet)均有效,这意味着你可以同时更新多条记录update()方法会返回一个整型数值,表示受影响的记录条数。

    注意,这里因为update返回的是一个整形,所以没法用query属性;对于每次创建一个对象,想显示对应的raw sql,需要在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',
            },
        }
    }
    复制代码

    Django项目完整版LOGGING配置:

    复制代码
    LOGGING = {
        'version': 1,
        'disable_existing_loggers': False,
        'formatters': {
            'standard': {
                'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]'
                          '[%(levelname)s][%(message)s]'
            },
            'simple': {
                'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'
            },
            'collect': {
                'format': '%(message)s'
            }
        },
        'filters': {
            'require_debug_true': {
                '()': 'django.utils.log.RequireDebugTrue',
            },
        },
        'handlers': {
            'console': {
                'level': 'DEBUG',
                'filters': ['require_debug_true'],  # 只有在Django debug为True时才在屏幕打印日志
                'class': 'logging.StreamHandler',
                'formatter': 'simple'
            },
            'default': {
                'level': 'INFO',
                'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
                'filename': os.path.join(BASE_LOG_DIR, "xxx_info.log"),  # 日志文件
                'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
                'backupCount': 3,
                'formatter': 'standard',
                'encoding': 'utf-8',
            },
            'error': {
                'level': 'ERROR',
                'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
                'filename': os.path.join(BASE_LOG_DIR, "xxx_err.log"),  # 日志文件
                'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
                'backupCount': 5,
                'formatter': 'standard',
                'encoding': 'utf-8',
            },
            'collect': {
                'level': 'INFO',
                'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自动切
                'filename': os.path.join(BASE_LOG_DIR, "xxx_collect.log"),
                'maxBytes': 1024 * 1024 * 50,  # 日志大小 50M
                'backupCount': 5,
                'formatter': 'collect',
                'encoding': "utf-8"
            }
        },
        'loggers': {
           # 默认的logger应用如下配置
            '': {
                'handlers': ['default', 'console', 'error'],  # 上线之后可以把'console'移除
                'level': 'DEBUG',
                'propagate': True,
            },
            # 名为 'collect'的logger还单独处理
            'collect': {
                'handlers': ['console', 'collect'],
                'level': 'INFO',
            }
        },
    }
    复制代码

    ORM之查(filter,value)

    查询API:

    复制代码
    # 查询相关API:
    
    #  <1>filter(**kwargs):      它包含了与所给筛选条件相匹配的对象
    
    #  <2>all():                 查询所有结果
    
    #  <3>get(**kwargs):         返回与所给筛选条件相匹配的对象,返回结果有且只有一个,如果符合筛选条件的对象超过一个或者没有都会抛出错误。
    
    #-----------下面的方法都是对查询的结果再进行处理:比如 objects.filter.values()--------
    
    #  <4>values(*field):        返回一个ValueQuerySet——一个特殊的QuerySet,运行后得到的并不是一系列 model的实例化对象,而是一个可迭代的字典序列
                                         
    #  <5>exclude(**kwargs):     它包含了与所给筛选条件不匹配的对象
    
    #  <6>order_by(*field):      对查询结果排序
    
    #  <7>reverse():             对查询结果反向排序
    
    #  <8>distinct():            从返回结果中剔除重复纪录
    
    #  <9>values_list(*field):   它与values()非常相似,它返回的是一个元组序列,values返回的是一个字典序列
    
    #  <10>count():              返回数据库中匹配查询(QuerySet)的对象数量。
    
    #  <11>first():               返回第一条记录
    
    #  <12>last():                返回最后一条记录
    
    #  <13>exists():             如果QuerySet包含数据,就返回True,否则返回False
    复制代码

    使用示例

    复制代码
    models.Class.objects.values("id")
    <QuerySet [{'id': 1}, {'id': 3}]>
    (0.001) SELECT `app01_class`.`id` FROM `app01_class` LIMIT 21; args=()
    models.Class.objects.values("cname")
    <QuerySet [{'cname': '全栈8期'}, {'cname': '全栈9期'}]>
    (0.000) SELECT `app01_class`.`cname` FROM `app01_class` LIMIT 21; args=()
    models.Class.objects.order_by("-id")
    <QuerySet [<Class: 全栈9期>, <Class: 全栈8期>]>
    (0.001) SELECT `app01_class`.`id`, `app01_class`.`cname`, `app01_class`.`first_day` FROM `app01_class` ORDER BY `app01_class`.`id` DESC LIMIT 21; args=()
    models.Class.objects.order_by("id")
    <QuerySet [<Class: 全栈8期>, <Class: 全栈9期>]>
    (0.000) SELECT `app01_class`.`id`, `app01_class`.`cname`, `app01_class`.`first_day` FROM `app01_class` ORDER BY `app01_class`.`id` ASC LIMIT 21; args=()
    models.Class.objects.values_list("id","cname")
    (0.001) SELECT `app01_class`.`id`, `app01_class`.`cname` FROM `app01_class` LIMIT 21; args=()
    <QuerySet [(1, '全栈8期'), (3, '全栈9期')]>
    复制代码

    注意:一定区分Object与QuerySet的区别 !!!

    QuerySet有update方法而Object默认没有

    总结:

    返回的是QuerySet对象:
    1. all()
    2. filter()
    3. exclude()
    4. valus()
    5. values_list()
    6. order_by()
    7. reverse()
    8. distinct()
    返回数字的:
    1. count()
    返回布尔值的:
    1. exists()
    返回具体的数据对象的:
    1. get()
    2. first()
    3. last()

    单表查询之神奇的双下划线

    复制代码
    models.Tb1.objects.filter(id__lt=10, id__gt=1)   # 获取id大于1 且 小于10的值
     
    models.Tb1.objects.filter(id__in=[11, 22, 33])   # 获取id等于11、22、33的数据
    models.Tb1.objects.exclude(id__in=[11, 22, 33])  # not in
     
    models.Tb1.objects.filter(name__contains="ven")  # 获取name字段包含"ven"的
    models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
     
    models.Tb1.objects.filter(id__range=[1, 3])      # id范围是1到3的,等价于SQL的bettwen and
     
    类似的还有:startswith,istartswith, endswith, iendswith 
    
    date字段还可以:
    models.Class.objects.filter(first_day__year=2017)
  • 相关阅读:
    springboot的build.gradle增加阿里仓库地址以及eclipse增加lombok
    mqttfx无法选择证书
    EMQX源码编译过程
    新环境chart包helmlint校验
    安装rebar3
    Ubuntu16.04下,erlang安装和rabbitmq安装步骤
    UTF-8,GBK,ANSI之间的关系和区别
    write命令
    bunzip2命令
    bzip2命令
  • 原文地址:https://www.cnblogs.com/QQ279366/p/8331360.html
Copyright © 2011-2022 走看看