Testing AJAX in Django - django

I want to test an AJAX call in my Django app.
What is does is adding a product to a favorite list. But I can't find a way to test it.
My views.py:
def add(request):
data = {'success': False}
if request.method=='POST':
product = request.POST.get('product')
user = request.user
splitted = product.split(' ')
sub_product = Product.objects.get(pk=(splitted[1]))
original_product = Product.objects.get(pk=(splitted[0]))
p = SavedProduct(username= user, sub_product=sub_product, original_product = original_product)
p.save()
data['success'] = True
return JsonResponse(data)
My html:
<form class="add_btn" method='post'>{% csrf_token %}
<button class='added btn' value= '{{product.id }} {{ sub_product.id }}' ><i class=' fas fa-save'></i></button
My AJAX:
$(".row").on('click', ".added", function(event) {
let addedBtn = $(this);
console.log(addedBtn)
event.preventDefault();
event.stopPropagation();
var product = $(this).val();
console.log(product)
var url = '/finder/add/';
$.ajax({
url: url,
type: "POST",
data:{
'product': product,
'csrfmiddlewaretoken': $('input[name=csrfmiddlewaretoken]').val()
},
datatype:'json',
success: function(data) {
if (data['success'])
addedBtn.hide();
}
});
});
The problem is that I pass '{{product.id }} {{ sub_product.id }}' into my views.
My test so far:
class Test_add_delete(TestCase):
def setUp(self):
self.user= User.objects.create(username="Toto", email="toto#gmail.com")
self.prod = Product.objects.create(
name=['gazpacho'],
brand=['alvalle'],
)
self.prod_2 = Product.objects.create(
name=['belvita'],
brand=['belvita', 'lu', 'mondelez'],
)
def test_add(self):
old_saved_products = SavedProduct.objects.count()
user = self.user.id
original_product = self.prod.id
sub_product = self.prod_2.id
response = self.client.post(reverse('finder:add', args=(user,))), {
'product': original_product, sub,product })
new_saved_products = SavedProducts.objects.count()
self.assertEqual(new_saved_products, old_saved_products + 1)
My test is not running and I get a SyntaxError 'product': original_product, sub_product. I know it's not the proper way to write it but my AJAX send the two ids with a space in between to the view.

If all you want to do is test if the data was actually saved, instead of just returning data['success'] = True you can return the whole entire new object... That way you can get back the item you just created from your API, and see all the other fields that may have been auto-gen (ie date_created and so on). That's a common thing you'll see across many APIs.
Another way to test this on a Django level is just to use python debugger
import pdb; pdb.set_trace() right before your return and you can just see what p is.
The set_trace() will stop python and give you access to the code scope from the command line. So just type 'l' to see where you are, and type(and hit enter) anything else that's defined, ie p which will show you what p is. You can also type h for the help menue and read the docs here

Related

Using Modelform with ModelChoicefield does not work for me, gives undefined error when submitting the form

