django-6

前言

在django中,视图是一个可调用的对象,能够接受用户的请求并返回响应。在视图中通过模型操作数据库,通过模板构造返回数据。

约定将视图放在名为views.py的文件里,这个文件放置在项目或者应用目录里。

django中视图有两种:

  • 函数视图
  • 类视图

一、视图

1、函数视图

最简单的视图,就是一个普通的python函数,它接受web请求并返回一个web响应

例如:

from django.http import HttpResponse

def index(request):
    return HttpResponse("我是首页面")

特点:

  1. 逻辑清晰好理解
  2. 复用性较差

2、类视图

类视图是以类的形式组织视图,类视图并不能替代基于函数的视图,与函数视图相比,类视图有如下优势:

  • 与特定的HTTP方法关联 ,能通过单独的方法替代条件分支的方式 处理不同的HTTP请求
  • 面向对象技术,可用于将代码分解为可重用的组件。

2.1 类视图的基本使用

本质上来说,基于类的视图允许你使用不同的实例方法响应不同 HTTP 请求方法,而不是在单个视图函数里使用有条件分支的代码。

因此在 函数视图 里处理 HTTP GET 的代码应该像下面这样:

from django.http import HttpResponse

def my_view(request):
    if request.method == 'GET':
        # <view logic>
        return HttpResponse('result')

而在基于类的视图里,会变成:

from django.http import HttpResponse
from django.views import View

class MyView(View):
    def get(self, request):
        # <view logic>
        return HttpResponse('result')

2.2 类视图的路由

Django的路由系统会使用请求和相关参数来调动一个函数而不是一个类。基于类的视图有一个as_view()类方法,这个方法会返回一个函数。

# 应用中的路由urls.py
from django.urls import path
from myapp.views import MyView

urlpatterns = [
    path('about/', MyView.as_view()),
]

二、ORM

对象关系映射(Object Relational Mapping,简称ORM)!简单的说就是用面向对象的方式,描述数据库,操作数据库,达到不用编写SQL语句就能对数据库进行增删改查。

django内置了一套ORM框架,它的映射关系是:

1685182342059-9e51faae-af21-45c5-9747-68e7cb56f841.png

然后通过对类,类属性,实例的各种操作,达到操作数据库的功能,底层是生成原生sql语句进行数据库操作

2.1安装数据库

我们的项目选择使用MariaDB,它是MySQL的一个分支,开源免费,越来越多的web项目开始使用它。直接安装数据库相对比较麻烦和难以维护,推荐使用docker安装数据库。安装docker的方案如下:

1. 方案一:

windows直接安装Docker Desktop,不推荐,个人感觉还是很影响系统的使用,且需要开启虚拟服务,会与某些软件冲突。

mac可以直接安装Docker Desktop。

2. 方案二:

windows系统,安装虚拟机,然后安装linux的虚拟机,再到虚拟机中安装docker环境。

虚拟机软件有,virtualbox(免费开源),vimware(收费),安装简单,使用稍复杂,资源占用大。

用过虚拟机的童靴可以选择此方案,从来没用过的可以忽略。

mac不建议。

3. 方案三:

买一台云服务器,在云服务器中安装docker。

阿里云,百度云,腾讯云,华为云,都有新用户优惠,几十块钱一年,推荐使用。

最后项目的部署也会使用云服务器。

阿里云新人优惠连接(opens new window)

注意:系统选择ubuntu或者centos。

docker命令:

docker run --name mariadb --restart=always -d -v mariadb:/var/lib/mysql -e MARIADB_ROOT_PASSWORD=pythonvip -p 4000:3306 -e MARIADB_DATABASE=easytest mariadb:latest

上面这条命令会根据mariadb数据库镜像mariadb:latest,创建一个名为mariadb的容器,并创建一个名为easytest的数据库,设置root账号的密码pythonvip,映射3306端口到宿主机4000端口。

2.2 django配置数据库

2.2.1 安装驱动

django官方支持一下数据库:

不同的数据库都需要对应的python数据库驱动程序。

我们接下来使用MariaDB数据库,它是MySQL的一个分支,在Django中它的配置和MySQL一致。

django推荐使用mysqlclient作为MySQLMariaDB的数据库驱动

windows环境

在网站https://www.lfd.uci.edu/~gohlke/pythonlibs/#mysqlclient(opens new window)下载与python版本对应的mysqlclient本地安装文件,再使用pip命令安装,例如:

pip install mysqlclient‑1.4.6‑cp38‑cp38‑win_amd64.whl  # py3.8 64位

mac环境

mac环境下依赖mysql客户端。

$ brew install mysql-client
$ echo 'export PATH="/usr/local/opt/mysql-client/bin:$PATH"' >> ~/.bash_profile
$ source .bash_profile
$ pip install mysqlclient

linux环境

linux环境下需要对应的依赖,根据环境不同依赖有所不同,下面的只是基本的步骤,不能保证在所有的环境上都有效。

Debian/Ubuntu

$ sudo apt-get install python3-dev default-libmysqlclient-dev build-essential
$ pip install mysqlclient

Red Hat /CentOS

sudo yum install python3-devel mysql-devel
pip install mysqlclient

2.2.2 连接配置

安装好数据库和数据库驱动后,还需要在settings.py配置模块中进行连接配置:

  1. 直接配置
# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',		# 对应数据库引擎
        'NAME': 'easytest',							# 数据库名
        'USER': 'root',								# 用户名
        'PASSWORD': '12345678',						# 密码
        'HOST': '127.0.0.1',						# 主机
        'PORT': '3306',								# 端口
    }
}
  1. 通过配置文件配置
# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'OPTIONS': {
            'read_default_file': '/path/to/my.cnf',		# 配置文件路径
        },
    }
}

# my.cnf
database = easytest				# 数据库名
user = root						# 用户名
password = 12345678				# 密码
host = 127.0.0.1				# 主机
port = 3306						# 端口

配置好后,运行python manage.py runserver如果可以正常运行说明数据库配置成功,项目成功连接数据库。

三、模型(model)

Django中模型准确且唯一的描述了数据。它包含储存的数据的重要字段和行为。一般来说,每一个模型都映射一张数据库表。

  • 每个模型都是一个 Python 的类,这些类继承django.db.models.Model
  • 模型类的每个属性都相当于一个数据库的字段
  • 利用这些,Django 提供了一个自动生成访问数据库的 API

在创建模型前,先为我们的crm系统设计一张student表,表结构如下:

  	  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NUll,
  `age` tinyint(1) DEFAULT NULL,
  `sex` tinyint(1) DEFAULT 1,
  `phone` varchar(20) DEFAULT NULL UNIQUE,
  `c_time` datetime(6) NOT NULL

然后根据表设计,在crm/models.py中编写如下模型类:

# models.py
class Student(models.Model):
    name = models.CharField('姓名', max_length=20, help_text='姓名')
    age = models.SmallIntegerField('年龄', null=True, blank=True, help_text='年龄')
    sex = models.SmallIntegerField('性别', default=1, help_text='性别')
    phone = models.CharField('手机号码', max_length=20, null=True, blank=True, unique=True, help_text='手机号码')
    c_time = models.DateTimeField('创建时间', auto_now_add=True)
    
    class Meta:
        db_table = 'tb_student'
        verbose_name = '学生信息'
        verbose_name_plural = verbose_name
        ordering = ['-c_time']
        
    def __str__(self):
		return self.name

上面的代码很简单,Student类就是一个模型,表示一张数据库表。类中有几个变量,每个变量表示数据库中的一个字段

3.1 字段类型

每个字段由一个字段类的实例表示。

例如:字符字段使用CharField,日期时间使用DateTimeField

每个字段实例的名称(例如,name,age)就是字段的名称。数据库将会使用它作为列名。

下面是常用字段类型和数据库字段类型的映射关系:

1685183404657-9c426627-9ab8-4909-9db5-97e3224aa70b.png

更多字段类型见官方文档

3.2 字段参数

