Last active
April 22, 2022 01:56
-
-
Save cwurld/c2381290d60d770ba09d01e1132ca565 to your computer and use it in GitHub Desktop.
Django model formset example
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from django.forms.models import inlineformset_factory | |
from django.shortcuts import render | |
from django.http import HttpResponseRedirect | |
from django import forms | |
from crispy_forms.helper import FormHelper | |
# The forms --------------------------------------------------------------------------------------------------------- | |
class ParentForm(forms.ModelForm): | |
# For crispy | |
helper = FormHelper() | |
helper.form_class = 'form-horizontal' | |
helper.label_class = 'col-md-2' | |
helper.field_class = 'col-md-10' | |
helper.include_media = False | |
helper.form_tag = False | |
class Meta: | |
model = models.Parent | |
fields = ('field1', 'field2') | |
class ItemForm(forms.ModelForm): | |
class Meta: | |
model = models.Item | |
fields = ('field1', 'field2') | |
class BaseItemFormSet(forms.models.BaseInlineFormSet): | |
""" | |
Use this to do cross form validation. This is commonly needed. Although sometimes a better solution is to | |
use model constraints such as "unique_together" | |
""" | |
def clean(self): | |
super(BaseItemFormSet, self).clean() | |
if any(self.errors): | |
# Don't bother validating the formset unless each form is valid on its own | |
return | |
# Do error cross form checking here | |
for form in self.forms: | |
if error: | |
raise forms.ValidationError("Opps. There was an error") | |
# For using django crispy | |
class ItemFormSetFormHelper(FormHelper): | |
def __init__(self, *args, **kwargs): | |
super(ItemFormSetFormHelper, self).__init__(*args, **kwargs) | |
self.template = 'bootstrap/table_inline_formset.html' | |
self.include_media = False | |
self.form_tag = False | |
self.form_show_errors = True | |
# The view ---------------------------------------------------------------------------------------------------------- | |
def model_w_items_view(request, parent_id=None): | |
if parent_id: | |
parent_instance = models.Parent.objects.get(id=int(parent_id)) | |
headline = 'Update' | |
extra_lines = 1 | |
else: | |
parent_instance = None | |
headline = 'Create' | |
extra_lines = 5 | |
# fk_name = the name of the foreign key field in models.Item | |
ItemFormSet = inlineformset_factory( | |
models.Parent, models.Item, form=ItemForm, formset=BaseItemFormSet, | |
fk_name='parent', extra=int(extra_lines) | |
) | |
# Use this if you are using crispy to layout the Item forms | |
formset_helper = ItemFormSetFormHelper() | |
if request.method == "POST": | |
main_form = ItemForm(request.POST, instance=parent_instance) | |
if main_form.is_valid(): | |
parent_instance = main_form.save() | |
formset = ItemFormSet(request.POST, request.FILES, instance=parent_instance) | |
if main_form.is_valid() and formset.is_valid(): | |
main_form.save() | |
formset.save() | |
return HttpResponseRedirect('/') | |
else: | |
formset = ItemFormSet(instance=parent_instance) | |
main_form = ParentForm(instance=parent_instance) | |
return render(request, "my_template.html", { | |
'main_form': main_form, | |
'item_formset': formset, | |
'formset_helper': formset_helper, | |
'parent_instance': parent_instance, | |
'headline': headline, | |
}) | |
# The template ------------------------------------------------------------------------------------------------------ | |
# This template uses Bootstrap panels | |
''' | |
{% load crispy_forms_tags %} | |
<div class="panel panel-default"> | |
<div class="panel-heading"> | |
<h4 class="panel-heading">{{ headline }}</h4> | |
</div> | |
<div class="panel-body"> | |
<form class="form-horizontal" method="post"> | |
{% if main_form.non_form_errors %} | |
{{ main_form.non_form_errors }} | |
{% endif %} | |
{% crispy main_form %} | |
{% if item_formset.non_form_errors %} | |
{{ item_formset.non_form_errors }} | |
{% endif %} | |
{% crispy item_formset helper %} | |
<div> | |
<input type="submit" name="save" value="Save" class="btn btn-primary"> | |
<a onclick='window.history.back();' class="btn btn-default">Cancel</a> | |
</div> | |
</form> | |
</div> | |
</div> | |
''' |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment