Python 现代开发学习笔记

(十二)ModelForm 与当前用户的字段规则

# 项目文件:reading_project/src/library/forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm

from .models import Book


class SignupForm(UserCreationForm):
    # 使用 Django 已提供的用户名、两次密码和密码规则检查。
    pass


class BookForm(forms.ModelForm):
    tags_text = forms.CharField(
        label="标签", required=False, help_text="用英文逗号分隔,最多 10 个标签。"
    )

    class Meta:
        model = Book
        fields = ["title", "finished", "notes", "tags_text"]  # owner 不交给客户端赋值。
        widgets = {"notes": forms.Textarea(attrs={"rows": 4, "maxlength": 2000})}

    def __init__(self, *args, user, **kwargs):
        self.user = user
        super().__init__(*args, **kwargs)
        if self.instance.pk and not self.is_bound:
            self.initial["tags_text"] = ", ".join(
                tag.name for tag in self.instance.tags.all()
            )

    def clean_title(self):
        title = self.cleaned_data["title"]
        same_title = Book.objects.filter(owner=self.user, title=title).exclude(
            pk=self.instance.pk
        )
        if same_title.exists():
            raise forms.ValidationError("书架里已经有同名的书")
        return title

ModelForm.Meta.model 连接模型,fields 列出页面允许编辑的字段。owner 没在列表里,所以客户端多带一个同名字段也不会给书籍指定所有者;服务函数从当前用户赋值。

user 是表单额外接收的关键字参数,用于检查当前书架中的重名。clean_title 在基本字段转换之后读取合格书名,再排除正在编辑的自己。未绑定的编辑表单用现有标签初始化;绑定失败时保留本次输入,而不是被旧标签覆盖。

# 续写同一文件:reading_project/src/library/forms.py
    def clean_tags_text(self):
        # 先拆分和去空白,再按第一次出现的次序去重。
        tags = list(
            dict.fromkeys(
                piece.strip()
                for piece in self.cleaned_data["tags_text"].split(",")
                if piece.strip()
            )
        )
        if len(tags) > 10 or any(len(tag) > 30 for tag in tags):
            raise forms.ValidationError("最多 10 个标签,每项不超过 30 个字符")
        return tags

tags_text 是自己声明的普通表单字段,转换结果为去重标签列表;实际多对多关系由服务保存。备注的长度规则来自模型 max_length,表单和后台也会执行它。

form.save(commit=False) 构造或更新 Python 模型对象,暂不写入。这样可以先设置归属,再在同一事务中保存模型与标签。ModelForm 的保存过程可对照 Django ModelForm。

ModelForm 从模型取得字段规则,当前用户参与重名检查,排除 owner 输入,标签文本转成列表,commit=False 先得到尚未保存的模型对象

笔记正文与全部题解 · Ctrl / ⌘ K