Files
DailyChallenge_ProgrammingD…/Day_6/README.md
T

76 lines
3.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 的"基础 → 函数 → 面向对象"闭环就完成了。