Django - edit HTML table rows and update database - python

I created a HTML table, it contains some information. However I want to add the possibility to edit table row text and by clicking "save", the database would be updated.
Can someone help me?
Do I need to use Ajax? If so please can I get some guidance?
<table style="width:100%">
<tr>
<th>Questions</th>
<th>Answers</th>
<th>Action</th>
</tr>
{% for q in questions%}
<tr>
<td contenteditable='true'>{{q.question}}</td>
<td contenteditable='true'>{{q.answer}}</td>
<td>
<center>Save Edit --- Delete</center>
</td>
</tr>
{%endfor%}
</table>
Here is my view, I know it shouldn't look like this because there is no parameter passed between the view and the HTML table, this needs to be fixed:
def edit_question(request,id):
question = Question.objects.get(id=id)
if(question):
question.update(question = quest, answer = ans)
return redirect('list_questions')
return render(request, 'generator/questions.html', {'question': question})
UPDATE
I used the solution that #xxbinxx provided, However in the view function, the condition request.method == "POST" doesn't seem to be verified even if it's in ajax function ?
heres the updated code :
<script type="text/javascript">
function saveQuestionAnswer(e, id) {
e.preventDefault();
console.log(id)
editableQuestion = $('[data-id=question-' + id + ']')
editableAnswer = $('[data-id=answer-' + id + ']')
console.log(editableQuestion.text(), editableAnswer.text())
$.ajax({
url: "list/update/"+id,
type: "POST",
dataType: "json",
data: { "question": editableQuestion.html(), "answer": editableAnswer.html() },
success: function(response) {
// set updated value as old value
alert("Updated successfully")
},
error: function() {
console.log("errr");
alert("An error occurred")
}
});
return false;
}
</script>
HTML :
<table style="width:90%">
<tr>
<th ><font color="#db0011">Questions</th>
<th><font color="#db0011">Answers</th>
<th><font color="#db0011">Action</th>
</tr>
{% for q in questions%}
<tr>
<td width="40%" height="50px" contenteditable='true' data-id="question-{{q.id}}" data-old_value='{{q.question}}' onClick="highlightEdit(this);">{{q.question}}</td>
<td width="45%" height="50px" contenteditable='true' data-id="answer-{{q.id}}" data-old_value='{{q.answer}}'>{{q.answer}}</td>
<td width="15%" height="50px"><center>
<a class="button" href="{% url 'edit_question' q.id %}" onclick="saveQuestionAnswer('{{q.id}}')">Edit</a>
<a class="button" href="{% url 'delete_question' q.id %}">Delete</a>
</center></td>
</tr>
{%endfor%}
</table>
views.py
def edit_question(request,id):
quest = Question.objects.get(id=id)
print(quest)
if request.method=='POST': # It doesn't access this condition so the updates won't occur
print("*"*100)
quest.question = request.POST.get('question')
quest.answer = request.POST.get('answer')
print(quest)
quest.save()
return redirect('list_questions')
return render(request, 'browse/questions.html', {'quest': quest})
Can someone help me solve this final issue ?

