将模型实例保存到 ManyToMany 字段 thows AttributeError 'Post' object has no attribute 'save_m2m' but when db is checked, has saved
Saving instances of model to ManyToMany field thows AttributeError 'Post' object has no attribute 'save_m2m' but when db is checked, has saved
我的 Django 3.1.7 博客应用程序有一个带有标签字段的 Post 模型,它是 PostTag 模型的 ManyToManyField。我希望我的用户能够从一些流行的标签中 select,或者在我在 models.py 中创建的 new_tags 字段中添加他们自己的标签。 new_tags 在 Post 模型中不存在。
我的想法是抓取“新”标签的字符串,在空格处拆分它,然后检查标签是否已经存在 - 如果存在则创建它。这段代码正在运行,因为新标签正在添加到数据库中。
当我尝试将这些新标签附加到正在使用表单更新的 Post 实例时,我的问题就出现了。
我已经阅读了大量 SO 帖子,并得出了下面的视图代码。这会将新标签保存到数据库中的 Post 实例。但是,在提交表单时,它仍然会抛出一个 AttributeError: 'Post' object has no attribute 'save_m2m'
但是,如果我删除 post.save_m2m()
行,则会创建实例,但不会附加到我的 Post 实例。
views.py
class EditPostView(LoginRequiredMixin, UpdateView):
""" Renders view to edit an existing post """
model = Post
template_name = 'edit_post.html'
context_object_name = 'post'
form_class = EditPostForm
def form_valid(self, form):
post = form.save(commit=False)
new_tags = self.request.POST.get('new_tags')
tags_list = new_tags.split()
for new_tag in tags_list:
post.tags.add(PostTag.objects.get_or_create(name=new_tag)[0])
post.save_m2m()
return super().form_valid(form)
def get_success_url(self):
return reverse(
'post_detail',
kwargs={'pk': self.object.pk, 'slug': self.object.slug})
我对代码为何有效感到困惑,但仍然会抛出错误。我能做些什么来解决它!
非常感谢任何关于我做错了什么或我可以从这里去哪里的建议!!
forms.py
class EditPostForm(forms.ModelForm):
"""
Create form for edit post pg
"""
...
new_tags = forms.CharField(
max_length=300, required=False,
widget=forms.TextInput(
attrs={
'pattern': '[a-zA-Z0-9 ]+',
}
))
class Meta:
model = Post
fields = [
'title',
...
'tags',
'new_tags'
]
widgets = {
'tags': forms.CheckboxSelectMultiple
}
models.py
我已经从 Post 模型中删除了额外的字段,以免使问题复杂化。如果需要,您可以查看完整模型here
class PostTag(models.Model):
"""
Create tags for posts.
"""
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Meta:
ordering = ['name']
class Post(models.Model):
"""
Create an instance of Post
"""
title = models.CharField(
max_length=70,
unique=True,
error_messages={
'unique': "This post title already exists, please choose another."
})
tags = models.ManyToManyField(
PostTag, related_name='post_tags', blank=True)
...
def __str__(self):
return f"{self.title}"
.save_m2m(…)
是 form
的方法,而不是 Post
模型的方法,因此您应该使用:
调用它
class EditPostView(LoginRequiredMixin, UpdateView):
# …
def form_valid(self, form):
post = form.save(commit=False)
new_tags = self.request.POST.get('new_tags')
tags_list = new_tags.split()
for new_tag in tags_list:
post.tags.add(PostTag.objects.get_or_create(name=new_tag)[0])
<b>form</b>.save_m2m()
return super().form_valid(form)
EDIT:这还不够,因为 form.save_m2m()
将在多对多关系上调用 .set()
,从而删除旧的项目。我建议将其重写为:
from django.http import <b>HttpResponseRedirect</b>
class EditPostView(LoginRequiredMixin, UpdateView):
# …
def form_valid(self, form):
post = <b>form.save()</b> save to the database, including the tags
new_tags = self.request.POST.get('new_tags')
tags_list = new_tags.split()
for new_tag in tags_list new_tags.split():
post.tags.add(PostTag.objects.get_or_create(name=new_tag)[0])
return <b>HttpResponseRedirect(</b>self.get_success_url()<b>)</b>
来自docs:
To work around this problem, every time you save a form using commit=False, Django adds a save_m2m() method to your ModelForm subclass
在表单上调用 .save_m2m() 而不是 post.
我的 Django 3.1.7 博客应用程序有一个带有标签字段的 Post 模型,它是 PostTag 模型的 ManyToManyField。我希望我的用户能够从一些流行的标签中 select,或者在我在 models.py 中创建的 new_tags 字段中添加他们自己的标签。 new_tags 在 Post 模型中不存在。
我的想法是抓取“新”标签的字符串,在空格处拆分它,然后检查标签是否已经存在 - 如果存在则创建它。这段代码正在运行,因为新标签正在添加到数据库中。
当我尝试将这些新标签附加到正在使用表单更新的 Post 实例时,我的问题就出现了。
我已经阅读了大量 SO 帖子,并得出了下面的视图代码。这会将新标签保存到数据库中的 Post 实例。但是,在提交表单时,它仍然会抛出一个 AttributeError: 'Post' object has no attribute 'save_m2m'
但是,如果我删除 post.save_m2m()
行,则会创建实例,但不会附加到我的 Post 实例。
views.py
class EditPostView(LoginRequiredMixin, UpdateView):
""" Renders view to edit an existing post """
model = Post
template_name = 'edit_post.html'
context_object_name = 'post'
form_class = EditPostForm
def form_valid(self, form):
post = form.save(commit=False)
new_tags = self.request.POST.get('new_tags')
tags_list = new_tags.split()
for new_tag in tags_list:
post.tags.add(PostTag.objects.get_or_create(name=new_tag)[0])
post.save_m2m()
return super().form_valid(form)
def get_success_url(self):
return reverse(
'post_detail',
kwargs={'pk': self.object.pk, 'slug': self.object.slug})
我对代码为何有效感到困惑,但仍然会抛出错误。我能做些什么来解决它! 非常感谢任何关于我做错了什么或我可以从这里去哪里的建议!!
forms.py
class EditPostForm(forms.ModelForm):
"""
Create form for edit post pg
"""
...
new_tags = forms.CharField(
max_length=300, required=False,
widget=forms.TextInput(
attrs={
'pattern': '[a-zA-Z0-9 ]+',
}
))
class Meta:
model = Post
fields = [
'title',
...
'tags',
'new_tags'
]
widgets = {
'tags': forms.CheckboxSelectMultiple
}
models.py
我已经从 Post 模型中删除了额外的字段,以免使问题复杂化。如果需要,您可以查看完整模型here
class PostTag(models.Model):
"""
Create tags for posts.
"""
name = models.CharField(max_length=50)
def __str__(self):
return self.name
class Meta:
ordering = ['name']
class Post(models.Model):
"""
Create an instance of Post
"""
title = models.CharField(
max_length=70,
unique=True,
error_messages={
'unique': "This post title already exists, please choose another."
})
tags = models.ManyToManyField(
PostTag, related_name='post_tags', blank=True)
...
def __str__(self):
return f"{self.title}"
.save_m2m(…)
是 form
的方法,而不是 Post
模型的方法,因此您应该使用:
class EditPostView(LoginRequiredMixin, UpdateView):
# …
def form_valid(self, form):
post = form.save(commit=False)
new_tags = self.request.POST.get('new_tags')
tags_list = new_tags.split()
for new_tag in tags_list:
post.tags.add(PostTag.objects.get_or_create(name=new_tag)[0])
<b>form</b>.save_m2m()
return super().form_valid(form)
EDIT:这还不够,因为 form.save_m2m()
将在多对多关系上调用 .set()
,从而删除旧的项目。我建议将其重写为:
from django.http import <b>HttpResponseRedirect</b>
class EditPostView(LoginRequiredMixin, UpdateView):
# …
def form_valid(self, form):
post = <b>form.save()</b> save to the database, including the tags
new_tags = self.request.POST.get('new_tags')
tags_list = new_tags.split()
for new_tag in tags_list new_tags.split():
post.tags.add(PostTag.objects.get_or_create(name=new_tag)[0])
return <b>HttpResponseRedirect(</b>self.get_success_url()<b>)</b>
来自docs:
To work around this problem, every time you save a form using commit=False, Django adds a save_m2m() method to your ModelForm subclass
在表单上调用 .save_m2m() 而不是 post.