自定义模型字段要写哪几个方法?各自什么时候被调用?为什么 deconstruct 那么重要?这篇写一个最小的 CSVField,给每个方法打点,把调用时机一步步记下来。
构造 Post(tags=["a","b"]) → []
save() → ["get_prep_value(['a', 'b'])"]
Post.objects.get(pk) → ["from_db_value('a,b')"] tags = ['a', 'b']
Post.objects.filter(tags="a,b") → ["get_prep_value('a,b')", "from_db_value('a,b')"]
Post.objects.filter(tags=["a","b"]) → ["get_prep_value(['a', 'b'])", "from_db_value('a,b')"]
values_list("tags") → ["from_db_value('a,b')"] [['a', 'b']]
full_clean() → ["to_python(['a', 'b'])"]
serializers.serialize("json") → ['value_to_string', "get_prep_value(['a', 'b'])"] 输出: [{"model": "lab.post", "pk": 1, "fields": {"title": "t", "tags": "a,b"}}]规律:Python → 数据库用 get_prep_value(保存、过滤都走它);数据库 → Python用 from_db_value(取对象、values()、values_list() 都走它);校验 / 反序列化用 to_python;序列化用 value_to_string。
sqlmigrate:
BEGIN;
--
-- Create model Post
--
CREATE TABLE "lab_post" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "title" varchar(20) NOT NULL, "tags" text NOT NULL);
COMMIT;
迁移文件里: ('tags', lab.fields.CSVField(default=list)),
deconstruct(separator=";") → ('lab.fields.CSVField', [], {'separator': ';'})
deconstruct(默认) → ('lab.fields.CSVField', [], {})继承 TextField 时列类型自然是 text;要换类型就实现 db_type(本文没有测)。迁移文件里字段怎么写,由 deconstruct() 返回的 (name, path, args, kwargs) 决定。
改 separator=";" 后 makemigrations: [' lab/migrations/0002_alter_post_tags.py', ' ~ Alter field tags on post', "Full migrations file '0002_alter_post_tags.py':"] 同样的修改,但 deconstruct 里没有写 separator: No changes detected in app 'lab'
Django 用 deconstruct() 比较新旧字段是否相同。只要你的字段有构造参数,就必须在 deconstruct 里返回,否则改参数不会生成迁移,数据库和代码悄悄不一致。