views:

660

answers:

2

[I have posted this at the Django users | Google Groups also.]

Using the example at http://docs.djangoproject.com/en/dev/topics/forms/modelforms/#inline-formsets, I am able to edit objects belonging a particular model (using modelforms). I have been trying to follow the same pattern for creating new objects using inline formsets, but have been unable to clear my head enough to bring out a working view for this purpose.

Using the same example as in the above link, how would I go about creating a new instance of an "Author" model together with its related "Book" objects?

+3  A: 

First, create a Author model form.

author_form = AuthorModelForm()

then create a dummy author object:

author = Author()

Then create a inline formset using the dummy author like so:

formset = BookFormSet(instance=author)  #since author is empty, this formset will just be empty forms

Send that off to a template. After the data is returned back to the view, you create the Author:

author = AuthorModelForm(request.POST)
created_author = author.save()  # in practice make sure it's valid first

Now hook the inline formset in with the newly created author and then save:

formset = BookFormSet(request.POST, instance=created_author)
formset.save()   #again, make sure it's valid first


edit:

To have no checkboxes on new forms, do this is a template:

{% for form in formset.forms %}
    <table>
    {% for field in form.fields %}
        <tr><th>{{field.label_tag}}</th><td>{{field}}{{field.errors}}</td></tr>
    {% endfor %}

    {% if form.pk %} {# empty forms do not have a pk #}
         <tr><th>Delete?</th><td>{{field.DELETE}}</td></tr>
    {% endif %}
    </table>
{% endfor %}
nbv4
This works, and is a logical solution. The only thing is that this makes can_delete = True so there are corresponding checkboxes for inline instances (which does not make real sense for the user as the instances do not exist as yet). Right now I am hiding these checkboxes with css / JQuery. Or do you know of a better way to hide them?
chefsmart
Yeah I it is kinda dumb how those delete checkboxes are added even on unbound forms. I'll update my answer with how I do it.
nbv4
I wasn't thinking!! inlineformset_factory accepts can_delete (of course!) so I just passed can_delete=False.
chefsmart
To clarify, if you're following the documentation's example, you would do BookFormSet = inlineformset_factory(Author, Book, can_delete=False) instead of BookFormSet = inlineformset_factory(Author, Book) and then you won't need to do the {% if form.pk %} check in nbv4's answer above.
chefsmart
+1  A: 

models.py (Contact)

first = models.CharField(max_length=30)
middle = models.CharField('M.I.',max_length=30, blank=True)
last = models.CharField(max_length=30)
sort_order = models.PositiveIntegerField(default=99)

models.py (Link)

class Link(models.Model):
    contact = models.ForeignKey(Contact)
    link = models.URLField()
    description = models.CharField(max_length=30)
    access_date = models.DateField(blank=True,null=True)

forms.py

from django.forms import ModelForm
from contacts.models import Contact

class ContactAjaxForm(ModelForm):
    class Meta:
    model=Contact

views.py

def edit(request,object_id=False):
    LinkFormSet = inlineformset_factory \
           (Contact,Link,extra=1)
    if object_id:
        contact=Contact.objects.get(pk=object_id)
    else:
        contact=Contact()
    if request.method == 'POST':
        f=forms.ContactAjaxForm(request.POST, request.FILES, instance=contact)
        fs = LinkFormSet(request.POST,instance=contact)
        if fs.is_valid() and f.is_valid():
            f.save()
            fs.save()
            return HttpResponse('success')
    else:
        f  = forms.ContactAjaxForm(instance=contact)
        fs = LinkFormSet(instance=contact)
    return render_to_response('contacts/edit.html', \
               {'fs': fs,'f':f,'contact':contact})

This is not based on the example in the book, it's edited down from some code on my site. I haven't tested it so there might be some bugs bot overall it should be solid. Using an empty instance of Contact isn't the suggested method but it saves a bit of logic and it works.

Edit: Added Link Model, switched to normal Foreign Key instead of Generic Foreign Key which is confusing

Dennis Baker
Could you post the details of your Link model?
chefsmart
-O- I just realized Links is a generic foreign key... probably not the best example, let me see if I can fix it.
Dennis Baker
Yes, it's a lot more clearer now. This is excellent. +1
chefsmart