Yes you'll have to use AJAX to achieve in-place editing. I'm posting quick code so you get the idea.
NOTE: the below code has errors ;) I don't want to write in detail as it would be of no good to you. You must brainstorm and try it yourself.
contenteditable=”true” is to make column editable.
an attribute data-old_value to keep old value to check before making Ajax request to update changed value in your database table.
Code is making use of function saveQuestionAnswer()
on blur event to update changed value and function highlightEdit() to highlight column in edit mode.
<table style="width:100%">
<tr>
<th>Questions</th>
<th>Answers</th>
<th>Action</th>
</tr>
{% for q in questions%}
<tr>
<td contenteditable='true' data-id="question-{{q.id}}" data-old_value='{{q.question}}' onClick="highlightEdit(this);">{{q.question}}</td>
<td contenteditable='true' data-id="answer-{{q.id}}" data-old_value='{{q.answer}}' onClick="highlightEdit(this);">{{q.answer}}</td>
<td>
<center><a onclick="saveQuestionAnswer('{{q.id}}')">Save your edit --- </a>Delete</center>
</td>
</tr>
{%endfor%}
</table>
<script>
function saveQuestionAnswer(id) {
editableQuestion = $('a[data-id=question-'+id+']') //this will get data-id=question-1 where 1 is the question ID
editableAnswer = $('a[data-id=answer-'+id+']') //this will get data-id=answer-1 where 1 is the question ID
// no change change made then return false
if ($(editableQuestion).attr('data-old_value') === editableQuestion.innerHTML && $(editableAnswer).attr('data-old_value') === editableAnswer.innerHTML)
return false;
// send ajax to update value
$(editableObj).css("background", "#FFF url(loader.gif) no-repeat right");
$.ajax({
url: "/my-save-url/",
type: "POST",
dataType: "json",
data: {"question": editableQuestion.innerHTML, "answer": editableAnswer.innerHTML}
success: function(response) {
// set updated value as old value
$(editableQuestion).attr('data-old_value', response.question);
$(editableQuestion).css("background", "#FDFDFD");
$(editableAnswer).attr('data-old_value', response.answer);
$(editableAnswer).css("background", "#FDFDFD");
},
error: function() {
console.log("errr");
alert("An error occurred")
}
});
}
function highlightEdit(elem){
$(elem).css("background", "#e3e3e3") //just add any css or anything, it's only to depict that this area is being edited...
}
</script>
Your view will now get data in json format as {'question': <value>, 'answer': <value>}
you can add another key 'id' in this json if you want, or you can keep your url like this /saveQuestionAnswer/<id>. Here you have your id in url itself.
I hope you understand now.
Thanks

Related

Need update value passed by render_template with flask in html template td tag every 10 seconds

I have this:
#views.route('/')
def home():
while True:
try:
token=getToken()
if(token!='null' or token!=''):
plazas=getInfo(token,id)
except:
print('Conection failed')
time.sleep(secs)
return render_template("home.html", plazas=plazas)
Need update "plazas" variable value which is constantly refreshing with "while True" loop in my html template on td tag:
{% for parking in parkings %}
<tr>
<td class="par"><img src={{parking.image}} alt="img"></td>
<td class="nombre">{{parking.nombre}}</td>
{% if plazas|int >= (totalplazas*30)/100 %}
<td class="num" style="color:#39FF00">
{{plazas}}</td>
{% elif plazas|int < 1%}
<td class="num" style="color:red"><p class="an">COMPLETO</p></td>
{% elif plazas|int <= (totalplazas*10)/100%}
<td class="num" style="color:red">
{{plazas}}</td>
{% else %}
<td class="num" style="color:yellow">
{{plazas}}</td>
{% endif %}
<td class="dir"><img src={{parking.direccion}} alt="img"></td>
</tr>
{% endfor %}
I have tried to use javascript, but when the 10 seconds pass it tells me that the result of {{plazas}} is undefined.
Any help?
<script type="text/javascript">
window.onload = setInterval(refresh, 10000);
function refresh(places) {
var elements = document.getElementsByClassName("num");
for(let i = 0; i < elements.length; i++) {
elements[i].innerHTML = places;
}
return elements[i].innerHTML = places;
}
</script>
To refresh it with new text, you can fetch to your own flask route and update the information using setInterval
HTML
<td id="num" style="color:#39FF00">{{plazas}}</td>
<script>
var element = document.findElementById("num")
async function reload() {
const promise = await fetch('/myroute')
const data = await promise.text()
element.innerHTML = data
}
window.onload = setInterval(reload, 1000)
</script>
Flask
#app.route('/myroute')
def myroute():
# time is just an example
return time.time()

Ajax returning `none` in django

