0%

Django(十)模型:django模型类对数据库的:增/删/改/查、自关联、管理器、元选项(指定表名)

详情请点阅读全文

一、插入、更新和删除

  • 调用一个模型类对象的save方法的时候就可以实现对模型类对应数据表的插入和更新。
  • 调用一个模型类对象的delete方法的时候就可以实现对模型类对应数据表数据的删除。

二、自关联

在这里插入图片描述

自关联是一种特殊的一对多的关系。

【案例】:显示广州市的上级地区和下级地区。

  • 地区表:id, atitle, aParent_id;
  • mysql终端中批量执行sql语句:source areas.sql;

第1步,添加地区模型类 app1/models.py

【关系属性】,代表当前地区的父级地区

1
2
3
4
5
6
7
8
9
class AreaInfo(models.Model):
'''地区模型类'''
# 地区名称
atitle = models.CharField(max_length=20)
# 【关系属性】,代表当前地区的父级地区
aParent = models.ForeignKey('self', null=True, blank=True)

# class Meta:
# db_table = 'areas'

第2步,创建迁移,应用迁移创建对应表

1
2
3
4
5
6
7
8
9
10
11
D:\a\django-app\project1>py manage.py makemigrations
Migrations for 'app1':
app1\migrations\0004_auto_20200107_1148.py
- Alter field btitle on bookinfo
- Create model AreaInfo

D:\a\django-app\project1>py manage.py migrate
Operations to perform:
Apply all migrations: admin, app1, auth, contenttypes, sessions
Running migrations:
Applying app1.0004_auto_20200107_1148... OK

查看表mysql命令窗:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sakila |
| spiders |
| sys |
| test888 |
| world |
+--------------------+
8 rows in set (0.07 sec)

mysql> use test888
Database changed
mysql> show tables;
+----------------------------+
| Tables_in_test888 |
+----------------------------+
| app1_areainfo |
| app1_bookinfo |
| app1_heroinfo |
| auth_group |
| auth_group_permissions |
| auth_permission |
| auth_user |
| auth_user_groups |
| auth_user_user_permissions |
| django_admin_log |
| django_content_type |
| django_migrations |
| django_session |
+----------------------------+
13 rows in set (0.00 sec)

mysql> desc app1_areainfo;
+------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| atitle | varchar(20) | NO | | NULL | |
| aParent_id | int(11) | YES | MUL | NULL | |
+------------+-------------+------+-----+---------+----------------+
3 rows in set (0.11 sec)

第3步,把地区数据表导入刚建的表

  • 表位置:q网盘\源码【area.sql】
  • 把例如:
    INSERT INTO app1_areainfo VALUES ('110000', '北京市', NULL);
    中的【app1_】改成 【对应的表名_】
进入sql命令窗:即可导入(路径不要有中文空格等)
1
2
3
4
use test888; #使用对应的表
source E:/pro_sql/test.sql; #导入数据

#执行效果: Query OK, 1 row affected (0.12 sec)

查询刚导入的表效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
select * from app1_areainfo;
| 659000 | 省直辖行政单位 | 650000 |
| 659001 | 石河子市 | 659000 |
| 659002 | 阿拉尔市 | 659000 |
| 659003 | 图木舒克市 | 659000 |
| 659004 | 五家渠市 | 659000 |
| 990000 | 新疆建设兵团 | NULL |
| 990100 | 第一师 | 990000 |
| 990200 | 第二师 | 990000 |
| 990300 | 第三师 | 990000 |
| 990400 | 第四师 | 990000 |
| 990500 | 第五师 | 990000 |
| 990600 | 第六师 | 990000 |
| 990700 | 第七师 | 990000 |
| 990800 | 第八师 | 990000 |
| 990900 | 第九师 | 990000 |
| 991000 | 第十师 | 990000 |
| 991100 | 建工师 | 990000 |
| 991200 | 第十二师 | 990000 |
| 991300 | 第十三师 | 990000 |
| 991400 | 第十四师 | 990000 |
+--------+-----------------------------------------------+------------+

第4步,app1/views.py 编辑对应函数,读取地区信息

1
2
3
4
5
6
7
8
9
10
11
12
from booktest.models import BookInfo,AreaInfo

def areas(request):
'''获取广州市的上级地区和下级地区'''
# 1.获取广州市的信息
area = AreaInfo.objects.get(atitle='广州市')
# 2.查询广州市的上级地区
parent = area.aParent
# 3.查询广州市的下级地址
children = area.areainfo_set.all()
# 使用模板
return render(request, 'app1/areas.html', {'area':area,'parent':parent, 'children':children})

第5步,配置app1/urls.py

展示对应省市区信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from django.urls import path,re_path
from . import views

