Django学习笔记三

MySQL相关介绍

MySQL数据库

在网站开发中,数据库是的重要组成部分。只有提供数据为患,数据才能够动态的展示,而不是在网页中显示一个静态的页面。数据库有很多,比如有SQL Server、Oracle、PostgreSQL以及MySQL等。MySQL由于价格实惠、简单易用、不受平台限制、灵活度高等特性,目前已经取得绝大多数的市场份额

MySQL安装说明

安装说明

navicat操作数据库软件

官网地址

MySQL驱动程序安装:

我们使用Django来操作MySQL,实际上底层还是通过Python来操作的。因为我们想要用Django来操作Mysql,首先还是需要安装一个驱动程序。在Python3中,驱动程序有多种选择。比如有pymysql以及mysqlclient等。这里我们就使用mysqlclient来操作。mysqlclient安装非常简单。只需要通过pip install mysqlclient即可安装
常见MySQL驱动介绍:

  1. MySQL-python: 也就是MySQLdb。是对c语言操作MySQL数据库的一个简单封闭。遵循了Python DB API v2。但是只支持Python2,目前还不支持Python3
  2. mysqlclient: 是MySQL-python的另一个分支。支持Python3并且修改了一此bug
  3. pymysql: 纯Python实现的一个驱动。因为是纯Python编写的,因此执行效率不如MySQL-python。并且也纯Python编写的,因为可以和Python代码无缝衔接
  4. MySQL Connector/Python: MySQL官方推出的使用纯Python连接MySQL的驱动。因为是纯Python开始,效率不高

数据库操作

Django配置连接数据库:

在操作数据库之前,首先要先连接数据库。这里我们以配置MySQL为例来讲解。Django连接数据库,不需要单独的创建一个连接对象。中需要在settings.py文件中做好数据库相关的配置就可以了

DATABASES = {
    'default': {
        # 数据库引擎(sqlite3/mysql/oracle等)
        'ENGINE': 'django.db.backends.mysql',
        # 数据库的名字
        'NAME': 'Django_db1',
        # 连接数据库的用户名
        'USER': 'root',
        # 密码
        'PASSWORD': 'root',
        # 服务器地址
        'HOST': '192.168.0.100',
        # 服务器端口
        'PORT': '3306'
    }
}

在Django中操作数据库

在Django中操作数据库有两种方式。第一种方式就是使用原生sql语句操作,第二 种就是使用ORM模型来操作
在Django中使用原生sql语句操作其实就是python db api的接口来操作。如果你的mysql驱动使用的是pymysql,那么就使用pymysql来操作,只不过Django将数据库连接的这一部分封闭好了,我们只要在settings.py中配置好了数据库连接信息后直接使用Django封装好的接口就可以操作了

from django.shortcuts import render
from django.db import connection

def index(request):
    cursor = connection.cursor()
    #cursor.execute("insert into book(id, name, author) values (null, '三国演义', '罗贯中')")
    cursor.execute("select * from book")
    res = cursor.fetchall()
    for var in res:
        print(var)
    return render(request, "index.html")

以上的execute以及fetchall方法都是Python DB API规范中定义好的。任何使用Python来操作MySQL的驱动程序都应该遵循这个规范。所以不管是使用pymysql或者mysqldb,他们的接口都是一样的。更多规范
https://www.python.org/dev/peps/pep-0249/

Python DB API下规范下cursor对象常用接口

  1. description: 如果cursor执行了查询的sql代码。那么读取cursor.description属性的时候,将返回一个列表,这个列表中装的是元组,元组中装的分别是(name,type_code,display_size,internal_size,precision,scale,nullok),其中name代码的是查找出来的数据的字段名称,其它参数暂时用处不大
  2. rowcount: 代表的是在执行了sql语句后受影响的行数
  3. close: 关闭游标。关闭游标以后就再也不能使用了,否则会抛出异常
  4. excute(sql[,parameters]): 执行某个sql语句。如果执行sql语句的时候还需要传递参数,那么可以传给parameters参数
cursor.execute("select * from article where id=%s"%(1,))
  1. fetchon: 在执行了查询操作以后,获取第一条数据
  2. fetchmany(size): 在执行查询操作以后,获取多条数据。具体是多少条要看的size参数。如果不传size参数,那么默认是获取第一条数据
  3. fetchall: 获取所有满足sql语句的数据

ORM模型

ORM模型介绍

