Django之操作数据库

1:数据库:

2:MySQL驱动程序的安装

3:Django配置连接数据库

4:在Django中操作数据库

  (1)原生SQL语句操作

  (2)ORM模型操作

5:ORM模型的介绍

6:创建一个ORM模型并映射模型到数据库中

7:模型常用的属性

  (1)常用的Field字段

  (2)Field字段常用的参数

  (3)模型中Meta的配置

8:外键

9:表关系

  (1)一对一

  (2)一对多(多对一)

  (3)多对多

10:模型的操作

  (1)数据的增删改查

  (2)聚合函数

  (3)aggregate和annotate的区别

  (4)F表达式和Q表达式

11:查询集(QuerySet

12:ORM模型迁移

-------------------------------------------------------------------------------------------------------------------------------

1数据库:简而言之就是存放数据的一个库,用户可以对数据进行增删改查;是网站重要的一个组成部分,只有提供了数据库,网站才能动态的展示。常用的数据库有MySQL,Oracle等。

2MySQL驱动程序的安装:我们使用Django来操作mysql,实际上底层使用的还是python;因此如果我们想要操作数据库首先要安装一个驱动;python3中有多个驱动可供选择如下;

  (1)MySql-python :也就是MySQLdb 。是对C语言操作mysql的一个简单封装;只支持python2,不支持python3,运行速度最快。

  (2)mysqlclient:是MySql-python的另一个分支,支持python3。

  (3)pymsql:纯python实现的一个驱动,效率不高,可以和python无缝连接。

  (4)mysql-connector-python:mysql官方推出的,纯python开发的,效率不高

本文使用的是mysqlclient:   安装方式: pip install mysqlclient

3:Django配置连接数据库:在操作数据库之前需要Django先连接上数据库,不需要单独创建一个连接对象,只需要在setting中做好相关的配置即可。

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',   #数据库引擎(mysql还是Oracle)
        'NAME': "my01",  #数据库的名字
        "USER":"root",    #数据库的用户名
        "PASSWORD":"qwe123",    #数据库密码
        "HOST":"localhost",   #数据库的主机地址
        "PORT":"3306"      #数据库的端口号
    }
}

4:在Django中操作数据库:Django操作数据库有两种方式。第一种是原生的SQL语句操作,另一种是ORM模型操作

  (1)原生SQL语句操作

    缺点:(1)重复利用率不高(2)不易修改(3)安全问题(SQL注入)

  (2)ORM模型操作

5:ORM模型的介绍

  (1)ORM,全称Object Relational Mapping,中文叫做对象-关系映射。

  (2)通过ORM我们可以通过类的方式来操作数据库,通过把类映射成表,把实例映射为行,把属性映射为字段;ORM在执行对象操作的时候最终还是会把对应的操作转换为SQL语句。

6:创建一个ORM模型并映射模型到数据库中

  第一步:创建一个User模型类(在数据库中的表现:类--表名(app的名字_类名(小写)),属性--字段,实例-行)

class User(models.Model):
    username = models.CharField(max_length=10)
    telephone = models.CharField(max_length=11)
    email = models.EmailField()
    sex = models.BooleanField()

  第二步:执行makemigrations [app_name]       #后面可以跟APP的名字,生成迁移脚本文件

  第三步:执行migrate [app_name]     #映射到数据库中

7:模型常用的属性

  (1)常用的Field字段

    AutoField:int类型,自动增长

    BigAutoField:类似于AutoField,范围1-9223372036854775807

    BooleanField:tinyint类型,True/False 

    CharField:  varchar类型,使用时必须指定最大长度,即max_length参数必须传递

    DateField: 日期类型;两个参数auto_now:每次保存的时候都是用当前的时间,比如记录一个记录修改的日期;auto_now_add:在数据第一次创建的时候用当前的时间,比如记录第一次入库的时间。

    DateTimeField:日期时间类型;不仅可以存储日期还可以存储时间。两个参数(auto_now和auto_now_add)

    TimeField:时间类型;

    EmailField:varchar类型;最大长度254

    FileField:用来存储文件的

    FloatField:浮点类型

    ImageField:存储图片

    IntegerField:整型,-2147483648 - 2147483647

    BigIntegerField:大整形,

    PositiveIntegerField:正整型;0-2147483647

    SmallIntegerField:小整型,-32768 - 32767

    TextField:大量的文本类型,longtext

    UUIDField:存储uuid格式的字符串,是一个32位的全球唯一的字符串,一般做主键

    URLField:存储url格式的字符串,max_length默认是200

  (2)Field字段常用的参数

    null:表示映射为表的时候是否可以为空,默认为False(不能为空);在使用字符串相关的Field时(CharField、TextField)一般保持默认的False,因为即便是Flase,Django也会创建一个空字符串 " "作为默认值传递进去,如果再设置为True的话,就会用null来存储空值,默认值就变为了两个(""和null)

    blank:表示验证的时候是否可以为空,默认是False。(和null的区别:null是一个数据库级别的,blank是表单验证级别的)

    db_column:表示字段在数据库中的名字

    default:默认值,可以是一个值或者函数,不支持lambda表达式,并且不支持列表、字典。集合等可变的数据结构

    primary_key:默认值false,是否为主键

    unique:是否唯一,一般手机号或者邮箱用

  (3)模型中Meta的配置

    对于一些模型级别的配置,我们可以在模型中定义一个类,叫做Meta,然后在这个类中添加一些类属性来控制模型。比如在映射到数据库中表的名字使用自己定义的。

