Bit of a problem. What i am trying to do is determine what date the user has selected from an option picker and print the value from a main.py function.
So let me show my set up.
first thing i have done is calculate the date and in my views.py:
def index(request):
#GOING TO PUT THE DATE DIFFERENCE CALCULATION HERE
the_uploaded_excel_file = excel_upload.objects.order_by('-id').first()
excel_file_path_from_db = the_uploaded_excel_file.my_excel_file.path
print(excel_file_path_from_db)
wb = load_workbook(excel_file_path_from_db)
ws = wb["Sheet1"]
df = pd.read_excel(excel_file_path_from_db, engine='openpyxl', sheet_name='Sheet1', skiprows=1)
max_date_column = ws.max_column
last_date_entered = ws.cell(row=1, column=max_date_column).value
todays_date = datetime.datetime.now().date()
Date_difference = pd.date_range(start= last_date_entered, end=todays_date, freq='D')
print("last date entered was ")
print(last_date_entered)
print("todays date is ")
print(todays_date)
print("the days not done are")
print(Date_difference)
for date in Date_difference:
print(date)
return render(request, "index.html", {
'Date_difference': Date_difference
})
Then I show the dates in my html:
<h2>
{% for daterange in Date_difference %}
<h5>{{ daterange|date:"d M Y" }}</h5>
{% endfor %}
</h2>
<select style="background-color: #ceedd0;" id="date_selector"
onchange="what_date_has_been_picked()" name="filter_for">
{% for daterange in Date_difference %}
<option value={{ daterange }}>{{ daterange|date:"d M Y" }}</option>
{% endfor %}
</select>
this produces the following:
now what I want to do is to be able to print this result in my main.py. so i set up an ajax function.
the function from my main.js is called when the date selector is changed:
function what_date_has_been_picked(){
var obj = {'FileName': Date_difference};
var myJSON = JSON.stringify(Date_difference);
$.ajax({
type: 'GET',
url: '/send_date/',
data: myJSON,
success: function (response) {
document.location.reload(true);
}
});
}
Then i set the URL:
path('send_date/', views.send_date, name="send_date"),
views.py:
def send_date(request):
return send_date(request)
main.py :
def send_date(request):
selected_date = json.load(list(request.GET)[0])
print(selected_date)
data = {
'status': 'ok',
}
return JsonResponse(data)
But the problem is the Date is not printed. Where did i go wrong?
your problem
Date_difference is a context variable for the render of index function view on Django Fronted.
Your have write Date_difference on main.js.That is an static file content. Django serve this file but is not rending it.
quick solution
Pick the value of <select> with jquery
function what_date_has_been_picked(){
var myJSON = JSON.stringify({'FileName': $("date_selector").val()});
$.ajax({
type: 'GET',
url: '/send_date/',
data: myJSON,
success: function (response) {
document.location.reload(true);
}
});
}
If you need the text instead of value change the method from .val() to .text()
others in main.py
You can't convert a string to json with json.load
for obtain GET parameters:
selected_date = json.load(list(request.GET)[0])
better replace with this
parameter = request.GET.get('FileName')
And then you can construct a JSON with this data.
Related
I have an Altair chart which I want to render in a Django Template. The chart is converted into a json object in the views. Here is the code
views.py
def home(request):
if request.method=='POST':
year = request.POST['year']
df, cus_dict = generate_df(year)
bar_obj = barGraphAltair(year, df)
bar_obj = json.loads(bar_obj.to_json())
print(bar_obj)
context = {'bar': bar_obj}
return render(request, 'index.html', context=context)
return render(request, 'index.html')
template
<div id='altair-viz'>
{% if bar %}
{{ bar|safe }}
{% endif %}
</div>
This just prints the json in the template. I know I have to use Vega to render the graph but I am not sure how to do that in jinja syntax
A temp solution
One way I got this to work, is by creating a different view and calling that view in the template as follows
views.py
def renderAltair(request):
df, cus_dict = generate_df('2017')
bar_obj = barGraphAltair('2017', df)
bar_obj = json.loads(bar_obj.to_json())
return JsonResponse(bar_obj)
template
<script>
vegaEmbed('#altair-viz', "{% url 'altair' %}")
</script>
This works, but as you can see from the original code, I get the year by submitting a form and passing that to the function for generating the graph. So I need the graph to be created in the home view
You can try this way.
def home(request):
if request.method=='POST':
year = request.POST['year']
context = {'year': year}
return render(request, 'index.html', context=context)
return render(request, 'index.html', {})
Not passing data in home view, will get that using json view.
template
<div id='altair-viz' data-url="{% url 'altair' %}?year={{year}}"></div>
<script>
var data_url = $('#altair-viz').attr('data-url');
vegaEmbed('#altair-viz', data_url)
</script>
and get data function
def renderAltair(request):
year = request.GET.get('year')
df, cus_dict = generate_df(year)
bar_obj = barGraphAltair(year, df)
bar_obj = json.loads(bar_obj.to_json())
return JsonResponse(bar_obj)
I'm having a problem with accessing the response data from my http request. I'm able to get it if I pass the response through to my html and then take the specific data I want out of it but if I try to take that same part of the response in my python file and pass that through to my html it says "there is no books attribute in the dict".
my html
{% extends "layout.html" %}
{% block heading %}
Search Page
{% endblock %}
{% block body %}
the result of the http request:
<p> {{res}} </p>
I want to add this info from the request
<p>{{res.books[0].average_rating}}
{{res.books[0].work_ratings_count}}</p>
to this dictionary
{{apiDict}}
but the when I use the same syntax to access the average rating and ratings count
from
'res' in my python file it says the respose has no book object, why does this
happen?
{% endblock %}
Here is my python/flask code:
#app.route("/api/<isbn>", methods=["GET"])
def apiacc(isbn):
res = requests.get("https://www.goodreads.com/book/review_counts.json", params=.
{"key": "lzhHXUd9kUpum244vufV2Q", "isbns": isbn}).json()
# avg = res.books[0].average_rating
# rc = res.books[0].work_ratings_count
book = db.execute("SELECT * FROM books WHERE isbn = :i", {"i": isbn}).fetchone()
db.commit()
apiDict = {
"title": book.title,
"author": book.author,
"year": book.year,
"isbn": isbn
}
# apiDict["average_score"] = res.books[0].average_rating
# apiDict["review_count"] = res.books[0].work_ratings_count
return render_template("api.html", res = res, apiDict=apiDict)
I would like to have the python code like this:
apiDict = {
"title": book.title,
"author": book.author,
"year": book.year,
"isbn": isbn,
"average_score": avg,
"review_count": rc
}
and just pass in the apiDict to api.hmtl as the only value but I get the error I mentioned earlier.enter image description here
The res returned by requests will be a dict. In the template, Jinja support to get the dict value with the dot operator, like:
{{ res.books }}
But in Python, you have to use the bracket operator to get the value in a dict (dot operator used to get the attribute):
data = res['books']
I'm trying to integrate in my django modal an ajax call to modify directly the value of my data.
In other words I have the following views.py
class UpdateCrudUser(View):
def get(self, request):
id1 = request.GET.get('id', None)
conto1 = request.GET.get('conto', None)
obj = Materiale.objects.get(id=id1)
obj.conto = conto1
obj.save()
element = {
'id':obj.id,
'conto':obj.conto}
data = {
'element': element}
return JsonResponse(data)
After that I have build before my table as following:
{% for element in elements %}
<tr id="element-{{element.id}}">
<td class="elementConto userData" name="conto">{{element.conto}}</td>
</tr>
{% endfor %}
...
And after that the modal:
<form id="updateUser">
<div class="modal-body">
<input class="form-control" id="form-id" type="hidden" name="formId"/>
<label for="conto">Conto</label>
<input class="form-control" id="form-conto" name="formConto"/>
Finally the script with the ajax call:
function editUser(id) {
if (id) {
tr_id = $("#element-" + id);
conto = $(tr_id).find(".elementConto").text();
$('#form-id').val(id);
$('#form-conto').val(conto);
}
}
$("form#updateUser").submit(function() {
var idInput = $('input[name="formId"]').val();
var contoInput = $('input[name="formConto"]').val();
if (contoInput && tipologiaInput ) {
// Create Ajax Call
$.ajax({
url: '{% url "crud_ajax_update" %}',
data: {
'id': idInput,
'conto': contoInput,
},
dataType: 'json',
success: function (data) {
if (data.element) {
updateToUserTabel(data.element);
}
}
});
} else {
alert("Vediamo se funziona.");
}
$('form#updateUser').trigger("reset");
$('#myModal').modal('hide');
return false;
});
But If I try to modify using the modal my data, django give me the following error:
ValueError: Cannot assign "'Materia Prima'": "Materiale.conto" must be a "Conto" instance.
It's possible that this error arises from the model?Becouse conto is a foreingKey:
class Materiale(models.Model):
conto = models.ForeignKey(Conto, on_delete=models.CASCADE, null=True)
class Conto(models.Model):
nome=models.CharField('Nome Conto', max_length=30, blank=True, default="")
def __str__(self):
return self.nome
In this case how could I overcome that problem?
In your UpdateCrudUser, the variable conto1 has been assign to be a string, more precisely when you do this: conto1 = request.GET.get('conto', None).
Then, when you instantiate the Materiale model (through the obj object), you're attempting to set that string to its conto field, which expects an object of the Conto model, rather than a string.
How to overcome this? Instead of assigning that string to obj.conto directly, first make a query to get a Conto object by its field nome. Something like this in your UpdateCrudUser view:
obj = Materiale.objects.get(id=id1)
conto_obj, created = Conto.objects.get_or_create(nome=conto1)
obj.conto = conto_obj
Note that I used the get_or_create() method because in case that the user's input for Conto name doesn't match any existent object, this will create a new Conto object that then can be assign to the Materiale instance that you want to update.
I am trying to upload a CSV file into my Django model. Although the upload of the data works fine (all the rows get copied into the database), at the end Django returns a ValidationError ["'' value must be a decimal number."] error message.
From the local vars section of the error message I kind of get the reason - when the iteration reaches the end of the rows containing data, there is obviously no decimal number. So Django throws an error. However, I do not understand why, as there is always a last row after which there is no more data. I fiddled around a bit to try to find the problem. A method that worked is so copy the whole data from the original CSV into a new CSV - there was no error message any more. I would love to accomplish the whole process with the original CSV file and no error message! Would appreciate any help.
My CSV files are CSV UTF-8 and they are saved in Excel
models.py
from django.db import models
class Testdata3(models.Model):
key = models.CharField(max_length=100, primary_key=True)
assetclass = models.CharField(max_length=25)
value = models.DecimalField(max_digits=25,decimal_places=10)
performance = models.DecimalField(max_digits=25,decimal_places=10)
def __str__(self):
return self.key
views.py
from django.shortcuts import render
from .models import Testdata3
import csv, io
from django.contrib import messages
def file_upload(request):
template = "upload.html"
prompt = {
'order': 'Order of the CSV should be "placeholder_1", "placeholder_2", "placeholder_3" '
}
if request.method == "GET":
return render(request, template, prompt)
csv_file = request.FILES['file']
if not csv_file.name.endswith('.csv'):
messages.error(request, 'This is not a csv file')
data_set = csv_file.read().decode('UTF-8')
io_string = io.StringIO(data_set)
next(io_string)
for column in csv.reader(io_string, delimiter=';', quotechar="|"):
_, created = Testdata3.objects.update_or_create(
key = column[0],
defaults = {
'key' : column[0],
'assetclass' : column[10],
'value' : column[16],
'performance' : column[18],
}
)
context = {}
return render(request, template, context)
upload.html
{% if messages %}
{% for message in messages %}
<div>
<strong>{{message|safe}}</strong>
</div>
{% endfor %}
{%else %}
{{ order }}
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<label>Upload a file</label>
<input type="file" name="file">
<p>Only accepts CSV files</p>
<button type="submit">Upload</button>
</form>
{% endif %}
I've found a solution! I included an if-statement, which checks if the current iteration has only empty values, and if yes it jumps to the next iteration
# ...
views.py
for column in csv.reader(io_string, delimiter=';', quotechar="|"):
# Check for empty rows in CVS and jump to next iteration if empty
if all(elem == "" for elem in column):
next
else:
_, created = Testdata3.objects.update_or_create(
key = column[0],
defaults = {
'key' : column[0],
'assetclass' : column[10],
'value' : column[16],
'performance' : column[18],
}
)
# ...
I meet difficulties as below :
I have a blog page. In blog ,i create 'comment' function to comment post. And comments has 'like' function. For this ,i create two view function ,one of them simple function ,second is api function. And create jquery ajax for to call api function. After api calling ,it update data in db. Problem is :
If i create two comment ,ajax function works only for first comment for to like comment. It looks like ,for first comment CommentLikeAPIToggle works ,for next comments CommentLikeToggle works. Here is my codes :
views.py
class CommentLikeToggle(RedirectView):
def get_redirect_url( self, *args, **kwargs):
id = self.kwargs.get('id')
obj = get_object_or_404(Comment,id=id)
url_ = obj.content_object.get_absolute_url()
user = self.request.user
if user.is_authenticated():
if user in obj.likes.all():
obj.likes.remove(user)
else:
obj.likes.add(user)
return url_
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import authentication, permissions
class CommentLikeAPIToggle(APIView):
authentication_classes = (authentication.SessionAuthentication,)
permission_classes = (permissions.IsAuthenticated,)
def get(self, request,id=None, format=None):
obj = get_object_or_404(Comment,id=id)
url_ = obj.get_absolute_url()
user = self.request.user
updated = False
liked = False
if user.is_authenticated():
if user in obj.likes.all():
liked = False
obj.likes.remove(user)
else:
liked = True
obj.likes.add(user)
updated = True
data = {
'updated':updated,
'liked':liked
}
return Response(data)
Ajax function :
function updateComLike (newCount,btn,verb){
btn.text(" "+newCount+ " " + verb);
btn.attr({"data-likes": newCount,"class":"fa fa-thumbs-up"})
}
$("#com-like").click(function(e){
e.preventDefault()
var this_ = $(this)
var likeUrl = this_.attr("data-href")
var likeCount = parseInt(this_.attr("data-likes"))
$.ajax({
url: likeUrl,
method: "GET",
data : {},
success: function(data){
var newLikes;
if (data.liked){
newLikes = likeCount + 1
updateComLike(newLikes,this_ ,gettext("Unlike"))
} else {
newLikes = likeCount - 1
updateComLike(newLikes,this_ ,gettext("Like"))
}
}, error: function(error){
}
})
})
Template tag :
{% for comment in comments %}
{{ comment.content }}
<footer>
<a data-href="{{comment.get_api_com_like_url}}" data-likes="
{{comment.likes.count}}" href="{{comment.get_com_like_url}}" id="com-like">
<i class="fa fa-thumbs-up"></i> {{comment.likes.count}}
{% if request.user in comment.likes.all %} {% trans "Unlike" %}
{%else%}{%trans "Like" %}{% endif %}
</a>
</footer>
{% endfor %}
Urls :
url(r'^api/(?P<id>\d+)/com-like/$',CommentLikeAPIToggle.as_view(), name='com-like-api-toggle'),
url(r'^(?P<id>\d+)/com-like/$',CommentLikeToggle.as_view(), name='com-like-toggle'),
I have found my problem and solved it myself. The problem is :i'm using id in template tags. And id should be unique to each element. So i used class instead of id and problem fixed
{{comment.likes.count}}" href="{{comment.get_com_like_url}}" class="com-like">
And in the ajax
$('a.com-like').click(function(e){