禁止直接赋值给多对多集合的前向侧。请使用 emails_for_help.set() 代替。
- 2025-04-16 08:57:00
- admin 原创
- 14
问题描述:
我是 Django 新手,没有找到任何关于此问题的参考资料。我在 Django 模型 () 中使用多对多字段时出现此错误models.py
。我猜问题出在views.py
form( forms.py
) 中的 view() 中分配了 m2m 字段。
如何在视图中分配 m2m 字段?(Django version 2.0
,python - 3.5
)
模型.py
class User(AbstractUser):
username=models.CharField(max_length=20)
email = models.EmailField(_('email address'), unique=True)
class Setupuser(models.Model):
organization=models.CharField(max_length=200,blank=False,null=True)
emails_for_help = models.ManyToManyField(User)
视图.py
class Set_user(FormView):
template_name="pkm_templates/set_up_user.html"
form_class = Set_User_Form
success_url = '/thanks/'
def form_valid(self, form):
org = form.cleaned_data.get('organization')
emails = form.cleaned_data.get("emails_for_help")
instance = Setupuser(organization=org,emails_for_help=emails)
instance.save()
return redirect("/")
表单.py
class Set_User_Form(ModelForm):
emails_for_help = forms.ModelMultipleChoiceField(
queryset=User.objects.all(),
widget=forms.CheckboxSelectMultiple
)
class Meta:
model = Setupuser
fields = ["organization","emails_for_help"]
解决方案 1:
您需要获取User
对象,然后将其添加到emails_for_help
字段中。创建实例时无法添加对象ManyToManyField
。请查看文档。
class Set_user(FormView):
template_name="pkm_templates/set_up_user.html"
form_class = Set_User_Form
success_url = '/thanks/'
def form_valid(self, form):
org = form.cleaned_data.get('organization')
emails = form.cleaned_data.get("share_email_with")
users = User.objects.filter(email__in=emails)
instance = Setupuser.objects.create(organization=org)
for user in users:
instance.emails_for_help.add(user)
return redirect("/")
另一种方法是使用.set()
。
class Set_user(FormView):
template_name="pkm_templates/set_up_user.html"
form_class = Set_User_Form
success_url = '/thanks/'
def form_valid(self, form):
org = form.cleaned_data.get('organization')
emails = form.cleaned_data.get("share_email_with")
users = User.objects.filter(email__in=emails)
instance = Setupuser.objects.create(organization=org)
instance.emails_for_help.set(users)
return redirect("/")
或者您可以简单地使用它.add()
来添加任意数量的对象。
class Set_user(FormView):
template_name="pkm_templates/set_up_user.html"
form_class = Set_User_Form
success_url = '/thanks/'
def form_valid(self, form):
org = form.cleaned_data.get('organization')
emails = form.cleaned_data.get("share_email_with")
users = User.objects.filter(email__in=emails)
instance = Setupuser.objects.create(organization=org)
instance.emails_for_help.add(*users)
return redirect("/")
解决方案 2:
我尝试了上述所有解决方案,但在 Django 3.0 上都不起作用。因此,我做了一些研究,最终找到了解决方案。这个解决方案其实很简单。我的答案很简单。假设存在一个表单字段specialFieldName
,它被定义为ManyToManyField
in models.py
。
为什么会出现这个错误?
用户通过“Django 表单”输入的此字段的选项存储为QuerysetManyToManyField
。在这种情况下,我们根本无法在基于此查询集创建对象时将此查询集分配给属性。这就是您收到上述错误的原因。
因此,我们首先根据从 django-form 获得的所有信息创建对象,specialFieldName
然后使用 add() 方法将此查询集的所有元素添加到我们刚刚创建的对象中。
因此,我们需要迭代这个查询集。
returnedQueryset = form.cleaned_data.get('specialFieldName')
dummyObject = ObjectModel.objects.create(..using all the info except specialFieldname..)
for member in returnedQueryset:
dummyObject.add(member)
不幸的是,循环并没有遍历 returnedQueryset 的所有成员(阅读为什么?)。因此,上面的方法不起作用。我们需要更高级一点,改用 iterator() 方法。
for member in returnedQueryset.iterator():
dummyObject.add(member)
现在一切正常。
(PS:这是我在 Stackoverflow 上的第一个答案。感谢我所有的导师 ;-))
解决方案 3:
尝试此代码,因为我在使用时遇到了同样的问题ManyToManyField
。
class Set_user(FormView):
template_name="pkm_templates/set_up_user.html"
form_class = Set_User_Form
success_url = '/thanks/'
def form_valid(self, form):
org = form.cleaned_data.get('organization')
instance = Setupuser(organization=org,emails_for_help=emails)
instance.save()
instance.email.add(request.user.email)
instance.save()
return redirect("/")
解决方案 4:
Direct assignment to the forward side of a many-to-many set is prohibited. Use plan_options.set() instead
从旧版本 Django 升级到 Django 2.2 LTS 时,我遇到了同样的错误。
但是当我使用时,.set()
我得到了SyntaxError: can't assign to function call
。对我来说,这个问题通过添加以下内容得到了解决_set
:
existing_plan.plan_options_set = [x.pk for x in old_plan.plan_options.all()]
解决方案 5:
我可能没补充太多,但我在为一个完全没有表单的 REST API 进行单元测试时遇到了这个错误,所以我想提供一个示例,说明我是如何在这种情况下修复它的。我使用的是 Django 4.1.7 和 Django Rest Framework 3.14.0。
这是一个 Pokemon 卡片项目,因此有一个 PokemonType 模型(表示“火”、“水”、“草”等类型以及它们的抵抗力和弱点):
class PokemonType(models.Model):
name = models.CharField(max_length=100, blank=False)
strong_vs = models.ManyToManyField('self', symmetrical=False, related_name="strong_versus")
weak_vs = models.ManyToManyField('self', symmetrical=False, related_name="weak_versus")
resistant_to = models.ManyToManyField('self', symmetrical=False, related_name="resists")
vulnerable_to = models.ManyToManyField('self', symmetrical=False, related_name="vulnerable")
我想编写一个简单的测试来验证是否可以创建 PokemonType,但是这个测试导致了Direct assignment to the forward side of a many-to-many set is prohibited
错误:
class PokemonTypeModelTests(TestCase):
def test_create_pokemontype(self):
pokemon_type = PokemonType.objects.create(
name = 'type 1',
strong_vs = [],
weak_vs = [2],
resistant_to = [3, 4],
vulnerable_to = [2, 5]
)
pokemon_type_from_db = PokemonType.objects.get(id=pokemon_type.id)
self.assertEqual(pokemon_type_from_db.name, 'type 1')
人们可能会认为创建对象然后添加这样的关系可能会有所帮助:
pokemon_type = PokemonType.objects.create(name='type 1')
pokemon_type.resistant_to.set([3, 4])
但这会引发错误django.db.utils.IntegrityError: The row in table 'api_pokemontype_resistant_to' with primary key '1' has an invalid foreign key: api_pokemontype_resistant_to.to_pokemontype_id contains a value '3' that does not have a corresponding value in api_pokemontype.id.
发生这种情况是因为 ID 为 3 和 4 的 PokemonType 对象尚不存在(请记住,Django 为了测试目的创建了一个特殊的数据库)。多对多关系实际上是在数据库中作为单独的表实现的,Django 需要这些对象确实存在于数据库中,然后才能在它们之间添加关系。
这就是为什么在这种情况下的解决方案是首先创建所有对象,将它们添加到数据库,然后用于set()
创建多对多关系:
def test_create_pokemontype(self):
# this is the "main" object:
pokemon_type = PokemonType.objects.create(name='type 1')
# these are only created for the relationship fields:
type2 = PokemonType.objects.create(name='type 2')
type3 = PokemonType.objects.create(name='type 3')
type4 = PokemonType.objects.create(name='type 4')
type5 = PokemonType.objects.create(name='type 5')
# now we can "set" these objects in each ManyToManyField:
pokemon_type.strong_vs.set([])
pokemon_type.weak_vs.set([type2])
pokemon_type.resistant_to.set([type3, type4])
pokemon_type.vulnerable_to.set([type2, type5])
# and perform assertions with them:
pokemon_type_from_db = PokemonType.objects.get(id=pokemon_type.id)
self.assertEqual(pokemon_type_from_db.name, 'type 1')
self.assertEqual(set(pokemon_type_from_db.strong_vs.all()), set())
self.assertEqual(set(pokemon_type_from_db.weak_vs.all()), {type2})
self.assertEqual(set(pokemon_type_from_db.resistant_to.all()), {type3, type4})
self.assertEqual(set(pokemon_type_from_db.vulnerable_to.all()), {type2, type5})
扫码咨询,免费领取项目管理大礼包!