I am trying to use a form that adds data to the model RaportProductie using AJAX.
In the form I have 2 dropdown inputs that take data from the ManoperaRaportareBloc model.
These are the attributes from ManoperaRaportareBloc : categorie_lucrare and subcategorie_lucrare
When I submit the form it shows an error with undefined.
Please help.
ty.
forms.py:
class RaportProductieForm(forms.ModelForm):
data = forms.DateField(initial=datetime.date.today)
categorie_lucrare = forms.ModelChoiceField(queryset=ManoperaRaportareBloc.objects.all().values_list('categorie_lucrare', flat=True))
subcategorie_lucrare = forms.ModelChoiceField(queryset=ManoperaRaportareBloc.objects.all().values_list('subcategorie_lucrare', flat=True))
class Meta:
model = RaportProductie
fields = ['lucrare', 'data', 'tip', 'subcontractor', 'obiectiv', 'categorie_lucrare', 'subcategorie_lucrare', 'um', 'cantitate', 'valoare_prod']
views.py:
def raportproductie_create_view(request):
# request should be ajax and method should be POST.
if request.is_ajax and request.method == "POST":
# get the form data
form = RaportProductieForm(request.POST)
# save the data and after fetch the object in instance
if form.is_valid():
instance = form.save()
# serialize in new friend object in json
ser_instance = serializers.serialize('json', [ instance, ])
# send to client side.
return JsonResponse({"instance": ser_instance}, status=200)
else:
# some form errors occured.
data = {
'result': 'error',
'message': 'Form invalid',
'form': 'oops.'
}
return JsonResponse(data, status=400)
# some error occured
return JsonResponse({"error": ""}, status=400)
template.html:
$("#friend-form").submit(function (e) {
// preventing from page reload and default actions
e.preventDefault();
// serialize the data for sending the form data.
var serializedData = $(this).serialize();
console.log(serializedData)
// make POST ajax call
$.ajax({
type: 'POST',
url: "{% url 'proiecte:raportprod-create' %}",
data: serializedData,
success: function (response) {
// display the newly friend to table.
var instance = JSON.parse(response["instance"]);
var fields = instance[0]["fields"];
$("#table-ajax tbody").prepend("<tr><td>"+fields.data+"</td><td>"+fields.tip+"</td><td>"+fields.subcontractor+"</td><td>"+fields.obiectiv+"</td><td>"+fields.categorie_lucrare+"</td><td>"+fields.subcategorie_lucrare+"</td><td>"+fields.um+"</td><td>"+fields.cantitate+"</td><td>"+fields.valoare_prod+"</td></tr>")
},
error: function (xhr, status, error) {
var err = JSON.parse(xhr.responseText);
alert(err.error);
}
})
})
later edit:
i've used pdb to debug, printed the form before checking if valid and it returns this:
form.data
<QueryDict: {'csrfmiddlewaretoken': ['*********'], 'lucrare': ['1'], 'date': ['2023-01-10'], 'tip': ['1'], 'subcontractor': ['TGC Tadjiki'], 'obiectiv': ['obiectiv'], 'categorie_lucrare': ['CONFECTII_METALICE'], 'subcategorie_lucrare': ['CONSTRUCTIE ATIC - CONF METALICA'], 'um': ['km'], 'cantitate': ['2'], 'valoare_prod': ['0']}>
so...the inputs are working,
also in the ajax code, i've also gave a console.log(serializedData) and it outputs this:
csrfmiddlewaretoken=***********=1&date=2023-01-10&tip=1&subcontractor=TGC%20Tadjiki&obiectiv=obiectiv&categorie_lucrare=HIDRO_TERASE&subcategorie_lucrare=CONSTRUCTIE%20ATIC%20-%20CONF%20METALICA&um=mp.&cantitate=2&valoare_prod=0
later later edit:
when I am not using ModelChoiceField in the forms.py, and write the inputs by hand, the form submits...
I found an answer to my question, in the Modelform modified the custom queryset so that they remain Charfield and have added choices:
class RaportProductieForm(forms.ModelForm):
date = forms.DateField(initial=datetime.date.today)
queryset=ManoperaRaportareBloc.objects.all()
OPTIONS1 = [(choice.pk, choice.categorie_lucrare) for choice in queryset]
OPTIONS2 = [(choice.pk, choice.subcategorie_lucrare) for choice in queryset]
queryset2 = Echipa.objects.all()
OPTIONS3 = [(choice.pk, choice.nume) for choice in queryset2]
categorie_lucrare = forms.CharField(widget=forms.Select( choices = OPTIONS1 ))
subcategorie_lucrare = forms.CharField(widget=forms.Select( choices = OPTIONS2 ))
subcontractor = forms.CharField(widget=forms.Select( choices = OPTIONS3 ))
class Meta:
model = RaportProductie
fields = ['lucrare', 'date', 'tip', 'subcontractor', 'obiectiv', 'categorie_lucrare', 'subcategorie_lucrare', 'um', 'cantitate', 'valoare_prod']

How to process two GET requests, at the same time and collect the data in javascript

