在Django中使ModelForm与ManyToMany关系与中间模型一起工作的步骤是什么?

时间:2008-12-22 22:58:25

标签: python django django-models django-templates django-forms

  • 我有一个客户端 Groupe 模型。
  • 客户端可以是多个的一部分。
  • 作为群组一部分的
  • 客户可以随时使用其群组的免费租赁费率,但只能使用一次。这就是中间模型( ClientGroupe )带来额外数据的地方。

目前,当我尝试保存m2m数据时,它就死了,并说我应该使用ClientGroupe Manager ...那么缺少什么?

以下是我的模特:

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

5 个答案:

答案 0 :(得分:18)

如果你现在使用save方法,Django将尝试使用管理器(Django不允许)进行保存。不幸的是,你想要的行为比ModelForm默认的行为有点棘手。您需要做的是创建 formset

首先,您需要更改ClientForm的选项,使其不显示groupes属性。

class ClientForm(ModelForm):
    class Meta:
        model = Client
        exclude = ('groupes',)

接下来,您必须更改视图以显示formset:

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 formsets
Formsets

答案 1 :(得分:10)

…
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()
    …

答案 2 :(得分:4)

您可能需要从客户端模型中删除ManyToMany字段,否则请小心地将其从表单中排除。遗憾的是,ManyToMany字段的默认小部件无法正确填充ClientGroupe模型(即使缺少的字段dt已设置为autonow = True)。这是你需要突破到另一种形式,或在你的视图中处理。

答案 3 :(得分:0)

保存表单时,保存Client对象。现在,如果要将客户端分配给组,则应执行此操作:

clientgroupe = ClientGroupe.objects.create(client=client_instance, groupe=groupe_instance, dt=datetime.datetime.now())

其中client_instance和groupe_instance您的客户和groupe objets。

答案 4 :(得分:0)

由于我没有调用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中排除,然后单独保存。