I am building a basic Flask application with a sqlite database.
The feature I am having trouble implementing is for a user to be able to accept or refuse a contact request (like a friend request) from another user.
On the "/contacts" page, you can see all the contact requests you received from other users without problem, but I want to add the option to accept or refuse them and this is where I'm stuck.
A form is dynamically generated by fetching from the database all the contact requests the current user has received and displaying them on the page.
I have tried using two <input type="submit" name="accept/delete" value="id_of_the_request"> tags for each requests, one with the accept option, the other with the delete option, both leading to the same route, but unlike some other input types, the "value" property controls what text appears on the button, so I can't set that to, say, the id of the contact request (which I did in the code further below) because then I see two buttons with a number on my page.
I thought about doing the opposite and setting the name of the tag to the request's id instead, and the value to "delete" or "accept", but then on the server side I wouldn't know what name to get with request.form.get() since the request's id are dynamically generated in the form depending on what's in the database.
I feel like I'm missing some basic knowledge and that it shouldn't be too hard to do that though.
Here is my html code (the template is passed a list of dictionaries (requests) from the database, corresponding to the list of contact requests received by the current user. Each request consists of 3 columns : request_id, user_email, contact_email. request_id is the primary key, user_email is the email of the person who sent the request, while contact_email is the email of the person who received it. ):
<form action="/manage_requests" method="post">
<ul>
{% for request in requests %}
<li>{{request.user_email}} sent you a contact request.</li>
<input type="submit" name="accept" value="{{request.r_id}}">
<input type="submit" name="refuse" value="{{request.r_id}}">
{% endfor %}
</ul>
</form>
Here is my python code to handle accepting or refusing the request :
#app.route("/manage_requests", methods = ["POST"])
#login_required
def manage_requests():
acceptedID = int(request.form.get("accept"))
refusedID = int(request.form.get("refuse"))
## Add the user who sent the request as a contact for both them and us, then delete the request.
if acceptedID :
# fetch the info of the request corresponding id from the database requests table
# get the sender's user_email
# insert the data into the database contacts table for both the sender and the receiver (current user)
# delete the request from the requests table in the database
return redirect("/contacts")
## Delete the request
elif refusedID :
# delete the request from the database requests table
return redirect("/contacts")
This is how I would have done it:
First a route that returns all contact requests like you did:
#app.route("/contacts", methods = ["POST"])
#login_required
def contacts():
# requests_list = Find all the requests for a user
return render_template('contacts.html', requests_list=requests_list)
Then I return in my template all the requests found:
<ul>
{% for request in requests %}
<li>{{request.user_email}} sent you a contact request.</li>
<a href="{{ url_for('manage_requests', request_id=request.r_id, action='accept' )}}">
<input type="submit" name="accept">
</a>
<a href="{{ url_for('manage_requests', request_id=request.r_id, action='refuse' )}}">
<input type="submit" name="refuse">
</a>
{% endfor %}
</ul>
Note how I use the <a></a> tag around the submit button, with the Jinja2 url_for function passed to the href attribute, with the request_id as a parameter and a variable action which takes as the value either accept or refuse:
And finally:
#app.route("/manage_requests", methods = ["POST"])
#login_required
def manage_requests():
action = request.args.get('action')
request_id = int(request.args.get('request_id'))
## Add the user who sent the request as a contact for both them and us, then delete the request.
if action == "accept" :
# fetch the info of the request corresponding id from the database requests table
# get the sender's user_email
# insert the data into the database contacts table for both the sender and the receiver (current user)
# delete the request from the requests table in the database
return redirect("/contacts")
## Delete the request
else :
# delete the request from the database requests table
return redirect("/contacts")
What is the preferred way of pre-populating database (Model) objects in a Django app? I am inclined to try to script POSTing data to the relevant endpoints, but am being stymied by the CSRF protection.
This is not part of the testing framework, this is for setting up demonstration and training instances in a beta testing or production environment.
As a notional example, I'd like to populate the the "Player" database
with three entries: Alice(sender), Bob(reciever) and Charlie(eavesdropper), and I'd like to script/automate the process of creating these entries after deploying and starting the application.
I already have a form based mechanism for creating new Players. By visiting /create via a browser, there is a form that allows a person to type in the name, e.g. "Bob" and a role, e.g. "reciever", and submit the form to create the new Player instance.
Thus it makes sense to me to try to try to use the existing web API for this: e.g. make calls like
requests.post('0.0.0.0:8000/create', data={'name':'Bob', 'role':'reciever'}) in the script that pre-populates the database. However doing this results in 403 errors due to the CSRF tokens, which I don't want to disable. This problem also occurs if I just try to use a requests.Session to try to maintain the cookies between calls.
One viable solution would involve effectively managing the cookies involved to allow for posting data. However, I'm open to different ways to allow for creating model instances for initial system configuration.
Relevant code snippets:
def create(request):
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = PlayerForm(request.POST)
# check whether it's valid:
if form.is_valid():
data = form.cleaned_data
s = models.Player()
s.name = data['name']
s.role = data['role']
s.save()
msg = "TODO: make a senisble return message"
return HttpResponse(msg)
else:
msg = "TODO: make invalid sources message"
return HttpResponse(msg)
# if a GET (or any other method) we'll create a blank form
else:
form = PlayerForm()
return render(request, 'player/create.html', {'target':'/create', 'form': form})
class Player(Model):
name = models.CharField(max_length=168)
role = models.CharField(max_length=64)
class PlayerForm(forms.Form):
name = forms.CharField(label='Name:', max_length=168)
role = forms.CharField(label='Role:', max_length=64)
Note that the 'target':'/create' is the target for the form's submit action, i.e. when the user hits "Submit" the data from the form are posted to this endpoint (which then hits the if request.method == 'POST' branch to create and save the new instance.
The form is just
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="">
<style>
</style>
<script src=""></script>
<body>
<form action="{{target}}" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
</body>
</html>
I'm very new to Python & Flask and as of rn I'm trying to build a website which generates random colour with a press of a button, but the idea is that you can also save those random colours on your profile and later on manage them (delete, move around, etc).
The problem I'm facing now is on my "randomise" page I can't seem to figure out how to implement a save button without rendering the whole page, randomising the colour again and therefore "saving" wrong colour into the database. Here's what I have so far:
app.py
#app.route("/l_randomize", methods=["GET", "POST"])
def l_randomize():
if request.method == "GET":
return render_template("l_randomize.html")
else:
color = '#{:02x}{:02x}{:02x}'.format(*random.sample(range(256), 3))
if 'favourite' in request.form:
db.execute("INSERT INTO fav (user_id, color) VALUES (:user_id, :color)", user_id=session["user_id"], color=color)
return render_template("l_randomized.html",color=color)
app.html
{%block body%}
<div class="container">
<h3>Your color is</h3>
<div id="l_randomized" style="background-color: {{ color }};">
<form method="post" action="/l_randomize">
<input type="submit" name = "randomize" value="Randomize" >
<input type="submit" name = "favourite" value="Favourite" >
</form>
</div>
</div>
{%endblock%}
I kind of understand the flaw in code that I already wrote and why it's rendering the template again, but I just need a hint or where to look at, so after that if statement it wouldn't refresh the page or go further. Any suggestions, comments and hints on how to implement this better are highly appreciated!
This is a lot trickier than you imagine. You will need to use some kind of asynchronous update. In this case, without learning React et al. it's probably easier to use JQuery. Essentially, you want to intercept your form submission to save the favourite colour (note, I'm in the UK so my spelling is probably inconsistent in the below example, sorry) and then only change a particular element in the DOM.
There's a lot of code below, but it makes a self-contained example - just run the script and go to 127.0.0.1:5000. The major changes:
I've switched to the Flask-SQLAlchemy ORM (Object-relational Mapper). This was partly just because it's easier for me to set up a minimal example, but it's also usually easier in app development. So, I've made two classes that will create the database tables for you.
I've implemented two JavaScript functions that use AJAX. One is attached to a button click, the other intercepts a form submission.
I've split the Flask routes up so that different events can be handled separately
Essentially:
If we don't know the user's favourite colour, we'll just give a white background.
If they select a new colour at random, we'll update the background colour only. We'll also update a hidden field in the form that saves their preference, in case that's the one they want to keep.
If they click "favourite", we'll write that to the database. If you close the page and open it again after this point, you'll notice that we load their previous favourite
Don't use render_template_string in your actual code, it just allowed me to put the template into a single script.
This is for illustrative purposes only. The intention is to connect dots on the general setup; it's missing things like form validation, amongst other things.
from flask import Flask, render_template_string, session, request
from flask_sqlalchemy import SQLAlchemy
import random
# =============================================================================
# BASIC APP INIT
# =============================================================================
db = SQLAlchemy()
app = Flask('__main__')
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
app.config['SECRET_KEY'] = 'change_me'
db.init_app(app)
# =============================================================================
# DATABASE MODELS
# =============================================================================
class Users(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String)
class Fav(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
color = db.Column(db.String)
with app.app_context():
db.create_all()
user = Users(username='testing') # Create a fake user
db.session.add(user)
db.session.commit()
# =============================================================================
# TEMPLATE
# =============================================================================
l_randomize_html = """
{%block body%}
<div class="container">
<h3>Your color is</h3>
<div id="l_randomized" style="background-color: {{ color }};">
<button onclick="changeColor()">Change</button>
<form method="post" action="{{ url_for('l_save_favourite') }}" id="favColorForm">
<input type="hidden" name="currentColor" id="currentColor" value="{{ color }}">
<input type="submit" name="favourite" value="Favourite" >
</form>
<div id="colorSaveResp"></div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script>
function changeColor() {
$.ajax({
type: 'POST',
url: "{{ url_for('change_color') }}",
success: function(color) {
$("#currentColor").val(color);
$("#l_randomized").css("backgroundColor", color);
}
});
}
</script>
<script>
$("#favColorForm").submit(function(e) {
e.preventDefault();
var form = $(this);
var url = form.attr('action');
$.ajax({
type: "POST",
url: url,
data: form.serialize(),
context: form,
success: function(resp) {
$("#dynamic_div").html(resp);
}
});
});
</script>
{%endblock%}
"""
# =============================================================================
# ROUTES
# =============================================================================
#app.route('/', methods=['GET'])
def l_randomize():
session['user_id'] = 1 # Add to the session
# See whether we have a favourite colour
favourite = Fav.query.filter_by(user_id=session['user_id']).first()
if favourite:
color = favourite.color
else:
# They don't, give a default
color = '#FFF'
return render_template_string(l_randomize_html, color=color)
#app.route('/change_color', methods=['POST'])
def change_color():
return '#{:02x}{:02x}{:02x}'.format(*random.sample(range(256), 3))
#app.route('/save_color', methods=['POST'])
def l_save_favourite():
data = request.form.to_dict()
favourite = Fav.query.filter_by(user_id=session['user_id']).first()
if not favourite:
# We don't know this user's favourite colour yet, add it
favourite = Fav(user_id=session['user_id'],
color=data['currentColor'])
db.session.add(favourite)
db.session.commit()
else:
# We'll update their favourite
favourite.color = data['currentColor']
db.session.commit()
return "Saved"
if __name__ == '__main__':
app.run(debug=True)
There's a lot there, and still plenty more that could be implemented, but it's already getting too long. Hopefully you can run it and work through the individual points one-by-one.
Django is creating two records in MySQL instead of one.
I call a function via a link
<button class="btn btn-primary">Thats Me!</button>
The function itself is very straight forward. I take the variable via a request.get, create a new object, and finally save it. However, when I check the DB there are two records, not just one.
def markpresent(request, id):
new_attendance = attendance(clientid_id = id, date = datetime.datetime.now(), camp = 3)
new_attendance.save()
return render(request, 'clienttracker/markpresent.html', {
'client': id,
})
Model
class attendance(models.Model):
clientid = models.ForeignKey(newclients, on_delete=models.CASCADE)
date = models.DateField()
camp = models.CharField(max_length = 3, default=0)
Any help and direction would be appreciated.
SOLUTION BASED ON ANSWERS
<form action="{% url 'markpresent' %}" method="post">
{% csrf_token %}
<button type="submit" name="client" value="{{ c.id }}" class="btn btn-primary">Thats Me!</button>
</form>
def markpresent(request):
id = request.POST.get('client')
new_attendance = attendance(clientid_id = id, date = datetime.datetime.now(), camp = 3)
new_attendance.save()
return render(request, 'clienttracker/markpresent.html', {
'client': id,
})
Thanks
You should avoid modifying your database on a GET request. Various things could cause a duplicate request - for instance, a request for an asset or favicon being caught by the same URL pattern and routed to the same view - so you should always require a POST before adding an entry in your database.
Are you using Google Chrome? If yes, then Google Chrome has something like lazy loading. So if you will type your URL in Google Chrome, it will try to load site behind the scenes and if you will tap enter, then you will get this URL again. The same is when you're trying to go over anchor with a link. It's an edge case, but it happens. Try with firefox or disable that function.
I am new to Django and pretty new to Ajax. I am working on a project where I need to integrate the two. I believe that I understand the principles behind them both, but have not found a good explanation of the two together.
Could someone give me a quick explanation of how the codebase must change with the two of them integrating together?
For example, can I still use the HttpResponse with Ajax, or do my responses have to change with the use of Ajax? If so, could you please provide an example of how the responses to the requests must change? If it makes any difference, the data I am returning is JSON.
Even though this isn't entirely in the SO spirit, I love this question, because I had the same trouble when I started, so I'll give you a quick guide. Obviously you don't understand the principles behind them (don't take it as an offense, but if you did you wouldn't be asking).
Django is server-side. It means, say a client goes to a URL, you have a function inside views that renders what he sees and returns a response in HTML. Let's break it up into examples:
views.py:
def hello(request):
return HttpResponse('Hello World!')
def home(request):
return render_to_response('index.html', {'variable': 'world'})
index.html:
<h1>Hello {{ variable }}, welcome to my awesome site</h1>
urls.py:
url(r'^hello/', 'myapp.views.hello'),
url(r'^home/', 'myapp.views.home'),
That's an example of the simplest of usages. Going to 127.0.0.1:8000/hello means a request to the hello() function, going to 127.0.0.1:8000/home will return the index.html and replace all the variables as asked (you probably know all this by now).
Now let's talk about AJAX. AJAX calls are client-side code that does asynchronous requests. That sounds complicated, but it simply means it does a request for you in the background and then handles the response. So when you do an AJAX call for some URL, you get the same data you would get as a user going to that place.
For example, an AJAX call to 127.0.0.1:8000/hello will return the same thing it would as if you visited it. Only this time, you have it inside a JavaScript function and you can deal with it however you'd like. Let's look at a simple use case:
$.ajax({
url: '127.0.0.1:8000/hello',
type: 'get', // This is the default though, you don't actually need to always mention it
success: function(data) {
alert(data);
},
failure: function(data) {
alert('Got an error dude');
}
});
The general process is this:
The call goes to the URL 127.0.0.1:8000/hello as if you opened a new tab and did it yourself.
If it succeeds (status code 200), do the function for success, which will alert the data received.
If fails, do a different function.
Now what would happen here? You would get an alert with 'hello world' in it. What happens if you do an AJAX call to home? Same thing, you'll get an alert stating <h1>Hello world, welcome to my awesome site</h1>.
In other words - there's nothing new about AJAX calls. They are just a way for you to let the user get data and information without leaving the page, and it makes for a smooth and very neat design of your website. A few guidelines you should take note of:
Learn jQuery. I cannot stress this enough. You're gonna have to understand it a little to know how to handle the data you receive. You'll also need to understand some basic JavaScript syntax (not far from python, you'll get used to it). I strongly recommend Envato's video tutorials for jQuery, they are great and will put you on the right path.
When to use JSON?. You're going to see a lot of examples where the data sent by the Django views is in JSON. I didn't go into detail on that, because it isn't important how to do it (there are plenty of explanations abound) and a lot more important when. And the answer to that is - JSON data is serialized data. That is, data you can manipulate. Like I mentioned, an AJAX call will fetch the response as if the user did it himself. Now say you don't want to mess with all the html, and instead want to send data (a list of objects perhaps). JSON is good for this, because it sends it as an object (JSON data looks like a python dictionary), and then you can iterate over it or do something else that removes the need to sift through useless html.
Add it last. When you build a web app and want to implement AJAX - do yourself a favor. First, build the entire app completely devoid of any AJAX. See that everything is working. Then, and only then, start writing the AJAX calls. That's a good process that helps you learn a lot as well.
Use chrome's developer tools. Since AJAX calls are done in the background it's sometimes very hard to debug them. You should use the chrome developer tools (or similar tools such as firebug) and console.log things to debug. I won't explain in detail, just google around and find out about it. It would be very helpful to you.
CSRF awareness. Finally, remember that post requests in Django require the csrf_token. With AJAX calls, a lot of times you'd like to send data without refreshing the page. You'll probably face some trouble before you'd finally remember that - wait, you forgot to send the csrf_token. This is a known beginner roadblock in AJAX-Django integration, but after you learn how to make it play nice, it's easy as pie.
That's everything that comes to my head. It's a vast subject, but yeah, there's probably not enough examples out there. Just work your way there, slowly, you'll get it eventually.
Further from yuvi's excellent answer, I would like to add a small specific example on how to deal with this within Django (beyond any js that will be used). The example uses AjaxableResponseMixin and assumes an Author model.
import json
from django.http import HttpResponse
from django.views.generic.edit import CreateView
from myapp.models import Author
class AjaxableResponseMixin(object):
"""
Mixin to add AJAX support to a form.
Must be used with an object-based FormView (e.g. CreateView)
"""
def render_to_json_response(self, context, **response_kwargs):
data = json.dumps(context)
response_kwargs['content_type'] = 'application/json'
return HttpResponse(data, **response_kwargs)
def form_invalid(self, form):
response = super(AjaxableResponseMixin, self).form_invalid(form)
if self.request.is_ajax():
return self.render_to_json_response(form.errors, status=400)
else:
return response
def form_valid(self, form):
# We make sure to call the parent's form_valid() method because
# it might do some processing (in the case of CreateView, it will
# call form.save() for example).
response = super(AjaxableResponseMixin, self).form_valid(form)
if self.request.is_ajax():
data = {
'pk': self.object.pk,
}
return self.render_to_json_response(data)
else:
return response
class AuthorCreate(AjaxableResponseMixin, CreateView):
model = Author
fields = ['name']
Source: Django documentation, Form handling with class-based views
The link to version 1.6 of Django is no longer available updated to version 1.11
I am writing this because the accepted answer is pretty old, it needs a refresher.
So this is how I would integrate Ajax with Django in 2019 :) And lets take a real example of when we would need Ajax :-
Lets say I have a model with registered usernames and with the help of Ajax I wanna know if a given username exists.
html:
<p id="response_msg"></p>
<form id="username_exists_form" method='GET'>
Name: <input type="username" name="username" />
<button type='submit'> Check </button>
</form>
ajax:
$('#username_exists_form').on('submit',function(e){
e.preventDefault();
var username = $(this).find('input').val();
$.get('/exists/',
{'username': username},
function(response){ $('#response_msg').text(response.msg); }
);
});
urls.py:
from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('admin/', admin.site.urls),
path('exists/', views.username_exists, name='exists'),
]
views.py:
def username_exists(request):
data = {'msg':''}
if request.method == 'GET':
username = request.GET.get('username').lower()
exists = Usernames.objects.filter(name=username).exists()
data['msg'] = username
data['msg'] += ' already exists.' if exists else ' does not exists.'
return JsonResponse(data)
Also render_to_response which is deprecated and has been replaced by render and from Django 1.7 onwards instead of HttpResponse we use JsonResponse for ajax response. Because it comes with a JSON encoder, so you don’t need to serialize the data before returning the response object but HttpResponse is not deprecated.
Simple and Nice. You don't have to change your views. Bjax handles all your links. Check this out:
Bjax
Usage:
<script src="bjax.min.js" type="text/javascript"></script>
<link href="bjax.min.css" rel="stylesheet" type="text/css" />
Finally, include this in the HEAD of your html:
$('a').bjax();
For more settings, checkout demo here:
Bjax Demo
AJAX is the best way to do asynchronous tasks. Making asynchronous calls is something common in use in any website building. We will take a short example to learn how we can implement AJAX in Django. We need to use jQuery so as to write less javascript.
This is Contact example, which is the simplest example, I am using to explain the basics of AJAX and its implementation in Django. We will be making POST request in this example. I am following one of the example of this post: https://djangopy.org/learn/step-up-guide-to-implement-ajax-in-django
models.py
Let's first create the model of Contact, having basic details.
from django.db import models
class Contact(models.Model):
name = models.CharField(max_length = 100)
email = models.EmailField()
message = models.TextField()
timestamp = models.DateTimeField(auto_now_add = True)
def __str__(self):
return self.name
forms.py
Create the form for the above model.
from django import forms
from .models import Contact
class ContactForm(forms.ModelForm):
class Meta:
model = Contact
exclude = ["timestamp", ]
views.py
The views look similar to the basic function-based create view, but instead of returning with render, we are using JsonResponse response.
from django.http import JsonResponse
from .forms import ContactForm
def postContact(request):
if request.method == "POST" and request.is_ajax():
form = ContactForm(request.POST)
form.save()
return JsonResponse({"success":True}, status=200)
return JsonResponse({"success":False}, status=400)
urls.py
Let's create the route of the above view.
from django.contrib import admin
from django.urls import path
from app_1 import views as app1
urlpatterns = [
path('ajax/contact', app1.postContact, name ='contact_submit'),
]
template
Moving to frontend section, render the form which was created above enclosing form tag along with csrf_token and submit button. Note that we have included the jquery library.
<form id = "contactForm" method= "POST">{% csrf_token %}
{{ contactForm.as_p }}
<input type="submit" name="contact-submit" class="btn btn-primary" />
</form>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Javascript
Let's now talk about javascript part, on the form submit we are making ajax request of type POST, taking the form data and sending to the server side.
$("#contactForm").submit(function(e){
// prevent from normal form behaviour
e.preventDefault();
// serialize the form data
var serializedData = $(this).serialize();
$.ajax({
type : 'POST',
url : "{% url 'contact_submit' %}",
data : serializedData,
success : function(response){
//reset the form after successful submit
$("#contactForm")[0].reset();
},
error : function(response){
console.log(response)
}
});
});
This is just a basic example to get started with AJAX with django, if you want to get dive with several more examples, you can go through this article: https://djangopy.org/learn/step-up-guide-to-implement-ajax-in-django
Easy ajax calls with Django
(26.10.2020)
This is in my opinion much cleaner and simpler than the correct answer. This one also includes how to add the csrftoken and using login_required methods with ajax.
The view
#login_required
def some_view(request):
"""Returns a json response to an ajax call. (request.user is available in view)"""
# Fetch the attributes from the request body
data_attribute = request.GET.get('some_attribute') # Make sure to use POST/GET correctly
# DO SOMETHING...
return JsonResponse(data={}, status=200)
urls.py
urlpatterns = [
path('some-view-does-something/', views.some_view, name='doing-something'),
]
The ajax call
The ajax call is quite simple, but is sufficient for most cases. You can fetch some values and put them in the data object, then in the view depicted above you can fetch their values again via their names.
You can find the csrftoken function in django's documentation. Basically just copy it and make sure it is rendered before your ajax call so that the csrftoken variable is defined.
$.ajax({
url: "{% url 'doing-something' %}",
headers: {'X-CSRFToken': csrftoken},
data: {'some_attribute': some_value},
type: "GET",
dataType: 'json',
success: function (data) {
if (data) {
console.log(data);
// call function to do something with data
process_data_function(data);
}
}
});
Add HTML to current page with ajax
This might be a bit off topic but I have rarely seen this used and it is a great way to minimize window relocations as well as manual html string creation in javascript.
This is very similar to the one above but this time we are rendering html from the response without reloading the current window.
If you intended to render some kind of html from the data you would receive as a response to the ajax call, it might be easier to send a HttpResponse back from the view instead of a JsonResponse. That allows you to create html easily which can then be inserted into an element.
The view
# The login required part is of course optional
#login_required
def create_some_html(request):
"""In this particular example we are filtering some model by a constraint sent in by
ajax and creating html to send back for those models who match the search"""
# Fetch the attributes from the request body (sent in ajax data)
search_input = request.GET.get('search_input')
# Get some data that we want to render to the template
if search_input:
data = MyModel.objects.filter(name__contains=search_input) # Example
else:
data = []
# Creating an html string using template and some data
html_response = render_to_string('path/to/creation_template.html', context = {'models': data})
return HttpResponse(html_response, status=200)
The html creation template for view
creation_template.html
{% for model in models %}
<li class="xyz">{{ model.name }}</li>
{% endfor %}
urls.py
urlpatterns = [
path('get-html/', views.create_some_html, name='get-html'),
]
The main template and ajax call
This is the template where we want to add the data to. In this example in particular we have a search input and a button that sends the search input's value to the view. The view then sends a HttpResponse back displaying data matching the search that we can render inside an element.
{% extends 'base.html' %}
{% load static %}
{% block content %}
<input id="search-input" placeholder="Type something..." value="">
<button id="add-html-button" class="btn btn-primary">Add Html</button>
<ul id="add-html-here">
<!-- This is where we want to render new html -->
</ul>
{% end block %}
{% block extra_js %}
<script>
// When button is pressed fetch inner html of ul
$("#add-html-button").on('click', function (e){
e.preventDefault();
let search_input = $('#search-input').val();
let target_element = $('#add-html-here');
$.ajax({
url: "{% url 'get-html' %}",
headers: {'X-CSRFToken': csrftoken},
data: {'search_input': search_input},
type: "GET",
dataType: 'html',
success: function (data) {
if (data) {
console.log(data);
// Add the http response to element
target_element.html(data);
}
}
});
})
</script>
{% endblock %}
I have tried to use AjaxableResponseMixin in my project, but had ended up with the following error message:
ImproperlyConfigured: No URL to redirect to. Either provide a url or define a get_absolute_url method on the Model.
That is because the CreateView will return a redirect response instead of returning a HttpResponse when you to send JSON request to the browser. So I have made some changes to the AjaxableResponseMixin. If the request is an ajax request, it will not call the super.form_valid method, just call the form.save() directly.
from django.http import JsonResponse
from django import forms
from django.db import models
class AjaxableResponseMixin(object):
success_return_code = 1
error_return_code = 0
"""
Mixin to add AJAX support to a form.
Must be used with an object-based FormView (e.g. CreateView)
"""
def form_invalid(self, form):
response = super(AjaxableResponseMixin, self).form_invalid(form)
if self.request.is_ajax():
form.errors.update({'result': self.error_return_code})
return JsonResponse(form.errors, status=400)
else:
return response
def form_valid(self, form):
# We make sure to call the parent's form_valid() method because
# it might do some processing (in the case of CreateView, it will
# call form.save() for example).
if self.request.is_ajax():
self.object = form.save()
data = {
'result': self.success_return_code
}
return JsonResponse(data)
else:
response = super(AjaxableResponseMixin, self).form_valid(form)
return response
class Product(models.Model):
name = models.CharField('product name', max_length=255)
class ProductAddForm(forms.ModelForm):
'''
Product add form
'''
class Meta:
model = Product
exclude = ['id']
class PriceUnitAddView(AjaxableResponseMixin, CreateView):
'''
Product add view
'''
model = Product
form_class = ProductAddForm
When we use Django:
Server ===> Client(Browser)
Send a page
When you click button and send the form,
----------------------------
Server <=== Client(Browser)
Give data back. (data in form will be lost)
Server ===> Client(Browser)
Send a page after doing sth with these data
----------------------------
If you want to keep old data, you can do it without Ajax. (Page will be refreshed)
Server ===> Client(Browser)
Send a page
Server <=== Client(Browser)
Give data back. (data in form will be lost)
Server ===> Client(Browser)
1. Send a page after doing sth with data
2. Insert data into form and make it like before.
After these thing, server will send a html page to client. It means that server do more work, however, the way to work is same.
Or you can do with Ajax (Page will be not refreshed)
--------------------------
<Initialization>
Server ===> Client(Browser) [from URL1]
Give a page
--------------------------
<Communication>
Server <=== Client(Browser)
Give data struct back but not to refresh the page.
Server ===> Client(Browser) [from URL2]
Give a data struct(such as JSON)
---------------------------------
If you use Ajax, you must do these:
Initial a HTML page using URL1 (we usually initial page by Django template). And then server send client a html page.
Use Ajax to communicate with server using URL2. And then server send client a data struct.
Django is different from Ajax. The reason for this is as follows:
The thing return to client is different. The case of Django is HTML page. The case of Ajax is data struct.
Django is good at creating something, but it only can create once, it cannot change anything. Django is like anime, consist of many picture. By contrast, Ajax is not good at creating sth but good at change sth in exist html page.
In my opinion, if you would like to use ajax everywhere. when you need to initial a page with data at first, you can use Django with Ajax. But in some case, you just need a static page without anything from server, you need not use Django template.
If you don't think Ajax is the best practice. you can use Django template to do everything, like anime.
(My English is not good)