Hello I have a question and a doubt, I am in a view, when I give a button I do the following code
function pagar_recibo(id) {
$.ajax ({
delay: 250,
type: 'GET',
url: '/general/recibo/add',
data: {
'id': id,
'action_al': 'add_alumnos',
},
});
};
This button is a href that goes to another url, that is to say it makes another request to the server with GET then in the new view in the get_context_data I have the following
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['arg'] = json.dumps(self.alum())
# context['arg'] = self.alum()
return context
and the function is as follows
def alum(self, **kwargs):
data = []
try:
action = self.request.GET.get('action_al', False)
print(action)
if action == False:
return
elif action == 'add_alumnos':
data = []
id = int(self.request.GET['id'])
Alumnos = Alumno.objects.filter(pk=id)
for i in Alumnos:
item = i.toJSON()
item['text'] = i.get_full_name()
data.append(item)
print(data)
except Exception as e:
data['error'] = str(e)
return data
This is how I pick it up in the template
window.data = {{ arg|safe }};
The problem is that one GET request is empty, the url change and the other is the ajax request with the given key and value, but I always get empty windows data, how could I fix that?
It could be done with a POST request? then how do I collect the value in the js file?
Please I would need help. Regards

Show loading gif until the django view performs the data processing and renders the template with this data

I have a django project where the page has multiple nav links representing different agents. On clicking any nav link, the urls.py redirects to nav specific view and the view needs to perform some processing to get the data needed to render the template. However as this is syncrhonous rendering it takes a long while to load data (in the order of 15-20s).
Below is my urls.py:
from django.urls import path
from . import views
app_name = 'agent'
urlpatterns = [
path('agent1/', views.agent1, name='agent1'),
path('agent2/', views.agent2, name='agent2'),
path('agent3/', views.agent3, name='agent3'),
path('agent4/', views.agent4, name='agent4'),
]
My views method looks as below:
def agent1(request):
agent_data = Agent1.objects.all()
agent_details = get_agent_details(agent_data)
return render(request, 'manager/data.html', {'agent_data': agent_data, 'agent_details': agent_details})
I am using the {{ agent_data.name }}, {{ agent_data.code }}, {{ agent_data.qty }} and {{ agent_data.price }} along with data from agent_details dictionary in my html to populate a table's rows. How should I change my view method, so that it loads the data via AJAX (javascript) in order to show a loading gif in the meantime and also provide me the data so that I can populate the table. Could someone help me with the Ajax code and the steps as I am new to this technology and not finding any help going through the online tutorials.
So for this to work with ajax, you'll need some javascript in manager/data.html which knows the url to fetch data from.
As an example, I've got an ajax setup which checks a given email address isn't already in use;
(function($) {
$(document).ready(function() {
var validateEmailURL = $section_user_signup.data('ajax-email-url');
function validateEmailUnique() {
var valid = true;
clearError($email);
// Fetch unique status of the provided email
$.ajax({
async: false,
url: validateEmailURL,
method: 'POST',
type: 'POST',
dataType: 'json',
data: {
'email': $email.val(),
'csrftoken': $form.find('input[name="csrfmiddlewaretoken"]').val()
},
success: function (response) {
valid = true;
},
error: function (response) {
setError($email, response["responseJSON"]["error"]);
valid = false;
}
});
return valid;
}
});
})(window.jQuery);
This javascript uses the data attribute of a div for the URL to check;
<div data-ajax-email-url="{% url 'account_ajax_validate_email' %}">
The view which the ajax call goes to looks like this;
def ajax_check_email_unique(request, *args, **kwargs):
"""
Return an JsonResponse to identify if an email is unique.
"""
if not request.is_ajax():
return HttpResponseBadRequest()
if request.is_ajax and request.method == "POST":
email = request.POST.get('email')
if email_address_exists(email):
return JsonResponse(
{
"error":
"Email address already exists. Click "
f"here "
"to login"
},
status=400
)
return JsonResponse(
{"email": email},
status=200
)
# some error occurred
return JsonResponse({"error": ""}, status=400)
The important thing for any view which will be used by javascript is that you return a JsonResponse.
So if I was you, I'd setup a new view for ajax, and that makes your existing one really simple;
def agent1_ajax(request):
agent_data = Agent1.objects.all()
agent_details = get_agent_details(agent_data)
return JsonResponse({
"agent_data": agent_data, "agent_details": agent_details
}, status=200)
def agent1(request):
return render(request, 'manager/data.html', {})
And as far as a loading gif goes, you'd need an element that contains the gif and then you can bind to the ajax event to show/hide;
$(document).ajaxStart(function() {
$("#loading").show();
});
$(document).ajaxStop(function() {
$("#loading").hide();
});