随着项目越来越大,采用写原生SQL的方式在代码中会出现大量的SQL语句,那么问题出现了:

  1. SQL语句重复利用率不高,越复杂的SQL语句条件越多,代码越长。会出现很多相近的SQL语句
  2. 很多SQL语句是在业务逻辑中拼出来的,如果有数据库需要更改,就要去修改这些逻辑,这会很容易漏掉对某些SQL语句的修改
  3. 写SQL时容易忽略web安全问题,给未来造成隐患

ORM,全称Object Relational Mapping,中文叫做对象关系映射,通过ORM我们可以通过类的方式操作数据库,而不是再写碑的SQL语句。通过把表映射成类,把行作实例,把字段作为属性,ORM在执行对象操作的时候最终还是会把对应的操作转换为数据库原生语句。使用ORM有许多优点:

  1. 易用性:使用ORM做数据库的开发可以有效的减少重复SQL语句的概率,写出的模型也更加直观、清晰。
  2. 性能小:ORM转换成底层数据库操作指令确实会有一些开销。但从实际的情况来看, 这种性能损耗很少(不足5%),只要不对性能有严苛的要求,综合考虑开发效率、代码的阅读性,带来的好处要远远大于性能损耗,而且项目越大作用越明显。
  3. 设计灵活:可以轻松的写出的查询
  4. 可移植性:Django封装了底层的数据库实现,支持多个关系数据库引擎,包括流行的MySQL、PostgreSQL和SQLite。可以非常轻松的切换数据库

创建ORM模型

ORM模型一般都是就先在app的models.py文件中。每个app都可以拥有自己的模型。并且如果这个模型想要映射到数据库中,那么这个app必须要放在settings.py的INSTALLED_APP中进行安装。以下写一个简单的书籍ORM模型

class Book(models.Model):

    # 1. id:int类型,是自增长的
    id = models.AutoField(primary_key=True)
    # 2. name:varchar(100),图书名字
    name = models.CharField(max_length=100, null=False)
    # 3. author:varchar(100),图书的作者
    author = models.CharField(max_length=100, null=False)
    # 4. price:float,图书价格
    price = models.FloatField(null=False, default=0)

以上便定义了一个模型。这个模型继承自django.db.models.Model,如果这个模型想要映射到数据库中,就必须继承自这个类。这个模型以后映射到数据库中,表名是模型名称的小写形式,为book。在这个表中,有四个字段,第一个id,这个字段保存的是书的ID,是int类型,自动增长。第二个为name,这个字段保存的是书的名称,是varchar类型,长度不能超过100个字符,并且不能为空。第三个字段是作者名字类型,同样也是varchar类型,长度不能超过100个字符,并且不能为空。第四个是这本书的价格,是浮点类型
如果一个模型没有定义主键,那么将会自动生成一个自动增长的int类型的主键,并且这个主键的名字叫做id。

映射模型到数据库中

将ORM模型映射到数据库中,总结起来就是以下几步:

  1. 在settings.py中,配置好DATABASES,做好数据库相关的配置
  2. 在app中的models.py中定义好模型,这个模型必须继承自django.db.models。
  3. 将这个app添加到settings.py的INSTALLED_APP中。
  4. 在命令行终端,进入到项目所在的路径,然后执行命令 python manage.py makemigrations来生成迁移脚本文件
(django-env) D:\STUDY\Django项目\orm_intro_demo>python manage.py makemigrations
  1. 同样在命令行中,执行命令python manage.py migrate来将迁移脚本文件映射到数据库中。
(django-env) D:\STUDY\Django项目\orm_intro_demo>python manage.py migrate

ORM对数据库的基本操作

  1. 添加数据
    只要使用ORM模型创建一个对象,然后调用这个ORM模型的save方法就可以保存了
	book = Book(name='西游记', author='吴承恩', price=220)
    book.save()
  1. 查找数据
    所有的查找工作都是使用模型上的Object属性来完成的。当然也可以自定义查询对象。
  • 根据主键进行查找:可以使用 object.get 方法。然后传递 pk=xx 的方式进行查找
	book = Book.objects.get(pk=1)
  • 根据其他字段进行查找:可以使用 object.get方法进行查找
	book = Book.objects.filter(name='三国').first()

使用 filler 方法返回来的是一个 ** QuerySet**对象,这个对象类似于列表。我们可以使用这个对象的 first 方法来获取第一个值
3. 删除数据
首先查找到对应的数据模型。然后再执行这个模型的 delete 方法即可删除

	book = Book.objects.get(pk=1)
    book.delete()
  1. 修改数据
    首先查找到对应的数据模型。然后修改这个模型上的属性的值。再执行 save 方法即可修改完成
    book = Book.objects.get(pk=2)
    book.price = 500
    book.save()

