简体   繁体   English

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

[英]What are the steps to make a ModelForm work with a ManyToMany relationship with an intermediary model in Django?

  • I have a Client and Groupe Model. 我有一个客户端Groupe模型。
  • A Client can be part of multiple groups . 客户可以是多个组的一部分。
  • Clients that are part of a group can use its group's free rental rate at anytime but only once. 属于某个群组的客户可以随时使用其群组的免费租赁费率,但只能使用一次。 That is where the intermediary model ( ClientGroupe ) comes in with that extra data. 这就是中间模型( ClientGroupe )带来额外数据的地方。

For now, when I try to save the m2m data, it just dies and says I should use the ClientGroupe Manager...so what's missing? 现在,当我尝试保存m2m数据时,它就死了,并说我应该使用ClientGroupe Manager ...那么缺少什么?

Here are my models: 这是我的模特:

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'

and here's my view: 这是我的看法:

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

EDIT : 编辑

and here's the ClientForm code: 这是ClientForm代码:

class ClientForm(ModelForm):
    class Meta:
        model = Client

EDIT #2 : here's the error message: 编辑#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

If you use the save method right now, Django will try to save using the manager (which Django doesn't allow). 如果你现在使用save方法,Django将尝试使用管理器(Django不允许)进行保存。 Unfortunately, the behavior you want is a little bit trickier than what ModelForm does by default. 不幸的是,你想要的行为比ModelForm默认的ModelForm有点棘手。 What you need to do is create a formset . 您需要做的是创建一个formset

First of all, you will need to change the options of your ClientForm so that it doesn't display the groupes attribute. 首先,您需要更改ClientForm的选项,以便它不显示groupes属性。

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

Next, you must change the view to display the formset: 接下来,您必须更改视图以显示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)
    )

And obviously, you must also tweak your template to render the formset. 显然,您还必须调整模板以呈现formset。

If you need any other advice on formsets, see these articles: 如果您需要有关formset的任何其他建议,请参阅以下文章:

Model formsets 模型表格集
Formsets 表单集

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

You probably need to remove the ManyToMany field from your Client model, or else carefully exclude it from your form. 您可能需要从客户端模型中删除ManyToMany字段,否则请小心地将其从表单中排除。 Unfortunately, the default widget for the ManyToMany field cannot populate the ClientGroupe Model properly (even if the missing field, dt, had been set to autonow=True). 遗憾的是,ManyToMany字段的默认小部件无法正确填充ClientGroupe模型(即使缺少的字段dt已设置为autonow = True)。 This is something you'll either need to break out into another form, or handle in your view. 这是你需要突破到另一种形式,或在你的视图中处理。

When you save your form, you save Client object. 保存表单时,保存客户端对象。 Now if you want to assign client to the group you should do this: 现在,如果要将客户端分配给组,则应执行以下操作:

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

where client_instance and groupe_instance your client and groupe objets. 其中client_instance和groupe_instance您的客户和groupe objets。

I'm providing an alternative solution due to issues I encountered with forms_valid not being called: 由于我没有调用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

I've copied the logic from djangos forms/models.py, my field Genres is a manytomany with an intermediary table - I exclude it from the save_instance and then save it separately. 我已经从djangos forms / models.py中复制了逻辑,我的字段Genres是一个带有中间表的多种语言 - 我将它从save_instance中排除,然后单独保存。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM