类Twitter应用创建Tweet遇NOT NULL约束错误:如何返回用户名而非ID?
解决创建Tweet时的NOT NULL约束错误 & 返回作者用户名的问题
我来帮你分析下问题所在,然后一步步解决:
问题根源
你在TweetSerializer里把author字段改成了SerializerMethodField(),这个字段是只读的——它只会在序列化(把数据库的Tweet转成JSON返回)时生效,用来返回用户名,但在反序列化(把前端的JSON转成数据库的Tweet实例)时,这个字段不会处理传入的数据,导致模型的author字段没有被赋值,触发了NOT NULL constraint failed错误。
另外,你的get_author方法里还有个小错误:模型里关联User的字段是author,所以应该用obj.author.username,而不是obj.user.username,否则序列化时也会报错。
解决方案
根据你的需求(返回作者用户名,同时能正常创建Tweet),分两种常见场景处理:
场景1:用户只能创建自己的推文(类Twitter的常规逻辑)
这种情况下不需要前端传作者信息,直接用当前登录的用户作为作者,最安全也符合逻辑:
class TweetSerializer(serializers.ModelSerializer): # 只读字段,用来返回作者用户名 author = serializers.SerializerMethodField(read_only=True) class Meta: model = Tweet fields = ["content", "author", "nb_likes", "nb_comments", "slug"] # 设置自动生成/后端维护的字段为只读,避免前端传入无效数据 extra_kwargs = { "slug": {"read_only": True}, "nb_likes": {"read_only": True}, "nb_comments": {"read_only": True} } def get_author(self, obj): # 修正模型关联字段的调用 return obj.author.username def create(self, validated_data): # 从请求上下文获取当前登录用户,赋值给author validated_data["author"] = self.context["request"].user return super().create(validated_data)
对应的前端请求只需要传content即可:
{ "content": "blablabla" }
场景2:需要前端指定作者用户名(比如管理员创建推文)
如果必须让前端通过用户名指定作者,需要添加一个可写的临时字段来接收用户名,然后在创建时查询用户实例:
class TweetSerializer(serializers.ModelSerializer): # 只读字段,返回作者用户名 author = serializers.SerializerMethodField(read_only=True) # 可写字段,仅用来接收前端传入的用户名,不会返回给前端 author_username = serializers.CharField(write_only=True) class Meta: model = Tweet fields = ["content", "author", "author_username", "nb_likes", "nb_comments", "slug"] extra_kwargs = { "slug": {"read_only": True}, "nb_likes": {"read_only": True}, "nb_comments": {"read_only": True} } def get_author(self, obj): return obj.author.username def create(self, validated_data): # 取出前端传入的用户名,查询对应的User实例 username = validated_data.pop("author_username") try: user = User.objects.get(username=username) except User.DoesNotExist: raise serializers.ValidationError("指定的用户名不存在") # 把查询到的用户赋值给author字段 validated_data["author"] = user return super().create(validated_data)
对应的前端请求:
{ "content": "blablabla", "author_username": "valentin" }
额外注意点
- 模型里的
slug是通过save方法自动生成的,所以前端不需要传,而且要在Serializer里设为只读,避免前端传入的slug被后端覆盖或者引发冲突。 nb_likes和nb_comments这类统计字段,建议由后端逻辑维护(比如点赞时更新),不要让前端传入,防止恶意修改数据。
内容的提问来源于stack exchange,提问作者Valentin
相关产品推荐
相关产品推荐

