I am following this code to create a formset (subform/nested form): example-inline-formsets |
django inline formeset with class based views and crispy forms
I don’t get any errors on the form or in my code. But when I try to save the form nothing saves and I remain on the same page. However in VS Code terminal I get the following message.
"C:\Users\Dane Miller\Dropbox\3. Personal\Software Development\1. Projects\1. Completed Projects\Django\HSSE_Mgt_System\HSSE_Solution\myvenv\lib\site-packages\django\template\defaulttags.py:65: UserWarning: A {% csrf_token %} was used in a template, but the context did not provide the value. This is usually caused by not using RequestContext. "A {% csrf_token %} was used in a template, but the context "
Can someone point me in the right direction. I have uploaded my code as reference:
models
class BBSO_Records(models.Model):
COMPANY_OR_CONTRACTOR = (
('company', 'Company'),
('contractor', 'Contractor'),
)
severityLevel = (
('1', 'Level 1'),
('2', 'Level 2'),
('3', 'Level 3'),
)
severity_level = models.CharField(max_length=6, default= 3 , choices=severityLevel)
date_recorded = models.DateTimeField()
observer_name_ID = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, verbose_name="Observer Name") # i need to double check how I would be handling this.
observer_department_ID = models.ForeignKey(Observer_Department, on_delete=models.CASCADE, verbose_name="Observer Department")
site_location_ID = models.ForeignKey(Site_Locations, on_delete=models.CASCADE, verbose_name='Location of Observation')
location_details = models.CharField(max_length=50, help_text='e.g. In the kitchen on the 4th floor.', blank=True)
company_or_contractor = models.CharField(max_length=10, choices=COMPANY_OR_CONTRACTOR)
number_of_persons_observed = models.IntegerField(default=1, validators=[MinValueValidator(0),
MaxValueValidator(25)],verbose_name='# of Persons Observed') # https://stackoverflow.com/questions/849142/how-to-limit-the-maximum-value-of-a-numeric-field-in-a-django-model
time_spent = models.IntegerField(verbose_name="Time Spent Observing (mins)")
PTW = models.CharField(max_length=10, verbose_name="Permit To Work Number", blank=True)
JSA = models.CharField(max_length=10,verbose_name="Job Safety Analysis Number", blank=True)
details_of_observation = models.TextField()
date_created = models.DateTimeField(auto_now_add=True, editable=True) # setting editable equal=False wouldnot make this contorl show on the form
date_updated = models.DateTimeField(auto_now=True, editable=True)
def __str__(self) :
return self.details_of_observation
def get_absolute_url(self):
return reverse ('bbso_records_detail', args=[str(self.id)])
class Meta:
ordering = ["-date_created"]
verbose_name_plural = "BBSO_Records"
class BBSO_Record_Actions(models.Model):
bbso_record_ID = models.ForeignKey(BBSO_Records, on_delete=models.CASCADE, verbose_name="BBSO Record", related_name='relatednameBBSO_Records')
classification_code_ID = models.ForeignKey(Classification_Code, on_delete=models.CASCADE)
recommended_action = models.TextField(verbose_name='Recommended Action') # models.CharField(max_length=300)
# closed_action = models.TextField(max_length=300, blank=True, help_text='Enter if the closed actions are different from the Recommended Actions')
assigned_to = models.ForeignKey(get_user_model(), on_delete=models.CASCADE, verbose_name='Assigned To')
date_closed = models.DateField(blank=True)
date_created = models.DateTimeField(auto_now_add=True, editable=True) # editable=True causes the field not to show on a form.
def __str__(self):
return self.recommended_action
class Meta:
ordering = ["-date_created"]
verbose_name_plural = "BBSO_Record_Actions"
forms
# REFERENCES
# https://stackoverflow.com/questions/6536373/how-can-i-set-the-size-of-rows-columns-in-textfield-in-django-models
# https://dev.to/zxenia/django-inline-formsets-with-class-based-views-and-crispy-forms-14o6/comments
class BBSO_RecordsForm(forms.ModelForm):
class Meta:
model = BBSO_Records
fields = '__all__'
widgets = { 'date_recorded': DateTimePickerInput(options={"showClose": True,"showClear": True,"showTodayButton": True,}), } # https://pypi.org/project/django-bootstrap-datepicker-plus/
# exclude = ['date_created', 'date_updated']
def __init__(self, *args, **kwargs):
super(BBSO_RecordsForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = True
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-md-3 create-label'
self.helper.field_class = 'col-md-9'
self.helper.layout = Layout(
Div(
Field('severity_level'),
Field('date_recorded'), # css_class='form-group'
Field('observer_department_ID'),
Field('site_location_ID'),
Field('location_details'),
Field('company_or_contractor'),
Field('number_of_persons_observed'),
Field('PTW'),
Field('JSA'),
Field('details_of_observation'),
TabHolder(
Tab('SOS Actions',
Fieldset('Add SOS Actions', Formset('bbso_record_actions')),
# Div('field_name_2')
),
Tab('Second Tab',
# Field('field_name_3', css_class="extra")
),
),
# Fieldset('Add SOS Actions', Formset('bbso_record_actions')),
HTML("<br>"),
ButtonHolder(Submit('submit', 'Save')),
)
)
class BBSO_Record_ActionsForm(ModelForm):
class Meta:
model = BBSO_Record_Actions
fields = '__all__'
# exclude = ()
widgets = { 'recommended_action': Textarea(attrs={'rows':2, 'cols':25}),
'date_closed': DatePickerInput(options={"showClose": True,"showClear": True,"showTodayButton": True,}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
formtag_prefix = re.sub('-[0-9]+
```, '', kwargs.get('prefix', ''))
self.helper = FormHelper()
self.helper.form_tag = False
self.helper.layout = Layout(
# styling the formset
# https://github.com/sibtc/advanced-crispy-forms-examples/blob/master/mysite/core/forms.py
# HTML("<hr size='8' color='4DDA2A'>"),
Row(
Column('classification_code_ID',css_class='form-group col-md-2 mb-0'),
Column('recommended_action',css_class='form-group col-md-5 mb-0' ),
Column('assigned_to', css_class='form-group col-md-2 mb-0'),
Column('date_closed', css_class='form-group col-md-2 mb-0'),
Column('DELETE'), # because of the dynmic inline formset
css_class='formset_row-{}'.format(formtag_prefix)
# wrapper_class='form-row'
),
)
# self.helper.template = 'bootstrap/table_inline_formset.html'
BBSO_Record_ActionsFormset = inlineformset_factory(BBSO_Records,
BBSO_Record_Actions,
fields = ['classification_code_ID',
'recommended_action',
'assigned_to',
'date_closed' ],
form = BBSO_Record_ActionsForm,
extra = 2,
views
class BBSO_RecordsDetailView(DetailView):
model = BBSO_Records
template_name = 'sos/bbso_records/bbso_record_detail.html'
fields = ('date_recorded', 'observer_department_ID', 'site_location_ID',
'location_details', 'company_or_contractor','number_of_persons_observed', 'time_spent',
'PTW', 'JSA', 'details_of_observation')
def get_context_data(self, **kwargs):
context = super(BBSO_RecordsDetailView, self).get_context_data(**kwargs)
return context
class BBSORecord_NestedForm_CreateView(CreateView): # main form and nested form for BBSO Records
model = BBSO_Records
template_name = 'sos/bbso_records/bbso_record_create.html' # 'sos/bbso_records/bbso_record_nested_new.html'
form_class = BBSO_RecordsForm
# fields = ('date_recorded','time_of_observation', 'observer_department_ID', 'site_location_ID',
# 'location_details', 'company_or_contractor','number_of_persons_observed', 'time_spent',
# 'PTW', 'JSA', 'details_of_observation')
# exclude = ('observer_name_ID')
# success_url = None # reverse_lazy('home')
def get_context_data(self, **kwargs):
data = super(BBSORecord_NestedForm_CreateView, self).get_context_data(**kwargs)
if self.request.POST:
data['bbso_record_actions'] = BBSO_Record_ActionsFormset(self.request.POST)
else:
data['bbso_record_actions'] = BBSO_Record_ActionsFormset()
return data
def form_valid(self, form):
context = self.get_context_data()
bbso_record_actions = context['bbso_record_actions']
with transaction.atomic():
form.instance.observer_name_ID = self.request.user # add the logged on user to the record.
self.object = form.save()
if bbso_record_actions.is_valid():
bbso_record_actions.instance = self.object
bbso_record_actions.save()
return super(BBSORecord_NestedForm_CreateView, self).form_valid(form)
def get_success_url(self):
# return reverse_lazy('bbso_records_detail', kwargs={'pk': self.object.pk})
return reverse_lazy('bbso_records_detail', kwargs={'pk': self.object.pk})
template
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% load static %}
{% block extrahead %} {# Extra Resources Start #}
{{ form.media }} {# Form required JS and CSS #}
{% endblock %} {# Extra Resources End #}
{% block content %}
<div class="container">
<div class="card">
<div class="card-header">
SOS Data Entry Form
</div>
<div class="card-body">
{% crispy form %}
</div>
</div>
</div>
{% endblock content %}
base template
<!DOCTYPE html>
{% load static %}
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}INSPINIA | Main view {% endblock title %}</title>
{% load bootstrap4 %} {# imports bootstrap3 #}
{% bootstrap_css %} {# Embeds Bootstrap CSS #}
{% bootstrap_javascript jquery='full' %} {# Embeds Bootstrap JS #}
{% block extrahead %} {# Embeds Extra Resources #}
{% endblock %} {# Ends Extra Resources #}
</head>
<body>
<p><a href="{% url 'bbso_records_nested_new' %}">New Nested SOS </a></p>
{% block content %}
{% endblock content %}
</body>
</html>
custom layout object
from crispy_forms.layout import LayoutObject, TEMPLATE_PACK
from django.shortcuts import render
from django.template.loader import render_to_string
###### Thanks!
###### https://stackoverflow.com/questions/15157262/django-crispy-forms-nesting-a-formset-within-a-form/22053952#22053952
class Formset(LayoutObject):
template = "mycollections/formset.html"
def __init__(self, formset_name_in_context, template=None):
self.formset_name_in_context = formset_name_in_context
self.fields = []
if template:
self.template = template
def render(self, form, form_style, context, template_pack=TEMPLATE_PACK):
formset = context[self.formset_name_in_context]
return render_to_string(self.template, {'formset': formset})
formset
{% load crispy_forms_tags %}
{% load static %}
<style type="text/css">
.delete-row {
align-self: center;
}
</style>
{{ formset.management_form|crispy }}
{% for form in formset.forms %}
{% for hidden in form.hidden_fields %}
{{ hidden|as_crispy_field }}
{% endfor %}
{% crispy form %}
{% endfor %}
<br>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<script src='{% static "mycollections/libraries/django-dynamic-formset/jquery.formset.js" %}'></script>
<script type="text/javascript">
$('.formset_row-{{ formset.prefix }}').formset({
addText: 'Add Row',
deleteText: 'Delete',
prefix: '{{ formset.prefix }}',
});
</script>