使 ModelForm 与 Django 中的中间模型的多对多关系工作的步骤是啥?
Posted
技术标签:
【中文标题】使 ModelForm 与 Django 中的中间模型的多对多关系工作的步骤是啥?【英文标题】:What are the steps to make a ModelForm work with a ManyToMany relationship with an intermediary model in Django?使 ModelForm 与 Django 中的中间模型的多对多关系工作的步骤是什么? 【发布时间】:2010-09-28 02:40:06 【问题描述】: 我有一个 Client 和 Groupe 模型。 一个客户可以是多个组的一部分。 属于组的客户可以随时使用其组的免费租金,但只能使用一次。这就是中间模型 (ClientGroupe) 带来额外数据的地方。目前,当我尝试保存 m2m 数据时,它只是死了,并说我应该使用 ClientGroupe 管理器...所以缺少什么?
这是我的模型:
class Groupe(models.Model):
nom = models.CharField(max_length=1500, blank=True)
class Client(models.Model):
nom = models.CharField(max_length=450, blank=True)
prenom = models.CharField(max_length=450, blank=True)
groupes = models.ManyToManyField(Groupe, null = True, blank = True, through='ClientGroupe')
class ClientGroupe(models.Model):
client = models.ForeignKey(Client)
groupe = models.ForeignKey(Groupe)
dt = models.DateField(null=True, blank=True) # the date the client is using its group's free rental rate
class Meta:
db_table = u'clients_groupes'
这是我的观点:
def modifier(request, id):
client = Client.objects.get(id=id)
form = ClientForm(instance = client)
dict =
"form": form
, "instance" : client
if request.method == "POST":
form = ClientForm(request.POST, instance = client)
if form.is_valid():
client_mod = form.save()
id = client_mod.id
return HttpResponseRedirect(
"/client/%(id)s/?err=success" % "id" : id
)
else:
return HttpResponseRedirect(
"/client/%(id)s/?err=warning" % "id" : id
)
return render_to_response(
"client/modifier.html"
, dict
, context_instance=RequestContext(request)
)
编辑:
这是 ClientForm 代码:
class ClientForm(ModelForm):
class Meta:
model = Client
编辑#2: 这是错误消息:
AttributeError at /client/445/
Cannot set values on a ManyToManyField which specifies an intermediary model. Use ClientGroupe's Manager instead.
Request Method: POST
Request URL: http://localhost/client/445/
Exception Type: AttributeError
Exception Value: Cannot set values on a ManyToManyField which specifies an intermediary model. Use ClientGroupe's Manager instead.
Exception Location: C:\Python25\lib\site-packages\django\db\models\fields\related.py in __set__, line 574
Python Executable: C:\xampp\apache\bin\apache.exe
Python Version: 2.5.2
【问题讨论】:
你能告诉我们确切的错误信息吗?谢谢 你应该用 print 语句来告诉我们这个 ir 的确切传播位置,即在 form.is_valid() 之前或之后 相关问题在ORM级别有解决方案:***.com/questions/22964448/… 你能展示你的模板吗?显然,它不仅仅是“ form.as_p ”和“submit”,因为您想显示在使用表单变量“form”时不会随表单呈现的 dt 字段。 @Timo 这个问题已经解决了:) 【参考方案1】:如果您现在使用 save 方法,Django 将尝试使用管理器进行保存(Django 不允许这样做)。不幸的是,你想要的行为比ModelForm
默认的行为有点棘手。你需要做的是创建一个formset。
首先,您需要更改ClientForm
的选项,使其不显示groupes
属性。
class ClientForm(ModelForm):
class Meta:
model = Client
exclude = ('groupes',)
接下来,您必须更改视图以显示表单集:
from django.forms.models import inlineformset_factory
def modifier(request, id):
client = Client.objects.get(id=id)
form = ClientForm(instance = client)
# Create the formset class
GroupeFormset = inlineformset_factory(Client, Groupe)
# Create the formset
formset = GroupeFormset(instance = client)
dict =
"form": form
, "formset" : formset
, "instance" : client
if request.method == "POST":
form = ClientForm(request.POST, instance = client)
formset = GroupeFormset(request.POST, instance = client)
if form.is_valid() and formset.is_valid():
client_mod = form.save()
formset.save()
id = client_mod.id
return HttpResponseRedirect(
"/client/%(id)s/?err=success" % "id" : id
)
else:
return HttpResponseRedirect(
"/client/%(id)s/?err=warning" % "id" : id
)
return render_to_response(
"client/modifier.html"
, dict
, context_instance=RequestContext(request)
)
显然,您还必须调整模板以呈现表单集。
如果您需要有关表单集的任何其他建议,请参阅以下文章:
Model formsetsFormsets
【讨论】:
我有一个相同的模型,对 inlineformset_factory 的调用对我来说失败了。错误是“Client”和“Groupe”之间没有外键 是的,这实际上不起作用。 inlineformset_factory 需要一个 ForeignKey GroupeFormset = inlineformset_factory(Client, Groupe) 应该是 GroupeFormset = inlineformset_factory(Client, ClientGroupe)。否则会出现antonis_wrx遇到的错误。【参考方案2】:…
if form.is_valid():
client_mod = form.save(commit=False)
client_mod.save()
for groupe in form.cleaned_data.get('groupes'):
clientgroupe = ClientGroupe(client=client_mod, groupe=groupe)
clientgroupe.save()
…
【讨论】:
不要忘记删除db中存在但不在cleaned_data中的ClientGroupe实例,也不要忘记以这种形式保存其他m2m-fields。此外,我更愿意将它放在 ClientForm.save() 方法中。 这并没有解决主要问题,通过模型 'dt' 中的额外字段【参考方案3】:您可能需要从 Client 模型中删除 ManyToMany 字段,或者小心地将其从表单中排除。不幸的是,ManyToMany 字段的默认小部件无法正确填充 ClientGroupe 模型(即使缺失的字段 dt 已设置为 autonow=True)。这是你要么需要分解成另一种形式,要么在你的视图中处理的东西。
【讨论】:
【参考方案4】:当您保存表单时,您将保存 Client 对象。现在,如果您想将客户端分配给该组,您应该这样做:
clientgroupe = ClientGroupe.objects.create(client=client_instance, groupe=groupe_instance, dt=datetime.datetime.now())
client_instance 和 groupe_instance 您的客户端和 groupe 对象在哪里。
【讨论】:
【参考方案5】:由于我遇到未调用 forms_valid 的问题,我正在提供替代解决方案:
class SplingCreate(forms.ModelForm):
class Meta:
model = SplingModel
fields = ('Link', 'Genres', 'Image', 'ImageURL',)
def save(self, commit=True):
from django.forms.models import save_instance
if self.instance.pk is None:
fail_message = 'created'
else:
fail_message = 'changed'
fields = set(self._meta.fields) - set(('Genres',))
instance = save_instance(self, self.instance, fields,
fail_message, commit, construct=False)
genres = self.cleaned_data.get('Genres')
for genre in genres:
SplingGenreModel.objects.get_or_create(spling=instance, genre=genre)
return instance
我从 djangos forms/models.py 中复制了逻辑,我的字段 Genres 是一个带有中间表的多线程 - 我将它从 save_instance 中排除,然后单独保存。
【讨论】:
以上是关于使 ModelForm 与 Django 中的中间模型的多对多关系工作的步骤是啥?的主要内容,如果未能解决你的问题,请参考以下文章
django中的modelform和modelfoemset