模型常用属性

类型说明
AutoField一个自动增加的整数类型字段。通常你不需要自己编写它,Django会自动帮你添加字段:id = models.AutoField(primary_key=True),这是一个自增字段,从1开始计数。如果你非要自己设置主键,那么请务必将字段设置为primary_key=True。Django在一个模型中只允许有一个自增字段,并且该字段必须为主键!
BigAutoField64位整数类型自增字段,数字范围更大,从1到9223372036854775807
BooleanField布尔值类型。默认值是None。在HTML表单中体现为CheckboxInput标签。如果要接收null值,请使用NullBooleanField。
CharField字符串类型。必须接收一个max_length参数,表示字符串长度不能超过该值。默认的表单标签是input text。最常用的filed,没有之一!
TextField大量文本内容,在HTML中表现为Textarea标签,最常用的字段类型之一!如果你为它设置一个max_length参数,那么在前端页面中会受到输入字符数量限制,然而在模型和数据库层面却不受影响。只有CharField才能同时作用于两者。
DateFieldclass DateField(auto_now=False, auto_now_add=False, **options)日期类型。一个Python中的datetime.date的实例。在HTML中表现为TextInput标签。在admin后台中,Django会帮你自动添加一个JS的日历表和一个“Today”快捷方式,以及附加的日期合法性验证。两个重要参数:(参数互斥,不能共存) auto_now:每当对象被保存时将字段设为当前日期,常用于保存最后修改时间。auto_now_add:每当对象被创建时,设为当前日期,常用于保存创建日期(注意,它是不可修改的)。设置上面两个参数就相当于给field添加了editable=False和blank=True属性。如果想具有修改属性,请用default参数。例子:pub_time = models.DateField(auto_now_add=True),自动添加发布时间。
EmailField邮箱类型,默认max_length最大长度254位。使用这个字段的好处是,可以使用DJango内置的EmailValidator进行邮箱地址合法性验证。
FileFieldclass FileField(upload_to=None, max_length=100, **options)上传文件类型
URLField一个用于保存URL地址的字符串类型,默认最大长度200。
UUIDField用于保存通用唯一识别码(Universally Unique Identifier)的字段。使用Python的UUID类。在PostgreSQL数据库中保存为uuid类型,其它数据库中为char(32)。这个字段是自增主键的最佳替代品
ImageField用于保存图像文件的字段。其基本用法和特性与FileField一样,只不过多了两个属性height和width。默认情况下,该字段在HTML中表现为一个ClearableFileInput标签。在数据库内,我们实际保存的是一个字符串类型,默认最大长度100,可以通过max_length参数自定义。真实的图片是保存在服务器的文件系统内的

外键和表关系

  1. 外键
    在MySQL中,表有两种引擎,一种是InnoDB,另上一种是myisam。如果使用的是InnoDB引擎,是支持外键约束的。外键的存在使得ORM框架在处理表关系的时候异常的强大。因此这里我们首先来介绍下外键在Django中的使用
    类定义为class ForeignKey(to, on_delete, **options)。第一个参数是引用的是哪个类型,第二个参数是在使用外键引用的模型数据被删除了,这个字段该如何处理,比如有CASCADE、SET_NULL等。这里以一个实际案例来说明。比如有一个User和一个Article两个模型。一个User可以发表多篇文章,一个Article只能一个Author,并且通过外键进行引用。那么相关的示例如下:
class User(models.Model):
    username = models.CharField(max_length=20)
    password = models.CharField(max_length=100)

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    
    author = models.ForeignKey("User", on_delete=models.CASCADE)

以上使用ForeignKey来定义模型之间的关系。即在article的实例中可以通过author属性来操作对应的User模型,这样使用起来非常方便

	category = Category(name='最新文章')
	category.save()
	article = Article(title='abc', content='111')
	article.category = category
	article.save()

	# 修改值
	article.category.name = '李四'
	article.save()

为什么使用了 ForeignKey后,就能通过category访问到对应的user对象呢。因为在底层,Django为Article表添加了一个“属性名_id”的字段(比如category的字段名称是category_id),这个字段是一个外键,记录着对应的作者的主键。以后通过artcle.category访问的时候,实际上是先通过category_id找到对应的数据,然后再提取user表中的这条数据,形成一个模型
如果想要引用另外一个app的模型,那么应该在传递to参数的时候,使用app.model_name进行指定。以上例为例,如果user和Article不是同一个app中,那么引用的时候的救命代码如下:

