我是 Django 的新手,没有找到任何关于这个问题的参考资料。当我在 Django 模型中使用多对多字段时,我收到此错误 (models.py
)。我想问题是从表单(forms.py
)分配视图中(views.py
)中的m2m字段。
如何在视图中分配 m2m 字段? (Django version 2.0
,python - 3.5
)
models.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)
views.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("/")
forms.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"]
您需要获取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("/")
我尝试了上述所有解决方案,但它不适用于 Django 3.0 .所以,我做了自己的研究,想出了解决方案。 解决方案将非常简单。我的回答是一般的。 假设存在一个表单字段specialFieldName
它被定义为models.py
中的ManyToManyField
。
为什么会发生此错误?
用户通过"django 表单"输入的此字段的选项存储为Queryset。在这种情况下,在基于此查询集创建对象期间,我们根本无法将此查询集分配给ManyToManyField
属性。这就是您收到上述错误的原因。
因此,我们首先根据从 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)
不幸的是,循环不会遍历返回的 Queryset 的所有成员(阅读原因?因此,上述内容不起作用。我们必须更高级一点,改用 iterator() 方法。
for member in returnedQueryset.iterator():
dummyObject.add(member)
现在它工作正常。
(附言这是我在Stackoverflow上的第一个答案。感谢我所有的导师;-))
尝试此代码,因为我在使用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("/")
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()]
我可能不会添加太多内容,但是在为根本没有表单的 rest api 进行单元测试时遇到了此错误,因此我想提供一个示例说明在这种情况下如何修复它。我正在使用Django 4.1.7和Django Rest Framework 3.14.0。
这是一个口袋妖怪卡片项目,所以有一个口袋妖怪类型模型(表示类型为"火"、"水"、"草"等及其阻力和弱点):
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")
我想写一个简单的测试来验证可以创建口袋妖怪类型,但这个测试会导致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})