I did this using Django class representations.
Here is my approach:
models.py
from django.db import models class Author(models.Model): name = models.CharField(max_length=100) class Book(models.Model): author = models.ForeignKey(Author) title = models.CharField(max_length=100)
forms.py
from django.forms import ModelForm from django.forms.models import inlineformset_factory from crispy_forms.helper import FormHelper from crispy_forms.layout import Layout, Fieldset from .models import Author, Book class AuthorForm(ModelForm): class Meta: model = Author fields = ('name', ) @property def helper(self): helper = FormHelper() helper.form_tag = False
views.py
from django.views.generic import CreateView from django.http import HttpResponseRedirect from .forms import AuthorForm, BookFormset, BookFormHelper from .models import Book, Author class AuthorCreateView(CreateView): form_class = AuthorForm template_name = 'library/manage_books.html' model = Author success_url = '/' def get(self, request, *args, **kwargs): self.object = None form_class = self.get_form_class() form = self.get_form(form_class) book_form = BookFormset() book_formhelper = BookFormHelper() return self.render_to_response( self.get_context_data(form=form, book_form=book_form) ) def post(self, request, *args, **kwargs): self.object = None form_class = self.get_form_class() form = self.get_form(form_class) book_form = BookFormset(self.request.POST) if (form.is_valid() and book_form.is_valid()): return self.form_valid(form, book_form) return self.form_invalid(form, book_form) def form_valid(self, form, book_form): """ Called if all forms are valid. Creates a Author instance along with associated books and then redirects to a success page. """ self.object = form.save() book_form.instance = self.object book_form.save() return HttpResponseRedirect(self.get_success_url()) def form_invalid(self, form, book_form): """ Called if whether a form is invalid. Re-renders the context data with the data-filled forms and errors. """ return self.render_to_response( self.get_context_data(form=form, book_form=book_form) ) def get_context_data(self, **kwargs): """ Add formset and formhelper to the context_data. """ ctx = super(AuthorCreateView, self).get_context_data(**kwargs) book_formhelper = BookFormHelper() if self.request.POST: ctx['form'] = AuthorForm(self.request.POST) ctx['book_form'] = BookFormset(self.request.POST) ctx['book_formhelper'] = book_formhelper else: ctx['form'] = AuthorForm() ctx['book_form'] = BookFormset() ctx['book_formhelper'] = book_formhelper return ctx
urls.py
from django.conf.urls import patterns, url from django.views.generic import TemplateView from library.views import AuthorCreateView urlpatterns = patterns('', url(r'^author/manage$', AuthorCreateView.as_view(), name='handle-books'), url(r'^$', TemplateView.as_view(template_name='home.html'), name='home'), )
manage_books.html
{% load crispy_forms_tags %} <head> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css"> </head> <div class='container'> <form method='post'> {% crispy form %} {{ book_form.management_form }} {{ book_form.non_form_errors }} {% crispy book_form book_formhelper %} <input class='btn btn-primary' type='submit' value='Save'> </form> <div>
Note:
- This is a simple example that uses
inlineformset_factory and general Django role representations. - I am installing
django-crispy-forms and it is configured correctly. - The code repository is located at: https://bitbucket.org/slackmart/library_example
I know that there is more code that the solutions shown, but starting to use Django Class-Based Views is fine.