calling ajax function in views.py to get the data from database

I want to fetch data from the database. I am using ajax function to get it in the index.html. How should I call this ajax function to the views.py so i can display it in view. How should I attain it?
My codes:
index.html
<script type="text/javascript">
function submitData(){
// Get answer from the input element
var dt = document.getElementById("major1").value;
var dtt = document.getElementById("major2").value;
var dttt = document.getElementById("major3").value;
var dtttt = document.getElementById("major4").value;
var dttttt = document.getElementById("major5").value;
// add the url over here where you want to submit form .
var url = "{% url 'home' %}";
$.ajax({
url: url,
data: {
'major1': dt,
'major2': dtt,
'major3': dttt,
'major4': dtttt,
'major5': dttttt,
},
dataType: 'JSON',
success: function(data){
// show an alert message when form is submitted and it gets a response from the view where result is provided and if url is provided then redirect the user to that url.
alert(data.result);
if (data.url){
window.open(data.url, '_self');
}
}
});
}
</script>
views.py:
def home(request):
majors = Major.objects.filter(percentages__isnull=False).distinct().order_by("pk")
if request.method == 'POST':
form = request.POST.get('be_nextyr_total')
line_chart = pygal.Line(width=1500)
line_chart.title = 'Budget Estimation'
context = {
"chart": line_chart.render_data_uri(),
'majors': majors
}
return render(request, "website/index.html" , context )
If you are doing a post request with Ajax, then you have to write in your ajax code like
type: "POST",
if you want to access your form data in view than you have to write
request.POST.get('your_variable_name_like_major1')

How to update database after clicking on html element?

In my flask application, I have a points column in the database, which is initialized to 0 on user registration. Now, I want to be able to update the points in database after clicking an html element.
class User(UserMixin, db.Model):
points = db.Column(db.Integer)
#bp.route('/activity1')
#login_required
def activity1():
return render_template('activity1.html', title='Activity 1')
Activity1.html has the point-generating html element
#bp.route('/register', methods=['GET', 'POST'])
def register():
...
form = RegistrationForm()
if form.validate_on_submit():
user.points = 0
db.session.add(user)
db.session.commit()
...
<a data-toggle="collapse" class="w3-large" href="#tip4" onclick="getPoints()">...</a>
<script>
function getPoints(){
points += 20; #How do i access the database.points in this case?
}
</script>
I'm not sure if I understand the code correctly, but are you asking how to update the number of points from activity1.html in the last code snippet? That won't work, the HTML is rendered on the server and once presented to the browser, there's no link between the two.
If you need to pass some data to the template while rendering it, you have to pass it in the render_template call, e.g.
render_template('activity1.html', title='Activity 1', user=user)
Then you can access it in the template like this:
<script>
function getPoints(){
points = {{ user.points|int }} + 20;
// post to backend to update data in database (using jQuery)
$.ajax({
type: 'POST',
contentType: 'application/json',
data: {
id: "{{ user.id }}",
points: points
},
dataType: 'json',
url: 'http://xxx/update_points',
success: function (e) {
// successful call
},
error: function(error) {
// an error occured
}
});
}
</script>
If you then want to update the user points in the database, you'll have to make a request to the server, which will take care of this. On the server side in Flask, define a view to handle this request:
#bp.route('/update_points', methods=['POST'])
def update_points():
data = request.get_json()
# don't know exactly what's the model and how to get the 'user' instance
user = User.get_user_by_id(data['id'])
user.points = data['points']
db.session.add(user)
db.session.commit()
Take it as an example code, I didn't test it and ommited quite a lot of details (e.g. how to get user instance in update_points etc.) Also, I might have missed what you are really asking for.