Django笔记一之运行系统Django笔记七之ManyToMany和OneToOne介绍

发布时间 2023-05-04 00:00:20作者: shuzihua

Django笔记一之运行系统、创建视图并访问

本文首发于微信公众号:Django笔记。
原文链接:Django笔记一之创建并运行系统

从这一篇笔记开始将根据 Django 的官方文档,阅读整理之后,出一系列笔记教程,用作新手入门教程或者自己做查阅。

此次 Django 的版本为 3.2,且之后的一系列笔记都将以这个版本为基础,做功能的测试和验证。
本篇笔记的目录索引如下:

  1. 创建环境
  2. 运行系统,在浏览器中访问
  3. 介绍 Django 的 application 的基本框架
  4. 创建视图和接口,在浏览器中访问接口

1、创建环境

首先默认我们已经配置好了环境,或者能够使用 pycharm 来操作。

使用 conda 创建一个虚拟环境,用的是 py3.6:

conda create -n func_test python=3.6

进入虚拟环境:

source activate func_test

安装 Django 的版本为 3.2,通过指定源来加速安装:

pip3 install django==3.2 -i https://mirrors.aliyun.com/pypi/simple/

然后选择一个目录,假设是 ~/ 下,创建一个项目名称为 hunter 的项目,以下是创建命令:

django-admin startproject hunter

然后可以看到在 ~/ 下会出现一个 名为 hunter 的文件夹,这就是我们刚刚创建好的系统。

至此,一个 Django 系统就创建完成了。

2、运行系统,在浏览器中访问

进入这个文件夹,然后就进入了这个项目的根目录,(在此,称进入这个文件夹下第一级目录,为项目根目录):

cd hunter

运行启动命令,以下是启动命令:

python3 manage.py runserver 0:9898

当我们在浏览器中访问 localhost:9898,看到如下页面,即系统运行成功:
在这里插入图片描述
如果是自己服务器或者其他 ip 运行的命令,更改前面的 localhost 即可。

3、介绍 Django 的 application 的基本框架

当我们进入项目的根目录,可以看到有一个 manage.py 文件 和一个与我们项目名称相同的文件夹。
manage.py 文件是我们运行系统,以及之后同步数据库结构等操作的一个文件,后续会用到。
hunter 文件夹是我们系统放置的一些默认的配置文件等,进入其中,可以看到一个 settings.py 文件和 urls.py 文件,后面我们会用上。

一个系统,会有很多的功能模块,相对应的功能都会被归属到相应的模块下面,现在我们来创建一个 模块,以下是创建命令:

python3 manage.py startapp polls

python3 manage.py startapp 是固定语法,表示我们需要创建一个模块,在 Django 称之为 application, polls 是模块的名称,可以任意指定。
之后便可以看到项目根目录下出现一个新的名为 polls 的文件夹,有关 polls 的功能函数都可以在里面撰写。

每创建一个新的 application,里面的文件都是固定的,可以看到里面有一个 migrations 的文件夹,里面会存放每次对数据库表进行的更改的文件,这个后续再详述。
还会有一个 views.py 文件,这个文件是视图文件,我们对接口的相关处理逻辑都会在这个里面填写。
那么接口名称的定义文件呢,我们下一步来操作。

4、创建视图和接口,在浏览器中访问接口

可以看到在项目根目录下的 hunter 文件夹下 有一个 urls.py 文件,那是我们 接口定义的总入口,如果我们需要为 某个 application 定义自有的接口前缀,需要进行如下配置,编辑 hunter/urls.py,使其如下:

# hunter/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('polls/', include('polls.urls')),
]

然后,所以 polls 下的接口前缀就如上面的代码所示,都是 polls 开头了。

然后我们在 polls 这个application 下新增一个 urls.py 文件,写上如下代码:

from django.urls import path

from polls import views

urlpatterns = [
    path("", views.index),
]

可以看到 在 polls/urls.py 下,我们引入了 polls/views.py 文件,并使用了其下的 index 函数,接下来编辑 polls/views.py 文件:

# polls/views.py
from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, this is polls")

然后在项目根目录下运行我们的系统:

python3 manage.py runserver 0:9898

在浏览器中访问 http://localhost:9898/polls,可以看到如下截图:
在这里插入图片描述
说明我们刚刚用 Django 跑通了我们写的第一个接口。

以上就是本文全部内容。

 

Django笔记二之连接数据库、执行migrate数据结构更改操作

 

本文首发于微信公众号:Hunter后端
原文链接:Django笔记二之连接数据库、执行migrate数据结构更改操作

本篇笔记目录索引如下:

  1. Django 连接mysql,执行数据库表结构迁移步骤介绍
  2. 操作数据库,对数据进行简单操作

接下来几篇笔记都会介绍和数据库相关,包括数据库的连接、操作(包括增删改查)、对应的字段类型、model 里Meta 相关参数和 QueryAPI 的详解等。

这一篇先介绍数据库的连接和简单的增删改查操作。

首先介绍一些 Django 的操作表的逻辑,Django 提供了一套非常方便的 orm 方法,可用于Django 能直接对表和数据进行增删改查,对应的数据库里每一张表都有一个对应的 model 的class,class 下每一个字段都对应 mysql 中表的字段,我们在 Django 中定义好这些内容,然后通过命令迁移到 数据库中来完成修改。

1、Django 连接mysql

前一篇笔记我们创建了 一个 名为 polls 的application,接下来我们要在 polls 下面建立和数据库的连接。

连接上 mysql 分为以下几步:

  • 安装依赖
  • 创建数据库
  • 修改settings.py 填写数据库信息
  • 迁移默认表
  • 定义 model 信息
  • 生成 migrations 文件
  • 执行表的迁移

安装依赖

Django 和 mysql 连接需要安装一个 driver,我们这里选择 mysqlclient,以下是安装命令:

pip3 install mysqlclient -i https://mirrors.aliyun.com/pypi/simple/

还是通过 -i 指定源,加速安装。

创建数据库

可以自己在服务器或者本地安装一个可以运行的 mysql,我这里安装的是 mysql 5.7 版本。

创建数据库的时候记得指定编码为 utf-8:

create database func_test default character set='utf8'

在这里,数据库名称为 func_test,库编码为 utf-8。

修改settings.py 填写数据库信息

在 hunter/settings.py 文件下,有一个 DATABASES 的变量,将其修改为以下内容:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'func_test',
        "USER": "user",
        "PASSWORD": "password",
        "HOST": "xx.xx.xx.xx",
        "PORT": 3306,
    }
}

其中,default 表示系统默认连接的数据库,一个 Django 系统可以连接多个数据库,可以通过这个地方定义变量名称来指定。
如果需要连接多个数据库,后续的操作可以再介绍。在使用的时候通过 using() 来区分
ENGINE:在这里值为 django.db.backends.mysql ,表示连接的数据库类型是 mysql
NAME:表示连接的数据库名称,在这里我们指定的是 func_test
USER:连接数据库使用的用户名
PASSWORD:连接数据库用户名对应的密码
HOST:你的数据库的地址,本地的话是 localhost 或者 127.0.0.1,服务器上填写相应的 ip 即可
PORT:端口,mysql 默认是 3306

以上的那些变量,记得更换成本地的变量。

好了,关于数据库的基本连接配置就都设置完成了。

迁移默认表

Django 系统里是有一些默认的配置表的,
比如用户表,如果使用Django的默认用户系统的话可以用上
django_session表,用于记录用户的登录的 session相关记录
django_migrations表,记录每一次表及字段的创建和修改操作等等

这些表,在我们执行以下操作命令的时候,会自动写入数据库:

python3 manage.py migrate

定义 model 信息

在上一篇笔记里我们创建了一个名为 polls 的 application,里面有一个 models.py 的文件,然后我们编辑该文件,内容如下:

from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')


