76 lines
3.2 KiB
Markdown
76 lines
3.2 KiB
Markdown
# Day_6 · 面向对象(三):复刻 models.CharField(进阶)
|
||
|
||
> 这是整个系列最难的一题,做不出来很正常——卡住就回头把 Day_4 / Day_5 再过一遍。
|
||
> Django 里这样声明字段:
|
||
>
|
||
> ```python
|
||
> class Article(models.Model):
|
||
> title = models.CharField(max_length=50)
|
||
> ```
|
||
>
|
||
> `title` 是**类属性**,但每个实例的 `title` 值又各自不同——这是怎么做到的?
|
||
> 答案是 **descriptor(描述符)协议**:类属性如果是实现了 `__get__` / `__set__` 的对象,
|
||
> 那么对实例属性的一切读写都会先经过它。Django 的 Field 就是这种对象。
|
||
|
||
## 今日知识点 → 以后在 Django 里对应什么
|
||
|
||
| 今天练的 | 以后在 Django 里会看到 |
|
||
| --- | --- |
|
||
| descriptor(`__get__` / `__set__`) | Django Field 的核心机制 |
|
||
| 类属性声明字段 | `models.CharField(max_length=50)` 的真相 |
|
||
| `__init_subclass__` 元编程 | 框架"自动注册"类信息的通用手段 |
|
||
|
||
---
|
||
|
||
## 题目(必做):类属性当"字段声明"——复刻 models.CharField
|
||
|
||
**要求**:
|
||
|
||
1. 定义字段类型类(都实现 `__get__` 和 `__set__`):
|
||
|
||
- `CharField(max_length)`:`__set__` 里校验"必须是字符串、长度 ≤ max_length",否则抛 `ValueError`
|
||
- `EmailField`:继承 `CharField`,额外校验必须含 `@` 和 `.`
|
||
- `IntegerField(min_value, max_value)`:校验"必须是 int(注意 `True` 不算)、在范围内"
|
||
|
||
值的存储要写到**实例自己的** `__dict__` 里;还没赋过值时返回一个哨兵对象
|
||
(用一个模块级 `_NOT_SET = object()`)。`__get__` 里 `instance is None` 表示
|
||
"通过类访问",此时应返回描述符本身(这样 `Article.title` 看到的是 Field 对象,
|
||
这正是 Django 里 `Article.title` 的行为)。
|
||
|
||
2. 写一个基类 `ModelBase`,用 `__init_subclass__`(在子类定义完成后自动调用)
|
||
遍历子类的 `cls.__dict__`,把每个 Field 对象的 `.name` 填上它的属性名。
|
||
这样描述符才知道自己对应哪个属性。
|
||
|
||
3. 定义 `Article(ModelBase)`,用类属性声明字段:
|
||
|
||
```python
|
||
class Article(ModelBase):
|
||
title = CharField(max_length=50)
|
||
content = CharField(max_length=1000)
|
||
author_email = EmailField(max_length=100)
|
||
views = IntegerField(min_value=0)
|
||
```
|
||
|
||
4. 测试:
|
||
- 正常赋值、读取,一切正常
|
||
- `a.title = "x" * 100` → 抛 `ValueError`(超长)
|
||
- `a.views = -5` → 抛 `ValueError`(越界)
|
||
- `a.author_email = "not-an-email"` → 抛 `ValueError`(格式错)
|
||
- `print(Article.title)` → 打印出的是 `CharField` 对象本身
|
||
|
||
**出题意图**:这就是 Django Field 的核心机制。做完这一题,你再去看
|
||
`models.CharField(max_length=50)` 时,看到的就不是魔法,而是你今天亲手写过的代码。
|
||
|
||
---
|
||
|
||
## 完成标准
|
||
|
||
- [ ] 三种 Field 的校验全部生效
|
||
- [ ] 类访问(`Article.title`)返回描述符本身
|
||
|
||
完成后打开 `参考解答.py` 对照。
|
||
|
||
> 进度提示:Day_6 是整个系列里最难的一题,卡住很正常。
|
||
> 它对应的正是后面写真正 Django Model 时会遇到的机制。
|
||
> 这一关过了,Day_2~Day_6 的"基础 → 函数 → 面向对象"闭环就完成了。
|