Search code examples
djangoformsmanytomanyfieldinline-formsetdetailview

Form for ManyToMany relationship on Detail page


I have the following (simplified) models:

class Idea(models.Model):
    tagline = models.TextField()

class Lot(models.Model):
    address = models.CharField()
    ...other fields...
    ideas = models.ManyToManyField(Idea)

I want to display a Lot detail page that lists all the info about the lot, including ideas associated with that lot. This is simple to do.

However, in addition, I want the user to be able to add a new idea for that lot from this page. Upon submission, the user should return to the Lot detail page with their new idea now part of the list.

I've tried inline formsets for the new idea, but that only shows up as a drop down of existing ideas, it does not allow for a new idea to be created. Plus, it seems overkill as I only need the user to be able to add a new idea, not edit/remove ideas already submitted. And I also don't need them to be able to edit the other lot information, only add a related idea.

I know there is probably a simple way to achieve this, but I'm a bit stuck at the moment.

Any help would be appreciated.

Thanks!

EDIT: I'm not referring to the Django admin. This is for user facing forms.


Solution

  • Here's a solution I found to work: (ref: https://docs.djangoproject.com/en/1.5/topics/class-based-views/mixins/#using-formmixin-with-detailview)

    class LotDisplay(DetailView):
        model = Lot
    
        def get_context_data(self, **kwargs):
            context = super(LotDisplay, self).get_context_data(**kwargs)
            context['form'] = IdeaForm()
            return context
    
    class LotAddIdeaView(FormView, SingleObjectMixin):
        model=Lot
        form_class = IdeaForm
        template_name = 'lotxlot/lot_detail.html'
    
        def post(self, request, *args, **kwargs):
            if not request.user.is_authenticated():
                request.session['post'] = request.POST
                url = "%s?next=%s" % (reverse('account_login'), request.path)
                return HttpResponseRedirect(url)
            else:
                self.object = self.get_object()
                return super(LotAddIdeaView, self).post(request, *args, **kwargs)
    
        def get_success_url(self):
            return reverse('lotxlot_lot_detail', kwargs={'pk': self.object.pk})
    
        def form_valid(self, form):
            """
             Auto-populate user
            and save form.
            """
            instance = form.save(commit=False)
            instance.user = self.request.user
            instance.save()
            instance.lots.add(self.object)
            instance.save()
    
            return HttpResponseRedirect(self.get_success_url())
    
    class LotDetailView(View):
        def get(self, request, *args, **kwargs):
            view = LotDisplay.as_view()
            return view(request, *args, **kwargs)
    
        def post(self, request, *args, **kwargs):
            view = LotAddIdeaView.as_view()
            return view(request, *args, **kwargs)