from django.db import models

class User(models.Model):
    username = models.CharField(max_length=10)
    telephone = models.CharField(max_length=11)
    email = models.EmailField()
    sex = models.BooleanField(db_column="sex_a")
    create_time = models.DateTimeField(auto_now_add=True)
    
    class Meta:
        db_table = "User",      //表的名字
        ordering = ["create_time"]   //在查找数据的时候根据添加的时间排序

 8:外键

   如果一张表中非主键的某个字段指向了另一张表中的主键,那么这个字段就是该表的外键。

  外键的作用是:防止一张表过于复杂(比如一个需求是一个名字对应一张照片,如果保存在同一种表中数据可能有些乱,最好是分开两张表,一种保存名字,另一张保存图片,同时两张表又有一种制约,可以通过名字找到这张图片,或者通过图片找到这个名字)。

  在mysql中,表有两种引擎,一种是InnoDB,支持外键的使用;另一种是myisam,暂时不支持外键的使用。

  Django中的外键使用ForeignKey(to,on_delete,**options),第一个参数是引用的是哪个模型,第二个参数是使用外键引用的模型被删除的时候,这个字段该如何处理。

  注意:如果引用的模型在另一个APP中,则第一个参数需要添加app_name.模型名;如果引用的是模型本身自己可以用self或者模型自己的名字

from django.db import models
 
class User(models.Model):         //用户模型
    username = models.CharField(max_length=10)
    password = models.CharField(max_length=20)


class Article(models.Model):   //文章模型
    title = models.CharField(max_length=20)
    content = models.TextField()
    author = models.ForeignKey("User",on_delete=models.CASCADE)         //外键
    category = models.ForeignKey("login.Tags",on_delete=models.CASCADE)  //另一个app,login中model的Tags模型


class Comment(models.Model):
    content = models.TextField()
    origin_comment = models.ForeignKey("self",on_delete=models.CASCADE,null=True)   //外键是模型自己
    # origin_comment = models.ForeignKey("Comment",on_delete=models.CASCADE,null=True)

  外键删除操作:

  如果一个模型引用了外键,在对方那个模型被删除掉,该进行什么样的操作;通过on_delete来指定:

    CASCADE:级联操作,如果外键对应的那条数据被删除,那么这条数据也被删除。

    PROTECT:受保护,即只要这条数据引用了外键的那条数据,那么就不能删除外键的那条数据。

    SET_NULL:设置为空,如果外键的那条数据被删除,那么本条数据设置为空,前提是这个字段可以为空。

    SET_DEFAULT:设置为默认值,如果外键的那条数据被删除,那么本条数据设置默认值,前提是要指定一个默认值。

    SET():如果外键那条数据被删除,则调用set函数的值,作为外键的值

    DO_NOTHING:不采取任何行为,看数据库级别的约束。

 

9:表关系:表之间的关系都是通过外键进行关联的,三种关系,一对一,一对多(多对一),多对多

  (1)一对一

    应用场景:一个用户和一个用户信息表

    实现方式:通过OneToOneField来实现 

  (2)一对多(多对一)

    应用场景:比如作者和文章之间的关系,一个作者可以有多篇文章,但是一篇文章只能有一个作者

    实现方式:一对多或者多对一,都是通过ForeignKey来实现的    

  (3)多对多

    应用场景:比如文章和标签之间的关系;一篇文章可以有多个标签,一个标签可以被多篇文章引用

    实现方式:通过ManyToManyField来实现

  表之间的查询方式:related_name和related_query_name;我们依然使用上面的User模型和Article模型来举例:

  (1)如果我们想要通过文章获取该作者:

 article = Article.objects.first()   //获取这篇文章
 print(article.author.username)    //直接通过author获取作者

  (2)如果想要通过作者获取其下的所有文章方式有两种:

#方式一:Django默认每个主表都有一个默认的属性来获取子表的信息,这个属性就是子表模型的小写加上_set(),默认返回一个querydict对象,可以进一步操作 
author = User.objects.first()
 print(author.article_set.all())

#方式二:在默认中添加related_name,以后主表直接通过这个值来获取子表的信息,得到的也是一个querydict对象。
author = models.ForeignKey("User",on_delete=models.CASCADE,related_name="article")

#我们可以直接使用这个值
print(author.article.first().title)

  (3)relate_name=“+”,表示禁止使用反向查询

10:模型的操作:在ORM框架中,所有的模型操作其实都是映射到数据库中一条数据的操作,因此模型操作也就是数据库表中数据的操作。

  (1)数据的增删改查

      一:以上面表User为例:

        (1)增加数据:

User.objects.create(username="王三",password="123")

       (2)查数据:(all(),get(),filter())

  #查找所有的数据:返回的是一个queryset集合,没有满足条件返回空列表
    users = User.objects.all()
    for user in users:
        print(user.username)
    print(users)

    #查找单个的对象,返回一个对象,没有满足条件时报错
    user = User.objects.get(username="张帆")
    print(user.username)

    #数据过滤查找filter;查询的时候有时需要对数据进行过滤再查询,返回所有满足条件的QuerySet集合
    user = User.objects.filter(username="张帆",password="213123")
    print(user)

    #数据排序;查找数据的时候都是无序的,如果想对某个数据进行排序使用order_by,反向排序加个减号
    users = User.objects.order_by("username")

       (3)改数据:

 #修改数据:获取这条数据,对属性修改,然后保存
    user = User.objects.get(username="张帆")
    user.username = "李四"
    user.save()

       (4)删除数据

 #删除数据;获取数据,然后执行delete()函数
    user = User.objects.get(username="李四")
    user.delete()

      二:查询条件:查找的时候一般使用filter,get,exclude三个方法来实现,我们可以对其传递不同的参数,来实现不同的需求。格式(约束条件_查找条件)

        第一组:精确和like

          exact:使用精确 = 进行查找 ; User.objects.get(id_exact = 2),如果提供一个None值,sql层面被解释为null

          iexact:  使用like进行查找

        第二组:包含(title_contains = "hello")

          contains:大小写敏感

          icontains:大小写不敏感

          第三组:提供包含在容器中值,容器可以是列表,元组,任意一个可迭代的对象

          in:User.objects.get(id_in=[1,2,3])

          也可以传递一个queryset对象进去:获取标题中包含张三的文章

          username = User.objects.filter(username_contains = "张三")

          user = Article.objects.filter(title_in = username)

        第四组:(id__gt = 3)

          gt:大于

          gte:大于等于

          lt:小于

          lte:小于等于

        第五组:判断某个字段是否以某个值为开始(结束) (title_startwith = "hello")

          startwith:

          istartwith:

          endswith:

          iendswith:

        第六组:

          range:判断是否在某个区间;(create_time = range(start,end))

        第七组:根据时间查询

          date:年月日  (create_time_date=date(2018,3,23))

          year:年(create_time_year = 2108)

          month:月(同上)

          day:天(同上)

          wek_day:星期几(1表示星期天,7表示星期六)

          time:时分秒(create_time= datetime_time(2,2,2)),2点2分2秒

        第八组:是否为空

          isnull:(title_isnull = False) 不为空

        第九组:正则

          regex:大小写敏感:(title_regex=r"hello")

          iregex:大小写不敏感:(title_regex=r"hello")

     三:关联表查询 

        比如有两个模型,一个是User,一个是Article;想要获取文章标题中带有“中国”的用户

        user = User.objects.filter(article_title_contains = "hello")    

  (2)聚合函数:聚合函数是同aggregate方法来实现的。

      (1)Avg:求平均值

      (2)Count:获取指定对象的个数

      (3)Max和Min:获取最大值最小值

      (4)Sum:求和

假设有这样一个模型:

class BookOrder(models.Model):
    book_name = models.CharField(max_length=12)
    price = models.FloatField()

数据表为:

  # result = BookOrder.objects.aggregate(Avg("price"))    //所有书的平均价格,名字规则为field_avg组成
    result = BookOrder.objects.aggregate(book_price = Avg("price"))    //给这个价格指定一个名字
    print(result)
 
    result = BookOrder.objects.aggregate(book_num = Count("id"))   //求所有的书
    print(result)

    result = BookOrder.objects.aggregate(book_price_max = Max("price"))  //求最大值
    print(result)

    result = BookOrder.objects.aggregate(book_price_min=Min("price"))  //求最小值
    print(result)

    result = BookOrder.objects.aggregate(books_price = Sum("price"))  //求最和
    print(result)

  (3)aggregate和annotate的区别

 

  (4)F表达式和Q表达式

   F表达式是用来优化ORM模型操作数据库的,并不会立刻去数据库中获取数据,而是在生成SQL语句的时候,动态的获取传给F表达式的值