每个字段在实例化时可以接收多个参数,用来提供不同的功能。常用的字段参数有:

  • verbose_name
    • 人类可读的字段详细名称,如果没有给定会使用字段的属性名自动创建。
  • primary_key
    • 主键设置,如果为True,则将该字段设置为模型的主键。如果编写模型是没有设置任何字段为主键,django会自动添加一个id字段作为主键。
  • unique
    • 唯一索引,如果为True,这个字段会在数据库层面创建唯一索引。
  • blank
    • 如果为True,该字段允许为空。默认为False。注意该字段只与验证相关。
  • null
    • 如果为True,django将在数据库存储空值为NULL。默认为False。注意如果希望表单中允许空值,还需要设置blank=True,因为null参数只影响数据库的存储。
  • default
    • 默认值。可以是一个值或者一个调用对象。当创建模型实例且没有为该字段提供值时,使用默认值。
  • help_text
    • 额外的帮助文本,会随表单控件一同显示。它对生成文档也很有用。
  • db_column
    • 这个参数可以设置字段的数据库列名。如果不设置默认使用字段名作为数据库列名。
  • validators
    • 该字段运行的验证器列表。
  • error_messages
    • 这个参数可以覆盖字段引发的默认错误信息。传入一个与你想覆盖的错误信息相匹配的键值的字典。
  • max_length
    • CharFiled类型字段必须传递的参数,表示该字段的最大长度(以字符为单位)。max_length会在数据库层面强制执行。
    • 可以通过在ForeignKey定义中设置 related_name参数来覆盖FOO_set名称
    • 举例子:
    • >>> b = Blog.objects.get(id=1) 
    • >>> b.entry_set.all() # Returns all Entry objects related to Blog. 
    • # b.entry_set is a Manager that returns QuerySets. 
    • >>> b.entry_set.filter(headline__contains="Lennon") 
    • >>> b.entry_set.count()
    • >>> b = Blog.objects.get(id=1) 
    • >>> b.entries.all() # Returns all Entry objects related to Blog. 
    • # b.entries is a Manager that returns QuerySets. 
    • >>> b.entries.filter(headline__contains="Lennon") 
    • >>> b.entries.count()

3.3 自动设置主键

默认情况下,django给每个模型一个自动递增的主键,其类型在AppConfig.default_auto_field中指定,或者在DEFAULT_AUTO_FIELD配置中全局指定。

例如:

id = models.BigAutoField(primary_key=True)

如果你想自己指定主键,在你想要设置为主键的字段上设置参数primary_key=True。

注意:每个模型都需要一个主键字段

3.4 Meta选项

在模型内定义Meta类来给模型赋予元数据。

在我们的例子中db_table='tb_project'表示创建表的时候指定表名为tb_project,不使用默认的表名。默认会用应用名_模型名小写作为表名。

verbose_nameverbose_name_plural表示模型的可读名称的单数和复数,主要在admin站点中使用。

ordering=['-c_time']表示查询数据时默认按照c_time字段降序排列。

Meta选项较多,且和很多高级功能有关,在后面的课程中用到再详细讲解。

更多的选项参考官方文档

3.5 __str()__

每个模型都应该定义一个__str__方法,当打印模型对象时,它的值友好的展示一个对象

3.6 数据库迁移

编写模型后,django会将模型的修改应用至数据库进行关联,还需要将模型和数据库进行关联,这个操作称为迁移。

3.6.1 安装应用

要进行模型数据库迁移,首先需要将应用安装到项目中。在配置项INSTALLED_APPS中添加要安装的应用的设置类

例如,安装crm应用到项目中的配置如下:

study_django/settings.py
INSTALLED_APPS = [
    ...
    'crm.apps.CrmConfig'
]

应用的设置类是自动生成的,在应用根目录的apps.py模块中,安装应用时,使用点式路径,例如crm.apps.CrmConfig

3.6.2 生成迁移文件

在命令行输入如下命令:

python mamage.py makemigrations crm

你将会看到类似于下面这样的输出:

Migrations for 'crm':
  crm\migrations\0001_initial.py
    - Create model Student

通过运行makemigrations命令,django会检测你对模型文件的修改(当前是创建),并把修改的部分存储为一次迁移。迁移是django对模型定义的变化的记录。上面的命令会在crm/migrations目录中生成0001_initial.py,这就是迁移文件。感兴趣可以打开阅读它们,别担心,不需要每次都阅读迁移文件,但是它被设计成人类可阅读的形式,这是便于手动调整django的修改方式

3.6.3 执行迁移

生成迁移文件之后,还需要执行迁移,同步数据库。django通过命令migrate来执行迁移。

在运行迁移之前,我们可以看看,迁移会执行哪些SQL语句。

命令sqlmigrate接收一个迁移的名称,然后返回对应的SQl:

python manage.py sqlmigrate crm 0001

输出如下:

--
-- Create model Student
--
CREATE TABLE "tb_student" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(20) NOT NULL, "age" smallint N
ULL, "sex" smallint NOT NULL, "phone" varchar(20) NULL UNIQUE, "c_time" datetime NOT NULL);

输出格式跟你使用的数据库有关。

现在,运行migrate命令,在数据库里创建新定义的模型的数据表:

python .\manage.py migrate crm
Operations to perform:
  Apply all migrations: crm
Running migrations:        
  Applying crm.0001_initial... OK

django通过在数据库中创建一个特殊的表django_migrations来跟踪执行过哪些迁移。

