I'm using Django for an app that allows you to search and save recipes. I'm trying to have a form on a search results page that allows you to click 'save' and creates an object to the database without reloading the page. In the current setup when I click on the 'save' button for the form with id="add_form" I get redirected to a new url (add/) even though I've included preventDefault().
I thought this was due to me including the form action={%url 'add' %} however, I've tried removing the form action but that gave me a multiValueDictKeyError for raw_input = request.POST['ingredients'] so I thought it was calling the incorrect view function views.recipes instead of views.add. I've read some other ajax guides that explicitly state a form action so I thought that would help with the incorrect view function being called.
views.py
def recipes(request):
if request.method == 'POST':
# check for dietary restrictions
restrictions = request.POST.getlist('restrictions')
# format input ingredients
raw_input = request.POST['ingredients']
...
return render(request, 'recipes/recipes.html', {'results': results})
else:
return render(request, 'recipes/recipes.html', {'error': 'Please search for a recipe'})
#csrf_protect
def add(request):
if request.method == 'POST':
title = request.POST['title']
image = request.POST['image']
source = request.POST['source']
user = request.user
try:
recipe = Recipe.objects.get(image=image, title=title, source=source)
except ObjectDoesNotExist:
recipe = Recipe.objects.create(image=image, title=title, source=source)
finally:
recipe.users.add(user)
recipe.save()
return JsonResponse({'success': True})
else:
return JsonResponse({'success': False})
urls.py
urlpatterns = [
path('add/', views.add, name='add'),
path('', views.recipes, name='recipes'),
]
html file
{% block content %}
...
{% if user.is_authenticated %}
<form action="{% url 'add' %}" id="add_form" method="POST">
{% csrf_token %}
<input type="hidden" id="image" name="image" value="{{ result.image }}">
<input type="hidden" id="title" name="title" value="{{ result.title }}">
<input type="hidden" id="source" name="source" value="{{ result.source }}">
<button type="submit" name="recipe" class="btn btn-sm btn-outline-secondary">Save</button>
</form>
{% endif %}
{% endblock %}
{% block javascript %}
<script type="text/javascript">
$(document).ready(function() {
$("#add_form").on('submit', function(e) {
e.preventDefault();
console.log( "form submitted");
$.ajax({
type:'POST',
async: 'false',
cache: 'false',
url:'{% url 'add' %}',
data:{
image:$('#image').val(),
title:$('#title').val(),
source:$('#source').val(),
},
success:function(){
alert("Saved Recipe")
}
})
})
})
</script>
I'm expecting any clicks to the save button to stay on the same page without a reload and to call views.add which will save/create an object to the database
Change the url to $(this).attr('action'). It will automatically get the url from the form instead of explicitly defining it again while making ajax call.
$.ajax({
type:'POST',
async: 'false',
cache: 'false',
url: $(this).attr('action'),,
data:{
image:$('#image').val(),
title:$('#title').val(),
source:$('#source').val(),
},
success:function(){
alert("Saved Recipe")
}
})
Related
I'm using ajax to submit a form, and it's working. But it's not working asynchronously. When I'm trying to upload files it uploaded successfully and then the page loads again. I want it to make asynchronously. In addition, I want to make a progress bar too. But things not working as I expected.
my forms.py
from django import forms
from .models import Comment
from .models import post
class UploadForm(forms.ModelForm):
class Meta:
model = post
fields = ('image', 'video', 'content',)
my views.py
def django_image_and_file_upload_ajax(request):
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
form.instance.author = request.user
form.save()
return JsonResponse({'error': False, 'message': 'Uploaded Successfully'})
else:
return JsonResponse({'error': True, 'errors': form.errors})
else:
form = UploadForm()
return render(request, 'blog/upload.html', {'form': form})
and my upload.html
{% extends "blog/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<main>
<div class="container">
<div class="row">
<div class="col-md-8 my-5">
<div class="content-section" style="padding:10px 20px">
<form
enctype="multipart/form-data"
id="id_ajax_upload_form" method="POST"
novalidate="">
<fieldset class="form-group">
{% csrf_token %}
<legend class="border-bottom mb-4"><i class="fas fa-feather-alt text-muted mr-2"></i>Create a post</legend>
{{ form.as_p }}
</fieldset>
<div class="form-group">
<input type="submit" />
</div>
</form>
</div>
</div>
</div>
</div>
</main>
{% endblock content %}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">
// form upload
$('#id_ajax_upload_form').submit(function(e){
e.preventDefault();
$form = $(this)
var formData = new FormData(this);
$.ajax({
url: window.location.pathname,
type: 'POST',
data: formData,
success: function (response) {
$('.error').remove();
console.log(response)
if(response.error){
$.each(response.errors, function(name, error){
error = '<small class="text-muted error">' + error + '</small>'
$form.find('[name=' + name + ']').after(error);
})
}
else{
alert(response.message)
window.location = ""
}
},
cache: false,
contentType: false,
processData: false
});
});
// end
where i get wrong?
Ok, Got it! But anyone know about how to add a progressbar will be helpful.
I have a form that I am trying to implement with Ajax, after inputing some content on the textbox, when I click on the sumbit button using my mouse, everything works fine (page didnt refresh, data posted on database, and new data displayed on the page). But when I try to submit data by pressing enter, the page is displaying only the serialized data from my form (nothing more, html file do not work)and it says in the console:
Resource interpreted as Document but transferred with MIME type application/json: "http://localhost:8000/try/".
current page looks exactly like this after pressing enter button(nothing more):
{"task": {"id": 52, "todo": "wws", "author": 1, "completed": false}}
these are the data that came from my form.
this is my views.py
class TodoList(View):
def get(self, request):
todo_list = ToDo.objects.filter(author_id=request.user.id)
form = AddTodo()
context = {
'todo_list': todo_list,
'form': form,
}
return render(request, 'trylangto/try.html', context)
def post(self, request):
form = AddTodo(request.POST)
if form.is_valid():
form.instance.author_id = request.user.id
new_form = form.save()
return JsonResponse({'task': model_to_dict(new_form)}, status=200)
else:
return redirect('trylang:todo-list')
return redirect('trylang:todo-list')
this is my main.js:
$(document).ready(function(){
$("#createbutton").click(function(){
var formdata = $("#addtodoform").serialize();
$.ajax({
url: $("#addtodoform").data('url'),
data: formdata,
type: 'post',
success: function(response){
$("#tasklist").append('<div class="card mb-1" ><div class="card-body">'+ response.task.todo +'<button type="button" class="close"><span aria-hidden="true">×</span></button></div></div>')
}
});
});
});
and here is my template:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
{% if user.is_authenticated %}
<div class="media content-section">
<form class="media-body pt-3" method="POST" id="addtodoform" data-url="{% url 'trylang:todo-list' %}">
{% csrf_token %}
<legend><h5>add new tasks</h5></legend>
<fieldset class="form-group ">
{{ form|crispy}}
</fieldset>
<button class="btn btn-outline-info float-right" type="button" id="createbutton">add</button>
</form>
</div>
{% endif %}
<div id="tasklist">
{% for task in todo_list %}
<div class="card mb-1" >
<div class="card-body">
{{task.todo}}
<button type="button" class="close">
<span aria-hidden="true">×</span>
</button>
</div>
</div>
{% endfor %}
</div>
{% endblock %}
UPDATE: I am including my models.py just in case
class ToDo(models.Model):
todo = models.CharField(max_length=500)
date_posted = models.DateTimeField(default=timezone.now, editable=False)
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='users')
completed = models.BooleanField(default=False)
this is what I get on the console when I use console.log(formdata):
csrfmiddlewaretoken=N91OUGSd6kBFkB1E2DMZuMW6qJD7cc3frPprwGMLTuupcg6PKYtt44jA4z5Lx6xX&todo=this%20is%20the%20content%20of%20my%20textbox
You don't need model_to_dict. So, try:
if form.is_valid():
form.instance.author_id = request.user.id
new_form = form.save()
return JsonResponse({'task': new_form}, status=200)
else:
return redirect('trylang:todo-list')
return redirect('trylang:todo-list')
EDIT
You are submitting the form not through ajax. When you click button it was submitting the form. So, e.preventDefault() should do the job.
$(document).ready(function(){
$("#createbutton").click(function(e){
e.preventDefault(); <--- You were missing this
var formdata = $("#addtodoform").serialize();
$.ajax({
url: $("#addtodoform").data('url'),
data: formdata,
type: 'post',
success: function(response){
$("#tasklist").append('<div class="card mb-1" ><div class="card-body">'+ response.task.todo +'<button type="button" class="close"><span aria-hidden="true">×</span></button></div></div>')
}
});
});
});
Hello i need your help i need to disable this button send when the required field are empty. I am a beginner using django and i don't know how to resolve it. Please i need your help .. i lost my time trying to find a solution.
Views.py:
def contact(request):
form = FeedbackForm(request.POST or None)
if form.is_valid():
recaptcha_response = request.POST.get('g-recaptcha-response')
url = 'https://www.google.com/recaptcha/api/siteverify'
values = {
'secret': settings.GOOGLE_RECAPTCHA_SECRET_KEY,
'response': recaptcha_response
}
data = urllib.urlencode(values).encode()
req = urllib2.Request(url, data=data)
response = urllib2.urlopen(req)
result = json.loads(response.read().decode())
''' End reCAPTCHA validation '''
if result['success']:
form.save()
message = u'You have feedback\nName: %s\nEmail: %s\nPhone: %s\nCountry: %s\nFeedback:\n%s' % (
form.cleaned_data['name'],
form.cleaned_data['email'],
form.cleaned_data['phone'],
form.cleaned_data['country'],
form.cleaned_data['feedback'])
try:
send_mail('NEW FEEDBACK', message, '', settings.DEFAULT_FROM_EMAIL) # to admin
send_mail('THANK YOU for contacting us', 'We will be back to you promptly.', '', [form.cleaned_data['email'],]) # to user
messages.info(request, 'SUCCESS! Your message has been sent!')
form = FeedbackForm()
except:
messages.info(request, 'Sorry, can\'t send feedback right now.')
else:
messages.error(request, 'Invalid reCAPTCHA. Please try again.')
return render(request, 'contact.html', {'active_page':'contact','form': form,})
Contact.html:
<html>
<div class="col-md-6">
<form role="form" class="form" method="post">
{% csrf_token %}
{% for field in form %}
<label for="{{ field.label }}">{{ field.label_tag }}
{% if field.field.required %}<span class="red">*</span>{% endif %}</label>{{ field.errors }}{{ field }}
{% endfor %}
<p><span class="redText">*</span> Indicates a required field</p>
<script src='https://www.google.com/recaptcha/api.js'></script>
<div class="g-recaptcha" data-sitekey=""></div>
<input type="submit" value="Send" class="btn btn-lg">
</form>
The best way to do this would be to use JavaScript and jQuery.
In this example, when you click your button you can make sure the form is valid before it submits.
$(".validate").on("click", function () {
if (!valid()) {
alert("You are missing required fields.");
return false;
}
else {
return confirm("This will submit the form. Are you sure?");
}
});
function valid() {
return false;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="submit" class="validate" value="Send" class="btn btn-lg">
This code adds a class to your button. The jQuery listens for the click, then makes a JavaScript function that would check if it is valid. If it is not, it displays an alert. If it is, it displays a confirm message.
There are many other ways to do this with JS though.
I am really new to django, and I'm not sure I completely understand how forms work or how to use them. I've been looking through a couple tutorials concering file-uploading, but there are a lot of different ways it seems. I'll just include the whole process from top to bottom.
I get a 500 error, because form.is_valid does not return true.
I'll be really gratefull for any help/tips :)
profile.html
<form role="form" enctype="multipart/form-data" ng-submit="profile.upload_picture()">
<input id="id_image" type="file" class="" name="image" ng-model="profile.image">
<input type="hidden" value="{{ profile.user.email }}" ng-model="profile.email">
<button type="submit" class="btn btn-primary">Submit</button>
</form>
ProfileController.js
function upload_picture() {
ProfileHandler.setProfilePicture(vm.image, vm.email);
}
ProfileHandler.js
function setProfilePicture(profile_pic, email) {
return $http.post('/api/v1/profile/picture/', {
profile_pic: profile_pic,
email: email
}).then(imageSuccessFn, imageErrorFn);
}
ProfilePictureView
class ProfilePictureView(views.APIView):
def post(self, request):
if request.method == 'POST':
form = ProfileImageForm(request.POST, request.FILES)
if form.is_valid():
str_data = request.body.decode('utf-8')
data = json.loads(str_data)
email = data.get('email', None)
acc = Account.objects.get(email=email)
acc.model_pic = form.cleaned_data['image']
acc.save()
return Response({
'status': 'Accepted',
'message': 'Image uploaded.'
}, status=status.HTTP_202_ACCEPTED)
else:
return Response({
'status': 'Internal server error',
'message': 'Form not valid'
}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
else:
return Response({
'status': 'Method not allowed',
'message': 'Only post is accepted'
}, status=status.HTTP_405_METHOD_NOT_ALLOWED)
ProfileImageForm
class ProfileImageForm(forms.Form):
image = forms.FileField(label='Select a profile Image')
AccountModel
class Account(AbstractBaseUser):
....
image = models.ImageField(upload_to='profile_images', blank=True)
....
Urls (excluded some urls)
urlpatterns = patterns(
'',
url(r'^api/v1/', include(router.urls)),
.....,
url(r'^api/v1/profile/picture/$', ProfilePictureView.as_view(), name='profile'),
url('^.*$', IndexView.as_view(), name='index'),
) + static(settings.MEDIA_URL, document_root=settings.MEDIA_URL);
Settings.py
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
You don't have a csrf token input in your form (documentation). Also, add method="post" attribute to the form.
<form role="form" method="post" enctype="multipart/form-data" ng-submit="profile.upload_picture()">
{% csrf_token %}
<input id="id_image" type="file" class="" name="image" ng-model="profile.image">
<input type="hidden" value="{{ profile.user.email }}" ng-model="profile.email">
<button type="submit" class="btn btn-primary">Submit</button>
</form>
You need to add {% csrf_token %} in your form in the profile.htm file.
<form>
{% csrf_token %}
</form>
What this line will do is to add an hidden input field inside your form which contains a token. The token is submitted along with the form, and Django automatically checks the value of the token to see if this post request is actually coming from your "frontend" rather than somebody else's.
Also, add method="post":
<form method="post">
</form>
I am using a django form to update some details. once the save changes is clicked the ajax call is made, view executed, ajax response sent, success code executed n the page reloads again. n on reload as form is no longer valid, the else part of from.is_valid() is executed.
if form.is_valid:
#do something
return HttpResponse(simplejson.dumps(response), mimetype='application/json')
else:
#do something
return render_to_response('ts.html', {'form': form}, context_instance = RequestContext(request))
I want to avoid the page reload on the successful form submission and jus return ajax response. How can achieve that?
I have changed my code and here is the view, template and jquery.
if request.POST:
if form.valid():
if credentials.correct():
resp = {'success': True}
return HttpResponse(simplejson.dumps(resp), mimetype='application/json')
else:
resp = {'success': False}
return HttpResponse(simplejson.dumps(resp), mimetype='application/json')
else:
print "error in form"
return render(request, 'manage_accounts.html', {'creds': creds, 'form': form})
else:
form = SomeForm()
return render(request, 'manage_accounts.html', {'creds': creds, 'form': form})
Template
<form action="/accounts/" method="POST" id="bitlychangeform">
<div id="ajaxwrapper">
{% csrf_token %}
{{ form.non_field_errors }}
<!--{% include "Change_bitly_form.html" %}-->
{{ form.as_p }}
<div class="clearfix">
<label></label>
<div class="input" style="float:right">
<input type="submit" id="save" value="Save Changes" class="btn btn-primary "/>
</div>
</div>
</div>
</form>
Jquery:
$(function(){
var form = $('#bitlychangeform');
form.submit(function(e) {
jQuery("#save").attr('disabled', true)
jQuery("#ajaxwrapper").load(
form.attr('action') + ' #ajaxwrapper',
form.serializeArray(),
function(data) {
jQuery("#save").attr('disabled', false)
alert(data);
});
return false;
e.preventDefault();
});
});
The page reload doesn't occur when i use Django form as {{ form.as_p }} but when i use a "custom template for form with fields as password char fields with ** and editable only after one clicks edit button", the form.valid() returns false.
I require that the django form functions as my custom template for form as i desire. Can anyone guide me.
You can handle this in your JavaScript by returning false on submission of form.
form.submit(){
#do something
#make AJAX call
#do something
return false;
}
I would guess you didn't override the default form submission behavior on the front end, and you are submitting your form normally.
Make sure that you supress the default behavior of form submission.
This page
provides a great example under templates/contact/form.html