BookOrder.objects.update(price = F("price")+100)            //更新图书的价格

  Q表达式:一般查询条件比较多的时候使用,可以使用与或非,&,|,~

books = BookOrder.objects.filter(Q(id=3) | Q(book_name__contains=""))

11:查询集(QuerySet

 

  (1)查询集就是通过查询后得到的一个对象集合。

  Django中查询操作都是通过 模型名.objects的方式进行;这个objects是一个Manager对象,而这个Manager这个类是一个空壳的类,它本身没有任何的属性和方法;都是通过Python从QuerySet类中动态的添加过来的。

class Manager(BaseManager.from_queryset(QuerySet)):
    pass


class BaseManager:
    @classmethod
    def from_queryset(cls, queryset_class, class_name=None):
        if class_name is None:
            class_name = '%sFrom%s' % (cls.__name__, queryset_class.__name__)
        return type(class_name, (cls,), {
            '_queryset_class': queryset_class,
            **cls._get_queryset_methods(queryset_class),
        })

   
class QuerySet:
"""Represent a lazy database lookup for a set of objects."""

def __init__(self, model=None, query=None, using=None, hints=None):
self.model = model
self._db = using
self._hints = hints or {}
self.query = query or sql.Query(self.model)
self._result_cache = None
self._sticky_filter = False
self._for_write = False
self._prefetch_related_lookups = ()
self._prefetch_done = False
self._known_related_objects = {} # {rel_field: {pk: rel_obj}}
self._iterable_class = ModelIterable
self._fields = None


    ............(后面省略)
 

  (2)两大特性:

    惰性执行:创建查询集不会立刻执行,只到调用数据时才会去数据库中查询,调用数据包括:迭代,使用步长做切片操作,调用len函数,调用list函数,判断

    缓存:使用同一个查询集,第一次会发生数据库查询,然后django会把数据缓存起来,以后再使用的时候不会去数据库查询而是直接使用

  (3)常用的API:

    1.filter:将满足条件的数据提取出来,返回一个queryset对象

    2.exclude:排除满足条件的对象,返回一个queryset对象

    3.annotate:

    4.order_by:排序(倒序加减号),多个order_by链式使用时,会把前面的打乱,以后面的为准

    5.values:规定提取那些字段,返回的是一个字典类型。默认会把所有字段都提取出来

    6.values_list:同上,不过返回的是一个元组

    7.defer:过滤掉一些字段,返回的是一个模型

    8.only:只保留这个字段(和上面相反)

    9.all:返回queryset对象

    10.select_related:

    11.prefetch_related:

    12.get:只返回一条满足条件的数据,如果是多条或者没有都会报错

    13.create:创建一条数据,并保存

    14.get_or_create:如果数据库中存在这个数据就返回,不存在就创建;返回一个元组(第一个参数是这个对象,第二个参数是boolean类型)

    15.bulk_create:一次创建多个数据;bulk_create([book_name="111",book_name="222"])

    16.count:获取数据的个数,比len函数高效

    17.first和last:返回queryset中的第一条和最后一条

    18.aggregate:使用聚合函数

    19.exists:判断是否存在

    20.distinct:去掉重复的数据;

    21.update:更新

    22.delete:删除数据

    23.切片:Books.objects.all()[1:3];切片不是把数据都提取出来再做切片,而是在底层直接使用LIMIE和OFFSET

 12:ORM模型迁移

  (1)makemigrations:将模型生成迁移脚本

      app_label:后面跟一个或者多个app,只会针对这几个app生成迁移脚本,如果没有写就是针对所有都生成

      --name:给这个迁移脚本生成一个名字

      --empty:生成一个空的迁移脚本

  (2)migrate:将生成的迁移脚本映射到数据库中

      app_label:规定那几个app

      app_label migrationname;将某个app下指定名字的migration文件映射到数据库中

      --fake:可以将指定的迁移脚本文件名字添加到数据库中,但是并不会把迁移脚本文件转换为sql语句,修改数据库中的表

      --fake-initial:将第一次生成的迁移文件版本号记录在数据库中,但并不会执行迁移脚本

  (3)showmigrations:查看某个app的迁移文件;如果后面没有app将查看所有的

  (4)sqlmigrate:查看某个迁移文件映射到数据库中转换的SQL语句

posted @ 2019-01-20 16:37  漏斗倒过来  阅读(522)  评论(0编辑  收藏  举报