I Have this form in a table with two button.
Code:
index.html
<form action="{% url 'Prediction' %}" method="post">
{% csrf_token %}
<table class="table table-striped table-dark" cellspacing="0">
<thead class="bg-info">
<tr>
<th>Company's Symbol</th>
<th>Current Price</th>
<th>View Current chart</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for a,b in stocks %}
<tr>
<th scope="row" class="comp_name">{{ a }}</th>
<td>{{ b }}</td>
<td>
<input type="submit" class="btn graph-btn" formaction="{% url 'Graph' %}" name="_graph" value="View Graph">
</td>
<td>
<input type="submit" class="btn predict-btn" name="_predict" value="Predict Closing Price">
</td>
</tr>
{% endfor %}
</tbody>
</table>
</form>
By clicking .graph-btn I need to pass the data of row that button is clicked on. Here is code that's working.
<script>
$(".graph-btn").click(function() {
var $row = $(this).closest("tr");
var $text = $row.find(".comp_name").text();
console.log($text);
$.ajax({
type:'POST',
url:'{% url 'Graph' %}',
data:{
text: $text,
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val(),
},
success:function(json){
console.log($text);
},
error : function(xhr,errmsg,err) {
}
});
});
</script>
But Problem is that In views.py returning none:
views.py
def graph(request):
if request.method == 'POST' and '_graph' in request.POST:
print(request.POST.get('text'))
# context = {'name': name}
# # print(name)
return render(request, 'StockPrediction/chart.html')
else:
return render(request, 'StockPrediction/greet.html')
urls.py
urlpatterns = [
path("", views.greet, name='greet'),
path("index/", views.index, name='Stock Prediction'),
path("prediction/", views.prediction, name='Prediction'),
path("view/", views.graph, name='Graph'),
]
try adding dataType: 'json' to the request. For example:
$.ajax({
type: 'post',
url: '{% url 'Graph' %}',
headers: {
'X-CSRFToken': $('input[name=csrfmiddlewaretoken]').val()
}
data: {
text: $text,
},
dataType: 'json',
success: function (data) {
console.log(data)
},
error: function(error) {
console.log(error)
}
});
In your views function graph(request) you are not returning a value but instead rendering an HTML page with the request.
A view function in Django should either return a JSON or a dictionary or can return a Webpage You can either do one of following
1) return a dictionary or JSON
return {"key":"value","key":"value"}
2) return a HttpResponse
return HttpResponse(status_code, response_data)
3) Redirect to another page
return redirect('/some/redirect/url/')
Try the above and comment if it helps

How to update template variable in Django

