I am trying to refresh a certain div in a django application when a form is submitted.
index.html
<div class="col-md-8" id="notesColumn">
{% crispy note_form %}
{% include 'item/item_notes.html' %}
</div>
item_notes.html
<div class="panel-group panel-group-simple m-b-0" id="notesList" aria-multiselectable="true" role="tablist">
{% for note in object.itemnote_set.all reversed %}
<div class="panel">
<div class="panel-heading" id="noteHeading{{ forloop.counter }}" role="tab">
<a class="panel-title collapsed" data-parent="#notesList"
data-toggle="collapse" href="#noteCollapse{{ forloop.counter }}"
aria-controls="noteCollapse{{ forloop.counter }}" aria-expanded="false">
<span class="tag tag-default">{{ note.owner.first_name }}</span>
{{ note.get_action_display|upper }}
<small class="panel-actions">{{ note.date_added }}</small>
</a>
</div>
<div class="panel-collapse collapse" id="noteCollapse{{ forloop.counter }}"
aria-labelledby="noteHeading{{ forloop.counter }}" role="tabpanel" aria-expanded="false"
style="height: 0px;">
<div class="panel-body">
{{ note.content }}
</div>
</div>
</div>
{% endfor %}
</div>
app.js (included in index.html)
$(document).ready(function () {
$("#notesTab form").submit(function(event){
event.preventDefault();
$('#notesList').remove();
$.ajax({
url: "{% url item_notes %}",
success: function(data){
$('#notesColumn').html('data');
}
})
})
views.py
def item_notes(request):
return render_to_response(request, 'candidate/candidate_notes.html')
urls.py
url(r'item/profile/(?P<pk>[0-9]+)/$', views.ItemProfile.as_view(), name='item_profile'),
url(r'item/notes', views.item_notes, name='item_notes'),
The error I get from chrome is:
http://127.0.0.1:8000/crm/item/profile/45/%7B%%20url%20item_notes%20%%7D
Failed to load resource: the server responded with a status of 404 (Not Found)
You can't use Django template tags in your external JS file - Django does not parse that file, which is why you can see the literal tag being appended to your Ajax URL.
You will need to set that value as a global JS var inside an inline script in your template itself.
First, try to use an absolute URL to see, if it's the URL that causes the error:
$.ajax({
url: "item/notes",
success: function(data){
$('#notesColumn').html('data');
}
})
Second, why do you GET the notes URL? Aren't you using 'item_profile' for that? In that case, try to GET that URL:
$.ajax({
url: "item/profile/" + "{{ object.pk }}",
success: function(data){
$('#notesColumn').html('data');
}
})
Third, check you JS code, you are missing a closing bracket on $("#notesTab form").submit.
Fourth, try to escape the URL. I am not sure which method to use in JS, but I had that problem multiple times where it broke because of unescaped code.
These are just some tips of the top of my head. Hope that helps.
Related
I am working on a small Flask app and the user is able to update a list of items via using a form to submit some data.
I am currently using SQL Alchemy as the ORM for my database and using the list in my template to display the items. I want to update it so that when the list is updated. The page is updated without the user having to reload the page.
I have tried this with AJAX using the below script but the update is not occurring.
$(document).ready(function(){
$('form').on('submit',function (e) {
$.ajax({
type: 'post',
url: '/todo',
data: $('#todoInput').serialize(),
success: function (q) {
console.log(q);
}
});
e.preventDefault();
});
}
My template:
{% extends "base.html" %}
{% block script %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="{{ url_for('static', filename='js/custom.js') }}"></script>
{% endblock %}
{% block content %}
{% with messages = get_flashed_messages() %}
{% if messages %}
{% for message in messages %}
<div class="alert alert-success" role="alert">
{{ message }}
</div>
{% endfor %}
{% endif %}
{% endwith %}
<div class="row justify-content-md-center">
<div class="col-md-auto">
<h1>What needs to be done today?</h1>
</div>
</div>
<div class="row justify-content-md-center">
<div class="col-md-auto">
<form action="" method="post">
{{ form.hidden_tag() }}
<div class="form-group">
{{ form.todo (id="todoInput") }}
{{ form.submit (class_="btn btn-primary btn-sm") }}
</div>
</form>
<div id="todoList">
<ul class="list-group">
{% for todo in todos %}
<li class="list-group-item d-flex justify-content-between align-items-center">
{{ todo.body }}
<button type="button" class="close" aria-label="Close"><span aria-hidden="true">×</span></button>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
{% endblock %}
Model:
from datetime import datetime
from app import db, login
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
todos = db.relationship('Todo', backref='owner', lazy='dynamic')
def __repr__(self):
return '<User {}>'.format(self.username)
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
#login.user_loader
def load_user(id):
return User.query.get(int(id))
class Todo(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def __repr__(self):
return '<Todo {}>'.format(self.body)
Route:
#app.route('/todo', methods=['GET', 'POST'])
def todo():
if current_user.is_authenticated:
form = TodoForm()
todos = current_user.todos.order_by(Todo.timestamp.desc()).all()
if form.validate_on_submit():
new_todo = Todo(body=form.todo.data, owner=current_user)
db.session.add(new_todo)
db.session.commit()
flash('Todo added')
todos = current_user.todos.order_by(Todo.timestamp.desc()).all()
return render_template('todo.html', form=form, todos=todos)
return render_template('todo.html', form=form, todos=todos)
flash('You first need to login or register')
return redirect(url_for('index'))
The todos list is what im trying to update. But it just reloads the page :/
You need to do two things. Firstly you need to be sure that your database is updated. That means that your form was submitted successfully. This can be done by submitting the form manually via an ajax call. This is more or less what you did. Did you check, whether your backend receives the form-submission and updates the database?
Secondly you need to update the html in the browser. Normally the page is refreshed automatically if your form is submitted. If you dont want a page reload, you need to add some client-side logic, to ensure that the page is updated. This can easily be done with a little bit of javascript.
Edit:
Your custom js might be missing }); at the end. Or did you just forget to post it here?
Where can i put ajax get data code in Wagtail? I have following page model:
class ScreencastPage(Page):
content_panels = Page.content_panels + [
InlinePanel(
'groupstage_screencast_relationship', label="Choose Teams",
panels=None, max_num=2),
]
parent_page_types = ['home.HomePage']
def matches(self):
matches = [
n.match for n in self.groupstage_screencast_relationship.all()
]
return matches
And my template:
{% for spiel in page.matches %}
{% if forloop.first == forloop.last %}
<div id="fullscreen">
<ul class="ulup">
<li class="logo_bg first_team">{% image spiel.team_1.team_logo width-400 class="logo" %}<p>{{spiel.team_1.title}}</p></li>
<li class="first_team_score">{{ spiel.team_1_total_score }}</li>
<li class="colons">:</li>
<li class="second_team_score">{{ spiel.team_2_total_score }}</li>
<li class="logo_bg second_team">{% image spiel.team_2.team_logo width-400 class="logo" %}<p>{{spiel.team_2.title}}</p></li>
</ul>
</div>
{% endif %}
{% endfor %}
I started writing js. Just exaple:
$(document).ready(function() {
setInterval(function(){
$.ajax({
type: "GET",
url: {% pageurl page %},
data: {},
success: function(data) {
console.log(data);
$(".first_team_score").contents()[0].textContent = data.team_1_total_score;
$(".second_team_score").contents()[0].textContent = data.team_2_total_score;
}
})
}, 10000);
});
The idea is that the page will automatically update the value of <li class="first_team_score">{{ spiel.team_1_total_score }}</li> and <li class="second_team_score">{{ spiel.team_2_total_score }}</li> without reloading the page.
I found here great example, but they using view.py
We also need to write a new view.py or have wagtail some method for that?
UPDATE
Thanks #Ben-Dickinson from wagtail slack community. He shared a link to the documentation where it is indicated how it's possible to solve such a problem.
I have here another problem. How to convert matches to json?
To catch ajax requests we can use the Page serve() method and use if request.is_ajax():. So I did following inside my ScreencastPage(Page):
def serve(self, request):
if request.is_ajax():
result = [
{
'team_1_name': match.team_1.title,
'team_1_score': match.team_1_total_score,
'team_2_name': match.team_2.title,
'team_2_score': match.team_2_total_score,
}
for match in self.matches()
]
json_output = json.dumps(result)
return HttpResponse(json_output)
else:
return super(ScreencastPage, self).serve(request)
This code from above was the result of help from #gasman, this topic you can find here Converting value to json inside serve method
The final result of the HTML/JS code is:
<div id="match1">
<ul class="ulup">
<li class="logo_bg first_team">{% image spiel.team_1.team_logo width-400 class="logo" %}<p>{{spiel.team_1.title}}</p></li>
<li class="first_team_score">{{ spiel.team_1_total_score }}</li>
<li class="colons">:</li>
<li class="second_team_score">{{ spiel.team_2_total_score }}</li>
<li class="logo_bg second_team">{% image spiel.team_2.team_logo width-400 class="logo" %}<p>{{spiel.team_2.title}}</p></li>
</ul>
</div>
JS:
$(document).ready(function() {
setInterval(function(){
$.ajax({
type: "GET",
url: {% pageurl page %},
dataType: 'json',
success: function(data) {
$("#match1 .first_team").contents()[0].textContent = data[0]["team_1_name"];
$(".first_team_score").contents()[0].textContent = data[0]["team_1_score"];
$("#match1 .second_team").contents()[0].textContent = data[0]["team_2_name"];
$(".second_team_score").contents()[0].textContent = data[0]["team_2_score"];
}
})
}, 10000);
});
data[0] is becoz my data returns database of two matches and i need only first one
I've been unable to find a solution to my problem from searching. So I'd like to ask what might be wrong with my code. I'm trying to validate a form from forms.ModelForm but in my views function it won't pass the form.is_valid(). printing form.errors gives me:
<li>title<ul class="errorlist"><li>This field is required.</li></ul>
Model:
class Paper(models.Model):
title = models.CharField(max_length=100, help_text='Hello World!')
forms.FormModel
class eventCreateForm(forms.ModelForm):
class Meta:
Model = Paper
fields = ['title']
widgets = {
'title': forms.TextInput(attrs={'class' :'form-control', 'placeholder' : 'Place title'}),
}
Views
def create_paper(request):
context = {}
if request.method == 'POST':
form = paperCreateForm(request.POST or None, request.FILES or None)
if form.is_valid():
form_data = form.cleaned_data
t1 = form_data['title']
print(t1)
else:
context['create_paper_form'] = form
form_template = "user/paper-event-template.html"
return HttpResponse(render_to_string(form_template, {'context' : context}))
The form dosen't get validated, and in the else clause it'll pass the error when trying to retrieve it from the cleaned_data
I did try and print the form, and it shows:
<tr><th><label for="id_title">Title:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul><input class="form-control" id="id_title" maxlength="100" name="title" placeholder="Place Title" type="text" required /></td></tr>
But it dosen't contain any value, which I guess it should: I use a jax method for sending the forms data:
ajax
$('#create_paper_form_id').submit(function (event) {
event.preventDefault();
$.ajax({
type: "POST",
url: "{% url 'Eapp:create_paper' %}",
data: {
csrfmiddlewaretoken : '{{ csrf_token }}',
form_data : $('#create_paper_form_id').serializeArray(),
},
success: function (data) {
console.log(data);
$('.create-paper').html(data);
},
error: function() {
console.log('err');
}
});
});
html
<div class="create-paper">
<div class="container-fluid">
<form class="form" id="create_paper_form_id" novalidate="novalidate" action="{% url 'Eapp:create_event' %}" method="POST">
{% for field in create_paper_form %}
<div class="form-group">
<div class="col-xs-12">
{{ field.label_tag }}
</div>
<div class="col-xs-12">
{{ field }}
</div>
<div class="col-xs-6">
{{ field.help_text }}
</div>
<div class="col-xs-6">
{{ field.errors }}
</div>
</div>
{% endfor %}
<div class="form-group">
<div class="col-xs-6 col-sm-6 col-md-2 col-lg-2">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
</div>
</form>
</div>
</div>
That's expected behaviour, the cleaned_data is only available on a valid form when there's actually cleaned data available.
The form.data is still available though, you can use that.
To quote the manual:
A Form instance has an is_valid() method, which runs validation
routines for all its fields. When this method is called, if all fields
contain valid data, it will:
return True
place the form’s data in its cleaned_data attribute.
[edit] As #fazil-zaid mentioned, you need to include model in your Meta class for it to function as a modelform.
I have been trying to come up with a scheme where the comments sections refreshes in a template when a user posts comments with the comment that was just posted being included. The page must not be refreshed.
Sorry for indentation.
template -
{% extends "home/header.html" %}
{% block content %}
{% if request.user.is_authenticated %}
<form>
{% csrf_token %}
<p>Comment: </p><input type="text" name="fname" id="posted_comment">
<input type="hidden" class='meme_char_id' meme_char_id={{meme.meme_char_id}}>
<input type="submit" id ="btnSubmit" name="submit" value="Post">
</form>
<!-- displaying comments here -->
<div class="box" id="comments_div">
<h3 class="h4">{{comment_count}} comments </h4>
<ul class="list-group">
{% for comment in all_comments %}
<h5>{{ comment.commentby.username }} : </h5>
<li class="list-group-item list-group-item-success">{{ comment.comment_text }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endblock %}
Now, my JS file is -
$(document).ready(function() {
$('#btnSubmit').click(function(e) {
e.preventDefault();
var data = new FormData();
var comment = $('#posted_comment').val()
var meme_char_id = $(".meme_char_id").attr('meme_char_id')
data.append('comment', comment);
data.append('meme_char_id', meme_char_id);
$.ajax({
type: 'POST',
url: '<mydomain>/comment/',
data: data,
processData: false,
contentType: false,
success: function(reply) {
$(".comments_div").html(reply)
}
})
});
});
And finally my view is -
def comment(request):
if request.is_ajax():
comment = request.POST['comment']
meme_char_id = request.POST['meme_char_id']
this_meme = Memes.objects.get(meme_char_id=meme_char_id)
print "comment - ", comment, " meme_char_id - ", meme_char_id
new_comment = Comments(comment_text=comment, meme_id=this_meme.meme_id, commentby_id=request.user.id)
new_comment.save()
all_comments = Comments.objects.filter(meme_id=this_meme.meme_id).order_by('-created_at').values()
return HttpResponse(all_comments, content_type='application/json')
else:
raise Http404
Couple questions -
I want to return an Comments query set through ajax(don't see a point in jsonifying it, if I can just send the query set to the template.)
The queryset I am returning to template isn't picked up by the comments_div in the view.
I am a rookie so please explain step by step what I am doing wrong. Thanks.
You are sending back a JSON in a response and doing$(".comments_div").html(reply). You must be sending back a template rendered with all_comments as response on successful comment. Also, a queryset is not JSON serializable as far as I understand.
I have a action in my controller for the index route
routing.yml
index:
pattern: /index
defaults: { _controller:AcmeDemoBundle:Default:index }
Controller for this path
public function indexAction()
{
return $this->render('AcmeDemoBundle:Plugin:index.html.twig');
}
And the index.html.twig template
{% extends'::base.html.twig' %}
{% block stylesheets %}
{% stylesheets filter='cssrewrite' output='css/*.css'
'bundles/acmedemo/css/*' %}
<link href="{{ asset_url }}" type="text/css" rel="stylesheet" />
{% endstylesheets %}
{% endblock stylesheets %}
{% block body %}
<br>
<div class="container">
<div class="wp_attachment_holder">
<div class="imgedit-response" id="imgedit-response-8"></div>
<div class="wp_attachment_image" id="media-head-8">
<p id="thumbnail-head-8"><img class="thumbnail" src="http://localhost/wordpress/wp-content/uploads/2014/06/121-1024x583.jpeg" style="max-width:100%" alt=""></p>
<p><a class="btn btn-sm btn-default" id="edik-wp-extended-edit">Редактировать</a> <span class="spinner"></span></p>
</div>
<div style="display:none" class="image-editor" id="image-editor-8">
</div>
</div>
<div id="output"></div>
<img class="thumbnail" data-attach-id="8" data-src="http://localhost/wordpress/wp-content/uploads/2014/06/121-1024x583.jpeg" style="max-width:100%" alt="">
<script>
$('#edik-wp-extended-edit').click(function() {
window.location= Routing.generate('ajax');
// $('#output').load('/ajax/index');
});
</script>
</div>
{% endblock %}`
When the button Редактировать is clicked i want to load another template with ajax.
another.html.twig
<div>Hello</div>
routing.yml
ajax:
pattern: /ajax/index
defaults: { _controller :AcmeDemoBundle:Default:ajax }
options:
expose: true
Controller for this path
public function ajaxAction()
{
$template = $this->renderView('AcmeDemoBundle:Plugin:another.html.twig');
return new Response($template);
}
But when i click the button my uri will be /ajax/index. What i want is that it stays by /index and the template will be rendered into my index template
What am i doing wrong?
Thanks.
First, your ajaxAction() should be a bit different as far as I know.
For me this works:
$template = $this->forward('AcmeDemoBundle:Plugin:another.html.twig')->getContent();
$json = json_encode($template);
$response = new Response($json, 200);
$response->headers->set('Content-Type', 'application/json');
return $response;
The forward() function renders the template and returns the rendered HTML code.
Your JavaScript file should look like this:
$.ajax({
type: "POST",
dataType: 'json',
url: Routing.generate('ajax'),
async: false //you won't need that if nothing in your following code is dependend of the result
})
.done(function(response){
template = response;
$('#your_div').html(template.html); //Change the html of the div with the id = "your_div"
})
.fail(function(jqXHR, textStatus, errorThrown){
alert('Error : ' + errorThrown);
});
You make an AJAX call to the your ajaxAction, which will return the HTML of the template you want to be rendered.
After that you just need to add a <div id="your_div"></div> at the position you want the template to be rendered. This workes perfectly for me.
To mention is that you need to break down the ajax template to just the code that should be shown.
Please try generate ajax route like this
window.location= '{{ path("ajax") }}';
Added:
For make ajax request change windows.location to ajax request
$( "#output" ).load( '{{ path("ajax") }}', function() {
alert('Load done');
});
Added explanation of use:
js code will work only if you put it on Twig template. If you put it to js file it will not work.
In case of original questions.
<div id="output"></div>
<img class="thumbnail" data-attach-id="8" data-src="http://localhost/wordpress/wp-content/uploads/2014/06/121-1024x583.jpeg" style="max-width:100%" alt="">
<script>
$('#edik-wp-extended-edit').click(function() {
$( "#output" ).load('{{ path("ajax") }}');
});
</script>
You can use something like FOSJsRoutingBundle to proper use SF2 routing in js