I'm trying to implement a feature on a web app where I have a menu of pizzas, and the price listed on each card (feature taken from Bootstrap) dynamically updates depending on the style (regular/sicilian), size (small/large) and name (cheese/special) of pizza selected. I'm using a loop to create all the cards by querying the database using ORM in the backend, which takes all existing pizzas the restaurant owner adds, and makes a menu item for each one. Then, I'm aiming to extract the name, style, and size selections for each, and give a dynamic render of the price of that specific type of pizza in the price area of the card, using AJAX, before implementing the checkout functionality whereby a user buys that pizza. The problem is mainly that I'm unsure how to extract the name, style, and size selections given that the cards are implemented using a templating loop, and also I think there are a few small errors littered through my AJAX/backend. But I suspect the solution would be something to do with Javascript's ForEach() after I extract an array of the styles from the cards? I really don't know how I'd go about proceeding. My code is:
views.py:
from django.http import HttpResponse, Http404, HttpResponseRedirect, JsonResponse
from django.contrib.auth.models import User
from django.shortcuts import render
from .models import Topping, Size, Pizza, Sub, Pasta, Platter, Salad, pizza_styles, sizes
from django.urls import reverse
# Create your views here.
def index(request): # this home page will be the menu page
return render(request, "orders/index.html")
def pizza(request): # this home page will be the menu page
styles = [x[0] for x in pizza_styles]
# these lists have to be created because pizza_styles and sizes are naturally a list of tuples
propSizes = [x[0] for x in sizes]
context = {
'pizzas': Pizza.objects.all(),
'styles': styles,
'sizes': propSizes,
}
print(propSizes, styles, Pizza.objects.first().name) # diagnostic purposes
return render(request, "orders/pizza.html", context)
#below is the function I'm currently stuck on (the front end is the tough part I think)
def get_price(request):
style = request.GET.get('style', None)
size = request.GET.get('size', None)
name = request.GET.get('name', None)
data = {
'price' = Pizza.objects.filter(name=name, style=style, size=size).price
# get the pizza style, size, and type from the front end and query database on those filters to get price
}
return JsonResponse(data)
urls.py:
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
path("pizza", views.pizza, name="pizza"),
path("get_price", views.get_price, name='get_price'),
]
pizza.html:
{% extends "orders/index.html" %}
{% block item %}
<main class="containerX">
<div class="row">
{% for pizza in pizzas %}
<div class="card border-primary mb-3" id="card">
<div class="card-body">
<h5 class="card-title" id="name">{{ pizza.name }}</h5>
<p class="card-text">
<select id="style">
{% for style in styles %}
<option value="{{ style }}">{{ style }}</option>
{% endfor %}
</select>
</p>
<p class="card-text">
<select id="size">
{% for size in sizes %}
<option value="{{ size }}">{{ size }}</option>
{% endfor %}
</select>
</p>
<p class="card-text" id="price"> price depending on selection </p>
Add to Cart
</div>
</div>
{% empty %}
<h2>No pizzas currently on offer. Sorry!</h2>
{% endfor %}
</div>
</main>
<script>
var size = document.querySelector('#size').value;
var name = document.querySelector('#name').textContent;
var style = document.querySelector('#style').value;
var price = document.querySelector('#price').value;
console.log(size, name, style, price)
// load default price set up in python too
(style.onchange || size.onchange) = function(){
// then send the ajax request
$.ajax({
url: '/get_price', // implement this url that will send back data
data: { // communication with the backend is to get the price from the database
'size': size,
'name': name,
'style': style,
},
dataType: 'json',
success: function (data) {
// simply update price here
price = data['price']
console.log(price)
}
});
};
</script>
{% endblock %}
I have a rudimentary understanding of how AJAX and Django work, and think my code for views, urls, and pizza.html are all, in principle, correct, but there are small implementation details I can't quite get a handle on, despite days of frustration.
It looks like your trying to listen to the change event on the actual value but you need to listen to the Dom node. Also ensure that you are including jquery before your JavaScript as $.ajax is a jquery function. Try changing your JavaScript to the following:
<script>
var size = document.querySelector('#size');
var name = document.querySelector('#name').textContent;
var style = document.querySelector('#style');
var price = document.querySelector('#price').value;
console.log(size, name, style, price)
// load default price set up in python too
(style.onchange || size.onchange) = function(e){
var styleValue = e.target.value;
var sizeValue = e.target.value;
// then send the ajax request
$.ajax({
url: '/get_price', // implement this url that will send back data
data: { // communication with the backend is to get the price from the database
'size': sizeValue,
'name': name,
'style': styleValue,
},
dataType: 'json',
success: function (data) {
// simply update price here
price = data['price']
console.log(price)
}
});
};
</script>
Let me know if you have further problems and I’ll try help.
Related
In a given Django form view, I'd like to filter a field's queryset by an option a user has selected while viewing the form. And I'd like that to happen asynchronously. I've read that AJAX might be what I should use, but I know very little of JS, and I can't make much sense of the information I've read.
The way I see it, I'd like a user to be able to click on one or more checkboxes that would filter 'loot.item_name' by 'item.in_itemlist' or 'item.item_type' (automatically, using onChange or onUpdate or smth?).
Would someone kindly give me some pointers? Cheers,
Here are my models:
models.py
class Itemlist(models.Model):
name = models.CharField([...])
class Item(models.Model):
name = models.CharField([...])
item_type = models.CharField([...])
in_itemlist = models.ForeignKey(Itemlist, [...])
class Loot(models.Model):
item_name = models.ForeignKey(Item, [...])
quantity = [...]
my view,
views.py
def create_loot(request):
# LootForm is a simple ModelForm, with some crispy layout stuff.
form = LootForm(request.POST or None)
if request.method == 'POST':
if form.is_valid():
form.save()
[...]
return render(request, 'loot_form.html', context={'form': form}
and the template,
loot_form.html
{% extends "base_generic.html" %}
{% block leftsidebar %}
/* for each unique itemlist referenced in the items, */
/* make a checkbox that once clicked, filters 'loot.item_name' by the selected option.*/
{% endblock leftsidebar %}
{% block content %}
<h1 class="page-title">Add Loot</h1>
<hr class="page-title-hr">
{% csrf_token %}
{% load crispy_forms_tags %}
{% crispy form form.helper %}
{% endblock content %}
You can create Api based view for your Model
in JavaScript, you can call the the API end and get the data
Ajax code can be something like below
$(document).ready(function () {
setTimeout(function () {
$(document).on('click', '#id', function () {
$.get("./api/v2/end_point?state=" + state, function (data, status) {
var inner_data = data[0].state_count;
change_text.html(inner_data);
});
});
}, 100);
})
I have a page that makes ajax calls, and my view checks if the call is ajax with .is_ajax function. However, when the page is initially loaded (for example if I stop running the server and then restart it) the is_ajax function seems to return True, which causes a MultiValueDictKeyError at /alltext, because the request does not contain the key "alltext", which contains the data from my other ajax call.
The page is a e-commerce product page, and the product has different variants (i.e. size and color), and when the user chooses variants from a dropdown menu (i.e. Large, Blue), it makes an ajax call to the backend to retrieve the price of this specific variant from the database.
Here is my code:
views.py
def product_info(request):
if request.method == "GET" and not request.is_ajax: # this is supposed to be loaded on page load
return render(request, "product_info.html")
elif request.is_ajax and request.method == "GET":
print(request.is_ajax)
'''When a user chooses a product variant on the page, this makes an ajax call
to retrieve the price of this combination'''
print("request was ajax")
combinations = request.GET["alltext"]
combinations_list = combinations.split(";")
product = Product.objects.all().latest("id")
var_names = Variation.objects.filter(product=product)
corresponding_values = []
for i in range(len(combinations_list)):
# finding this variant in database
var_name = var_names[i]
var_values = VariationValue.objects.filter(variation_name=var_name)
for val_obj in var_values:
val = val_obj.value
if val == combinations_list[i]:
corresponding_values.append(val_obj)
found_price = None
for i in range(len(corresponding_values)):
val = corresponding_values[i]
if i == 0:
combo_query = VariationCombination.objects.filter(variation_value=val)
else:
combo_query = combo_query.filter(variation_value=val)
price = combo_query[0].price
return HttpResponse("You chose: " + combinations + "price: " + price)
And here is the relevant part of the product_info page - the forms where the user chooses the variants and the script that sends the variants to the backend:
product_info.html
<form class="variations-form">
{% for name in variation_names %}
<div class="form-group">
<label class="btn-label">{{ name.variation_name }}:</label>
<!--<button type="button" class="btn btn-danger dropdown-toggle" data-toggle="dropdown">
<span class="selection"></span><span class="caret"></span>
</button>-->
<select name="variation{{ forloop.counter }}" class="form-control variations" id="variation{{ forloop.counter }}">
<option value="" selected disabled>{{ name.variation_name }}</option>
{% for key, value_list in variation_values.items %}
{% if key == name.variation_name %}
{% for value in value_list %}
<option value="{{ value }}">{{ value }}</option>
{% endfor %}
{% endif %}
{% endfor %}
</select>
</div>
<br>
{% endfor %}
</form>
Script tag with ajax call
<script>
$(document).ready(function () {
$('.form-control').change(function(){
if ($('#variation1').val()) {
var valueFrom = $('.variations option:selected').map(function () {
return $(this).val();
}).get();
var alltext = valueFrom.join(";")
$('#chosen-options').html(alltext).show('fast');
$.ajax(
{
type:"GET",
data:{
'action':'options_chosen',
alltext: alltext
},
success: function( data )
{
$('#chosen-options').html(data).show('fast');
}
});
};
});
});
</script>
Still not entirely sure what was wrong but I fixed it! At first, the page was setup so that it loads the template "homepage.html" at first, and then when you click on a product it opens in the same page, by loading the template "product_info.html". So I just changed it so that the product_info opens up on a new page when you click a product (and added the new page to urls.py), and now it works!
I have a django project that I'm trying to add a custom "online" state to (boolean field in a model). I want to refresh a div periodically to show if a user is now online. The div which is refreshed, with the class button-refresh, is in an included HTML file.
The issue is that the include doesn't work with my for loop in the original HTML file, none of the "professionals" data is retrieved from the server. I'm pretty new to django, my assumption is that the refresh_professionals.html file is retrieved with the ajax request and is entirely separate from the all_professionals.html and then included, without ever being a part of the for loop meaning that the {{ professional.professional_profile.online }} syntax doesn't work.
Any ideas on how to fix this issue? If there is a better way to do this, let me know. Thanks.
all_professionals.html
{% for professional in professionals %}
...
{{ professional.name }}
{% include 'professionals/refresh_professionals.html' %}
...
{% endfor %}
...
{% block postloadjs %}
{{ block.super }}
<script>var global_url = "{% url 'refresh_professionals' %}";</script>
<script src="{% static 'professionals/js/professionals.js' %}"></script>
{% endblock %}
refresh_professionals.html
<div class="col button-refresh">
{% if professional.professional_profile.online is True %}
<p class="custom-button mb-1 w-25 mx-auto">Chat</p>
{% else %}
<p class="custom-button-disabled mb-1 w-25 mx-auto">Unavailable</p>
{% endif %}
<p>{{ professional.price_chat }}/min</p>
</div>
professionals.js
$(document).ready(function(){
setInterval(function() {
$.ajax({
url: global_url,
type: 'GET',
success: function(data) {
$('.button-refresh').html(data);
}
});
}, 5000)
});
urls.py
urlpatterns = [
path('', views.view_all_professionals, name='view_all_professionals'),
path('refresh/', views.refresh_professionals, name='refresh_professionals'),
]
views.py
def view_all_professionals(request):
"""A view to return the professionals page"""
professionals = Professional.objects.all()
languages = Languages.objects.all()
context = {
'professionals': professionals,
'languages': languages,
}
return render(request, 'professionals/all_professionals.html', context)
def refresh_professionals(request):
"""A view to refresh the online button section"""
professionals = Professional.objects.all()
context = {
'professionals': professionals,
}
return render(request, 'professionals/refresh_professionals.html', context)
EDIT
I've followed Daniel's advice and am now returning a JSON object. This is the updated code
professionals.js
$(document).ready(function(){
setInterval(function() {
$.ajax({
url: global_url,
type: 'GET',
success: update_professionals,
});
}, 5000)
});
function update_professionals(response){
// unpack the response (context) from our view function:
var professionals = response.professionals;
// update html:
var i;
for (i = 0; i < professionals.length; i++) {
$('#professional-name' + i).text('professionals.name' + i);
};
};
views.py
def refresh_professionals(request):
"""A view to refresh the professionals section page"""
professionals = Professional.objects.all()
professionals = serializers.serialize("json", professionals)
context = json.dumps({
'professionals': professionals,
})
return HttpResponse(context)
The issue I'm facing now is referencing the professionals data. It's returning uncaught errors. The forloop is necessary because in my HTML I've got a series of IDs with a number attached to the end using a django forloop.counter. Any advice would be appreciated. Thanks.
Okay, lets assume we have a view function like so:
from django.http import HttpResponse
import json
def refresh_professionals(request):
"""A view to refresh the online button section"""
professionals = Professional.objects.all()
# convert to json friendly format:
professionals = ...
context = json.dumps({
'professionals': professionals,
})
return HttpResponse(context)
This assumes professionals now looks something like (the actual structure will obviously be different):
professionals = {"id":1, "name":"Jason"}
Now, in our js file we should have an ajax request like so (wrapped in a setInterval method, etc.):
$.ajax({
url: global_url,
type: 'GET',
success: update_professionals, // reference to an ajax-success function
});
And our success function like so:
update_professionals(response) {
// unpack the response (context) from our view function:
// use JSON.parse() to convert a string to json
var professionals = JSON.parse(response.professionals);
// update html:
$('#professional-name').text(professionals.name)
}
This assumes we have an HTML element like so:
<div id="professionals-name"> Sue </div>
The difference is we are using js to update the HTML on the fly instead of trying to re-render an HTML template likely will require a page refresh.
I am creating a form in my django app like this:
views.py
#method_decorator([login_required, client_required], name='dispatch')
class MaterialRequestFormView(CreateView):
model = MaterialRequest
template_name = 'packsapp/client/materialRequestForm.html'
fields = "__all__"
def form_valid (self, form):
product = form.save(commit=False)
product.save()
messages.success(self.request, 'The material request was created with success!')
return redirect('client:mat_req_table')
models.py
class MaterialRequest(models.Model):
owner = models.ForeignKey(Client, on_delete=models.CASCADE, related_name='allotment_sales')
flow = models.ForeignKey(Flow, on_delete=models.CASCADE, related_name='flow')
kit = models.ForeignKey(Kit, on_delete=models.CASCADE, related_name='kit')
quantity = models.IntegerField(default=0)
is_allocated = models.BooleanField(default=False)
quantity_p1 = models.IntegerField(default=0)
quantity_p2 = models.IntegerField(default=0)
quantity_p3 = models.IntegerField(default=0)
quantity_p4 = models.IntegerField(default=0)
quantity_p5 = models.IntegerField(default=0)
html
<h2>Material Request Form</h2>
<div class="row">
<div class="col-md-6 col-sm-8 col-12">
<form method="post" id="MaterialRequestForm" data-kit-url="{% url 'employee:ajax_load_kit' %}"
data-product-url="{% url 'employee:ajax_load_product' %}"
novalidate>
{% csrf_token %}
{{ form|crispy }}
<button type="submit">Save</button>
</form>
</div>
</div>
<div id="products-table">
</div>
<script>
$("#id_flow").change(function () {
console.log("function running");
var url = $("#MaterialRequestForm").attr("data-kit-url");
var flowId = $(this).val();
$.ajax({
url: url,
data: {
'flow': flowId
},
success: function (data) {
$("#id_kit").html(data);
console.log(data);
}
});
});
</script>
<script>
$("#id_kit").change(function () {
console.log("function running");
var url = $("#MaterialRequestForm").attr("data-product-url");
var kitId = $(this).val();
$.ajax({
url: url,
data: {
'kit': kitId
},
success: function (data) {
$("#products-table").html(data);
}
});
});
</script>
The ajax is to get the table.
Can I change it such that when I enter the quantity it multiply the std quantity from table with that value and fill that in respective columns?
Pic for better clarification:
The table is created by this:
<tbody>
{% for product in products %}
<tr>
<td>{{ product.id }}</td>
<td>{{ product.product_name }}</td>
{% for i in quantities %}
{% if forloop.counter == forloop.parentloop.counter %}
<td id="q1">{{ i }}</td>
{% endif %}
{% endfor %}
{% endfor %}
</tr>
</tbody>
To simplify: Multiply Quantity with Std Qty and fill Quantity p1
Looks like you want to perform this on the client side before data submission. This, as you might suspect, is best done with javascript. Since you're already using jquery, I'd suggest looking into the change handler: https://api.jquery.com/change/
You'd listen for changes on the elements with the raw data then compute the new values and fill in the ones that are dynamic. The documentation above has a few examples.
So how would you find out the elements' ids? Use the developer web tools in your browser (normally, right click > inspect element will do the trick). Typically, this don't change as long as you don't change the field names but they are also over-writable if you'd like. Just create an actual form (or modelform).
All the best!
You can do that through easily using jquery.
Give id to the fields in forms.py,
For eg, #std be the id for std & #quantity for quantity field.
$('#quantity').change(function(){
std = $('#std').val();
quantity = $('#quantity').val();
x = parseInt(std) * parseInt(quantity);
// Now assign x to your respective field
});
So dynamically value will be changed and you can then simply save it using django form or whatsoever.
Hope this helps!
I have an sqlite3 database setup with django 1.6 where I want web users to be able to enter text and see it in 3 live tables (high, low and latest) all on the same page. The one page ideally should have text entry, voting, display of the three tables updated when new entries go in or are voted on, and a search if possible if all that gets sorted (personal project, not commercial). I am also on win7 64 if that matters...
Currently I have: a working database the three tables displaying in a web page without update and some web text input via js (and failing to save to the database in Django).
I shied away from forms at first, as they seem to want separate html pages for input. Asking an experienced django coder, he helped me out with some javascript for text entry on the page. He said I didn't need to do it via forms and POST, GET, as the text was just going in with the initial score of 0 and the current datetime.
My issue now is that I cannot get the entered text to save into the database without error.
Since I need to program this in 2-3 weeks and am new to django (and oblivious to javascript, though I've done some Processing with PHP), my questions are;
Am I missing something obvious with the text input save to database?
-and-
Is there a way to have all this using forms and GET, POST in one page
so I can avoid a lot of javascript (unless it is truly easier)?
I am going to start to try to build this with Forms at the moment, but hope for a bit of guidance on best practice from wiser heads.
Here's the code so far:
urls.py
from django.conf.urls import patterns, include, url
from django.contrib import admin
admin.autodiscover()
urlpatterns = patterns('',
url(r'^admin/', include(admin.site.urls)),
url(r'^i/$', 'entries.views.index'),
url(r'^add/(.*)$', 'entries.views.add'),
)
Models.py
from django.db import models
import datetime
from django.utils import timezone
class Entry(models.Model):
text = models.CharField(max_length=15)
score = models.IntegerField(default=0)
pub_date = models.DateTimeField('date published')
def __unicode__(self):
return self.text
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
was_published_recently.admin_order_field = 'pub_date'
was_published_recently.boolean = True
was_published_recently.short_description = 'Published recently?'
index.html
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
</head>
<body>
<ul>
{% for entry in latest_entry_list %}
<li>{{ entry.text }}  {{ entry.score }}</li>
{% endfor %}
</ul>
<ul>
{% for entry in high_entry_list %}
<li>{{ entry.text }}  {{ entry.score }}</li>
{% endfor %}
</ul>
<ul>
{% for entry in low_entry_list %}
<li>{{ entry.text }}  {{ entry.score }}</li>
{% endfor %}
</ul>
<style type="text/css" media="screen">
div h2 span { color: #ff0000; }
div span { color: #00ff00; }
#box { width: 400px; height: 400px; }
#h { color: #ff0000; }
</style>
<h3 id="h">title</h3>
<p>message: {{ text }}</p>
<input type="text" name="word" value="" id="input"/>
<script type="text/javascript" src="{{STATIC_URL}}post.js"></script>
</body>
post.js
console.log("hi from js");
$(document).ready(function() {
$("#input").bind("keypress", function(e) {
//enter key pressed
if (e.keyCode == 13) {
var args = {};
var text = $("#input").val();
$.get("/add/" + text, args).done(function(data) {
console.log("message: " + data);
});
}
});
});
views.py
from django.shortcuts import render
from django.http import HttpResponse
from entries.models import Entry
from django.db import models
import datetime
from django.utils import timezone
def index(request):
context = {
'latest_entry_list': Entry.objects.order_by('-pub_date')[:10],
'high_entry_list': Entry.objects.order_by('-score')[:10],
'low_entry_list': Entry.objects.order_by('score')[:10],
}
return render(request, 'entries/index.html', context);
def add(request, thingtoadd):
#created_date = models.DateTimeField('date published', default=datetime.now)
#created_score = '0'
#created_text = 'test'
#e = Entry(text=created_text, score=created_score,pub_date=created_date)
#e.save()
return HttpResponse('done')
I am unsure of defining the fields for populating the Entry....does the above look right?
I can uncomment the e=Entry(etc...) without error,
but when I uncomment the e.save(), the error is:
GET http://127.0.0.1:8000/add/a 500 (INTERNAL SERVER ERROR) jquery.min.js:4
send jquery.min.js:4
n.extend.ajax jquery.min.js:4
n.(anonymous function) jquery.min.js:4
(anonymous function) post.js:15
n.event.dispatch jquery.min.js:3
r.handle
I will be getting on with trying to do this in forms, but wonder if there is some good advice as to if that is possible - I would ideally like to avoid js extras as I am very unfamiliar with it and it would be another level of unknowns at this point. Any input greatly appreciated...
Your mistake in view function add:
created_date = models.DateTimeField('date published', default=datetime.now)
It must be value assign:
created_date = datetime.now()
Not field definition.
In advance you could specify auto_now_add=True in your model: https://docs.djangoproject.com/en/dev/ref/models/fields/#datefield
In that case field will be filled automatically.
Additional:
It is error in urls.py
You should do some fixes:
urls.py:
url(r'^add/$', 'entries.views.add'),
post.js
$("#input").bind("keypress", function(e) {
//enter key pressed
if (e.keyCode == 13) {
var text = $("#input").val();
var args = {'text': text};
$.get("/add/", args).done(function(data) {
console.log("message: " + data);
});
}
});
views.py
def add(request):
created_date = default=datetime.now()
created_score = '0'
created_text = request.GET.get('text')
e = Entry(text=created_text, score=created_score,pub_date=created_date)
e.save()
return HttpResponse('done')
Update - Solution
The solution in addition to the changes below was to add 'from datetime import datetime' in views....