I have two models in a parent-child relationship: Idea and Comment. I am using DRF and nested DataTables to serve these models to the browser. To create a comment, the corresponding idea ID must be known. The button to create a new comment looks like this with parentObjData being the Idea id:
<button type="button" class="btn btn-primary js-create-idea-comment" data-url="/platform/ideas/comments/' + parentObjData + '/create/"><span class="fa fa-plus"></span> New Comment</button>
When clicked, this happens:
var loadForm = function () {
var btn = $(this)
$.ajax({
url: btn.attr("data-url"),
type: 'get',
dataType: 'json',
beforeSend: function () {
$("#modal-activity").modal()
},
success: function (data) {
$("#modal-activity .modal-content").html(data.html_form)
}
})
}
This works, and a request to the proper URL is sent when each button is clicked. What's supposed to happen with a successful request is demonstrated by these views:
def save_comment_form_create(request, form, template_name, parent_id):
data = dict()
if request.method == 'POST':
if form.is_valid():
instance = form.save(commit=False)
instance.created_by_id = request.user.id
instance.idea_id = parent_id
form.save()
data['form_is_valid'] = True
comments = IdeaComment.objects.all()
data['html_idea_comment_list'] = render_to_string('ic/includes/partial_idea_comment_list.html', {
'comments': comments
})
else:
data['form_is_valid'] = False
context = {'form': form}
data['html_form'] = render_to_string(template_name, context, request=request)
return JsonResponse(data)
def idea_comment_create(request, parent_id):
idea_id = parent_id
if request.method == 'POST':
form = IdeaCommentForm(request.POST)
else:
form = IdeaCommentForm()
return save_comment_form_create(request, form, 'ic/includes/partial_idea_comment_create.html', idea_id)
partial_idea_comment_create.html resolves to this form:
<form method="post" action="{% url 'idea_comment_create' parent_id %}" class="js-idea-comment-create-form">
{% csrf_token %}
<div class="modal-header">
<h4 class="modal-title">New Comment</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
{% include 'ic/includes/partial_idea_comment_form.html' %}
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="submit" class="btn btn-primary">Create</button>
</div>
</form>
And here is the URL:
url(r'^platform/ideas/comments/(?P<parent_id>\d+)/create/$', views.idea_comment_create, name='idea_comment_create'),
parent_id is the issue. If I hard code a number into partial_idea_comment_create.html, everything works great (except comments go to the wrong idea). The way it is now, I get this error:
NoReverseMatch at /platform/ideas/comments/1/create/
Reverse for 'idea_comment_create' with arguments '('',)' not found. 1 pattern(s) tried: ['platform/ideas/comments/(?P<parent_id>\\d+)/create/$']
For a normal pk that doesn't rely on a parent instance, I would do something like this:
<form method="post" action="{% url 'idea_update' form.instance.pk %}">
How should I dynamically load the parent_id into that URL?
I think this question gets partially there: How do I pass a parent id as an fk to child object's ModelForm using generic class-based views in Django? but I haven't been able to get it to work for me.
The issue is that you're not passing the parent_id in the template context. This is what you should do in the save_comment_form_create method:
...
context = {'form': form, 'parent_id': parent_id}
data['html_form'] = render_to_string(template_name, context, request=request)
return JsonResponse(data)
Related
I got problem with 'live' like unlike button in Django and JavaScript DOM
after button is clicked I got an error
POST http://127.0.0.1:8000/like/24 404 (Not Found)
likePost # javascripts.js:24
(anonymous) # javascripts.js:40
javascripts.js:7
I don't know if the problem is in the 'await fetch' function or maybe I used the wrong class or id somewhere.
Where to start?
javascript.js
const reloadPostHTML = async (postId) => {
const homePageResponse = await fetch(window.location.href);
const newHTML = await homePageResponse.text();
const newDocument = new DOMParser().parseFromString(newHTML, "text/html");
console.log(newDocument)
const newPostElem = newDocument
.querySelector(`[data-post-id='${postId}']`)
.closest(".post");
const oldPostElem = document
.querySelector(`[data-post-id='${postId}']`)
.closest(".post");
oldPostElem.innerHTML = newPostElem.innerHTML;
makeLikeButton(oldPostElem.querySelector(".like-button-wrapper"));
};
const likePost = async (postId, csrfToken) => {
await fetch(`/like/${postId}`, {
method: 'POST',
credentials: 'include',
headers: {
"X-CSRFToken": csrfToken
}
});
reloadPostHTML(postId);
};
const makeLikeButton = (elem) => {
elem.querySelector('button').addEventListener("click", (event) => {
event.preventDefault();
const postId = elem.dataset.postId;
const csrfToken = elem.dataset.csrfToken;
likePost(postId, csrfToken);
});
};
const makeLikeButtons = () => {
for (let elem of document.getElementsByClassName("like-button-wrapper")) {
makeLikeButton(elem);
}
};
makeLikeButtons();
urls.py
path(
'article_detail/<int:pk>/',
login_required(
ArticleDetail.as_view(template_name = "web/article_detail_view.html")
),
name='article_detail'
),
path('like/<int:pk>', views.like, name='like'),
In the views should I also use "if request.method == "POST":" ?
views.py
def like(request, pk):
article = get_object_or_404(Article, id=request.POST.get("article_id"))
if article.likes.filter(id=request.user.id).exists():
article.likes.remove(request.user)
liked = False
else:
article.likes.add(request.user)
liked = True
return HttpResponseRedirect(reverse("article_detail", args=[int(pk)]))
and detail_view.py
class .post is in thats why i used .closest(".post") in javascript.js
<div class="card post"> ........
<div class="like-button-wrapper"
data-post-id='{{ article.pk }}'
data-csrf-token='{{ csrf_token }}'>
{% if liked %}
<button class="btn btn-danger position-relative" type="submit" id="like" name="article_id"
value="{{article.id}}">
<i class="bi bi-hand-thumbs-down">
</i>
</button>
{% else %}
<button class="btn btn-primary position-relative" type="submit" id="like" name="article_id"
value="{{article.id}}">
<i class="bi bi-hand-thumbs-up">
</i>
<span
class="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-danger">
{{ likes }}
</span>
</button>
{% endif %}
</div>
Ok I got it it was a error 404 came from views.py get_or_404
Now I just rebuild the like function in views and its working as should
def like(request, pk):
if request.method == "POST":
#article = get_object_or_404(Article, id=request.POST.get("article_id"))
article = Article.objects.get(id=pk)
if article.likes.filter(id=request.user.id).exists():
article.likes.remove(request.user)
liked = False
else:
article.likes.add(request.user)
liked = True
return HttpResponseRedirect(reverse("article_detail", args=[int(pk)]))
I am trying to pass the values of form data through ajax .And getting method not allowed error. I am trying to add comment in a blog post.
This is my form which is inside blog_detail page
<form id="commentform" class="commentform" method="post">
{% csrf_token %}
{%with allcomments.count as total_comments%}
<p>
{{total_comments}} comment{{total_comments|pluralize}}
</p>
{%endwith%}
<select name="blog" class="d-none" id="id_blog">
<option value="{{blog.id}}" selected="{{blog.id}}"></option>
</select>
<label class="small font-weight-bold">{{comment_form.parent.label}}</label>
{{comment_form.parent}}
<div class="d-flex">
<img class="avatar_comment align-self-center" src="{% for data in avatar%}{{data.avatar.url}}{%endfor%}" alt="">
{{comment_form.content}}
</div>
<div class="d-flex flex-row-reverse">
<button value="commentform" id="newcomment" type="submit" class="newcomment btn btn-primary">Submit</button>
</div>
</form>
And when I click the button it should call the ajax
$(document).on('click','#newcomment',function(e){
e.preventDefault();
var button =$(this).attr("value");
var placement = "commentform"
if (button=="newcommentform"){
var placement = "newcommentform"
}
$.ajax({
type: 'POST',
url: '{% url "website:addcomment" %}',
data: $("#" + button).serialize(),
cache: false,
sucess: function(json){
console.log(json)
$('<div id="" class="my-2 p-2" style="border: 1px solid grey"> \
<div class="d-flex justify-content-between">By ' + json['user'] + '<div></div>Posted: Just now!</div> \
<div>' + json['result'] + '</div> \
<hr> \
</div>').insertBefore('#' + placement);
},
error: function(xhr,errmsg,err){
}
});
})
This is my urls.py
path('blog/<int:blog_id>', BlogDetails.as_view(), name="blog_detail"),
path('addcomment/',addcomment, name="addcomment"),
and my views.py is:
class BlogDetails(View):
def get(self, request, blog_id):
query = request.GET.get('query')
if query:
return redirect(reverse('website:search') + '?query=' + query)
blog = Blog.objects.get(id=blog_id)
total_comment = Comment.objects.filter(blog=blog).count()
allcomments = blog.comments.filter(status=True)
blog_list = Blog.objects.all()
comment_form = NewCommentForm()
data = {
'blog': blog,
'blog_list': blog_list,
'total_comment': total_comment,
'comment_form': comment_form,
'allcomments': allcomments
}
return render(request, "blog_detail.html", data)
def addcomment(request):
if request.method == 'post':
comment_form = NewCommentForm(request.POST)
print(comment_form)
if comment_form.is_valid():
user_comment = comment_form.save(commit=False)
user_comment.user = request.user
user_comment.save()
result = comment_form.cleaned_data.get('content')
user = request.user.username
return JsonResponse({'result': result, 'user': user})
Please help me with this it is not calling addcomment view
If how I've interpreted your code is correct, it would probably work if changed your BlogDetails class to this:
class BlogDetails(View):
def get(self, request, blog_id):
query = request.GET.get('query')
if query:
return redirect(reverse('website:search') + '?query=' + query)
blog = Blog.objects.get(id=blog_id)
total_comment = Comment.objects.filter(blog=blog).count()
allcomments = blog.comments.filter(status=True)
blog_list = Blog.objects.all()
comment_form = NewCommentForm()
data = {
'blog': blog,
'blog_list': blog_list,
'total_comment': total_comment,
'comment_form': comment_form,
'allcomments': allcomments
}
return render(request, "blog_detail.html", data)
def post(self, request, *args, **kwargs):
return self.addcomment(request)
def addcomment(self, request):
comment_form = NewCommentForm(request.POST)
print(comment_form)
if comment_form.is_valid():
user_comment = comment_form.save(commit=False)
user_comment.user = request.user
user_comment.save()
result = comment_form.cleaned_data.get('content')
user = request.user.username
return JsonResponse({'result': result, 'user': user})
Because you are trying to POST to a view that doesn't have a post method defined.
Then you would need to remove addcomment from the URL you are calling and just post to whatever URL you are currently at.
I'm using Django modelform and it's submitted by pressing the enter key. This is what I wanted, but I don't know why it works. I didn't add any JS codes related to keydown but other codes to practice Ajax. Also, I found out that when there's only one input inside the form, it's submitted with the enter key, but my form has two inputs.
What I'm doing is to add a comment on a post like instagram. I used Ajax to create a comment instance.
models.py
class Comment(models.Model):
parent_post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="comments")
author = models.CharField(max_length=10)
content = models.CharField(max_length=100)
forms.py
class CommentForm(ModelForm):
class Meta:
model = Comment
exclude = ["parent_post"]
HTML
{% for post in posts %}
<form method="POST" data-id="{{post.id}}" class="d-flex align-items-center w-100 mt-2">
{% load widget_tweaks %} <!-- this is a django package for easy CSS -->
{% csrf_token %}
{{ form.non_field_errors }}
<div class="fieldWrapper">
{{ form.author.errors }}
{{ form.author|add_class:"form__author"|attr:"placeholder:name" }}
</div>
<div class="fieldWrapper w-100">
{{ form.content.errors }}
{{ form.content|add_class:"form__content"|attr:"placeholder:content" }}
</div>
<button class="btn btn-sm btn-warning w-auto">Write</button>
</form>
{% endfor %}
JS (Here I didn't put the codes after getting JSON response from views.py)
const forms = document.querySelectorAll("form");
forms.forEach((value) => {
const post_id = Number(value.getAttribute("data-id"));
value.addEventListener("submit", (e) => {
e.preventDefault(); // prevents reload, still submits form data but views.py does nothing for this data
writeComment(post_id);
});
const requestWrite = new XMLHttpRequest();
const writeComment = (post_id) => {
const form = document.querySelector(`form[data-id="${post_id}"]`);
const author = form.querySelector(".form__author");
const content = form.querySelector(".form__content");
const url = "/write/";
if (author.value && content.value) {
requestWrite.open("POST", url, true);
requestWrite.setRequestHeader(
"Content-Type",
"application/x-www-form-urlencoded"
);
requestWrite.send(
JSON.stringify({
post_id: post_id,
author: author.value,
content: content.value,
})
);
author.value = null;
content.value = null;
}
};
views.py
#csrf_exempt
def write(request):
req = json.loads(request.body)
post_id = req["post_id"]
author = req["author"]
content = req["content"]
comment = Comment.objects.create(parent_post=get_object_or_404(Post, id=post_id), author=author, content=content)
return JsonResponse({"post_id":post_id, "comment_id":getattr(comment, "id"), "author":author, "content":content})
Thank you!
This question already has answers here:
Event binding on dynamically created elements?
(23 answers)
Closed 5 years ago.
I have a formset to update my models and I display forms from my formset based on the search query. I have an ajax keyup function to send post requests so I can generate search_results.html that I then pass to in search.html.
Now, if I generate the forms dynamically the submit button does not work. If I ditch the search and pass the content of search_results.html to search.html directly it does work.
I have two views, SearchReportView which supposed to be the main one, and SearchReports to handle generating the report. I guess I might just merge them at this point - it doesn't solve my problem though.
Also I had this working before when I used multiple forms but I had to switch to a formset.
body of search.html
<div class="container">
<div class="navbar">
<div class="navbarItem">
<img src="{% static 'back.png' %}" width="20px" height="12px" \>Back
</div>
</div>
<div>
<h3>Search:</h3>
{% csrf_token %}
<input type="text" id="search" name="search" />
<span class="button-checkbox">
<button id="mybutton" type="button" class="btn" data-color="primary">Recent</button>
<input type="checkbox" class="hidden" name="reports_checkbox" checked />
</span>
</div>
<div class="normalText" id="search-results"></div>
</div>
search_results.html
{% if reports_and_formset %}
<form action="/TP/auto/report/search/" method="post" enctype="multipart/form-data"> {% csrf_token %}
{{ formset.management_form }}
{% for x, form in reports_and_formset %}
<button type="submit" class="button" >Submit</button>
{{ form.checking_user }}
{{ form.comment }}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endfor %}
</form>
{% else %}
<tr> <td> <center>
No results found.
</center></td></tr>
{% endif %}
views.py
class SearchReportView(FormView):
def post(self, request, *args, **kwargs):
# form = ReportCommentForm(initial={'comment': ''})
# context = {'form': form}
if 'form-TOTAL_FORMS' in request.POST:
single_day_enb_reports = SingleDayEnbReport.objects.all()
report_form_set = modelformset_factory(SingleDayEnbReport, form=ReportCommentForm,
max_num=single_day_enb_reports.count())
formset = report_form_set(request.POST, queryset=single_day_enb_reports)
formset.save()
return HttpResponseRedirect('/TP/auto/report/search/report_search/')
# #requires_csrf_token
def get(self, request, *args, **kwargs):
return render(request, 'app/search.html')
class SearchReports(View):
def get(self, request, *args, **kwargs):
single_day_enb_reports = SingleDayEnbReport.objects.all()
report_form_set = modelformset_factory(SingleDayEnbReport, form=ReportCommentForm, max_num=single_day_enb_reports.count())
formset = report_form_set(queryset=single_day_enb_reports)
single_day_enb_reports_and_formset = zip(single_day_enb_reports, formset)
# context = {'single_day_enb_reports': single_day_enb_reports, 'formset': formset}
context = {'reports_and_formset': single_day_enb_reports_and_formset, 'formset': formset}
return render(request, 'app/search_results.html', context, context_instance=RequestContext(request))
#classmethod
def calculate_dates(self): #not important
def post(self, request, *args, **kwargs):
if 'form-TOTAL_FORMS' in request.POST:
single_day_enb_reports = SingleDayEnbReport.objects.all()
report_form_set = modelformset_factory(SingleDayEnbReport, form=ReportCommentForm,
max_num=single_day_enb_reports.count())
formset = report_form_set(request.POST, queryset=single_day_enb_reports)
# if formset.is_valid():
formset.save()
return HttpResponseRedirect('/TP/auto/report/search/report_search/')
if 'button_state' in request.POST:
search_text = request.POST['search_text']
button_state = request.POST['button_state']
if button_state == 'false':
button_state = False
else:
button_state = True
if button_state:
now, fit_date = self.calculate_dates()
single_day_enb_reports = SingleDayEnbReport.objects.all().filter(
Q(last_modification_date__range=[fit_date, now]) & (
Q(alarms__contains=search_text)|
Q(enb__contains=search_text)|
Q(creation_date__date__contains=search_text) |
Q(last_modification_date__date__contains=search_text)|
Q(comment__contains=search_text)|
Q(checking_user__name__contains=search_text)))
else:
single_day_enb_reports = SingleDayEnbReport.objects.all().filter(
Q(alarms__contains=search_text)|
Q(enb__contains=search_text)|
Q(creation_date__date__contains=search_text) |
Q(last_modification_date__date__contains=search_text)|
Q(comment__contains=search_text)|
Q(checking_user__name__contains=search_text))
report_form_set = modelformset_factory(SingleDayEnbReport, form=ReportCommentForm,
max_num=single_day_enb_reports.count())
formset = report_form_set(queryset=single_day_enb_reports)
single_day_enb_reports_and_formset = zip(single_day_enb_reports, formset)
context = {'reports_and_formset': single_day_enb_reports_and_formset, 'formset': formset}
return render_to_response('app/search_results.html', context, context_instance=RequestContext(request))
ajax.js
$('#search').keyup(function(){
delay(function(){
if($('input[name=reports_checkbox]:checked').val()) button_state = true
else button_state = false
$.ajax({
type: "POST",
url: "/TP/auto/report/search/report_search/",
data: {
'button_state' : button_state,
'search_text' : $('#search').val(),
'csrfmiddlewaretoken' : $("input[name=csrfmiddlewaretoken]").val()
},
success: searchSuccess,
dataType: 'html'
});
}, 500 );
});
});
function searchSuccess(data, textStatus, jqXHR){
//generated data is put into search-results <div>field in search.html
$('#search-results').html(data);
}
Please change your this line
$('#search').keyup(function(){
with
$(document).on('keyup','#search', function(){
I require a small fix. I simply need to POST my data (comments) to the datastore (GAE) using angularjs but it's not happening just yet. What's wrong with the following angularjs "post" or html?
ANGULAR:
$scope.addComment = function() {
var form_comment = $scope.formFields.comment
var payload = {
comment: form_comment
}
$http({
method: 'POST',
url: '/exp'
}).then(function successCallback(response) {
$scope.comments.push(payload);
}, function errorCallback(response) {
});
};
HTML:
{% extends "home.html"%}
{% block content %}
<div ng-controller="commentController" class="formcontent">
<div class ="container">
<form ng-submit="addComment()" method="post" id="frmComment">
<textarea ng-model="formFields.comment" id="comment" name="commento" class="form-control status-box sameline" rows="2" placeholder="Recommend Colin"></textarea>
</form>
<div class="button-group pull-right sameline">
<p class="counter">140</p>
<button form ="frmComment"class="btn btn-primary" type="submit">Post</button>
</div>
</div>
<div>
<ul class="posts">
<li ng-repeat = "c in comments">
{< c.comment >}
</li>
</ul>
</div>
</div>
{% endblock %}
PYTHON:
class expHandler(webapp2.RequestHandler):
def get(self):
title="Colin_MK: Experience"
recommendations = Recommendation.query()
self.response.out.write(json.dumps([rec.to_dict() for rec in recommendations]))
template_vars = {'title': title, 'recommendations': recommendations}
template = JINJA_ENVIRONMENT.get_template('/exp.html')
self.response.out.write(template.render(template_vars))
def post(self):
r = json.loads(self.request.body)
new_comment = Recommendation(comment=r['comment'])
new_comment.put()
app = webapp2.WSGIApplication([
('/', MainHandler),
('/bio', bioHandler),
('/exp', expHandler)
], debug=True)
The signature to the post method is the following:
post(url, data, [config]);
so you should also include the payload. Try something like this:
$http.post('/exp', payload).then(...)
Also, on the then() method of the promise, you should send a reference to the method:
.then(function(response) {
$scope.comments.push(payload);
}, function(response) {
});