I want to update value of 'log_data_full' from django template, i have extracted updated value in json from AJAX, i dont' want to refresh the page . i just want to update table view with updated value. Is that possible?
<table class="table table-bordered" id="dataTable" width="100%" cellspacing="0">
<thead>
<tr>
<th>=Id</th>
<th>Category</th>
<th>Fee</th>
<th>Search</th>
</tr>
</thead>
<tbody>
{% for log in log_data_full %}
<tr>
<td>{{ log.Id }}</td>
<td>13</td>
<td>100</td>
<td>{{ log.search }}</td>
</tr>
{% endfor %}
</tbody>
</table>
AJAX query
$.ajax({
url: "/filter",
type: "POST",
data: {
from_date:from_date,
to_date:to_date,
csrfmiddlewaretoken: '{{ csrf_token }}',
},
success : function(json) {
debugger
if(json != 'error'){
var res = JSON.parse(json);
}
else{
alert('Server Error! Could not get data');
}
},
error : function(xhr,errmsg,err) {
alert("Could not send URL to Django. Error: " + xhr.status + ": " + xhr.responseText);
}
});
How to update value by of template variable 'log_data_full'?
Assuming you want to show the updated data in the table received from the server.
in your success function
success : function(json) {
if(json != 'error'){
$("#dataTable").empty(); //remove existing data
var res = JSON.parse(json);
rowHtml = '';
$.each(res, function (i, item) {
rowHtml = '<tr><td>' + res.id + '</td><td>' + res.category + '</td><td>' + res.fee + '</td><td>'+ res.search +'</td></tr>';
});
$('#dataTable').append(rowHtml);
}

Update Django object field without refresh the page

How can I call a view function that change object field without refresh the page?
views.py
def add_like(request, pk):
book = Book.objects.get(pk=pk)
book.like = True
book.save()
return redirect('book_list')
urls.py
url(r'^add_like/(?P<pk>[0-9]+)/$', views.add_like, name='add_like'),
list.html
[...]
<td><span class="glyphicon glyphicon-heart" style="color: grey;"></span></td>
[...]
Once user click the button, I want to change like status and the tag content to:
<td><span class="glyphicon glyphicon-heart" style="color: red;"></span></td>
I know Ajax can be the answer, but I don't know how to implement it.
Thanks.
Instead of
<td><span class="glyphicon glyphicon-heart" style="color: red;"></span></td>
Use an input and give it an ID like
<td><input style="color: red" class="book btn btn-default glyphicon glyphicon-heart" value="{book.pk}" /></td>
Then according with this example call the AJAX function
<script>
$(".book").on('click', function () {
var id = $(this).val();
$.ajax({
url: '/add_like/',
data: {
'pk': id
},
dataType: 'json',
success: function (data) {
if (data) {
alert(data);
}
}
});
});
</script>
Then update your view with:
from django.http import JsonResponse
def add_like(request):
pk = request.GET.get('pk', None)
book = Book.objects.get(pk=pk)
book.like = True
book.save()
data = {'book', book}
return JsonResponse('data')

Ajax succesfull call show div with form data

views.py
def index(request):
""""""""""""""
registerform = UserRegisterForm(request.POST)
createprofileform = UserCreateProfileForm(request.POST)
if registerform.is_valid() and createprofileform.is_valid():
result = registerform.save(commit=False)
result.set_password(request.POST['password']) #set member password
result.username = username
result.save()
member.user_id = user.id
member.member_id = result.id
member.save() #new member registration
member_profile = UserProfile.objects.get(user=result.id)
createprofileform = UserCreateProfileForm(request.POST, instance=member_profile)
createprofileform.save() #create member profile
createprofileform = UserCreateProfileForm()
member_save_msg = 'New member has been added.'
""""""""""""
return render(request,'index.html',{ 'registerform': registerform,'createprofile': createprofileform,})
index.html
{% block main-content %}
<table width="98%" border="0" style="margin-left:0.7%;" cellpadding="0" cellspacing="0" id="rounded_table">
<tr >
<td width="50%">Main Account Holder</td><td width="50%">Authorised Reporters</td>
</tr>
<tr id="main_account">
<td width="50%">All data related to main account holder comes here</td>
</tr>
<tr id="authorised_reporter">
<td width="100%" colspan="2">
<div id="authorisedreporter" {% if not registerform.errors %}style="display:none"{% endif %}>
<form method="post" action="." id="reporter-form">{% csrf_token %}
<table width="100%">
<tr>
<td style="width:100px;">First name:</td><td>{{registerform.first_name}}</td>
</tr>
<tr>
<td>Last name:</td><td>{{registerform.last_name}} </td>
</tr>
""""""other form fields""""""""
<tr>
<td colspan=2""><p align="right"><button type="submit" title="Save" >Save <img src="{{ STATIC_URL }}images/button-icon-ir-fwd.png" width="12" height="17" alt="" /></button></p>
</td>
</tr>
</table></form>
</table>
{%endblock%}
The above views.py and index.html is for saving the new user entry.
My html template is divided into 2 section,Main Account Holder tab and Authorised Reporters tab.Main Account Holder tab is for saving profile info and Authorised Reporters tab is for creating new user.on page load Main Account Holder tab will be active and user tab will be hidden.If user tab is selected,Main Account Holder tab will be hidden.Once the user is saved,the user details are displayed below, in below format.
{% for list in member_list %}
<tr class="ir-shade">
<td style="width:120px;"><span><input type="submit" name="delete" value="{{list.0.id}}" class="delete_reporter" /></span><button> id="{{ list.0.id }}" class="openDiv">{{list.0.first_name|title}} {{list.0.last_name}}</button></td>
<td style="width:410px;"> {{list.0.email}} {{list.1.phone_daytime}} {{list.1.phone_mobile}}</td>
</tr>
{% endfor %}
What i actually want is Onclicking the <button> id="{{ list.0.id }}" class="openDiv">{{list.0.first_name|title}} {{list.0.last_name}}</button> saved user data should shown in same field in editable mode.i am passing the user id in button.On click the button,the data related to user id should shown in editable mode.
js:
$('.openDiv').click(function () {
var id = $(this).attr('id');
var csrf_token = $("#csrf_token").val();
$.ajax({
data:{
csrfmiddlewaretoken: ('{{csrf_token}}'),
id:id,
},
type:'POST',
url: '/setting/save-reporter/',
success: function(data) {
$('#authorisedreporter').html(data);
}
});
});
The below views.py and html are written for showing the saved form instance.Now i can show the saved form instance and i am loading the instance into authorisedreporter div(please check in js and index.html).In this time if i kit save,it is creating new record,it is calling the views.py related to index method.I want to update and not save the record.
save_reporter.html
<form method="post" action="." id="{{ id }}">
{% csrf_token %}
<table width="100%">
<tr>
<td style="width:100px;">First name:</td><td>{{form.first_name}}</td>
</tr>
<tr>
<td>Last name:</td><td>{{form.last_name}}</td>
</tr>
<tr>
<td>Daytime phone:</td><td>{{profile.phone_daytime}}</td>
</tr>
<tr>
<td>Mobile phone:</td><td>{{profile.phone_mobile}}</td>
</tr>
<tr>
<td>Email:</td><td>{{form.email}}</td>
</tr>
<tr>
<td>Password</td><td>{{form.password}}</td>
</tr>
<tr>
<td colspan=2"<p align="right">{% include "buttons/save.html" %}</p></td>
</tr></table></form>
views.py
def save_reporter(request):
user = request.user
id = request.POST.get('id')
user = User.objects.get(pk =id)
userprofile = UserProfile.objects.get(user=user.id)
form = ReporterRegisterForm(instance=user)
profileform = ProfilecontactForm(instance=userprofile)
return render(request, 'setting/save_reporter.html',
{'form': form,
'id':id,
'profile':profileform
})
I had explained my current issue i am facing,please have help me in doing this.Thanks
Let me analyze your JS code a bit, since I can see several errors/mistakes there:
$('.openDiv').click(function (e) {
e.preventDefault();
// where is following data taken from? At the point you click the .openDiv link, the form doesn't have any data yet so all of them will be empty string ''
var csrf_token = $("#csrf_token").val();
var id = $(this).closest('td').attr('id');
var firstname = $("#"+id).find('#id_first_name').val();
var lastname = $("#"+id).find('#id_last_name').val();
var phonedaytime = $("#"+id).find('#id_phone_daytime').val();
var phonemobile = $("#"+id).find('#id_phone_mobile').val();
var email = $("#"+id).find('#id_email').val();
// do you use AJAX to get the form or use it to save the form?
$.ajax({
data: $(this).serialize(), // this is wrong, $(this) is the link object, not a form
type:'POST',
url: '/setting/save-reporter/',
success: function(data) {
$('#authorisedreporter').html(data);
$('#authorisedreporter').show();
}
});
});
Ok as far as I understand, after clicking the link, you are using AJAX to send request to Django view to fetch back the correct instantiated form. So you should:
First, simplify your JS code:
$('.openDiv').click(function (e) {
e.preventDefault();
var this_id = $(this).closest('td').attr('id'); // get the user ID, since that's all you need
console.log(this_id); // making sure that you get the right ID
$.ajax({
data: { id: this_id },
type: 'POST',
url: '/setting/fetch-reporter-form/',
success: function(data) {
$('#authorisedreporter').html(data);
$('#authorisedreporter').show();
}
});
});
Next, split your old view to several views to focus on what it needs to do (note: you can leave your index view as it is now):
def fetch_reporter_form(request):
''' Change your save_reporter view name to this view '''
registerform = UserRegisterForm()
if request.method == 'POST':
id = request.POST.get('id', None)
if id:
user = get_object_or_404(pk=user.id)
userprofile = UserProfile.objects.get(user=user)
registerform = UserRegisterForm(request.POST, instance=user)
return render(request, 'setting/register_form.html', {
'user_id': id
'registerform':registerform
})
else:
return HttpResponse('Request does not contain any ID')
else:
return HttpResponse('Request is not POST')
def update_reporter(request):
''' This function is for update the reporter '''
registerform = UserRegisterForm()
if request.method == 'POST':
id = request.POST.get('id', None)
if id:
user = get_object_or_404(pk=user.id)
userprofile = UserProfile.objects.get(user=user)
registerform = UserRegisterForm(request.POST, instance=user)
if registerform.is_valid():
result = registerform.save(commit=False)
# saving code here ...
return HttpResponse('Success')
else:
return HttpResponse('Request does not contain any ID')
else:
return HttpResponse('Request is not POST')
You can see here there are 2 functions: 1 for fetching the right form from AJAX, the other for saving the data via normal form submit. Of course you should make the urls.py accordingly, something like:
urlpatterns = patterns('',
# ... your code ...
url(r'^setting/fetch-reporter-form/$', 'yourapp.views.fetch_reporter_form'),
url(r'^setting/update-reporter/$', 'yourapp.views.update_reporter'),
)
You might also notice that you should make a new template setting/register_form.html that include your registration form HTML only (note: you need a hidden id field that was returned by fetch_reporter_form view above to identify the form):
<form method="post" action="/setting/update-reporter" id="reporter-form">
{% csrf_token %}
<input type="hidden" name="id" value="{{ user_id }}" />
<!-- your code here -->
</form>
So the flow is:
You go to index view. There is several forms to save new reporter, etc. as normal.
You click on the .openDiv button. It will send the AJAX request above to fetch_reporter_form to get the correct form. (Your code is working fine at this point)
You click Save button on that form, it will submit the updated data (via POST) to update_report view and update the reporter.
I'm just trying to give you the basic idea. The rest is quite straightforward so I guess you can continue easily. Hope it helps!
Let me resume what you are doing:
$.ajax({
data:{
/* ... */
},
type:'POST',
url: '/report/save_reporter/',
success: function() {
return true;
}
});
Here you setup an Ajax asynchronous query to post data to the server. When the query reaches the server, if it doesn't crash, the success: callback is called and javascript does nothing (return true;).
$('#authorisedreporter').show();
Here, you show an HTML node before the asynchronous Ajax query ends (succeeds or fails). To show the element after the Ajax query is done, put this code inside the success: or error: callback.
Finally, if you count your (, ), { and } you will see than $('#authorisedreporter').show(); is outside of the click callback. Therefore, if it is out of the document ready callback, it will have no effect.
So the right Javascript code should be (I think):
$('.openDiv').click(function (e) {
e.preventDefault();
var id = $(this).attr('id');
var firstname = $("#"+id).find('#id_first_name').val();
var phonemobile = $("#"+id).find('id_phone_mobile').val();
$.ajax({
data:{
csrfmiddlewaretoken: csrf_token,
edit_reporter:true,
id:id,
first_name:firstname,
phone_mobile:phonemobile,
},
type:'POST',
url: '/report/save_reporter/',
success: function() {
$('#authorisedreporter').show();
}
});
});
EDIT:
About your view.py, you save a UserProfile but do not return anything to the client browser. Nothing.
def save_reporter(request):
user=User.objects.get(user=user) # you should use django.shortcuts.get_object_or_404
userprofile = Userprofile.objects.get(user=user) # same comment
if request.method == 'POST':
registerform = UserRegisterForm(request.POST,instance=user)
createprofileform = UserCreateProfileForm(request.POST,instance=userprofile)
# you create 2 forms above, but don't use them. Is it normal ?!
# you should do loops "if registerform .valid(): ... else: ..." and use it. Same for the 2nd form
if 'edit_reporter' in request.POST:
first_name=request.POST.get('first_name') # can be None
phone_mobile = request.POST.get('phone_mobile') # can be None
user = User.objects.get(pk=user)
user.first_name = first_name
user.save() # put it inside a try/except statment
userprofile = UserProfile.objects.get(pk=user) # use get_or_404
userprofile.phone_mobile = phone_mobile
userprofile.save() # put it inside a try/except statment
return HttpResponse() # this returns an empty html page, do you want to return the form here ?
Check if there is any error message returned from the server:
$.ajax({
// ...
error: function(err) {
console.log(err);
}
});
Edit:
You don't pass the user id when you submit the form. Try doing something like:
<form ...>
<!-- //.. -->
{{ form.id.as_hidden }}
</form>

Categories