urlpatterns=[
path('app1/',views.index),
path('books/',views.books),

# 书详情页,通过url接收参数2种写法以下两种都可:
# path(r"detail/<int:bookId>",views.detail), #参数用尖括号包起来<>
re_path(r"^detail/(\d+)",views.detail), #参数必须要带括号

path('addInfo/',views.addInfo), #添加三国书

path(r'delete/<int:bid>',views.deleteInfo), #删除对应图书

path(r'area/',views.areas), #展示对应省市区信息
]

第6步,添加展示模板templates/app1/areas.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>自关联案例</title>
</head>
<body>
<h1>当前地区</h1>
{{ area.atitle }}<br/>
<h1>父级地区</h1>
{{ parent.atitle }}<br/>
<h1>下级地址</h1>
<ul>
{% for child in children %}
<li>{{ child.atitle }}</li>
{% endfor %}
</ul>
</body>
</html>

效果:

当前地区:
广州市

父级地区:
广东省

下级地址:
荔湾区
越秀区
海珠区
天河区
白云区
黄埔区
番禺区
花都区
南沙区
萝岗区
增城市
从化市

三、管理器(Manager)

问:BookInfo.objects.all()->objects是一个什么东西呢?

  • 答:objects是Django帮我自动生成的管理器对象,通过这个管理器可以实现对数据的查询。
  • objects是models.Manger类的一个对象。自定义管理器之后Django不再帮我们生成默认的objects管理器。
1
2
3
4
py manage.py shell
>>> from app1.models import BookInfo,HeroInfo
>>> type(BookInfo.objects)
<class 'django.db.models.manager.Manager'>

1 自定义模型管理器类

1)自定义一个管理器类,这个类继承models.Manger类。
2)再在具体的模型类里定义一个自定义管理器类的对象。

例1:在app1/models.py下定义一个管理器类

自定一个Manager类对象,管理器对象 book = models.Manager()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 一类
# booktest2_bookinfo
class BookInfo(models.Model):
'''图书模型类'''
# 图书名称
btitle = models.CharField(max_length=20, db_column='title')
# 图书名字唯一
# btitle = models.CharField(max_length=20, unique=True, db_index=True)
# 价格,最大位数为10,小数为2
# bprice = models.DecimalField(max_digits=10, decimal_places=2)
# 出版日期
bpub_date = models.DateField()
# bpub_date = models.DateField(auto_now_add=True) # 创建时间
# bpub_date = models.DateField(auto_now=True) # 更新时间
# 阅读量
bread = models.IntegerField(default=0)
# 评论量
bcomment = models.IntegerField(default=0)
# 删除标记
isDelete = models.BooleanField(default=False)

book = models.Manager() # 自定一个Manager类对象,管理器对象
#objects = BookInfoManager() # 自定义一个BookInfoManager类的对象

自定义管理器类之后再查询数据就变成如下

由:BookInfo.objects.all() 变:BookInfo.book.all()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> quit()

D:\a\django-app\project1>py manage.py shell
Python 3.7.1 (v3.7.1:260ec2c36a, Oct 20 2018, 14:57:15) [MSC v.1915 64 bit (AMD6
4)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from app1.models import BookInfo

>>> BookInfo.book.all() #【变】
<QuerySet [<BookInfo: 天龙八部>, <BookInfo: 三国演义>, <BookInfo: 红楼梦>, <Book
Info: 水浒传>]>

>>> BookInfo.objects.all() #【原】
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: type object 'BookInfo' has no attribute 'objects'

2 自定义管理器类的应用场景

1)改变查询的结果集。

  • 比如调用BookInfo.books.all()返回的是没有删除的图书的数据。

    例2:自定义Manager类

    【1】自定义一个BookInfoManager类,实现查寻数据只返回isDelete=False(删除标记(伪删除))的书
    【2】使用自定义的manager类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    from django.db import models
    # 设计和表对应的类,模型类

    #【1】自定义一个BookInfoManager类
    class BookInfoManager(models.Manager): #继承自models.Manager
    '''图书模型管理器类'''
    # 1.改变原有查询的结果集
    def all(self):
    # 1.调用父类的all方法,获取所有数据
    books = super().all() # QuerySet
    # 2.对books中的数据进行过滤
    books = books.filter(isDelete=False)
    # 返回books
    return books

    # 一类
    # 图书类
    class BookInfo(models.Model):
    '''图书模型类'''
    # 图书名称,CharField说明是一个字符串,max_length指定字符串的最大长度
    btitle = models.CharField(max_length=20,unique=True) #该字段不能重复,db_column='title'
    # 出版日期,DateField说明是一个日期类型
    bpub_date = models.DateField()
    # 阅读量
    bread = models.IntegerField(default=0)
    # 评论量
    bcomment = models.IntegerField(default=0)
    # 删除标记
    isDelete = models.BooleanField(default=False)

    #book = models.Manager() # 自定一个Manager类对象,管理器对象
    objects = BookInfoManager() # 自定义一个BookInfoManager类的对象 【2】使用自定义的manager类

