I have been trying to implement a form in a bootstrap modal, using django-crispy forms. I am using class based views. I know that I would require some Ajax, but I didn’t understand all the examples I have been seeing.
I tried django-bootstrap-modal-forms – this works with the modal, but sadly, it dosen’t support bootstrap styling, and I couldn’t find any way to add it. When I tried to add bootstrap sytling to django-bootstrap-modal-forms using bootstraps form class, form-control, the form dosen’t submit, and gives no errors.
So now, I want to fall back to django-crispy-forms.
So my question is:
-
How do I implement a bootstrap modal form with django-crispy-forms
-
How do I implement validation with ajax, to avoid page reload – The errors should also have the same error styling as django-crispy-forms
- Also, how to I also implement a success message using an alert, when the object has been successfully added into the database.
My code is shown below:
my_template.html
This currently contains the CSS classes for django-bootstrap-modal-forms. Check out https://pypi.org/project/django-bootstrap-modal-forms/
You can replace the text in the modal-body with {{ form | crispy }} to use django-crispy-forms
<form method="post" action="">
{% csrf_token %}
<div class="modal-header">
<h5 class="modal-title">Create new Task</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
{% for field in form %}
<div class="form-group{% if field.errors %} invalid{% endif %}">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
{% for error in field.errors %}
<p class="help-block invalid-feedback"><strong>{{ error }}</strong></p>
{% endfor %}
</div>
{% endfor %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="submit-btn btn btn-primary">Add Task</button>
</div>
</form>
javascript
$(document).ready(function() {
$(".create-task").modalForm({
formURL: "{% url 'create_task' %}"
});
});
forms.py
from .models import Task, Categories
from bootstrap_datepicker_plus import DateTimePickerInput
from django import forms
from django.db.models import Q
from bootstrap_modal_forms.forms import BSModalForm
from django.contrib.auth import get_user_model
class TaskForm(BSModalForm):
# Get al the categories from the database
categories_queryset = Categories.objects.all()
task_title = forms.CharField(max_length=100)
task_description = forms.CharField(widget=forms.Textarea)
due_date = forms.DateTimeField(
widget = DateTimePickerInput(format='%Y-%m-%d %H:%M')
)
is_completed = forms.BooleanField()
categories = forms.ModelChoiceField(categories_queryset, empty_label="---None---")
#parent = forms.ModelChoiceField()
#task_title.widget.attrs.update({'class': 'form-control'})
class Meta:
model = Task
fields = ['task_title', 'task_description', 'due_date', 'is_completed', 'categories', 'parent']
def __init__(self, *args, **kwargs):
super(TaskForm, self).__init__(*args, **kwargs)
# I have errors using the Queryset to find entities with Categories.user_id == null, and Categories.user_id=get_user_model() -- It gives an error about unpacking boolean.
#self.fields['categories_queryset'] = Categories.objects.filter(Categories.user_id=get_user_model())
# Q(Categories.user_id__isnull==True) |
# User.objects.filter(username=username).exists():
models.py
from django.db import models
from django.db.models import Q
from users.models import CustomUser
from django.urls import reverse
from django.contrib.auth import get_user_model
class Categories(models.Model):
category_type = models.CharField(max_length=50)
user = models.ForeignKey(CustomUser, null = True, on_delete=models.CASCADE)
def __str__(self):
return '%s ' % (self.category_type)
def get_absolute_url(self):
return reverse('task_list')
class Task(models.Model):
task_title = models.CharField(max_length=100)
task_description = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
due_date = models.DateTimeField()
is_completed = models.BooleanField(default=False)
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
categories = models.ForeignKey(Categories, on_delete=models.CASCADE)
parent = models.ForeignKey("self", on_delete=models.CASCADE)
class Meta:
verbose_name = "Task"
verbose_name_plural = "Tasks"
def __str__(self):
return '%s ID: %s' % (self.task_title, self.last_name)
def get_absolute_url(self):
return reverse('task_detail')
Thanks…
2
Answers
It is quit a long time this question was posted, but as I’m currently stucking with the same issue, I dare to share my experiences:
I’ve got it running as expected when I do not use crispy-forms as you did. I fear, you have to deal with a JS implementation, as described in the django-crispy-forms doc.
It took a long time to figure this out but luckily the solution is very simple.
The ajax request is working as expected in your example. However, django-modal-forms checks the response for errors with this line:
The error class in the default settings is set to ".invalid" but crispy forms render their errors with class ".is-invalid". Thats why the form is not recognized as erroneous and submitted (incl. page refresh) instead of rerendered with error messages.
Changing the javascript part to the following solved it for me.