我无法让ModelMultipleChoiceField显示模型实例的初始值。我一直无法找到有关该领域的任何文档,我一直在阅读的例子太令人困惑了。 Django: ModelMultipleChoiceField doesn't select initial choices似乎相似,但是那里给出的解决方案对模型实例来说并不是动态的。
以下是我的案例(每个数据库用户都连接到一个或多个项目):
models.py
from django.contrib.auth.models import User
class Project(Model):
users = ManyToManyField(User, related_name='projects', blank=True)
forms.py
from django.contrib.admin.widgets import FilteredSelectMultiple
class AssignProjectForm(ModelForm):
class Meta:
model = User
fields = ('projects',)
projects = ModelMultipleChoiceField(
queryset=Project.objects.all(),
required=False,
widget=FilteredSelectMultiple('projects', False),
)
views.py
def assign(request):
if request.method == 'POST':
form = AssignProjectForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
return HttpResponseRedirect('/index/')
else:
form = AssignProjectForm(instance=request.user)
return render_to_response('assign.html', {'form': form})
它返回的表单不是选择实例的链接项目(它看起来像:Django multi-select widget?)。此外,它不会使用保存表单时所做的任何选择来更新用户。
编辑:使用此方法管理解决此问题:http://code-blasphemies.blogspot.com/2009/04/dynamically-created-modelmultiplechoice.html
答案 0 :(得分:4)
ModelForm
不会自动用于反向关系。
save()
上没有任何事情发生,因为ModelForm
只知道如何处理自己的字段 - projects
不是User
模型上的字段,它只是一个您表单上的字段。
您必须告诉您的表单如何使用您的新字段保存自己。
def save(self, *args, **kwargs):
for project in self.cleaned_data.get('projects'):
project.users.add(self.instance)
return super(AssignProjectForm, self).save(*args, **kwargs)
答案 1 :(得分:3)
这是比旧版本更好的解决方案,它确实行不通。
创建表单时,您都必须从数据库中加载现有的相关值,并在保存表单时将其保存回来。我在相关名称(经理)上使用了set()
方法,该方法为您完成了所有工作:删除不再选择的现有关系,并添加已被选择的新关系。因此,您无需执行任何循环或检查。
class AssignProjectForm(ModelForm):
def __init__(self, *args, **kwargs):
super(AssignProjectForm, self).__init__(*args, **kwargs)
# Here we fetch your currently related projects into the field,
# so that they will display in the form.
self.fields['projects'].initial = self.instance.projects.all(
).values_list('id', flat=True)
def save(self, *args, **kwargs):
instance = super(AssignProjectForm, self).save(*args, **kwargs)
# Here we save the modified project selection back into the database
instance.projects.set(self.cleaned_data['projects'])
return instance
除了简单之外,如果您在m2m关系中使用Django信号(例如set()
等),则使用post_save
方法还有另一个优势:如果您在在一个循环中,您将获得每个对象的信号。但是,如果使用set()
在一个操作中执行此操作,则只会得到一个带有对象列表的信号。如果您的信号处理程序中的代码可以完成很多工作,那么这很重要。