You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django多对多关系表单报错:TypeError: Field 'id'期望数字却得到<Profile: user1>

问题分析与解决:推荐应用提交报错Field 'id' expected a number but got <Profile: user1>

问题描述

开发一款推荐应用,用户可向多位用户推荐影视、书籍。表单显示所有用户列表,勾选目标用户提交推荐时,出现错误:Field 'id' expected a number but got <Profile: user1>。尝试修改表单ModelMultipleChoiceField的queryset为values_list,仅返回类似(1,1)的元组,无法显示用户名也无法保存数据。

models.py

from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    follows = models.ManyToManyField( 
        'self',
        related_name='followed_by',
        symmetrical=False,
        blank=True,
    )

    def __str__(self):
        return self.user.username
    
class Recommendation(models.Model):
    user = models.ForeignKey(
        User, related_name="recs", on_delete=models.DO_NOTHING
    )
    recipients = models.ManyToManyField(
        User, related_name="received_recs", symmetrical=False, blank=True
    )
    title = models.CharField(max_length=300)
    description = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return (
            f"{self.user} "
            f"({self.created_at:%y-%m-%d %H:%M}): "
            f"{self.title}"
        )

views.py

from django.shortcuts import render, redirect
from .models import Profile, Recommendation
from .forms import RecForm

def dashboard(request):
  all_recs = Recommendation.objects.all()
  if request.method == "POST":
    form = RecForm(request.POST or None)
    if form.is_valid():
      rec = form.save(commit=False)
      rec.user = request.user
      rec.save()
      form.save_m2m()
      return redirect("watchthisshit:dashboard")
  form = RecForm()
  return render(request, "watchthisshit/dashboard.html", {
    "all_recs": all_recs,
    "form": form
  })

forms.py

from django import forms
from django.db.models.base import Model
from .models import Profile, Recommendation

class RecForm(forms.ModelForm):
  title = forms.CharField(required=True)
  description = forms.CharField(widget=forms.widgets.Textarea(
    attrs={
      "placeholder": "You don't have to write a description, but it would be pretty cool if you did...",
      "class": "textarea is-success is-medium"
    }
  ), label="",)
  recipients = forms.ModelMultipleChoiceField(
    queryset=Profile.objects.all(),
    widget=forms.CheckboxSelectMultiple
  )

  class Meta:
    model = Recommendation
    fields = ["title", "description", "recipients"]
    exclude = ("user", )

错误原因

表单的recipients字段关联了Profile模型,但Recommendation模型的recipients字段实际关联的是User模型。提交表单时,表单传递的是Profile对象,而数据库期望接收User的ID,导致类型不匹配,触发报错。

之前尝试用values_list的方式无效,因为ModelMultipleChoiceField需要接收模型实例的queryset,而非元组数据,无法正确识别和保存对象。

解决步骤

方案一:调整表单关联User模型(推荐)

修改forms.py中的recipients字段,将queryset改为User.objects.all(),同时可自定义显示文本:

class RecForm(forms.ModelForm):
  title = forms.CharField(required=True)
  description = forms.CharField(widget=forms.widgets.Textarea(
    attrs={
      "placeholder": "You don't have to write a description, but it would be pretty cool if you did...",
      "class": "textarea is-success is-medium"
    }
  ), label="",)
  recipients = forms.ModelMultipleChoiceField(
    queryset=User.objects.all(),
    widget=forms.CheckboxSelectMultiple,
    # 自定义显示用户名,若需显示Profile中字段可调整逻辑
    label_from_instance=lambda obj: obj.username
  )

  class Meta:
    model = Recommendation
    fields = ["title", "description", "recipients"]
    exclude = ("user", )

方案二:修改模型关联Profile(若业务需要)

如果业务逻辑更适合关联Profile而非User,修改models.py的Recommendation模型:

class Recommendation(models.Model):
    user = models.ForeignKey(
        User, related_name="recs", on_delete=models.DO_NOTHING
    )
    # 将关联对象改为Profile
    recipients = models.ManyToManyField(
        Profile, related_name="received_recs", symmetrical=False, blank=True
    )
    title = models.CharField(max_length=300)
    description = models.TextField(blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return (
            f"{self.user} "
            f"({self.created_at:%y-%m-%d %H:%M}): "
            f"{self.title}"
        )

此方案下,原表单的Profile queryset与模型匹配,无需修改表单,且Profile的__str__方法已返回用户名,显示正常。

额外提示

  • 若使用方案一,如需显示Profile中的自定义字段(如昵称),可调整label_from_instance逻辑:lambda obj: obj.profile.nickname if hasattr(obj, 'profile') else obj.username
  • 执行模型修改后,需运行python manage.py makemigrations和python manage.py migrate更新数据库。

内容的提问来源于stack exchange,提问作者Sarah

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 08:10:40