模型和字段(1)
# 链接资料
一个模型(model)就是一个单独的、确定的数据的信息源,包含了数据的字段和操作方法。通常,每个模型映射为一张数据库中的表。
基本的原则如下:
- 每个模型在Django中的存在形式为一个Python类
- 每个类都是`django.db.models.Model`的子类
- 模型(类)的每个字段(属性)代表数据表的某一列
- Django自动为你生成访问数据库的API
## 1.模型
```python
from django.db import models
#定义图书模型类BookInfo
class BookInfo(models.Model):
btitle = models.CharField(max_length=20, verbose_name='名称')
bpub_date = models.DateField(verbose_name='发布日期')
bread = models.IntegerField(default=0, verbose_name='阅读量')
bcomment = models.IntegerField(default=0, verbose_name='评论量')
is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
class Meta:
db_table = 'tb_books' # 指明数据库表名
verbose_name = '图书' # 在admin站点中显示的名称
verbose_name_plural = verbose_name # 显示的复数名称
def __str__(self):
"""定义每个数据对象的显示信息"""
return self.btitle
#定义英雄模型类HeroInfo
class HeroInfo(models.Model):
GENDER_CHOICES = (
(0, 'female'),
(1, 'male')
)
hname = models.CharField(max_length=20, verbose_name='名称')
hgender = models.SmallIntegerField(choices=GENDER_CHOICES, default=0, verbose_name='性别')
hcomment = models.CharField(max_length=200, null=True, verbose_name='描述信息')
hbook = models.ForeignKey(BookInfo, on_delete=models.CASCADE, verbose_name='图书') # 外键
is_delete = models.BooleanField(default=False, verbose_name='逻辑删除')
class Meta:
db_table = 'tb_heros'
verbose_name = '英雄'
verbose_name_plural = verbose_name
def __str__(self):
return self.hname
```
### a. **数据库表名**
模型类如果未指明表名,Django默认以 **小写app应用名_小写模型类名** 为数据库表名。
可通过**db_table** 指明数据库表名。
### b. **关于主键**
django会为表创建自动增长的主键列,每个模型只能有一个主键列,如果使用选项设置某属性为主键列后django不会再创建自动增长的主键列。
默认创建的主键列属性为id,可以使用pk代替,pk全拼为primary key。
### c. **属性命名限制**
- 不能是python的保留关键字。
- 不允许使用连续的下划线,这是由django的查询方式决定的。
- 定义属性时需要指定字段类型,通过字段类型的参数指定选项,语法如下:
```
属性=models.字段类型(选项)
```
**null是数据库范畴的概念,blank是表单验证范畴的**
## 2.****模型的属性****
每个模型都可以有很多属性,其中有Django内置的,也可以有你自定义的。
模型当中最重要的属性是 Manager管理器。它是 Django 模型和数据库查询操作之间的API接口,用于从数据库当中获取数据实例。如果没有指定自定义的 Manager ,那么它默认名称是 `objects`,这是Django自动为我们提供和生成的。Manager 只能通过模型类来访问,不能通过模型实例来访问,也就是说,只能`Person.objects`,不可以`jack.objects`。
模型还有一个不为人知的隐藏属性`_state`。
`_state`属性指向一个ModelState类实例,它持续跟踪着模型实例的生命周期。
`_state`自己又有2个属性:adding和db
- adding:一个标识符,如果当前的模型实例还没有保存到数据库内,则为True,否则为False
- db:一个字符串指向某个数据库,当前模型实例是从该数据库中读取出来的。
所以:
- 对于一个新创建的模型实例:`adding=True`并且`db=None`
- 对于从某个数据库中读取出来的模型实例:`adding=False`并且`db='数据库名'`
```python
>>> blog = Blog.create('mary', 'ss')
>>> blog._state
<django.db.models.base.ModelState object at 0x00000203CD717D30>
>>> blog._state.adding
True
>>> blog._state.db
# None
```
## 3.模型方法
模型的方法其实就是Python的实例方法。Django内置了一些,我们也可以自定义一些。
在模型中添加自定义方法会给你的模型提供自定义的“行级”数据操作能力,也就是说每个模型的实例都可以调用模型方法。与之对应的是类 Manager 的方法提供的是“表级”的数据操作。
建议:**如果你有一段需要针对每个模型实例都有效的业务代码,应该把它们抽象成为一个函数,放到模型中成为模型方法,而不是在大量视图中重复编写这段代码,或者在视图中抽象成一个函数。**
下面的例子展示了如何自定义模型方法:
```python
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
def baby_boomer_status(self):
"Returns the person's baby-boomer status."
import datetime
if self.birth_date < datetime.date(1945, 8, 1):
return "Pre-boomer"
elif self.birth_date < datetime.date(1965, 1, 1):
return "Baby boomer"
else:
return "Post-boomer"
@property
def full_name(self):
"Returns the person's full name."
return '%s %s' % (self.first_name, self.last_name)
# 基本操作
>>>jack = Person.objects.get(pk=1)
>>>jack.baby_boomer_status() # 以执行函数的方式调用
# ...
>>>jack.full_name # 以属性的方式调用
# jack Tomas
```
- baby_boomer_status作为一个自定义的模型方法,可以被任何Person的实例调用,进行生日日期判断
- full_name模型方法被Python的属性装饰器转换成了一个类属性
`
Django内置了一些模型方法,有些我们直接使用即可,有些会进行自定义重写:
- `__str__()`: 这个其实是Python的魔法方法,用于返回实例对象的打印字符串。为了让显示的内容更直观更易懂,我们往往自定义这个方法:
```python
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
birth_date = models.DateField()
def __str__(self):
return self.first_name + self.last_name
```
- **`[get_absolute_url()](https://docs.djangoproject.com/zh-hans/4.1/ref/models/instances/#django.db.models.Model.get_absolute_url)`**该方法告诉 Django 如何计算一个对象的 URL。Django 在后台接口使用此方法,或任意时间它需要计算一个对象的 URL。
- `__hash__()`:实际上,Django在内部还为`models.Model`实现了`__hash__()`魔法方法,给模型实例提供唯一的哈希值。
这个方法的核心是`hash(obj.pk)`,通过模型主键的值,使用内置的hash方法生成哈希值。如果实例还未保存,没有主键值,显然会发生错误。哈希值一旦生成就不允许修改。
### a. ****重写之前定义的模型方法****
还有一个 [模型方法](https://docs.djangoproject.com/zh-hans/4.1/ref/models/instances/#model-instance-methods) 的集合,包含了一些你可能自定义的数据库行为。尤其是这两个你最有可能定制的方法 **`[save()](https://docs.djangoproject.com/zh-hans/4.1/ref/models/instances/#django.db.models.Model.save)`** 和 **`[delete()](https://docs.djangoproject.com/zh-hans/4.1/ref/models/instances/#django.db.models.Model.delete)`**。
你可以随意地重写这些方法(或其它模型方法)来更改方法的行为。
一个典型的重写内置方法的场景是你想在保存对象时额外做些事。比如(查看文档 **`[save()](https://docs.djangoproject.com/zh-hans/4.1/ref/models/instances/#django.db.models.Model.save)`** 了解其接受的参数):
```python
# 示例一
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
do_something()
super().save(*args, **kwargs) # Call the "real" save() method.
do_something_else()
# 示例二
class UserManager(BaseUserManager):
"""重写用户管理器"""
def create_user(self, email, password, **kwargs):
"""重写create_user"""
if not email:
raise ValueError("请输入邮箱")
if not password:
raise ValueError("请输入密码")
user = self.model(email=email, username='游客_{}'.format(str(bson.ObjectId())), **kwargs)
user.set_password(password)
user.save()
return user
class User(AbstractBaseUser):
"""用户表"""
app_label = 'users'
id = ShortUUIDField(primary_key=True, verbose_name='用户id')
username = models.CharField(max_length=36, db_index=True, verbose_name='用户名')
telephone = models.CharField(max_length=11, null=True, blank=True, verbose_name='手机号')
email = models.EmailField(unique=True, max_length=64, db_index=True, verbose_name='邮箱')
email_activated = models.BooleanField(default=False, verbose_name='邮箱激活状态')
gender = models.CharField(max_length=10, choices=USER_GENDER_CHOICES,
default='secrecy', verbose_name='性别')
create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
is_admin = models.BooleanField(default=False, verbose_name='是否是管理员')
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['username']
objects = UserManager()
class Meta:
"""特性"""
db_table = 'cabits_user'
verbose_name = '用户表'
app_label = 'users'
def __str__(self):
"""字符串"""
return self.username
```
你也可以阻止保存:
```python
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
if self.name == "Yoko Ono's blog":
return # Yoko shall never have her own blog!
else:
super().save(*args, **kwargs) # Call the "real" save() method.
```
调用父类的方法非常重要——这里指 **`super().save(*args, **kwargs)`** ——确保对象正确的写入数据库。若你忘记调用父类方法,默认行为不会被触发,数据库也不会被操作。
同时传递模型方法接受的参数也很重要—— **`*args, **kwargs`** 会接受这些参数。Django 会不时地扩展模型内置方法的功能,也会添加新参数。如果你在重写的方法中使用了 **`*args, **kwargs`**,这将确保你的方法能接受这些新加的参数。
**重写的模型方法不会在批量操作中调用**
注意,删除一个模型对象不总是要调用 **`[delete()](https://docs.djangoproject.com/zh-hans/4.1/ref/models/instances/#django.db.models.Model.delete)`** 方法。例如, ref:使用 QuerySet 批量删除对象 <topics-db-queries-delete> 和 **`[级联删除](https://docs.djangoproject.com/zh-hans/4.1/ref/models/fields/#django.db.models.ForeignKey.on_delete)`**。为了确保自定义的删除逻辑被执行,你可以使用 **`[pre_delete](https://docs.djangoproject.com/zh-hans/4.1/ref/signals/#django.db.models.signals.pre_delete)`** 和 **`[post_delete](https://docs.djangoproject.com/zh-hans/4.1/ref/signals/#django.db.models.signals.post_delete)`** 信号。
不幸的是,批量 **`[creating](https://docs.djangoproject.com/zh-hans/4.1/ref/models/querysets/#django.db.models.query.QuerySet.bulk_create)`** 和 **`[updating](https://docs.djangoproject.com/zh-hans/4.1/ref/models/querysets/#django.db.models.query.QuerySet.update)`** 操作不支持上述操作,因为这两种操作未调用 **`[save()](https://docs.djangoproject.com/zh-hans/4.1/ref/models/instances/#django.db.models.Model.save)`**,**`[pre_save](https://docs.djangoproject.com/zh-hans/4.1/ref/signals/#django.db.models.signals.pre_save)`** 和 **`[post_save](https://docs.djangoproject.com/zh-hans/4.1/ref/signals/#django.db.models.signals.post_save)`**。
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
编辑 (opens new window)