我正在开发一个新的django项目,该项目具有使用"通过"对象的m2m关系。例:
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 对象的表单集的列表视图,这些表单可能会也可能不会 POST 到视图以在 ListView 上进行实时更新,或者有一个保存按钮来保存所有更改。
我的问题是这个PhotoTagForm对象应该是什么样子的?我是否要使用模型窗体或创建常规窗体对象?让我们假设表单对象继承自 Form 而不是 ModelForm。表单需要处理添加(向照片添加标签)和删除(从照片中删除标签)。
这就是我解决这个问题的方式:
# 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上,您将初始与form.cleaned_data进行比较,并执行必要的逻辑来更新PhotoTag(创建或删除直通对象)。 在这里,我使用了 url pk 中的照片,但您也可以在 photo.pk 中隐藏一个输入字段,并在发布过程中使用它来查找模型。 我选择这种方式是因为将父对象作为属性然后直接对其执行操作更容易。如果您将数据发布到 URL,然后更新列表视图,这应该适用于您的列表视图。