Django - AJAX - how to submit multiple forms? - ajax

Here are my two ajax codes for two forms. These two codes are exactly the same, except for button ID
$("#form_1_submit").on('click', function (e) {
e.preventDefault();
var form = $(this).closest("form");
var data = form.serializeArray();
$.ajax({
url: "",
dataType:"json",
type: "POST",
data: data,
success: function() {
alert('ajax request')
},
error: function() {
alert("error")
}
});
console.log(form.html())
});
$("#form_1_submit").on('click', function (e) {
e.preventDefault();
var form = $(this).closest("form");
var data = form.serializeArray();
$.ajax({
url: "",
dataType:"json",
type: "POST",
data: data,
success: function() {
alert('ajax request')
},
error: function() {
alert("error")
}
});
console.log(form.html())
});
And here is my views.py:
class BHA_UpdateView(UpdateView):
model = Different_Model
fields = '__all__'
def post(self, request, **kwargs):
if self.request.is_ajax():
print(self.request.POST)
form_1 = Form_2(request.POST, instance=Model_1.objects.filter(#some_filtering...)
form_2 = Form_1(request.POST, instance=Model_2.objects.filter(#some_filtering...)
if form_1.is_valid():
form_1.save()
return super().post(request, **kwargs)
if form_2.is_valid():
form_1.save()
return super().post(request, **kwargs)
return super().post(request, **kwargs)
There are two problems:
First: $.axax({...}) gives error, instead of success, and I don't know why. But it still saves to DB.
Second: Submitting one form results in the other form's values not saving to DB. This is my current page:
Ideally, clicking one of the Save button should result in saving data to each respective tables in DB. But if I click Save for Overall BHA, it saves
{'bha_name': 'form_1', 'depth_in' : 'form_1', 'depth_out': 'form_1'},
but at the same time saves this to my DB's table for Drill Bit:
{'bit_type': '', 'size': '', 'bit_model': ''}
emptying out the stored values for the table.
why this is happening, and how do I fix it?
++ form_1.is_valid() always returns True. I think this is why form_2's values are empty.

You could render several forms inside a single HTML form element and submit them all together without ajax
<form method="post">{% csrf_token %}
<div class="form-row">
<div class="col-sm">{{ form_a.as_p }}</div>
</div>
<div class="form-row">
<div class="col-sm">{{ form_b.as_p }}</div>
</div>
<div class="form-row">
<div class="col-sm">{{ form_c.as_p }}</div>
</div>
<button type="submit" class="save btn btn-default">Save</button>
</form>

Related

Ajax Call to PartialViewResult does not Replace Div with PartialView

I've done this dozens of times before and have been testing all morning, I must be missing something very obvious.
I have a form that submits data and if the data already exists, I just want to overwrite that form using a PartialView. I can debug the code and watch the POST get called and I even watch the PartialView reciev its model and data but the PartialView doesn't get rendered on the screen and my AJAX doesn't return anything to the console so I'm not sure how to Troubleshoot this.
My Controller
[HttpPost]
[Route("Send")]
public PartialViewResult Send([FromBody] InstantAlert InstantAlert)
{
string view = "~/views/shared/_InstantAlert_Exists.cshtml";
}
My View
<!-- Form -->
<div id="DivSubmitForm">
<partial name="~/views/home/_Partials/_SubmitForm.cshtml", model="Model" />
</div>
<!-- End Form -->
My Script
$(function () {
$(document).on("click", '#btnSubmit', function () {
if ($('form').valid()) {
Submit();
}
});
function Submit() {
//JSON data
var InstantAlert = {
url: $('#url').val(),
userId: $('#userId').val(),
institutionId: $('#institutionId').val()
}
var jsonToPost = JSON.stringify(InstantAlert);
$.ajax({
url: '/home/Send',
contentType: "application/json; charset=utf-8",
data: jsonToPost,
type: "POST",
success: function (result) {
console.log("Success");
//$('#DivSubmitForm').html(result);
},
error: function (xhr, ajaxOptions, thrownError) {
alert(xhr.status);
alert(thrownError);
}
});
}
});
PartialView
<div class="form-group">
<div class="alert alert-danger alert-dismissible" role="alert">
<strong>This article has already been submitted</strong>
<hr class="message-inner-separator">
<p>
test
</p>
</div>
</div>
Argh, so I discovered a form tag on my page which means the controller was inevitably always reloading my initial controller....

Django: Uploading files with AJAX: Form says that the file input field is empty (or CSRF token missing or incorrect)

I'm trying to add a very simple file upload modal form in my Django app.
But, when I click the submit button, the form shows me an error message: "this field is required".
Everything renders correctly:
My main page loads correctly
When I click in the "Agregar archivo adjunto" button ("add attachment"), the modal form shows correctly, and all the fields are rendered as I want them to.
The issue comes when I click on the "Adjuntar archivo" ("attach file") button in my modal field: The form throws an error, as if I was trying to upload a "null" file!
Now... I forgot to add cache: false, contentType: false, processData: false to the $.ajax() call, but, when I add them, I get the following error: Forbidden (CSRF token missing or incorrect.). So... I don't know how to proceed!
I've already wrote (successfully) a modal form which helps me add notes (related) to my lead object (using this reference), and I'm trying to reproduce exactly the same process for a file upload modal dialog... but it doesn't work :(
Any help will be really appreciated.
By the way: I'm testing this using Chrome (no IE will be used!)
Here is my code:
models.py
def lead_dir_path(instance, filename):
"""
Files will be saved to: MEDIA_ROOT/leads/<int:pk>/<filename>
where <int:pk> is lead's primary key, and <filename> is just that.
Filename will be set to an UUID value.
"""
ext = filename.split('.')[-1]
filename = '%s.%s' % (uuid.uuid4(), ext)
return 'leads/%s/%s' % (instance.lead.pk, filename)
class ArchivosAdjuntosLead(models.Model):
lead = models.ForeignKey(Lead, on_delete=models.CASCADE)
descripcion = models.CharField(max_length=100)
archivo = models.FileField(upload_to=lead_dir_path)
views.py
def agregar_adjunto_json(request, pk):
"""
Adds a file to lead with id=pk
"""
context = {}
data = {}
lead = get_object_or_404(Lead, pk=pk)
context['lead'] = lead
if request.method == 'POST':
form = AdjuntarArchivoLeadForm_v2(request.POST, request.FILES)
if form.is_valid():
form.save();
data['form_is_valid'] = True
else:
data['form_is_valid'] = False
else:
form = AdjuntarArchivoLeadForm_v2()
form.initial = {'lead': lead}
context['form'] = form
data['html_form'] = render_to_string(
template_folder + 'partial_templates/partial_adjuntar_archivo.html',
context,
request = request,
)
return JsonResponse(data)
forms.py
class AdjuntarArchivoLeadForm_v2(forms.ModelForm):
class Meta():
model = ArchivosAdjuntosLead
fields = ['lead', 'descripcion', 'archivo']
widgets = {
'lead': forms.TextInput(attrs={'class':'form-control', 'style':'display:none;'}),
'descripcion': forms.TextInput(attrs={'class':'form-control'}),
'archivo': forms.FileInput(attrs={'class':'form-control'}),
}
partial_adjuntar_archivo.html
I use this partial template to create a modal form:
<form method="POST" enctype="multipart/form-data"
action="{% url 'leads:agregar_adjunto_v2' pk=lead.pk %}"
id="js_adjuntar_archivo_form">
{% csrf_token %}
<div class="modal-header">
<h4 class="modal-title">Adjuntar archivo</h4>
</div>
<div class="modal-body">
{{ form.as_p }}
<div class="modal-footer">
<button type="submit" class="btn btn-primary col-4">Adjuntar archivo</button>
<button type="button" class="btn btn-secondary col-4" data-dismiss="modal">Cancelar</button>
</div>
</div>
</form>
my_lead_page.html
This is the page where I create the modal form:
{% extends "leads/base.html" %}
{% load static %}
{% block contenido %}
<!-- Lots and lots of info -->
<button type="button" class="btn btn-sm btn-primary col-2" id="btn_agregar_adjunto">
Agregar archivo adjunto
</button>
{% endblock %}
{% block other_scripts %}
<script type="text/javascript" src="{% static 'js/leads/archivos_adjuntos.js'%}"></script>
{% endblock %}
archivos_adjuntos.js
$(function() {
$("#btn_agregar_adjunto").click(function() {
$.ajax({
url: 'adjuntar_archivo/',
type: 'get',
dataType: 'json',
beforeSend: function() {
$("#modal-form").modal("show");
},
success: function(data) {
$("#modal-form .modal-content").html(data.html_form);
}
});
});
$("#modal-form").on("submit", "#js_adjuntar_archivo_form", function() {
var form = $(this);
$.ajax({
url: form.attr("action"),
data: form.serialize(),
type: form.attr("method"),
dataType: 'json',
cache: false,
contentType: false,
processData: false,
success: function(data) {
if(data.form_is_valid) {
alert("Archivo adjuntado");
} else {
$("#modal-form .modal-content").html(data.html_form);
}
}
});
return false;
});
});
Instead of form.serialize() try sending it with js formData() it should work.
Here is an example:
$("#modal-form").on("submit", "#js_adjuntar_archivo_form", function() {
$that = this;
var form = new FormData($(this)[0]);
$.ajax({
url:$that.attr("action"),
type:$that.attr("method"),
data:form,
processData: false,
contentType: false,
// rest of the code'''
});
return false;
});

Form post from partial view to API

I am trying to create an SPA application using Sammy. When I call #/entitycreate link, I return a partial view from Home controller which contains an html form to submit. Partial view comes as I expect but rest of it doesn't work. Below are my problems and questions, I'd appreciate for any help.
KO binding doesn't work in partial view, even though I did exactly how it's done in the default SPA project template (see home.viewmodel.js).
This one is the most critical: when I submit this form to my API with ajax/post, my model always comes back with a null value, therefore I can't create an entity via my API. I have tried with [FromBody] and without, model always comes null.
In some sense a general question, should I include Html.AntiForgeryToken() in my form and [ValidateAntiForgeryToken] attribute in my API action?
Partial View:
#model namespace.SectorViewModel
<!-- ko with: sectorcreate -->
<div class="six wide column">
<div class="ui segments">
<div class="ui segment">
<h4 class="ui center aligned header">Create New Sector</h4>
</div>
<div class="ui secondary segment">
<form id="entity-create-form" class="ui form" action="#/sectorcreatepost" method="post" data-bind="submit: createEntity">
<!-- I am not sure if I should include AntiForgeryToken for WebAPI call -->
<!-- Html.AntiForgeryToken() -->
<fieldset>
<div class="field required">
#Html.LabelFor(m => m.Name)
#Html.TextBoxFor(m => m.Name, new { data_bind = "value: name" })
</div>
<div class="ui two buttons">
<button class="ui positive button" type="submit">Create</button>
<button class="ui button" type="button" id="operation-cancel">Cancel</button>
</div>
</fieldset>
</form>
</div>
</div>
</div>
<!-- /ko -->
JS View Model:
function SectorCreateViewModel(app, dataModel) {
var self = this;
self.name = ko.observable("ko binding doesn't work");
self.createEntity = function () {
console.log("ko binding doesn't work");
}
Sammy(function () {
this.get("#sectorcreateget", function () {
$.ajax({
url: "/home/getview",
type: "get",
data: { viewName: "sectorcreate" },
success: function (view) {
$("#main").html(view);
}
});
return false;
});
this.post("#/sectorcreatepost",
function () {
$.ajax({
url: "/api/sectors",
type: "post",
data: $("#entity-create-form").serialize(),
contentType: "application/json; charset=utf-8",
success: function (response) {
console.log(response);
},
error: function (xhr, status, error) {
console.log(xhr);
console.log(status);
}
});
return false;
});
this.get("#/yeni-sektor", function () {
this.app.runRoute("get", "#sectorcreateget");
});
});
return self;
}
app.addViewModel({
name: "SectorCreate",
bindingMemberName: "sectorcreate",
factory: SectorCreateViewModel
});
API Action:
public HttpResponseMessage Post([FromBody]SectorViewModel model)
{
// model is always null, with or without [FromBody]
if (!ModelState.IsValid)
return Request.CreateResponse(HttpStatusCode.BadRequest);
// repository operations...
return response;
}
I have removed contentType: "application/json; charset=utf-8", from ajax request based on the article here. #2 is now resolved, #1 and #3 still remains to be answered.

How to access the global variable in Ajax sucess of kendo grid update?

Currently Developing web Application using AngularJS using with Kendo. When I save inline edit grid need to hide my save button and want to show back the Add button. For Show and Hide I use *ngIf. In this class I define public isAddEdit: Boolean; I cannot access the variable in success scope.
update: function (options) {
$.ajax({
url: HttpUrl.UpdateBlog,
contentType: "application/JSON",
type: "POST",
data: JSON.stringify(options.data.models),
success: function (result) {
options.success(result);
this.isAddEdit = false;
$('#save').remove();
$('#grid').data('kendoGrid').dataSource.read();
},
})
This is my view
<div id ="btndiv" class="col-sm-12">
<button *ngIf="!isAddEdit" id="addblog" class="k-button grid-top-button-override k-primary add-button page-name" (click)="addStock()">{{'Addblog' | translate}}</button>
<button *ngIf="isAddEdit" id ="save" class="k-button grid-top-button-override k-primary save-button page-name" (click)="clicksave()">{{'Save' | translate}}</button>
</div>
<div class="row grid-override">
<div id="grid"></div>
</div>
I think that the this is related to the AJAX callback function therefore you are not accesing the variable you want.
Try it with an arrow function:
success:(result) => {
options.success(result);
this.isAddEdit = false;
$('#save').remove();
$('#grid').data('kendoGrid').dataSource.read();
},

Django - Ajax modal login/registration

I have a project in which I need to pop a modal window for not authenticated users.
This modal will allow to login directly or create an account.
So it will contain two forms:
django.contrib.auth.forms.AuthenticationForm
registration.forms.RegistrationForm
Here is my view to get both forms:
def ajax_registration(request):
obj = {
'login_form': AuthenticationForm(),
'registration_form': RegistrationForm(),
}
return render(request, 'common/ajax_registration.html', obj)
And my template displaying the forms tabbed
<ul class="nav nav-tabs">
<li>{% trans 'Login' %}</li>
<li>{% trans 'Registration' %}</li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="tab1">
{{ login_form|bootstrap }}
</div>
<div class="tab-pane" id="tab2">
{{ registration_form|bootstrap }}
</div>
</div>
Question is: Since I'm using ajax to display this modal, How can I validate the selected form, preferably using the already written django-registrations register & django.contrib.auth login views ?
In addition to Maddog's answer you need some javascript to submit the form back to the URL that rendered the form. Using jquery it could be something like:
$('form').submit(function(e){
e.preventDefault();
var form = $(e.target);
$.ajax({
url: '{% url YOUR_REGISTRATION_URL %}',
type: 'post',
data: account_form.serialize() + '&' + form.serialize(),
error: function(xhr, ajaxOptions, thrownError){ alert(thrownError); },
success: function(){}
})
})
You don't need to do it with a form submit element, you could use any element with $().click(), of course.
Something like this?
def ajax_registration(request):
login_form, registration_form = False, False
if request.method == "POST":
if "email" in request.POST: # some condition to distinguish between login and registration form
login_form = AuthenticationForm(request.POST)
if login_form.is_valid():
# log in
else:
registration_form = RegistrationForm(request.POST)
if registration_form.is_valid():
# register
obj = {
'login_form': login_form if login_form else AuthenticationForm(),
'registration_form': registration_form if registration_form else RegistrationForm(),
}
return render(request, 'common/ajax_registration.html', obj)

Resources