Published 2021-03-19.
Last modified 2021-03-25.
Time to read: 3 minutes.
django
collection.
This article builds on a previous article:
Acknowledgement
I found an excellent article by Vitor Freitas entitled How to Work With AJAX Request With Django that described how to create a user registration form in Django webapp, such that the form checked whether the currently typed userid was available, without the user having to submit the form.
The userid uniqueness test was performed on the server by the Django webapp.
An Ajax GET
request was used to communicate from the web browser to the Django webapp.
The article was written 5 years and many versions of Django ago, so it’s detailed instructions no longer can be followed.
I decided to follow that basic recipe,
updated for current software,
and apply the article to django-oscar
,
using the standard django-oscar
user registration form.
Because django-oscar
is a more specialized framework than Django,
it is more complete out of the box, so I had less work to do than Vitor Freitas described for Django.
The remainder of the article are the updated instructions.
AuthenticationForm
Django-oscar
uses email addresses for user ids.
As discussed in the Django-Oscar Startup & User Registration article,
the HTML form for django-oscar
user signin is subclassed from the standard Django
AuthenticationForm
.
I used the web browser's view source to see what it looks when rendered:
<input type="email" name="login-username"
maxlength="150" class="form-control"
required id="id_login-username">
The most important detail here is the value of id
, which is id_login-username
.
We need to know this so we can write JavaScript that triggers when a user types in their username during the registration process.
The Bootstrap 4 documentation
discusses the form-control
CSS class.
JavaScript
The List of Django-Oscar Page Template Blocks
article lists out all of the Django template blocks defined by the base.html
and layout.html
Django templates.
“Hmm, which block should I store my JavaScript Ajax code in?”, I wondered.
There are 3 reasonable Django template blocks to choose from:
onbodyload
, scripts
, and extrascripts
.
Considering the code that I have in mind, I think selecting the onbodyload
Django template block makes the most sense.
I added the following to the end of
/mnt/f/work/django/frobshop/templates/oscar/customer/login_registration.html
:
{% block onbodyload %}
{{ block.super }}
$("#id_login-username").change(function () {
alert( $(this).val() );
});
{% endblock %}
{{ block.super }}
means that the rest of the block content is meant to
enhance the default value of the block content, not replace it.
My browser's view source shows me that the web page was automatically regenerated with the additional JavaScript. I highlighted the default value, which was (hopefully) enhanced by the code I provided:
<script>
$(function() {
oscar.init();
$("#id_login-username").change(function () {
alert( $(this).val() );
});
});
</script>
Now each time I type a value into the Email address field, and then the focus changes (perhaps by pressing the tab key), I get an alert that shows what I had typed. Fantastic!
Ajax Request
Now it is time to connect the JavaScript change
handler to the
Frobshop Django webapp that I've been working on
(actually it is a django-oscar
webapp, which is a subclass of Django webapps.)
I already had an app called main
in my Frobshop webapp.
No change was required to the
recipe
I am following with respect to the view.
I merely added the following to $frobshop/main/views.py
:
from django.contrib.auth.models import User from django.http import JsonResponse def validate_username(request): username = request.GET.get('username', None) data = { 'is_taken': User.objects.filter(username__iexact=username).exists() } if data['is_taken']: data['error_message'] = 'A user with this username already exists.' return JsonResponse(data)
Django 2.0
changed url
to re_path
.
I adjusted the recipe’s code example accordingly, and added the following to
$frobshop/main/urls.py
:
from main.views import validate_username
urlpatterns = [ re_path(r'^ajax/validate_username/$', validate_username, name='validate_username'), ]
Now I pasted in the JavaScript changes from the recipe into
/mnt/f/work/django/frobshop/templates/oscar/customer/login_registration.html
.
The changes are highlighted.
{% block onbodyload %} {{ block.super }}
$("#id_login-username").change(function () { var form = $(this).closest("form"); $.ajax({ url: form.attr("data-validate-username-url"), data: $(this).serialize(), dataType: 'json', success: function (data) { if (data.is_taken) { alert(data.error_message); } else { alert("New user"); // Delete this line once it works } } }); }); {% endblock %}
The Django console logged the request.
[25/Mar/2021 12:11:29] "GET /accounts/login/?login-username=adminx HTTP/1.1" 200 19692
OMG, it worked perfectly!