Django - Ajax modal login/registration - ajax

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)

Related

how to request django post method values using ajax

I would like to implement simple django login method using ajax. when i want to retreive html input values using ajax in views, the request.post values are returning None. for ex print(request.POST.get('username')), it returns None.
my html form
<form action="" method="POST">{% csrf_token %}
<input type="text" name="username1" class="form-control rounded-1" id="id_username1" value="">
<input type="password" name="password1" class="form-control rounded-1" id="id_password1" value="">
</div>
<input type="button" id="btnlogin" value='Valider'>
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.2/jquery.min.js"></script>
<script>
var token = '{{csrf_token}}';
//login
$('#btnlogin').click(function(){
//alert("/login");
$.ajax({
url: "{% url 'login' %}",
headers:{'X-CSRFToken':token},
method:"POST",
data:{},
dataType: 'json',
success: function (data) {
if (data.status){
console.log('Success');
}
else{
console.log("login failed");
}
}
});
})
</script>
urls.py
path('login/', views.login_page, name='login'),
views.py
def login_page(request):
if request.method == 'POST':
username = request.POST.get('username1')
password = request.POST.get('password1')
print('password :',password)
#result "password : None"
user = authenticate(username=username, password=password)
if user is not None:
login(request, user)
return JsonResponse({'status':True})
else:
return JsonResponse({'status':False})
context = {}
return render(request, 'core/login.html', context)
I do not know where is the error and how to fix this issue.
Thanx in advance.
i tried request.POST['password1']#result None
i changed to GET method. same issue.
I would like to collect forms input values in django view method using ajax

CSRF Token missing or invalid. -> First form fails other CSRF Token work

So I have a site with multiple buttons. These buttons are inside forms and use this { CSRF }. However the first button won't work.
This is a snippet of how the HTML looks like.
<form method="post" id="post-form">
{% csrf_token %}
<button type="submit" name="startVm" class="btn btn-default btn-block d-none d-md-block">StartVM</button>
</form>
<form method="post" id="post-form">
{% csrf_token %}
<button type="submit" name="stopVm" class="btn btn-default btn-block d-none d-md-block">StopVM</button>
</form>
And this is the Ajax function that I use.
$('#post-form').on('submit', function(e){
e.preventDefault();
console.log("form submitted!") // sanity check
post();
});
// AJAX for posting
function post() {
console.log("create post is working!") // sanity check
$.ajax({
url : '', // the endpoint
type : 'post', // http method
data : {},
csrfmiddlewaretoken: '{{ csrf_token }}',
contentType: 'application/x-www-form-urlencoded',
processData: true,
// handle a successful response
success : function() {
alert("Thank you for your comment!");
console.log("success"); // another sanity check
},
// handle a non-successful response
error : function(xhr,errmsg,err) {
$('#results').html("<div class='alert-box alert radius' data-alert>Oops! We have encountered an error: "+errmsg+
" <a href='#' class='close'>×</a></div>"); // add the error to the dom
console.log(xhr.status + ": " + xhr.responseText); // provide a bit more info about the error to the console
}
});
};
So as I said. The button StartVM won't work and it returns a 403 error.(Forbidden (CSRF token missing or incorrect.): /)
The second one however works without a problem.
This is the code in the view.py
def post (self, request):
if request.method == 'POST' and 'startVm' in request.POST:
print("startVM button")
return HttpResponse("{}",
content_type='application/json', status=204)
if request.method == 'POST' and 'stopVm' in request.POST:
print("stopVM button");
return HttpResponse("{}",
content_type='application/json', status=204)
return HttpResponse("{}",
content_type='application/json')
I am returning status 204 because e.preventDefault() won't work and it refreshes the whole site if I click on a button.
Firstly, ids should be unique, but you have id="post-form" on two separate forms.
You could do class="post-form" instead, and change your JS to use .post-form.
Or, for the template in your question, you could have a single <form> tag that contains both buttons.
Next, you need to include the CSRF token inside the form data.
data : {'csrfmiddlewaretoken': '{{ csrf_token }}'},
Alternatively, you could follow the suggestion in the docs, and add an X-CSRFToken header for ajax requests, then you don't need to include the token in the post data.

Django - AJAX - how to submit multiple forms?

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>

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;
});

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();
},

Resources