# user模型在user这个app中
class User(models.Model):
    username = models.CharField(max_length=20)
    password = models.CharField(max_length=100)
# Article模型在Article这个app中
class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    
    author = models.ForeignKey("user.User", on_delete=models.CASCADE)

如果模型的外键引用的是本身自己这个模型,那么to参数可以为’self’,或者这个模型的名字。在论坛开发中,一般评论都可以以进行二级评论,即可以针对另外一个评论进行评论,那么在定义模型的时候就需要使用外键来引用自身

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)
	
  1. 表关系
    表之间的关系都是通过外键来进行关联的。而表之间的关系,无非就是三种联系:一对一、一对多(多对一)、多对多等。以下将讨论一下三 种关系的应用场景及实现方式
  • 一对多
  1. 应用场景:比如文章和作者之间的关系。一个文章只能 由一个作者编写,但一个作者可以写多篇文章。文章和作者之间的关系就是典型的多对一的关系
  2. 实现方式:一对多或者多对一,都是通过 ForeignKey来实现的。
class User(models.Model):
    username = models.CharField(max_length=20)
    password = models.CharField(max_length=100)

class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    
    author = models.ForeignKey("User", on_delete=models.CASCADE)

那么以后在线Article对象指定author,就可以使用以下代码来完成

article = Article(title='abc', content='123')
author = User(username='zhiliao', password='11111')
# 要先保存到数据库中
author.save()
article.author = author
article.save()

并且以后如果想要获取某个用户所有的文章,可以通过article_set来实现

user = User.objects.first()
articles = user.article_set.all()
for article in articles:
	print(article)

并且如果想要将文章添加到某个分类中,可以使用以下的方式:

	article = Article(title='1111', 	content='222',price=200)
    article.author = FrontUser.objects.first()
    article.save()
    category = Category.objects.get(pk=2)
    category.articles.add(article, bulk=False)
  • 一对一
  1. 应用场景:比如一个用户表和一个用户信息表。在实际网站中,可能需要保存用户的许多信息,但是有些信息是不经常用的。如果把所有信息都存放到一张表中可能会影响查询效率,因此可以把用忘掉一些不常用的信息存放在另外一张表中我们叫做userExtension。但是用户表user和用户信息表UserExtension就是典型的一对一了
  2. 实现方式:Django为一对一提供了一个专门的Field叫做OneToOneField来实现一对一操作
class User(models.Model):
    username = models.CharField(max_length=20)
    password = models.CharField(max_length=100)

class Article(models.Model):
    birthday = models.DateTimeField(null=True)
    school = models.CharField(blank=True, max_length=50)
    
    user = models.OneToOneField("User", on_delete=models.CASCADE)

在UserExtension模型上增加了一个一对一的关系映射。其实底层是在UserExtension这个表上增加了一个user_id,来和user表进行关联,并且这个外键数据在表中必须是唯一的,来保证一对一

  • 多对多
  1. 应用场景:比如文章和标签的关系。一篇文章可以有多个标签,一个标签可以被多个文章所引用。因此标签和文章的关系是典型的多对多的关系
  2. 实现方式:Django为这种多对多的实现提供了专门的Field。叫做ManyToManyField。还是拿文章和标签为例进行讲解
class Article(models.Model):
    title = models.CharField(max_length=100)
    content = models.TextField()
    tags = models.ManyToManyField('Tag', related_name='articles')
    
class Tag(models.Model):
    name = models.CharField(max_length=50)

在数据库层面,实际上Django是为这种多对多的关系建立了一个中间表。这个中间表分别定义了两个外键,引用Updated a day age到article和tag两张表的主键

查询操作

查找数据库操作中一个非常重要的技术。查询一般就是使用filter、exclude以及get三个方法来实现。我们可以在调用这些方法的时候传递不同的参数来实现查询需求。在ORM层面,这些查询条件都是field+_+condition的庐江来使用的

  • 查询条件

exact

精确的提供条件。如果提供的是一个None,那么SQL层面就是被解释为NULL。

article = Article.objects.get(id__exact=14)
article = Article.objects.get(id__exact=None)

查找在翻译为SQL语句如下

select ... from article where id=14
select ... from article where id is NULL

在底层会被翻译成 ‘=’

iexact
使用like进行查找

article = Article.objects.filter(title__exact="三字经")

那么以上的查询就等价于以下的SQL语句

select ... from article where like "三字经";

在底层会被翻译成 ‘like’
*因为field__exact=xxx其实等价于field=xxx,因此我们直接使用field=xxx就可以了,并且因为大部分情况 exact 和 iexact 又是等价的,因此我们以后直接使用 field=xxx 就可以了

contains
大小写敏感,判断某个字段是否包含某个数据。

article = Article.objects.get(title__contains='hello')

在翻译成SQL语句如下:

select ... from article where title like binary '%hello%';

要注意的是,在使用contains的时候,翻译成的SQL语句左右两边是有百分号的,意味着使用的是模糊查询。而exact翻译成SQL语句左右两边是没有百分号的,意味着使用的是精确的查询

icontains
大小写不敏感的匹配查询。

article = Article.objects.get(title__icontains='hello')

在翻译成SQL语句如下:

select ... from article where title like binary '%hello%';

聚合函数

如果你用原生SQL,则可以使用聚合函数来提取数据。比如提取某个商品销售的数量,那么可以使用 Count,如果想要知道商品销售平均价格,那么可以使用avg。
聚合函数是通过aggregate方法来实现的。在讲解这些聚合函数的用法的时候,都是基于以下的模型对象来实现的

class Author(models.Model):
    # 作者模型
    name = models.CharField(max_length=100)
    age = models.IntegerField()
    email = models.EmailField()

    class Meta:
        db_table = 'author'


class Publisher(models.Model):
    # 出版社模型
    name = models.CharField(max_length=300)

    class Meta:
        db_table = 'publisher'


class Book(models.Model):
    # 图书模型
    name = models.CharField(max_length=300)
    pages = models.IntegerField()
    price = models.FloatField()
    rating = models.FloatField()
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)

    class Meta:
        db_table = 'book'


class BookOrder(models.Model):
    # 图书订单模型
    book = models.ForeignKey("Book", on_delete=models.CASCADE)
    price = models.FloatField()

    class Meta:
        db_table = 'book_order'
  1. avg: 求平均值。比如想要获取所有图书的价格平均值
from django.db.models import Avg
result = Book.objects.aggregate(Avg('price'))
print(result)

打印结果

{'price__avg': 97.25}

其中 price__avg 的结构是根据 field__avg 规则构成的。如果想要修改默认的名字,那么可以将 Avg 赋值给一个关键字参数。

from django.db.models import Avg
result = Book.objects.aggregate(my_avg=Avg('price'))
print(result)

打印结果

{'my_avg': 97.25}
  1. Count: 获取指定的对象的个数
from django.db.models import Count
result = Book.objects.aggregate(book_num=Count('id'))
print(result)

QuerySet API

我们通常做查询操作的时候,都是通过 模型名字.objects 的方法进行操作的。其实 模型名字.objects 是一个 django.db.models.manager.Manager 对象,而Manager 这个类是一个“空壳”的类,他本身是没有任何的属性和方法的。他的方法全部都是通过python动态添加的方法,从 QuerySet 类中拷贝过来的
在这里插入图片描述

  • 返回新的QuerySet的方法

在使用 QuerySet进行查找操作的时候,可以提供多种操作。比如过滤完后还要根据某个字段进行排序,那么这一系列的操作我们可以通过一个非常流畅的 链式调用 的方式进行。比如要从文章中获取标题为 123,并且提取后要将结果根据发布的时候进行排序,那么可以使用以下方式来完成

articles = Article.objects.filter(title='123').order_by('create_time')

可以看到 order_by 方法是直接在 filter 执行后调用的。这说明 filter 返回的对象是一个拥有 order_by 方法的对象。而这个对象正是一个新的 QuerySet 对象。因此可以使用 order_by 方法
那么以下将的那些会返回新的 QuerySet 对象的访求

  1. filter: 将满足条件的数据提取出来,返回一个新的QuerySet。
  2. exclude: 排除满足条件的数据,返回一个新的QuerySet。
articles = Article.objects.exclude(title__contains='hello')
  1. annotate: 给 QuerySet 中的每一个对象都添加一个使用查询表达式(聚合函数、F表达式、Q表达式、Func表达式等)的新字段
articles = Article.objects.annotate(author_name=F('author__name'))

以上代码将在每个对象中都添加一个 author__name 的字段,用来显示这个文章的作者的年龄


未写完,因为有很多自己也未理解…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值