我正在开发一个新的django项目,该项目的m2m关系使用' through'宾语。例如:
class Tag(models.Model):
value = models.CharField(max_length=50, db_index=True)
class Photo(models.Model):
tags = models.ManyToManyField(Tag, through='PhotoTag', related_name='tags', blank=True, db_index=True)
class PhotoTag(models.Model):
photo= models.ForeignKey(Photo,db_index=True)
tag = models.ForeignKey(Tag,db_index=True)
added= models.DateTimeField(null=True, blank=True, auto_now_add=True)
我希望用户能够浏览一个充满照片的屏幕并添加/删除标签。我在为此设计表单/视图时遇到一些麻烦。 我认为定义一个简单的表单对象可以创建" PhotoTag"对象,然后使用包含所有PhotoTagForm对象的formset创建一个ListView,这些表单可能会或可能不会POST到视图以便在ListView上进行实时更新,或者只有一个保存按钮来保存所有更改。
我的问题是这个PhotoTagForm对象应该是什么样的? 我想使用ModelForm还是创建常规表单对象?
让我们假设表单对象继承自Form而不是ModelForm。
表单需要处理添加(向照片添加标签)和删除(从照片中删除标签)。
答案 0 :(得分:1)
这就是我解决这个问题的方法:
# Forms.py:
class PhotoTagForm(forms.Form):
tags = forms.ModelMultipleChoiceField(queryset=Tag.objects.all(),required=False)
photo=None
initial_tags={}
def __init__(self, *args, **kwargs):
initial = kwargs.setdefault('initial', {})
if kwargs.get('photo'):
self.study=kwargs['photo']
self.initial_tags = set([l for l in self.photo.tags.all()])
initial['tags'] = self.initial_tags
forms.Form.__init__(self, *args, initial=initial)
def save(self,*args,**kwargs):
print(self.cleaned_data.keys())
if 'tags' in self.changed_data:
submitted_tags=set(self.cleaned_data.pop("tags",[]))
removed_tags=self.initial_tags.difference(submitted_tags)
added_tags = submitted_tags.difference(self.initial_tags)
# do logic with removed and added tags to update the m2m model
# views.py
def EditPhotoTagView(request,pk):
photo = Photo.objects.get(pk=pk)
if request.method == "POST" and 'save' in request.POST.keys():
form = PhotoTagForm(request.POST,photo=photo)
if form.is_valid():
form.save()
else:
form = PhotoTagForm(photo=photo)
return render(request, 'data/snippets/editm2m_form.html', {'form': form})
# urls.py
...
url(r'^photo/(?P<pk>[0-9|.]+)/edit/tag$', EditPhotoTagView, name='photo_tag_form'),
...
这里的基本概念是,您首先使用m2m对象的初始数据填充表单,在POST时将initial与form.cleaned_data进行比较,并执行必要的逻辑以更新PhotoTag(创建或删除直通对象)。在这里,我使用了url pk中的照片,但你也可以在photo.pk中使用隐藏的输入字段,并在帖子中使用它来查找模型。我选择这种方式是因为将父对象作为属性更容易,然后直接对其进行操作。 如果您将数据发布到URL,那么这应该适用于您的列表视图,然后更新列表视图。