Hello everyone, I have a problem when I want to send the data of a form and an inlineformset through ajax. The truth is I don’t know which is the correct way to do it and I hope to find it here, thanks in advance. In principle I have two forms in a createview (ModelForm and inlineformset), the ModelForm refers to the data of a Provider and the inlineformset to its Contacts. I will leave the code below.
Using ajax in this way throws me the error: SyntaxError: Unexpected token < in JSON at position 1
# models.py
class Proveedor(models.Model):
tipoDoc = models.CharField(
max_length=30, verbose_name='Documentación')
numDoc = models.CharField(
max_length=20, unique=True, verbose_name='Número')
razon_social = models.CharField(max_length=50, verbose_name='Razón Social')
personeria = models.CharField(
max_length=40, choices=PERSONERIA, default='F', verbose_name='Personería')
cat_impositiva = models.CharField(
max_length=50, choices=CATEGORIA_IMPOSITIVA, default='RI', verbose_name='Categoria Impositiva')
def __str__(self):
return self.razon_social
class Meta:
verbose_name = 'Proveedor'
verbose_name_plural = 'Proveedores'
ordering = ['id']
class Contacto(models.Model):
nombre = models.CharField(max_length=50, verbose_name='Nombre')
email = models.EmailField(
max_length=30, blank=True, null=True, verbose_name='Email')
telefono = models.CharField(
max_length=30, blank=True, null=True, verbose_name='Teléfono')
proveedor = models.ForeignKey(
Proveedor, on_delete=models.CASCADE)
def __str__(self):
return self.nombre
class Meta:
verbose_name = 'Contacto'
verbose_name_plural = 'Contactos'
ordering = ['id']
#forms.py
class ProveedorForm(ModelForm):
class Meta:
model = Proveedor
fields = '__all__'
class ContactoForm(ModelForm):
class Meta:
model = Contacto
fields = '__all__'
def save(self, commit=True):
data = {}
form = super()
try:
if form.is_valid():
form.save()
else:
data['error'] = form.errors
except Exception as e:
data['error'] = str(e)
return data
ContactoFormSet = inlineformset_factory(
Proveedor, Contacto, form=ContactoForm, extra=1, max_num=10)
# views.py
class ProveedorCreate(LoginRequiredMixin, CreateView):
model = Proveedor
form_class = ProveedorForm
template_name = 'proveedor/create.html'
success_url = reverse_lazy('app_compra:proveedor_list')
def get_context_data(self, **kwargs):
data = super().get_context_data(**kwargs)
data['list_url'] = reverse_lazy('app_compra:proveedor_list')
data['action'] = 'add'
if self.request.POST:
data['contacto_formset'] = ContactoFormSet(self.request.POST)
else:
data['contacto_formset'] = ContactoFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
contacto = context['contacto_formset']
with transaction.atomic():
self.object = form.save()
if contacto.is_valid():
contacto.instance = self.object
contacto.save()
return super(ProveedorCreate, self).form_valid(form)
<!-- create.html -->
{% extends 'body.html' %}
{% load widget_tweaks %}
{% load static %}
{% block content %}
<form method="post" action="." class='container-fluid' enctype="multipart/form-data">
<div class="card-body">
{% csrf_token %}
<input type="hidden" name="action" value="{{ action }}">
{{ form.tipoDoc }}
{{ form.numDoc }}
{{ form.razon_social }}
{{ form.personeria }}
{{ form.cat_impositiva }}
<table class="table">
{{ contacto_formset.management_form }}
{% for form in contacto_formset.forms %}
{% if forloop.first %}
<thead>
<tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr>
</thead>
{% endif %}
<tr class="formset_row">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
</div>
<div class="card-footer">
<button type="submit" class="btn btn-primary" id='btnAdd'>
<i class="fas fa-save"></i> Guardar registro
</button>
<a href="{{ list_url }}" class="btn btn-danger">
<i class="fas fa-times"></i> Cancelar
</a>
</div>
</div>
</form>
{% endblock %}
{% block script %}
<!-- SweetAlert2 -->
<script src="{% static 'libs/sweetalert2-11.3.10/sweetalert2.min.js' %}"></script>
<!-- jQueryConfirm -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-confirm/3.3.2/jquery-confirm.min.js"></script>
<script>
function submit_with_ajax(url, title, content, parameters, callback) {
$.confirm({
theme: 'material',
title: title,
icon: 'fa fa-info',
content: content,
columnClass: 'small',
typeAnimated: true,
cancelButtonClass: 'btn-primary',
draggable: true,
dragWindowBorder: false,
buttons: {
info: {
text: "Si",
btnClass: 'btn-primary',
action: function () {
$.ajax({
url: url,
type: 'POST',
data: parameters,
dataType: 'json',
processData: false,
contentType: false,
}).done(function (data) {
console.log(data);
if (!data.hasOwnProperty('error')) {
callback(data);
return false;
}
}).fail(function (jqXHR, textStatus, errorThrown) {
alert(textStatus + ': ' + errorThrown);
}).always(function (data) {
});
}
},
danger: {
text: "No",
btnClass: 'btn-red',
action: function () {
}
},
}
})
}
$('form').on('submit', function (e) {
e.preventDefault();
var parameters = new FormData(this);
submit_with_ajax(window.location.pathname, 'Notificación', '¿Estas seguro de realizar la siguiente acción?', parameters, function () {
location.href = '{{ list_url }}';
});
});
</script>
{% endblock script %}
It’s worth mentioning that without ajax it works fine, but without ajax I can’t use jQueryConfirm (I think so).