Class-based views for M2M relationship with intermediate model

爱⌒轻易说出口 提交于 2019-11-28 08:48:10
ecdani

You must extend CreateView:

from django.views.generic import CreateView

class GroupCreate(CreateView):
    model=Group

and override the form_valid():

from django.views.generic.edit import ModelFormMixin
from django.views.generic import CreateView

class GroupCreate(CreateView):
    model = Group

    def form_valid(self, form):
        self.object = form.save(commit=False)
        for person in form.cleaned_data['members']:
            membership = Membership()
            membership.group = self.object
            membership.person = person
            membership.save()
        return super(ModelFormMixin, self).form_valid(form)

As the documentation says, you must create new memberships for each relation between group and person.

I saw the form_valid override here: Using class-based UpdateView on a m-t-m with an intermediary model

class GroupCreate(CreateView):
    model = Group

    def form_valid(self, form):
        self.object = form.save(commit=False)

        ### delete current mappings
        Membership.objects.filter(group=self.object).delete()

        ### find or create (find if using soft delete)
        for member in form.cleaned_data['members']:
            x, created = Membership.objects.get_or_create(group=self.object, person=member)
            x.group = self.object
            x.person = member
            #x.alive = True # if using soft delete
            x.save()
        return super(ModelFormMixin, self).form_valid(form)

I was facing pretty the same problem just a few days ago. Django has problems to process intermediary m2m relationships.

This is the solutions what I have found useful:

1. Define new CreateView
class GroupCreateView(CreateView):
    form_class = GroupCreateForm
    model = Group
    template_name = 'forms/group_add.html'
    success_url = '/thanks'

Then alter the save method of defined form - GroupCreateForm. Save is responsible for making changes permanent to DB. I wasn't able to make this work just through ORM, so I've used raw SQL too:

1. Define new CreateView
class GroupCreateView(CreateView):


class GroupCreateForm(ModelForm):
    def save(self):
        # get data from the form
        data = self.cleaned_data
        cursor = connection.cursor()
        # use raw SQL to insert the object (in your case Group)
        cursor.execute("""INSERT INTO group(group_id, name)
                          VALUES (%s, %s);""" (data['group_id'],data['name'],))
        #commit changes to DB
        transaction.commit_unless_managed()
        # create m2m relationships (using classical object approach)
        new_group = get_object_or_404(Group, klient_id = data['group_id'])
        #for each relationship create new object in m2m entity
        for el in data['members']:
            Membership.objects.create(group = new_group, membership = el)
        # return an object Group, not boolean!
        return new_group

Note:I've changed the model a little bit, as you can see (i have own unique IntegerField for primary key, not using serial. That's how it got into get_object_or_404

'For reference, I didn't end up using a class-based view, instead I did something like this:

def group_create(request):
    group_form = GroupForm(request.POST or None)
    if request.POST and group_form.is_valid():
        group = group_form.save(commit=False)
        membership_formset = MembershipFormSet(request.POST, instance=group)
        if membership_formset.is_valid():
            group.save()
            membership_formset.save()
            return redirect('success_page.html')
    else:
        # Instantiate formset with POST data if this was a POST with an invalid from,
        # or with no bound data (use existing) if this is a GET request for the edit page.
        membership_formset = MembershipFormSet(request.POST or None, instance=Group())

    return render_to_response(
        'group_create.html',
        {
            'group_form': recipe_form,
            'membership_formset': membership_formset,
        },
        context_instance=RequestContext(request),
    )

This may be a starting point for a Class-based implementation, but it's simple enough that it's not been worth my while to try to shoehorn this into the Class-based paradigm.

Just one comment, when using CBV you need to save the form with commit=True, so the group is created and an id is given that can be used to create the memberships. Otherwise, with commit=False, the group object has no id yet and an error is risen.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!