2)添加额外的方法。

  • 管理器类中定义一个方法帮我们操作模型类对应的数据表。
  • 使用self.model()就可以创建一个跟自定义管理器对应的模型类对象。

例3,把插入图书各个语句封装成一个BookInfo类的函数:

app1/models.py
1.【封装插入图书方法为BookInfo的函数】
2.使用时只需要 BookInfo.create_book(书标题,日期) 即可使用详情:

1
2
3
4
'''使用下一步封装的函数'''
from app1.models import BookInfo
BookInfo.create_book('test''1991-1-1')
book.btitle #(返回刚插入的标题)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
'''定义类的封装插入图书函数'''
from django.db import models

# 一类
# booktest2_bookinfo
class BookInfo(models.Model):
'''图书模型类'''
# 图书名称
btitle = models.CharField(max_length=20, db_column='title')
# 图书名字唯一
# btitle = models.CharField(max_length=20, unique=True, db_index=True)
# 价格,最大位数为10,小数为2
# bprice = models.DecimalField(max_digits=10, decimal_places=2)
# 出版日期
bpub_date = models.DateField()
# bpub_date = models.DateField(auto_now_add=True) # 创建时间
# bpub_date = models.DateField(auto_now=True) # 更新时间
# 阅读量
bread = models.IntegerField(default=0)
# 评论量
bcomment = models.IntegerField(default=0)
# 删除标记
isDelete = models.BooleanField(default=False)

# book = models.Manager() # 自定一个Manager类对象,管理器对象
#objects = BookInfoManager() # 自定义一个BookInfoManager类的对象

@classmethod #【封装插入图书方法为BookInfo的函数】
def create_book(cls, btitle, bpub_date):#cls即类名
'''添加一本图书'''
# 创建一个cls类的对象
obj = cls()
obj.btitle = btitle
obj.bpub_date = bpub_date
# 添加进数据库
obj.save()
# 返回obj
return obj

例4,一般把增、删、改、查自定义函数写到Manger自定义类里

2.封装方法,操作模型类对应的数据表(增删改查)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class BookInfoManager(models.Manager):
'''图书模型管理器类'''
# 1.改变原有查询的结果集
def all(self):
# 1.调用父类的all方法,获取所有数据
books = super().all() # QuerySet
# 2.对books中的数据进行过滤
books = books.filter(isDelete=False)
# 返回books
return books

# 2.封装方法,操作模型类对应的数据表(增删改查)
def create_book(self, btitle, bpub_date):
'''添加一本图书'''
# 1.创建一个图书对象
# 获取self所在的模型类
model_class = self.model
book = model_class()
# book = BookInfo()
book.btitle = btitle
book.bpub_date = bpub_date
# 2.添加进数据库
book.save()
# 3.返回book
return book

调用:

1
2
3
4
5
from app1.models import BookInfo

#必须通过关键字传参
BookInfo.objects.create(btitle='test3',bpub_ date='1990-10-10' )
<BookInfo:BookInfo object>

上一步写的自定义函数先注释掉:

1
2
3
4
5
6
7
8
9
10
11
# @classmethod
# def create_book(cls, btitle, bpub_date):
# '''添加一本图书'''
# # 创建一个cls类的对象
# obj = cls()
# obj.btitle = btitle
# obj.bpub_date = bpub_date
# # 添加进数据库
# obj.save()
# # 返回obj
# return obj

小结:

在这里插入图片描述
【模型管理器对象】

  1. 改变原有查询的结果集。
  2. 封装方法,用户操作管理器对象所在模型类对应的数据表。

3. 模型管理器类、模型类间关系

在这里插入图片描述

四、元选项(指定表名)

Django默认生成的表名: 应用名小写_模型类名小写。

元选项:

需要在模型类中定义一个元类Meta,在里面定义一个类属性db_table就可以指定表名。
在这里插入图片描述

应用场景:应用名(app1)和表名不一致情形

【问题】比如对应用名不满意想换个别的名字,此时表名已经生成了,这时应该怎么办呢?

  • 【解决】就可通过元选项解决。

    实战app1/models.py

    【1】指定表的名字
    1
    2
    3
    4
    5
    6
    7
    8
    9
    class AreaInfo(models.Model):
    '''地区模型类'''
    # 地区名称
    atitle = models.CharField(max_length=20)
    # 关系属性,代表当前地区的父级地区
    aParent = models.ForeignKey('self', null=True, blank=True)

    class Meta: #【1】指定表的名字
    db_table = 'areas'