Files
DailyChallenge_ProgrammingD…/Day_2/README.md
T

6.4 KiB
Raw Blame History

Day_2 · Django 视角下的 Python 基础

今天还不用学 Django,也不需要会面向对象。 下面的题目只用到基础语法(字典、列表、字符串、函数、异常处理), 但每一题都是在模拟后端开发中真正会遇到的场景。 做完你会发现:Django 里那些看似神秘的东西,底层其实就是这些基础语法。

今日知识点 → 以后在 Django / Celery 里对应什么

今天练的 以后在 Django/Celery 里会看到
字典、列表的增删改查 ORM 查询结果、request.GET/request.POST、JSON 响应
字符串分割与格式化 URL 路由解析、日志输出、模板渲染
函数、*args/**kwargs 视图函数 def index(request, **kwargs)、中间件、信号
异常处理(自定义异常 + try/except) Http404、ValidationError、Celery 任务失败与重试

题目一(必做):解析 URL 查询参数

背景:浏览器访问 https://example.com/register?username=zhangsan&age=20&city=qingdao, ? 后面的 username=zhangsan&age=20&city=qingdao 叫做查询参数(query string)。 Django 里 request.GET 就是帮你解析好的字典。今天我们手写一个解析器,看清它背后发生了什么。

要求:写函数 parse_query_string(query),输入一行查询参数(不含 ?),返回一个字典。

必须处理的边界情况:

  1. 空输入 → 返回空字典 {}
  2. 重复键,如 name=a&name=b → 后者覆盖前者:{'name': 'b'}
  3. 参数没有 =,如 flag → 值为空字符串:{'flag': ''}
  4. 值为空,如 name= → {'name': ''}

输入输出示例

输入: username=zhangsan&age=20&city=qingdao
输出: {'username': 'zhangsan', 'age': '20', 'city': 'qingdao'}

输入: name=a&name=b&flag
输出: {'name': 'b', 'flag': ''}

输入: (空字符串)
输出: {}

进阶(加分):URL 编码还原——把值中的 + 还原为空格,%20 也还原为空格。 如 name=Zhang+San → {'name': 'Zhang San'}。

提示:

  • 先 split('&') 把整体切成若干段,再对每一段 split('=', 1)。 想想为什么 maxsplit 要写成 1?(如果值里本身就含 = 会怎样?)
  • 字典赋值 d[key] = value 天然处理了"覆盖"和"空值"两种情况。

出题意图:Django 的 QueryDict(request.GET)底层干的就是这件事。 把它吃透,以后看到 request.GET.get('page') 就不再是黑魔法,而是老朋友。


题目二(必做):万能日志记录器(函数 + *args/**kwargs)

背景:后端程序要记录"哪个函数被调用了、用了多久",这叫日志(logging)。 Django 中间件、Celery 任务、接口调用统计里,到处都是这种"给函数包一层"的操作。

要求:写函数 log_call(func),它接收一个函数作为参数,返回一个新函数。 新函数在被调用时:

  1. 打印 [LOG] 调用函数 <函数名>,参数为 args=(...), kwargs={...}
  2. 调用原函数,拿到结果
  3. 打印 [LOG] <函数名> 执行完成,耗时 x.xxxx 秒(用 time.time() 算耗时)
  4. 返回原函数的结果

参数要用 *args 和 **kwargs 打包——因为你不知道原函数会接收什么参数。

示例

import time

def add(a, b):
    return a + b

logged_add = log_call(add)
print(logged_add(1, 2))
# [LOG] 调用函数 add,参数为 args=(1, 2), kwargs={}
# [LOG] add 执行完成,耗时 0.0001 秒
# 3

进阶(加分):给 log_call 加第二个参数 label(默认空字符串), 打印日志时带上标签,如 log_call(add, label="核心计算") → [核心计算][LOG] ...。

提示:如果你已经看过装饰器语法,可以用 @log_call 直接写在函数头上; 没学过就写成 log_call(add)(1, 2) 这种普通调用,两种都行。

出题意图:Django 视图函数接收 request、URL 参数通过 **kwargs 传进来; Celery 的任务、日志中间件全部建立在"函数作为参数 + *args/**kwargs"之上。 这是后端开发里出现频率最高的 Python 技巧,没有之一。


题目三(进阶):模拟注册接口的参数校验

背景:后端接口收到请求后的第一件事永远是校验参数。 字段缺失、格式不对,都要返回明确的错误信息,绝不能带着脏数据往下走。 Django 里抛 ValidationError、Celery 里任务失败后重试——这一切的起点都是 try/except。

要求:

  1. 定义一个字典模拟前端传来的注册数据: data = {"username": "zhangsan", "email": "zs@163.com", "age": "20"}(注意 age 是字符串)
  2. 自定义一个异常类 ValidationError(Exception)。
  3. 写函数 validate_registration(data),按以下规则校验,任何一个不满足就抛 ValidationError:
    • username 必须存在且长度 ≥ 3
    • email 必须存在且同时包含 @ 和 .
    • age 必须存在、能转成 int、且 1 <= age <= 120
  4. 在调用方用 try/except 捕获:失败打印 校验失败: <异常信息>,成功打印 注册成功,欢迎 <username>!

进阶(加分):把规则本身做成数据——写一个通用函数 validate(data, rules),其中 rules 是一个字典: {字段名: (校验函数, 错误信息)},返回所有不满足的字段的错误列表。 (提示:校验函数就是你平时写的普通函数或 lambda。) 这是后端参数校验框架(比如 DRF 的 Serializer)的雏形,能想出来说明你已经开始有"抽象"的意识了。

出题意图:Django forms / serializers 的校验、Celery 任务的失败处理, 核心模式都是"校验 → 抛异常 → 上层捕获处理"。 今天的题目就是把这个模式焊死在你的肌肉记忆里。


完成标准

  • 题目一 4 个边界情况全部通过(含进阶则 +1)
  • 题目二能打印出完整的调用日志(含进阶则 +1)
  • 题目三能对"缺字段 / 格式错 / 年龄越界"三种情况分别报错(含进阶则 +1)

完成后打开 参考解答.py 对照。建议看完后关掉文件,自己再默写一遍。

写给还没开始的你:这三天是递进关系—— Day_2 用基础语法模拟后端场景,Day_3 用面向对象复刻 ORM 的雏形,Day_4 真正上手 Django。 每天只做必做题,也足够支撑你开学后跟上节奏。