migrate命令会执行所有没有执行过的迁移,将模型的修改同步到数据库结构上。

迁移是非常强大的功能,是在开发过程中持续的改变数据库结构而不需要重新删除和创建表,它专注于使用数据库平滑升级而不丢失数据。

记住,改变模型需要这三步:

  • 编辑models.py文件,修改模型
  • 运行python manage.py makemigrations为模型的改变 生成迁移文件
  • 运行python manage.py migrate来应用数据库迁移

3.6.4 数据库的增删改查

在执行数据的增删改查操作时,可以先进入django的调试 shell

先安装一个ipython,这个是一个交互式工具,有利于调试数据库,然后执行命令:

命令:

py.exe .\manage.py shell

stu = Student(name="小简", age=19)
stu.save()

stu.delete() # 删除当前对象

# 修改某个字段的值
stu.age = 10
stu.save()

# 查询某个字段的值
stu.name

# 查询表中的所有数据
queryset = Student.objects.all() #查询所有,等价于 select *

print(queryset.query) # 输入执行的sql
# out:SELECT `Student`.`id`, `Student`.`name`, `Student`.`age`, `Student`.`sex`, `Student`.`phone`, `Student`.`c_time` FROM `Student`

print(queryset)
# out:<QuerySet [<Student: 心蓝>, <Student: 张三>]>

3.7 表设计

为我们的crm系统设计学生表(tb_student)学生详情表(tb_student_detail),渠道表(tb_channel),课程表(tb_course),报名表(tb_entry),表关系和字段如下图:

1685188396389-28e6bbcf-2cc6-4d67-abbe-d5962488ff34.png

3.8 表关系

显然,关系型数据库的强大之处在于各表之间的关联关系。 Django 提供了定义三种最常见的数据库关联关系的方法:多对一,多对多,一对一

3.8.1 多对一

上图中的学生表和渠道表,一个学生会对应一个渠道,一个渠道对应多个学生,学生表中的一条数据和渠道表中的一条数据对应,渠道表中的一条数据与学生表中的多条数据对应,学生表和渠道表形成多对一的关系。

在django中要表达多对一的关系需要使用django.db.models.ForeignKey字段,创建一个渠道模型,然后在Student模型中添加一个外键字段如下:

from django.db import models


class Student(models.Model):
	...
    channel = models.ForeignKey('Channel', null=True, on_delete=models.SET_NULL, help_text='渠道')
    ...
    
class Channel(models.Model):
    title = models.CharField('名称', max_length=20, help_text='渠道名称')

    class Meta:
        db_table = 'tb_channel'
        verbose_name = '渠道表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.title

外键字段定义在多的一方,所以Student模型中定义了channel字段,这个字段名一般使用关系模型的小写名。

外键字段的第一个参数是一个位置参数,即需要关联的模型,可以是模型本身,也可以是模型的字符串形式的导入路径(当引用后定义的模型时很有用)。

在数据库层面,django会在字段名后附加_id来创建数据库列名。所以Student模型的数据库表将有一个channel_id列,然后会为这个列创建一个外键约束,被引用表为tb_channel,被引用字段为tb_channel.id

注意:有时候为了效率,在数据库不创建外键约束,而是通过代码逻辑来保证数据的完整性。在django中可以在ForeiginKey字段中指定db_constraint=False来控制不创建外键约束。

级联操作

当一个由ForeignKey引用的对象被删除时,django通过on_delete参数指定的方法实现数据库级联操作。

on_delete的可能值有:

  • CASCADE
    • 级联删除
  • PROTECT
    • 通过引发ProtectedErro防止删除 被引用字段
  • RESTRICT
    • 通过引发RestrictedError方式删除被引用字段
  • SET_NULL
    • 设置外键为空,只有null设置为True是才可以
  • SET_DEFAULT
    • 将外键设置为默认值,必须为外键设置默认值

注意:外键字段必须指定参数on_delete

当删除一个渠道时,在业务层面不大可能会删除对应的学生,应该是设置channel_id字段为null,所以这个外键字段的级联操作设置为null=Trueon_delete=models.SET_NULL

3.8.2 多对多

django自动创建第三张关联表

数据库中多对多的关系通过第三张表来表示。

一个学生可以报名多门课程,一个课程可以被多名学生报名。在我们的案例中学生表和课程表通过报名表实现多对多的关系。

在django中要表达多对多的关系要使用django.db.models.ManyToManyField字段。

对于多对多关系的两个模型,可以在其中任意一个模型中定义多对多字段,但只能选择一个模型设置该字段,不能同时在两个模型中添加该字段

注意:一般来说应该把多对多字段放到需要在表单中编辑的对象中,或者是在业务中需要查询次数更多的模型中。

在我们的案例中,编辑学生对象,或者编辑课程对象时都不需要彼此,而在查询时,"报名了某个课程的学生有哪些",这个需求会更多,所以把多对多的字段定义在课程表中。定义Course模型如下:

class Course(models.Model):
    name = models.CharField('名称', max_length=20, help_text='课程名称')
    students = models.ManyToManyField('Student', help_text='报名学生')

    class Meta:
        db_table = 'tb_course'
        verbose_name = '课程表'
        verbose_name_plural = verbose_name

多对多字段名一般设置为关系模型名的复数形式,表示关系模型的对象集合,所以Course模型中的多对多字段名为students。多对多字段的第一个参数是一个位置参数,既需要关联的模型,可以是模型本身,也可以是模型的字符串形式的导入路径(当引用后定义的模型时很有用)。

在数据库层面,django会自动创建一个中间表来表示多对多关系。默认情况下这个表名使用创建多对多字段的模型的表名字段名生成,上面的例子会生成表名tb_course_students。然后包含两个字段,分别是两个模型的名字和_id组成(student_id,course_id),并创建外键引用对应表的id,还会对这两个字段创建联合唯一的约束

1693991179737-35226b57-55a6-43fa-8b37-81c72458e3d6.png

自定义中间表

虽然django会自动创建第三张表,但是不能提供额外字段。如果中间表需要包含其他字段,就需要自定义中间表,然后在定义多对多字段时通过through参数指定第三张表。

【意思是:通过through后跟的表去和模型达成多对多的关系】

所以创建一个Entry模型如下:

class Entry(models.Model):
    student = models.ForeignKey(Student, verbose_name='学生', help_text='学生', on_delete=models.PROTECT)
    course = models.ForeignKey(Course, verbose_name='课程', help_text='课程', on_delete=models.PROTECT)
    c_time = models.DateTimeField('报名时间', help_text='报名时间', auto_now_add=True)

    def __str__(self):
        return '{}-{}'.format(self.student.name, self.course.name)

    class Meta:
        db_table = 'tb_entry'
        verbose_name = '报名表'
        verbose_name_plural = verbose_name

在其中定义studentcourse外键字段分别和对应的模型形成多对一的关系。再定义c_time字段,用来记录报名时间。学生表,课程表通过报名表来表达多对多关系。现在创建多对多字段主要是为了查询方便,可以在课程对象和学生对象上彼此关联,所以修改Course模型中的students字段如下:

class Course(models.Model):
    ...
    students = models.ManyToManyField('Student', help_text='报名学生', through='Entry')
	...

3.8.3 一对一

在django中要表达一对一的关系需要使用django.db.models.OneToOneField字段,概念上,这类似于ForeignKeyunique=True的结合。

crm中,学生详情与学生表就是一对一的关系,创建模型如下:

class StudentDetail(models.Model):
    STATION_CHOICES = [
        ('功能测试工程师', '功能测试工程师'),
        ('自动化测试工程师', '自动化测试工程师'),
        ('测试开发工程师', '测试开发工程师'),
        ('测试组长', '测试组长'),
        ('测试经理', '测试经理')
    ]

    class SalaryChoice(models.TextChoices):
        FIRST = '5000以下', '5000以下'
        SECOND = '5000-10000', '5000-10000'
        THIRD = '10000-15000', '10000-15000'
        FOURTH = '15000-20000', '15000-20000'
        FIFTH = '20000以上', '20000以上'
    student = models.OneToOneField('Student', verbose_name='学生', on_delete=models.CASCADE, help_text='学生')
    city = models.CharField('城市', max_length=20, help_text='所在城市', null=True)
    company = models.CharField('就职公司', max_length=64, help_text='就职公司', null=True)
    station = models.CharField('岗位', choices=STATION_CHOICES, max_length=10, default='功能测试工程师', help_text='岗位')
    salary = models.CharField('薪资水平', choices=SalaryChoice.choices, max_length=20, default=SalaryChoice.THIRD, help_text='薪资水平')

    class Meta:
        db_table = 'tb_student_detail'
        verbose_name = '学生详情表'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.student.name

在数据库层面会创建student_id列,然后为这个列创建一个外键约束,引用表为tb_student,字段为tb_student.id。与多对一不同,这个列还会创建一个唯一约束,形成一对一的关系。其他的级联操作与多对一一样。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值