class Choice(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

在上述文件里,每一个class 代表着一张表,每一行都代表着 表里的一个字段,字段的类型在上述的文件里对应数据库的类型为:
CharField:varchar
DateTimeField:datetime
IntegerField:int
ForeignKey:外键
这个类型我们后续会再介绍,这里只做一个简单的介绍。

然后我们还需要将我们这个 model 注册到我们的 settings.py 里的 INSTALLED_APPS:

INSTALLED_APPS = [
    'polls.apps.PollsConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

生成 migrations 文件

我们定义好 model 文件之后,在生成表结构之前,我们还需要生成一个 记录表结构变化的文件,也就是我们的migrations 文件,每一次修改都可以生成一个新的 migration 文件,然后写入数据库,该文件存放在 每一个 application 的 migrations 文件夹下面。
生成 migrations 文件命令:

python3 manage.py makemigrations polls

然后我们可以看到在 polls/migrations/ 文件夹下多了一个 0001_initial.py

执行表的迁移

然后执行下面的命令,Django会根据系统里上一次在 django_migrations 表里记录的上一次执行的地方,检测我们系统里新增的 migrations 文件,自动将数据结构的更改适配到数据库里:

python3 manage.py migrate polls

如果对 model.py 再进行一次更改,然后执行 makemigrations 命令,migrations 文件夹下会自动新增新的迁移命令。
每一个新增的前面都会有序号标识,比如我们最开始的是 0001,之后每一次都会按照序号往后新增。
有兴趣的可以看看 migrations 里的文件内容,那是 python 语言里对应 SQL 的更改内容,这里不做展开,有兴趣的可以看看。

如果我们想在每一次执行 migrate 前,查看会对数据库进行怎么样的更改,可以根据 我们上面说的 序号,来指定 migration 文件查看:

python3 manage.py sqlmigrate polls 0001

python3 manage.py sqlmigrate 是固定语法,
polls 是对应的 application 的名称,
0001 是每一次migration的前面的序号,这样就可以指定到 application 下的每一次更改。

比如上面那条命令,输出的结果如下:
在这里插入图片描述
会将转义执行到 数据库的语句打印出来。

2、操作数据库,对数据进行简单操作

现在我们根据上述的操作,创建了两张表和对应的 model,接下来介绍怎么对数据进行操作:

比如我们要创建一个 Question 数据,使用Django的语法来创建,可以通过 以下命令进入 Django 的交互界面:

python3 manage.py shell

然后挨个输入:

from polls.models import Question
q = Question(question_text="quesiton_text_1", pub_date="2022-01-01 00:00:00")
q.save()

然后查看数据库,可以看到我们通过 save() 操作已经创建了一条数据了。

获取单个数据呢:

q = Question.objects.get(id=1)
print(q.question_text)

以上就是我们这一篇笔记的全部内容,

 

 

Django笔记三之使用model对数据库进行增删改查

 

本文首发于微信公众号:Hunter后端
原文链接:Django笔记三之使用model对数据库进行增删改查

本篇笔记目录索引如下:

  1. model 准备

1、model 准备

在上一篇笔记中,我们新建了一个 application,增加了几个model 同步到了数据库,这次我们新建一个名为 blog 的application,同步数据结构。
大概分为以下几步:

  • python3 manage.py startapp blog
  • 将 'blog.apps.BlogConfig’, 写入 settings.py INSTALLED_APPS
  • 更新 blog/models.py
  • python3 manage.py makemigrations blog
  • python3 manage.py migrate blog

具体执行 migrate 的操作步骤,可以参见上一篇笔记。

blog/models.py 的具体内容如下:

# blog/models.py
from django.db import models


class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def __str__(self):
        return self.name


class Author(models.Model):
    name = models.CharField(max_length=200)
    email = models.EmailField()

    def __str__(self):
        return self.name


class Entry(models.Model):
    blog = models.ForeignKey(Blog, on_delete=models.CASCADE)
    headline = models.CharField(max_length=255)
    body_text = models.TextField()
    pub_date = models.DateField()
    mod_date = models.DateField()
    authors = models.ManyToManyField(Author)
    number_of_comments = models.IntegerField()
    number_of_pingbacks = models.IntegerField()
    rating = models.IntegerField()


    def __str__(self):
        return self.headline

2、增

有以下几种方法(以下操作皆在 python3 manage.py shell 环境中进行):

实例化,然后save() 保存

from blog.models import Blog
b = Blog(name='Beatles Blog', tagline='All the latest Beatles news.')
b.save()

当执行 save() 操作之后,数据就会创建到数据库,因为主键 id 为自动增长的,所以id会自动赋值。

当然也可以先实例化一个空的 Blog,然后再赋值:

from blog.models import Blog
b = Blog()
b.name = 'hunter'
b.tagline = 'tag lines'
b.save()

save 之后,如果需要修改 name 的值,可以直接进行修改:

b.name = ‘python’
b.save()

使用 create() 方法创建

from blog.models import Blog
b = Blog.objects.create(name='hunter', tagline='tagline')

调用 create() 方法,会返回这条数据保存后的对象。

批量创建
如果要批量创建数据,用上面的方法大概的就是在一个循环里,挨个去实例化一个 Blog,然后执行 save() 操作。

但Django 提供了一个 bulk_create() 的方法,可以提高这个效率,使用示例如下:

from blog.models import Blog

blog_1 = Blog(name='hunter1', tagline='tagline1')
blog_2 = Blog(name='hunter2', tagline='tagline2')
blog_obj_list = [blog_1, blog_2]

Blog.objects.bulk(blog_obj_list)

3、查

查询的语法有查询之后返回 QuerySet 的查询,比如 filter(),exclude()
也有 返回单个 object 的查询,比如 get()

对于 QuerySet,这个我们可以简单理解为是多个 object 实例形成的列表,但是这个列表是Django的一种特有的形式,具有能进行其他条件筛选的功能。

接下来简单介绍一下查询的功能:
filter(),过滤筛选,返回的是符合条件的数据
比如我们要搜索 Entry 表里,name 的值为 hunter 的数据,使用如下:

Entry.objects.filter(name='hunter')

exclude(),排除筛选,返回的是不符合条件的数据
比如我们要搜索 Entry 表里,name 的值不为 hunter 的数据:

Entry.objects.exclude(name='hunter')

链式查询:
Django 支持 链式查询,可以多个 filter 或者 exclude 条件累加,取的是 AND 的逻辑:

Entry.objects.filter(name='hunter').exclude(name='paul').filter(id=1)

懒加载:
Django 的查询有一个机制叫做懒加载,意思是只有当你真正需要去取数据的时候
系统才会去数据库获取数据,官方例子如下:

>>> q = Entry.objects.filter(headline__startswith="What")
>>> q = q.filter(pub_date__lte=datetime.date.today())
>>> q = q.exclude(body_text__icontains="food")
>>> print(q)

上述语句虽然看起来查询了三次数据库,但实际上只有最后 print(q) 的时候才去访问了数据库。

get()
前面讲的 filter 和 exclude 返回的都是 QuerySet,简单来说就是多个 object 形成的列表,而 get() 操作返回的直接是一条符合条件的 object。
比如:

blog = Blog.objects.get(id=1)

注意: get() 方法需要慎用,因为查询的条件在数据库里,有多条或者一条都没有的时候系统会报错。
get() 的查询一般仅用在我们能够确定 在数据库里有且仅有一条数据情况下。

对QuerySe进行切片
用 filter 对数据进行操作的时候,如果需要对数据的返回数量进行限制,需要用到 python 里的切片。
PS:数量的返回限制对应于 mysql 里的 limit 用法。
比如:

blog_list = Blog.objects.all()[1:5]

但是和 python 里的切片不一样的时候,Django 的查询不支持 负数查询,比如下面的操作是不被允许的:

Blog.objects.all()[-1]  # error
Blog.objects.all()[-1: 3]  # error

字段条件查找:
在我们使用 mysql 的时候 where 后面会跟一些查询的限制条件,在Django 里用 双下划线来实现
比如 id 的值大于 5

Model.objects.filter(id__gt=5)

大于:gt
大于等于:gte
小于:lt
小于等于:lte
包含:in
是否为 null :isnull

精确查找:
精确查找使用 exact,一般查询的时候 后面不加上面的字段条件,都属于精确查找,不过默认是将 exact 字段省略。
比如,下面两条代码是一致的:

Blog.objects.get(id__exact=14)
Blog.objects.get(id=14) 

查询外键关联数据
比如 Entry 这个 model 的外键是 Blog,我们想通过 查找 Blog 的 name 字段为 Hunter 的Entry 数据:

Entry.objects.filter(blog__name='Hunter')

如果你想反向搜索也是可以的,将 model 名称小写即可:

Blog.objects.filter(entry__headline='abc')

计算查找
在Django 中引用字段来进行比较,比如我们想实现如下功能:

select * from blog_entry where number_of_comments > number_of_pingbacks;

可以使用Django 中的 F,它的作用是 取值,取出其中的字段值,那么上述例子可以用 Django来实现可以是:

from django.db.models import F
Entry.objects.filter(number_of_comments__gt=F(“number_of_pinbbacks"))

还可以在使用中对 F() 进行一系列的操作:

Entry.objects.filter(number_of_comments__gt=F('number_of_pingbacks') * 2)
Entry.objects.filter(rating__lt=F('number_of_comments') + F('number_of_pingbacks'))

pk 使用方法
pk 意思是 primary key ,主键,可以直接使用 pk 来搜索,但在项目中一般是使用 id 作为主键,所以一般是等价于id的用法:

Blog.objects.filter(pk__gt=11)

Q 查询:
我们知道可以使用 filter 来进行 链式查询,那个逻辑是一个且的逻辑,那么 或 的逻辑应该如何处理呢
我们可以使用Q() 来实现

我们可以使用 Q() 来将一个个的条件 串起来,比如,我们想筛选 Blog 中 id= 3 或者 id = 4 的数据可以使用如下:

Blog.objects.filter(Q(id=3) | Q(id=4))

也可以实现 且 的功能Q() & Q()
取反:~Q()

4、删

如果要删除 objects,有两种方法,一种先获取 object,然后 delete()

blog = Blog.objects.get(id=1)
blog.delete()

或者通过 filter 来 批量删除:

Blog.objects.filter(id__gte=10).delete()

注意:如果有外键关联了 Blog,且 on_delete关系设置为 models.CASCADE,
那么删除相应的 Blog 的时候,对应的 关联的 Entry 数据也会被删除

5、改

批量更新:

Blog.objects.filter(id__gte=200).update(name='hunter')

单个更新:

blog = Blog.objects.get(id=1)
blog.name = ‘hunter’
blog.save()

以上就是我们这一篇笔记的全部内容

 

Django笔记四之字段属性 

 

本文首发于微信公众号:Hunter后端
原文链接:Django笔记四之字段选项

这篇笔记介绍的 field options,也就是 字段的选项属性。

首先,关于 model,是数据库与 python 代码里的一个映射关系,每一个 model 是django.db.models.Model 的一个子类。

model 里每一个属性值(即字段)代表数据库的字段,通过 定义 models.py 里的 class,可以自动生成数据库里的表和字段,比如之前的 Question。

主键 id 字段如果不手动设置都会默认创建。

比如下面这个 model,我们将以此为例介绍各个字段的属性值:

class Question(models.Model):
    SiZES = [
        ('S', 'small'),
        ('M', 'medium'),
        ("L", 'large'),
    ]
    question_text = models.CharField(verbose_name="问题文字", max_length=200, help_text="question_text")
    pub_date = models.DateTimeField('日期')
    test_blank_char = models.CharField(blank=True, max_length=100)
    size = models.CharField(max_length=10, choices=SiZES)

目录大致如下:

  1. null=True/False
  2. default
  3. blank=True/False
  4. primary_key
  5. unique
  6. verbose_name
  7. db_index
  8. auto_now
  9. auto_now_add
  10. choices

1、null=True/False

是否允许字段在数据库的字段为 null。

这个字段一般可以和下面的 default 默认值配合使用

2、default

字段的默认值

有时候创建一条数据的时候,指定了其他字段的值,该字段不指定,那么系统就会给这个字段赋值为 default 的值,可以是 任意值,比如整型,字符型,日期,以及 None。
比如下面的model:

class Question(models.Model):
	name = models.CharField(max_length=20, default='')
	pub_date = models.DateField()	

创建一条数据:

Question.objects.create(pub_date="2022-01-01")

创建数据的时候没有指定 name 的值,而我们设置了 default 为 空字符串,那么系统创建这条数据的时候,就会默认为 该字段赋值为 ''

而如果我们设置了 default=None,表示默认为 null(数据库的空值),系统会报错,因为字段一般默认是不允许为空的,所以我们需要和 null=True 配合使用,示例如下:

class Question(models.Model):
	name = models.CharField(max_length=20, default=None, null=True)
	pub_date = models.DateField()	

那么当我们创建一条数据的时候,如果不指定 name 的值,name 字段在数据库中就会默认为 null 了。

3、blank=True/False

blank 是空白,表示空字符串,字段是否允许为空字符串

4、primary_key

是否为主键

因为我们创建表的时候,比如上面那个 Question,一般不设置主键 id,系统为自动默认为我们创建一个主键为 id 的自增的整型数据。

5、unique

唯一约束

设置为 unique=True 后,该表会为该字段中建立一个唯一索引,该字段数据在该表中就只能是唯一的了,不可创建相同内容的数据了,否则会报错。

class Question(models.Model):
	name = models.CharField(max_length=20, unique=True)
	pub_date = models.DateField()	

比如我们创建了一条数据,name 的值为 'hunter',然后再创建一条 name 为 'hunter' 的数据,系统就会报错。

6、verbose_name

name = models.CharField(max_length=20, default=None, null=True, verbose_name='名称')

字段注释,除了可用于用户的查看理解字段含义,还可在 admin 页面数据该字段的标题显示(admin后台管理页面后续会开一篇笔记介绍)

7、db_index

索引

设置字段的这个参数为 True,然后运行 makemigrations 和 migrate 会为数据库的该字段创建索引

name = models.CharField(max_length=20, default=None, null=True, verbose_name='名称', db_index=True)

8、auto_now

常用于日期字段,每次更改该数据行的字段的内容时,该字段会自动更新为当前时间,常用于 updated_time 字段

示例:

updated_time = models.DateTimeField(auto_now=True)

9、auto_now_add

常用于日期字段,每次创建该数据行数据之后,该字段会被自动填入当前时间,且之后更改数据时不会变化,常用于 created_time 字段

示例:

created_time = models.DateTimeField(auto_now_add=True)

10、choices

字段可选值

常用于某个字段的只能选择特定的几个值的情况下,比如状态为 成功,失败,待处理这种。

下面是一个使用示例:

class Question(models.Model):
    SiZES = [
        ('S', 'small'),
        ('M', 'medium'),
        ('L', 'large'),
    ]
    question_text = models.CharField(verbose_name="问题文字", max_length=200)
    pub_date = models.DateTimeField('日期')
    test_blank_char = models.CharField(blank=True, max_length=100)
    size = models.CharField(max_length=10, choices=SiZES)

系统在保存的时候 使用的是 choices 中每一个元素的第一个值,也就是 SIZES 里的 ’S’ ‘M’, ‘L’

如果在项目中想要获取该字段对应的全称,可以使用 get_field_display() 方法,其中,field 为我们定义的字段名

比如在上面的 model 里,是 get_size_display()

官方的做法是 在 model 的类里面,定义一个 CHOICES,然后定义对于每个值定义一个合适的常量

以下是官方的一个示例:

class Student(models.Model):
    FRESHMAN = 'FR'
    SOPHOMORE = 'SO'
    JUNIOR = 'JR'
    SENIOR = 'SR'
    GRADUATE = 'GR'
    YEAR_IN_SCHOOL_CHOICES = [
        (FRESHMAN, 'Freshman'),
        (SOPHOMORE, 'Sophomore'),
        (JUNIOR, 'Junior'),
        (SENIOR, 'Senior'),
        (GRADUATE, 'Graduate'),
    ]
    year_in_school = models.CharField(
        max_length=2,
        choices=YEAR_IN_SCHOOL_CHOICES,
        default=FRESHMAN,
    )

以上就是本篇笔记全部内容,主要介绍的是字段里的选

 

 

Django笔记五之字段类型

 

本文首发于微信公众号:Hunter后端
原文链接:Django笔记五之字段类型

这篇笔记介绍字段的类型 Field Type。

Django 的model 下的 field 对应的是 MySQL 中的表字段,而我们定义的 field 的类型则对应 MySQL 中的字段类型。

本次主要介绍以下几种:

  1. CharField
  2. IntegerField
  3. DateField/DateTimeField
  4. BooleanField
  5. FloatField
  6. TextField
  7. DecimalField

其中,比较重要的 ForeignKey,ManyToMany,OneToOne 类型的我们单开笔记讲解

1、CharField

字符型

对应的是 MySQL 中的 varchar 类型,使用示例如下:

class TestModel(models.Model):
	name = models.CharField(max_length=256)

其中,max_length 为必填属性,用于创建字段的时候确认字段最大长度

字段的其他属性,比如 default,或者 unique 或者 db_index 这些上一篇笔记中提到的属性,都可以根据需要添加。

2、IntegerField

整型

对应的是 MySQL 的 int 类型,使用示例如下:

class TestModel(models.Model):
	page = models.IntegerField(default=0)

该字段无必填属性,相关属性可根据需要自行添加。

3、DateField/DateTimeField

分别对应 MySQL 中的 date 和 datetime 类型,使用示例如下:

class TestModel(models.Model):
	updated_date = models.DateField()
	updated_time = models.DateTimeField(auto_now=True)
	created_time = models.DateTimeField(auto_now_add=True)

无必填属性

auto_now:可作为更新时间字段的属性,后续在更改字段数据的时候可不用手动修改该值,系统会自动更新该值为当前时间

auto_now_add:可作为创建时间字段的属性,在某条数据建立之初不用手动填写,系统即为其自动写入当前时间,且后续其他字段的更改不会更改该值

以上两个属性默认为 False

4、BooleanField

对应 MySQL 中的 tinyint 类型,使用示例如下:

class TestModel(models.Model):
	is_right = models.BooleanField(default=False)

使用 Django 操作数据,写入这个字段的时候,我们可以写入 0/1,或者 False/True 这两个值。

因为 MySQL 中存入的时候 tinyint 类型的数据,所以其实在 数据库中 这个字段的值是不止 0/1 这两个可选值的,但是如果通过 Django 写入,系统会对写入的数据做规范,仅能写入 0/1 或 False/True,写入其他数据会返回报错。

注: 如果直接操作数据库,是可以写入 0/1 之外的数据的

5、FloatField

对应 MySQL 中的 DOUBLE 类型,使用示例如下:

class TestModel(models.Model):
	price = models.FloatField()

6、TextField

对应 MySQL 中的 LONGTEXT 字段,使用示例如下:

class TestModel(models.Model):
	text_field = models.TextField()

如果需要存储字符长度比较长的数据,可以使用这个类。

7、DecimalField

对应于 MySQL 中的 decimal 字段,使用示例如下:

class TestModel(models.Model):
	decimal_field = models.DecimalField(decimal_places=5, max_digits=20, default=0)

必填参数 为:max_digits、decimal_places

其中,max_digits 为可以储存的最大位数,这个数字必须大于或等于 decimal_places
decimal_places 为与数字一起存储的小数位数

也就是说如果要存储最高为 999.99 的数字,精度为小数点后两位,则为:

models.DecimalField(max_digits=5, decimal_places=2)

一般来说,如果是对精度要求比较严格的情况下,可以使用这个字段类型。

以上就是这篇笔记的全部内容,在接下来几篇笔记中将会介绍 ForeignKey,ManyToMany,OneToOne 也就是外键等字段类型。

如果想获取更多相关文章,可扫码关注阅读:

 

 

 

Django笔记六之外键ForeignKey介绍

 

本文首发于微信公众号:Hunter后端
原文链接:Django笔记六之外键ForeignKey介绍

这一篇笔记介绍 Django 系统 model 的外键处理,ForeignKey 以及相应的处理方法。

这是一种一对多的字段类型,表示两张表之间的关联关系。

本篇笔记的目录如下:

  1. on_delete
  2. related_name
  3. related_query_name
  4. 外键字段的保存

1、on_delete

假设有两个 application,app1 和 app2

app1 下的 某个 model 为 App1

app2 下的 某个 model 为 App2

# app1/models.py
class App1(models.Model):
	app2 = models.ForeignKey("app2.App2", on_delete=models.CASCADE)

# app2/models.py
class App2(models.Model):
	pass

当我们设置 ForeignKey 的时候,有一个 on_delete 参数,主要用于当被关联的外键的数据被删除时,自身数据的处理。

在我们上面的两个 model 的例子便是,当 App2 的某个数据被删除时,关联了该条数据的 App1 的数据的处理方式。

处理方式主要有以下几种:

CASCADE
关联删除,App2 的数据被删除时,App1 关联的数据也被删除

PROTECT
保护处理,如果 App2 的数据被 App1 关联,那么关联了的 App2 的数据不会被删除

SET_NULL
置空处理,如果 App2 的数据被删除,App1 中关联了该条被删除的App2的数据这个字段都会被设置为 NULL

DO_NOTHING
不处理,原数据不会有任何操作,也就是说 App2 的某条数据被删除,App1 中的引用还在。

但其实这是一种不推荐的做法,因为如果访问到 App1 中的这条数据,用到了 app2 这个字段,就会报错。

2、related_name

ForeignKey 有一个属性,related_name,用于表示从相关对象到此对象的关系的名称,仅用于展示,但是如果 related_query_name 字段没有被赋值的话,那么 related_query_name 则会默认使用 related_name 这个值。

注意: related_name 还有一个用途,就是在同一个 class 下面,如果有两个字段都是另一个 model 的外键字段,这时候添加 related_name 用来区分两个字段是必须的。

示例如下:

class Entry(models.Model):
	blog_old = models.ForeignKey(Blog, related_name='old_entry')
	blog_new = models.ForeignKey(Blog, related_name='new_entry')

3、related_query_name

这个字段主要用于反向过滤器的搜索,这个字段如果没有单独赋值,则会默认使用 related_name 的值。

关于反向过滤器,我们可以来看下这个功能,以下是两个 model:

class Blog(models.Model):
    pass

class Entry(models.Model):
    blog = models.ForeignKey(Blog)

现在我们有一个 Blog 的数据,

blog_1 = Blog.objects.get(id=1)

如果我们想要获取所有 Entry 表里 blog 这个字段的值为 blog_1,怎么处理呢,一般来说是:

entry_list = Entry.objects.filter(blog=blog_1)

但是因为 blog 这个字段是 外键,所以我们使用 反向过滤器 来处理:

entry_list = blog_1.entry_set.all()

上面就是我们 反向过滤器 的用法,就是使用关联的model 的名称的小写 + '_set' 来操作,这是一个固定用法。

但是如果我们设置了 related_query_name 这个字段, model名称小写+'_set' 的用法就可以废弃了,可以直接使用 related_query_name 来操作,比如 Entry 如下:

class Entry(models.Model):
    blog = models.ForeignKey(Blog, related_name="entries")

这时候,我们没有设置 related_query_name 的值,所以会自动使用 related_name 的值,使用 反向过滤器 的方法如下:

entry_list = blog_1.entries.all()

4、外键字段的保存

首先我们先来介绍一下外键字段在数据库和 model 里的样子。

在 model 里,以 Entry 为例,我们可以看到,关键 blog 字段,直接命名为 'blog',

但是在数据库的结构里,我们去查看的话,可以看到该字段为 blog_id。

当我们获取了一个 Entry 实例,为 entry_obj,

entry_obj = Entry.objects.get(id=1)

当我们输出 entry_obj.blog,返回的就是一个 Blog 的实例,是一个 object,

而输出 entry_obj.blog_id,返回的就是一个int 型的数据。

那么,我们如何为 Entry 实例保存 blog 字段呢,
如果我们有一个 blog 实例为 blog_1,那么可以直接使用:

entry_obj.blog = blod_1
entry_obj.save()
PYTHON 复制 全屏

如果我们有某个 Blog 的主键 id 为 blog_id,那么我们可以这样操作:

entry_obj.blog_id = blod_id
entry_obj.save()

以上就是我们这篇介绍外键的全部内容,接下来的笔记我们将介绍 ManyToMany,OneToOne 的用法。

 

 

 

Django笔记七之ManyToMany和OneToOne介绍

 

本文首发于本人微信公众号:Hunter后端
原文链接:Django笔记七之ManyToMany和OneToOne介绍

ManyToMany 是一种多对多的关系,在用途和使用方法上和外键 ForeignKey 类似。

以下是本篇笔记的目录:

  1. ManyToMany 的介绍
  2. through 参数
  3. through_fields 参数
  4. ManyToMany关系数据的增删改查
  5. OneToOne介绍

1、ManyToMany 的介绍

假设有两个 model,Person 和 Group,这两个model之间是多对多的关系。那么我们可以如下创建其关系:

# blog/models.py

class Person(models.Model):
	name = models.CharField(max_length=64)

class Group(models.Model):
	name = models.CharField(max_length=64)
	members = models.ManyToManyField(Person)

通过上述代码我们就创建了有多对多关系两个model,当我们执行 migrate 操作之后(可以先不执行,后续还会对其有所更改),系统除了会创建 Person 和 Group 这两个表之外,还会创建一个表。

表名为 blog_group_members,因为是放在 Blog 这个 application 下面,所以,表名的前缀是 blog,然后加上 model 名的小写为 group,加上字段名称 members。

这张第三方表会有两个字段,person_id 和 group_id,将这两个 model 关联起来。

通过往这张第三方表写入 person_id 和 group_id的数据,我们就将这两个 model 关联了起来。

而获取他们对应的关系的记录和 ForeignKey 的关系类似:

根据 Person 数据查询关联的 Group 数据:

person = Person.objects.get(id=1)
group_list = person.group_set.all()  # 使用 Group 的小写加 _set

根据 Group 数据查询关联的 Person 数据,这个查询方法略微不同,用到的是 Group 定义的 members 字段:

group = Group.objects.get(id=1)
person = group.members.all() 

# 根据条件来搜索 person 也是可以的
person = group.members.filter(name='hunter')

2、through参数

上面 ManyToMany 的定义中,我们没有加任何参数,所以自动创建的表名是默认的,字段也只是两个 model 的主键id。

而如果我们有一些额外的需求,比如说,为 Person 和 Group 添加关联关系时,需要加上关联时间,或者想自己指定表名或 model 名的时候,我们可以通过 through 属性来指定 model 的名称,然后为其添加我们需要的字段。

比如我们想为 Person 和 Group 创建一个多对多的关系,指定model 名为 Membership,且额外添加字段,比如添加时间,可以通过 through 参数来指定:

class Person(models.Model):
    name = models.CharField(max_length=50)

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(
        Person,
        through='Membership',
    )

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    

3、through_fileds参数

在我们上面创建的 Membership model 中,我们对应的多对多的字段分别是 person 和 group,所以系统可以自动找到对应的多对多的字段。

如果在第三方表,也就是 Membership 中,有多个相同的 Person 或者 Group 的字段,就需要通过 through_fields 参数来指定多对多的字段:

class Person(models.Model):
    name = models.CharField(max_length=50)


class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(
        Person,
        through='Membership',
        through_fields=('group', 'person'),
    )


class Membership(models.Model):
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    inviter = models.ForeignKey(
        Person,
        on_delete=models.CASCADE,
        related_name="membership_invites",
    )
    invite_reason = models.CharField(max_length=64)

4、ManyToMany关系数据的增删改查

接下来,我们定下最终的几个 model 内容如下,用于演示 ManyToMany 的增删改查的操作:

class Person(models.Model):
    name = models.CharField(max_length=50)

class Group(models.Model):
    name = models.CharField(max_length=128)
    members = models.ManyToManyField(
        Person,
        through='Membership',
    )

class Membership(models.Model):
    person = models.ForeignKey(Person, on_delete=models.CASCADE)
    group = models.ForeignKey(Group, on_delete=models.CASCADE)
    date_joined = models.DateField()
    invite_reason = models.CharField(max_length=64)
    

现在我们有 Person 和 Group 两个model,还有两个 model 之间的关系表 Membership,如果我们要创建一个对应的关系,则需要创建一个 Membership 实例。

创建

首先创建 Person 和 Group 的记录:

from blog.models import Person, Group, Membership
hunter = Person.objects.create(name='hunter')
group_1 = Group.objects.create(name='group_1')

创建多对多记录:

m1 = Membership.objects.create(person=hunter, group=group_1, date_joined='2022-01-01', invite_reason='xxx')

根据单个 Person 记录获取所有相关 Group 记录,使用方法同外键搜索方法:

groups = hunter.group_set.all()

根据单个 Group 记录获取所有相关 Person 记录,根据多对多字段来搜索:

persons = group_1.members.all()

根据 Membership 关系记录获取 Person 和 Group 记录,可以直接用外键的的方式使用:

m1.person
m1.group

根据 Group 使用 add 添加一条多对多记录:

paul = Person.objects.create(name='pual')
group_1.members.add(paul, through_defaults={'date_joined': '2022-01-01'})

其中,through_defaults 参数为字典,内容为多对多关系表的额外添加的字段。

根据 Group 使用 create 创建一条多对多记录:

如果没有相应的 Person 记录,可以根据 Group 来直接创建

group_1.members.create(name='mary', through_defaults={'date_joined': '2022-01-01'})

根据 Group 使用 set 刷新多对多记录:

使用 set 方法来设置多对多的关系:

jack = Person.objects.create(name='jack')
lucy = Person.objects.create(name='lucy')
group_1.members.set([jack, lucy], through_defaults={'date_joined': '2022-01-01'})

需要注意的是,使用 set() 方法加上关联之后,这个 Group 实例之前设置的关联数据都会被清除。

也就是说,set() 里设置的关联数据就是最终所有的关联数据。

根据 Group 使用 remove 删除一条多对多记录:

在上面 我们使用了 set() 方法设置了两条关联数据,jack 和 lucy,现在我们想要把 jack——group_1 这条关系删除,可使用 remove() 方法:

group_1.members.remove(jack)

使用 clear 清除某个 Group 实例上所有关系:

group_1.members.clear()

多对多的搜索:

根据 Person 的条件搜索 Group 的数据:

比如搜索 Person 的 name 字段为 'hunter' 的 Group 数据

Group.objects.filter(members__name='hunter')

根据 Group 的条件搜索 Person 的数据:

比如搜索 Group 的 name 字段为 'group_1' 的Person关联数据:

Person.objects.filter(group__name='group_1')

如果要搜索额外的关联字段:

Person.objects.filter(group__name='group_1', membership__date_joined='2022-01-01')

5、OneToOne 介绍

不同于 多对一和多对多的关系,OneToOne 是一对一的关系,也就是说 一条数据仅能被另一条数据关联。

下面是两个 OneToOne 对应的 model:

class Place(models.Model):
    name = models.CharField(max_length=50)
    address = models.CharField(max_length=80)

    def __str__(self):
        return "%s the place" % self.name


class Restaurant(models.Model):
    place = models.OneToOneField(Place, on_delete=models.CASCADE, default=None, related_name='place_restaurant', null=True)

接下来创建两条数据:

r_1 = Restaurant.objects.create()
p_1 = Place.objects.create(name='beijing', address='beijing')

根据 Restaurant 获取 Place 数据,直接根据字段获取数据:

r_1.place

如果根据 Place 获取 Restaurant 数据,因为是 OneToOne 的关系,所以可以直接获取:

上面的 model 中我们定义了 related_name,所以是:

p_1.place_restaurant

如果没有定义 related_name 和 related_query_name 那么就是 model 的小写:

p_1.restaurant

但是从 Place 到 Restaurant 获取数据,如果没有这种 OneToOne 的对应,比如我们上面直接创建的 p_1,使用这种方式获取关联数据就会报错,因为没有这种 OneToOne 的数据。

那么这个时候我们可以先判断是否有对应的这种 OneToOne 的数据:

hasattr(p_1, 'place_restaurant')  # 返回的是布尔型数据 

# 或者 
getattr(p_1, 'place_restaurant', None)

以上就是这篇笔记全部内容,接下来将要介绍 model 里的 Meta 的用法。

如果想获取更多相关文章,可扫码关注阅读: