mercredi 6 mai 2015

Create form from related models

I have 2 models, Message and Attachment. I want to use these in one form. The Attachment should have a reference to the message (a message can have more than 1 attachment).

Models:

class Message(models.Model): 
    createdby   = models.ForeignKey(User)
    subject     = models.CharField(max_length=200, null=True)
    body        = models.TextField(default=None,   blank=True)

class Attachment(models.Model):
    docfile     = models.FileField(upload_to='documents/%Y/%m/%d')
    message     = models.ForeignKey(Message,  null=True)

Forms

class MessageForm(forms.ModelForm):
    class Meta:
        model   = Message

AttachmentFormSet = inlineformset_factory(Message, Attachment, extra=1 )

Views

class MessageCreateView(CreateView):
     model       = Message
     fields      = ["subject", "body"]
     form_class  = MessageForm
     success_url = 'success/'

    def get(self, request, *args, **kwargs):
        """
        Handles GET requests and instantiates blank versions of the form
        and its inline formsets.
        """
        self.object = None
        form_class  = self.get_form_class()
        form        = self.get_form(form_class)
        attachment_form = AttachmentFormSet()

        return self.render_to_response(
            self.get_context_data(form  =form,
                                  attachment_form=attachment_form,
                                  ))

    def post(self, request, *args, **kwargs):
        """
        Handles POST requests, instantiating a form instance and its inline
        formsets with the passed POST variables and then checking them for
        validity.
        """
        self.object = None
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        attachment_form = AttachmentFormSet(self.request.POST)
        if (form.is_valid() and attachment_form.is_valid()):
            return self.form_valid(form, attachment_form)
        else:
            return self.form_invalid(form, attachment_form)

    def form_valid(self, form, attachment_form):
        """
        Called if all forms are valid. Creates a Message instance along with
        associated Attachment then redirects to a success page.
        """
        self.object   = form.save()

        attachment_form.instance = self.object
        self.object   = attachment_form.save()
        return HttpResponseRedirect(self.get_success_url())

I tried many things, but for some reason the Attachment is never saved. There must be something missing in the form_valid() function, but I can't figure out what.

For completeness, here also the template:

<script type="text/javascript">
        $(function() {
            $(".inline.{{ attachment_form.prefix }}").formset({
                prefix: "{{ attachment_form.prefix }}"
            })
        });
</script>
<form class="" action="/bugtrack/project/{{ project_id }}/tickets/{{ ticket_id }}/replyform/" method="post" id="replyform">
        {% csrf_token %}
        {{ form |crispy }}        

        {{ attachment_form.management_form }}
        {% for formattach in attachment_form  %}
            {{ formattach.id }}
            <div class="inline {{ attachment_form.prefix }}">
                {{ formattach.docfile }}
            </div>
        {% endfor %}

       <button class="btn btn-info pull-right btn-sm" type="submit" name="submit" value="createReply">Submit</button>
</form>

What would be the correct way to save the Message and Attachment correctly using a formset?

Aucun commentaire:

Enregistrer un commentaire