After almost a full day trying to make things work, I resign... I need some help because I don't understand where I'm doing things the wrong way.
I searched SO and stumbled on many answers about redirection and so on...
Let me give you some context :
I have a simple form where one can upload a file, so far, it's working well. I want the user to be redirected to another page after the upload is successful and this is where it fails :'(
My views.py :
# Create your views here.
def thank_you(request):
data = {'text': 'Thank you for your file'}
print('thank you blablabla')
print(data)
return render(request, 'app/thank_you.html', data)
def home(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
uploaded_file = request.FILES['file']
new_file = UploadFile(file=uploaded_file)
new_file.save()
return redirect(reverse(thank_you))
else:
form = UploadFileForm()
data = {'form': form}
return render(request, 'app/drop_file.html', data)
I do see the 2 prints in the 'thank_you' function, meaning that the redirect is working as expected. But the view doesn't refresh and I'm stuck.
And if I try to access the url directly (going to http://.../thank_you/ ) it does show correctly.
The thank_you.html :
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upload done</title>
</head>
<body>
<h1>Hello !</h1>
<h1>{{ text }} </h1>
</body>
</html>
The form looks like that :
<form id="my-dropzone" class="dropzone" action="{% url 'home' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
</form>
<button id="submit-all">
Submit all files
</button>
<script type="text/javascript">
Dropzone.options.myDropzone = {
// Prevents Dropzone from uploading dropped files immediately
autoProcessQueue : false,
init : function() {
var submitButton = document.querySelector("#submit-all")
myDropzone = this;
submitButton.addEventListener("click", function() {
myDropzone.processQueue();
// Tell Dropzone to process all queued files.
});
// You might want to show the submit button only when
// files are dropped here:
this.on("addedfile", function() {
// Show submit button here and/or inform user to click it.
});
this.on("queuecomplete", function() {
console.log('should we redirect ?');
});
}
};
</script>
And my urls.py file :
url(r'^$', views.home, name='home'),
url(r'thank_you/$', views.thank_you, name='thank_you'),
Nothing out of the ordinary I guess.
Even if I change the 'home' to :
if form.is_valid():
uploaded_file = request.FILES['file']
new_file = UploadFile(file=uploaded_file)
new_file.save()
data = {'text': 'Thank you for your file'}
print('thank you blablabla')
print(data)
return render(request, 'app/thank_you.html', data)
The view doesn't update, I do see the prints in the django console but the template does not render...
I would like the return render(request, 'app/thank_you.html', data) to actually refresh the page after the form is submitted but I can't achieve to do so :( Any help ?
Firebug show me this in the console :
console output
Related
I am trying to display a comment form on a page. So far I have created a link and I want that each time that link is clicked it displays the form on the same page as where the link is but my problem here is that the link redirects me to another page, which I don't want.
urls.py
url(r'^all/$', 'posts.views.articles'),
url(r'^get/(?P<post_id>\d+)/$', 'posts.views.article'),
url(r'^articles/$', 'posts.views.create'),
url(r'^like/(?P<post_id>\d+)/$', 'posts.views.like_article'),
url(r'^article/(?P<post_id>\d+)/$', 'posts.views.add_comment'),
views.py
def articles(request):
args = {}
args.update(csrf(request))
args ['posts'] = post.objects.filter(user = request.user)
args ['full_name'] = User.objects.get(username = request.user.username)
args ['form'] = PostForm()
return render_to_response('articles.html', args)
def article(request, post_id=1):
return render(request, 'article.html',
{'post': post.objects.get(id=post_id) })
def add_comment(request, post_id):
a = post.objects.get(id=post_id)
if request.method == "POST":
f = CommentForm(request.POST)
if f.is_valid():
c = f.save(commit=False)
c.pub_date = timezone.now()
c.article = a
c.save()
messages.success(request, "You Comment was added")
return HttpResponseRedirect('/posts/get/%s' % post_id)
else:
f = CommentForm()
args = {}
args.update(csrf(request))
args['post'] = a
args['form'] = f
return render_to_response('article.html', args)
#return HttpResponseRedirect('/posts/all')
article.html
<h2>Comments</h2>
{% for c in post.comment_set.all %}
<p>{{c.name}} : {{c.body}}</p>
{% endfor %}
<form action="/posts/article/{{post.id}}/" method="post">{% csrf_token %}
<ul>
{{form.as_ul}}
</ul>
<input type="submit" name="submit" value="Post Comment">
</form>
{% endblock %}
As from your question you want submit a comment in your article and when you submit the comment you want to redirect it to the same article page... If you are willing to do this then here is example:
First create a comment submit form either using model form or just form:
class CommentCreationForm(forms.ModelForm):
class Meta:
model = Comment
fields = ('comment_body',) # Set your field for comment
Now pass this form as context in ur article view. Like you did above.
def articles(request):
args = {}
args.update(csrf(request))
args ['posts'] = post.objects.filter(user = request.user)
args ['full_name'] = User.objects.get(username = request.user.username)
args ['comment_form'] = CommentCreationForm
return render_to_response('articles.html', args)
Your article.html
<h2>Comments</h2>
{% for c in post.comment_set.all %}
<p>{{c.name}} : {{c.body}}</p>
{% endfor %}
<form action=""{% url "comment_submit" post.id %}"" method="get">{% csrf_token %}
<ul>
{{form.as_ul}}
</ul>
<input type="submit" name="submit" value="Post Comment">
</form>
{% endblock %}
Catch the url with url(r'^comment/(?P<pk>\d+)/',CommentSubmitView, name="comment_submit"), and write a view.
def AnswerSubmitView(request, pk):
post = Post.objects.get(id=pk) # Get what you have set for your article
comment_text = request.GET.get('comment_body',False)
com = Comment()
post = post # or anything that you have named for your article..
com.comment_body = comment_text
com.save()
return HttpResponseRedirect('/post/%s' % pk) # Your url for your article I guess...
Enjoy...
Use an ajax call to fetch the form from the server without refreshing the page. This requires jQuery. Replace the placeholder selectors I've used with whatever you need for your app. I'd recommend wrapping all of article.html in a div and give that an id tag (and refer to this tag where I use '#form-id' selector below), so you know when the form is already displayed and you can access the entire chunk.
Also note that I'm not entirely sure how to get the html from render_to_response. Just figure out what kind of object is sent back to the ajax caller and how to get the html from that object. Shouldn't be hard.
Adapt and add the following to the bottom of the template containing the link to add the form
<script>
var showForm = function(url) {
$.ajax({
type: 'GET',
dataType: 'json',
url: url,
success: function(data, status, xhr) {
// Not positive if this is how things work with render_to_response
// I usually use render_to_string for this and just return pure HTML
$('#div-to-display-form-in').append(data);
},
error: function(error) {
// Handle error
}
});
}
$(document).ready(function() {
$('#link-to-show-form').click(function(event) {
event.preventDefault();
// The conditionals check if form is already showing
// If form already showing and link clicked again, form is removed
if ($('#form-id').length === 0) {
showForm($(this).attr('href'));
} else {
$('#form-id').remove();
}
});
});
</script>
I'm trying to upload multiple files in one request using dropzone.js. When I set the uploadMultiple option to true in dropzone, one request containing both files is sent to my view, but form validation fails.
Here is my Django form:
class UploadForm(forms.Form):
data = forms.IntegerField(widget=forms.HiddenInput())
file = forms.FileField()
My view:
def upload(request):
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES)
print request.FILES
if form.is_valid():
for file in request.FILES.getlist('file'):
print str(file)
else:
print form.errors
else:
form = UploadForm(initial={'data': 5})
return render(request, 'upload.html', {
'form': form
})
and my template:
<script type="text/javascript">
Dropzone.options.myAwesomeDropzone = {
autoProcessQueue : false,
uploadMultiple: true,
init : function() {
myDropzone = this;
this.element.querySelector("input[type='submit']").addEventListener('click', function(event) {
event.preventDefault();
event.stopPropagation();
myDropzone.processQueue();
});
}
}
</script>
<form id='my-awesome-dropzone' class="dropzone"
action="{% url 'upload.views.upload' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.data }}
<input type="submit" value="Upload" />
</form>
I see that request.FILES has two files:
<MultiValueDict: {u'file[]': [<InMemoryUploadedFile: Forest Flowers.jpg (image/jpeg)>,
<InMemoryUploadedFile: Forest.jpg (image/jpeg)>]}>
I guess the issue is Django doesn't recognize file[]. It expects file instead. How can I get Django to recognize the two uploads?
You are right assuming that the validation error is originated because of the input name dropzone.js sends to the server. The "file[n]" pattern when your Django form is expecting a field named "file" throws a validation error (required field).
In Dropzone.js you can specify the parameter "paramName" and this object property also accepts a function instead of a simple string, so if you set your paramName to something like:
...
paramName: function(){
return "file";
}
...
the name of the field sent to server doesn't change and you get a "file" field in request.FILES that is a dict with one element (file) that is an array of files as it is expected.
You just need to do:
file1 = request.FILES.get(file[][0], None) # For the first file
file2 = request.FILES.get(file[][1], None) # For the second file
... and so on...
Hope that helps.
I am using django 1.5, python 2.7 and jquery 1.9. I have a form which has precisely 2 fields i.e. title and document. When I press submit I want the users chosen document to be present in the request.FILES as shown in the view.
When I submit the regular form (without ajax), this works fine, but with ajax I do not get the file field in my request. Any suggestions on how to upload a file using ajax.
HTML:
<form enctype="multipart/form-data" action="{% url 'upload_document' %}" method="post" id="uploadForm">
{% csrf_token %}
<ul>
<li>
<div>Title</div>
<input id="title" type="text" maxlength="200"/>
<div class="error"></div>
</li>
<li>
<div>Upload File</div>
<input id="document" type="file" size="15" />
<div class="error"></div>
</li>
</ul>
<input type="submit" value="submit"/></p>
</form>
FORMS.PY:
class UploadForm( forms.Form ):
document = forms.FileField()
title = forms.CharField(max_length = 200)
def clean(self):
cleaned_data = super(UploadForm, self).clean()
return cleaned_data
def save(self, *args, **kwargs):
title = self.cleaned_data['title']
doc = self.cleaned_data['document']
document = Document(title = title, document = doc)
document.save()
return document
SCRIPT:
<script type="text/javascript">
$("#uploadForm").submit(function(event){
event.preventDefault();
$.ajax({
url : "{% url 'upload_document' %}",
type: "POST",
data : {csrfmiddlewaretoken: document.getElementsByName('csrfmiddlewaretoken')[0].value,
title: document.getElementById('title').value,
//document: document: document.getElementById('document'),
},
dataType : "json",
success: function( response ){
if(response == "True"){
// success
}
else {
//append errors
}
}
});
});
</script>
VIEWs.PY
def upload_document(request):
print request.POST
print request.FILES
if request.is_ajax():
if request.method == 'POST':
form = UploadForm(request.POST, request.FILES, user = request.user)
if form.is_valid():
form.save()
return HttpResponse(simplejson.dumps('True'), mimetype = 'application/json' )
else:
errors = form.errors
return HttpResponse(simplejson.dumps(errors), mimetype = 'application/json' )
The answer to that question is not that simple. First of all if you intend to support old browsers then indeed it gets nasty. You have to deal with hidden iframes and some JavaScript tricks. I do advice using some well-known scripts for that like jQuery-File-Upload.
But the world is evolving and new technologies arise including HTML5. There's a new File API which is available in most modern browsers ( IE10+, FireFox3.6+, Chrome13+, see: http://caniuse.com/fileapi ) which can be used for that. First you need some HTML:
<input type="file" id="file-select" />
Then you can bind to (for example) change event:
$('#file-select').change( handleFileSelect );
and finally the handler itself:
var data = {};
function createReaderHandler(name) {
return function(ev) {
data[name] = ev.target.result;
};
}
function handleFileSelect(ev) {
var files = ev.target.files; // FileList object
// Loop through the FileList
for (var i = 0; i < files.length; i++) {
var file = files[i],
name = file.name || file.fileName,
reader = new FileReader();
reader.onload = createReaderHandler(name);
reader.readAsText(file);
}
}
Once the data is loaded into JavaScript memory (note that the operation is asynchronous) you can send it via AJAX like any other data. There are more options: depending on your file you can read it as a binary data using .readAsBinaryString and so on. Google is your friend. :)
Also I think there already are good scripts for uploading files with a fallback to old methods. This one can be interesting (haven't tried it):
http://www.plupload.com/
I think the issue is in the submit button, change it into normal button
ie, <button type='button' id='submit'>submit</button>(by default all buttons in form are submit)
and the ajax as
$('#submit').on('click',function(){
frm = $(this).parents('form')
$.ajax({
type: frm.attr('method'),
dataType:'json',
url: frm.attr('action'),
data: frm.serialize(),
async: false,
success: function (data) {
console.log('success')
},
error: function(data) {
console.log("Something went wrong!");
}
})
All others will be same
Just try it will work
I'm working on a simple app that act similar to wikipedia using the tutorial "create a wiki in 20 minutes " from showmedo .
The app works by you create a page e.g dog if it doesn't exist then you add all the info about dogs and it display the dog and details of the dog.
When I try to edit the same page . the page doesn't get updated on the main page either does it get updated on my admin page.
I think the problem is with my save_page function and edit_page function.
So when I create a page , it works
but when I try to retrieve the page and save it . It doesn't save .
The only method that works is editing the page by admin.
I think the problem is here
def save_page(request, page_name):
content = request.POST.get('content', 'this is the default')
try:
page = Page.objects.get(pk=page_name)
page.content = content
This is the website
http://tafe.pythonanywhere.com/wikicamp/Dogs/
My views.py
from wiki.models import Page
from django.shortcuts import render_to_response
from django.http import HttpResponseRedirect
from django.template import RequestContext
def view_page(request,page_name):
try:
page = Page.objects.get(pk=page_name)
except Page.DoesNotExist:
return render_to_response("create.html",{"page_name":page_name})
content = page.content
return render_to_response("view.html",{"page_name":page_name , "content":content}, context_instance=RequestContext(request))
def edit_page(request,page_name):
try:
page = Page.objects.get(pk=page_name)
content = page.content
except Page.DoesNotExist:
content = ""
return render_to_response("edit.html",{"page_name":page_name, "content":content}, context_instance=RequestContext(request))
def save_page(request, page_name):
content = request.POST.get('content', 'this is the default')
try:
page = Page.objects.get(pk=page_name)
page.content = content
except Page.DoesNotExist:
page = Page(name=page_name, content=content)
page.save()
return HttpResponseRedirect("/wikicamp/" + page_name + "/")
My create.html
<html>
<head>
<title>{{page.name}} - Create </title>
</head>
<body>
<h1>{{page_name}} </h1>
This page does not exist. Create?
</body>
</html>
My edit.html
<html>
<head>
<title>{{page_name - Editing</title>
</head>
<body>
<h1>Editing {{page_name}} </h1>
<form method = "post" action="{% url wiki:save page_name %}"> {% csrf_token %}
<textarea name="content" rows="20" cols="60"> {{content}}
</textarea><br/>
<input type="submit" value="Save Page"/>
</form>
</body>
</html>
My view.html
<html>
<head>
<title>{{page_name}}</title>
</head>
<body>
<h1>{{page_name}} </h1>
{{content}}
<hr/>
Edit this page ?
</body>
</html>
try:
page = Page.objects.get(pk=page_name)
page.content = content
except Page.DoesNotExist:
page = Page(name=page_name, content=content)
page.save()
Two problems I see here. First, you are trying to retrieve the page by the page_name as the primary key, when you should be searching on the name attribute, and secondly after you have fetched the page successfully and updated its content, you forget to save it.
Since this is a common pattern, there is a shortcut in django get_or_create, it works like this:
page, created = Page.objects.get_or_create(name=page_name)
if created:
# new page was created
else:
# existing page was retrieved
In your scenario, you just want to fetch and update the contents in either scenario. So we don't need to use the created variable:
page, created = Page.objects.get_or_create(name=page_name)
page.content = content
page.save()
You're not saving the page in save_page, only when it doesn't exist. Try something like:
def save_page(request, page_name):
content = request.POST.get('content', 'this is the default')
try:
page = Page.objects.get(pk=page_name)
page.content = content
page.save()
except Page.DoesNotExist:
page = Page(name=page_name, content=content)
page.save()
return HttpResponseRedirect("/wikicamp/" + page_name + "/")
This is a quick ugly fix, I recommend looking into forms and class based views.
Two things I noticed with this:
try:
page = Page.objects.get(pk=page_name)
page.content = content
except Page.DoesNotExist:
page = Page(name=page_name, content=content)
page.save()
1) in the try block you are querying by pk and in the except you are setting the name.
2) you are not saving in the try block.
try this:
try:
page = Page.objects.get(name=page_name)
except Page.DoesNotExist:
page = Page(name=page_name)
page.content = content
page.save()
you forgot to put save()
def save_page(request, page_name):
content = request.POST.get('content', 'this is the default')
try:
page = Page.objects.get(pk=page_name)
page.content = content
page.save()
except Page.DoesNotExist:
page = Page(name=page_name, content=content)
page.save()
return HttpResponseRedirect("/wikicamp/" + page_name + "/")
in views:
return render_to_response("main.html", RequestContext(request, {'form':form, "result":result}))
in template i have this jquery function:
$('#submitButton').click(function(e) {
e.preventDefault();
var dataPosted = $("#mainSubmit").serialize();
$.ajax({
type: "POST",
data: dataPosted,
url: 'main/',
success: function(data) {
$("#mainDiv").html(data);
$(".response").html({{ result }});
$(".response").show();
}
});
});
});
<div id="mainDiv" class="part">
<form id="mainSubmit" action="main/" method="POST" name="submitForm">
{% csrf_token %}
{{ form.non_field_errors }}
{{ form.as_p }}
<input type="submit" value="Submit" id="submitButton"/>
<div class="response" style="display: none;"></div>
</form>
</div>
but it seems that data can't be assigned to response div like this(it seems data is not defined). So i don't know what is the way to send data to template. if i use Httpresponse(result) in views, then i can't have my form refreshed, and only i can display in response div data i send from view. so what is the way?
EDIT:
This is my views. before, i didn't put else for the condition if form.is_valid(): , but here i use, because i think if i don't do this, it might cause some probables. i don't know what is the best way.
def mainFunc(request):
if request.is_ajax():
if request.method == 'POST':
form = mainForm(request.POST)
if form.is_valid():
// process the form
result = "successful"
to_json = {'form':form, 'result':result}
return HttpResponse(json.dumps(to_json), mimetype='application/json')
else:
result = ""
to_json = {'form':form, 'result':result}
return HttpResponse(json.dumps(to_json), mimetype='application/json')
else:
form = mainForm()
return render_to_response('main.html', RequestContext(request, {'form':form}))
else:
return render_to_response("ajax.html", {}, context_instance=RequestContext(request))
You need to return a response in format like JSON
You can use this snippet or more simple code like this:
from django.utils import simplejson
to_json = {'form':form, "result":result}
return HttpResponse(simplejson.dumps(to_json), mimetype='application/json')
Then you will be able to use data.result and data.form in your JS code.
If you use the same view for ajax and non-ajax call you can check for it with request.is_ajax()
Also you will not be able to use template tags and filters in your JS callback. So you need to pre-render your form before sending it via JSON
So the final code:
to_json = {'form':form, "result":result}
if request.is_ajax():
to_json['form'] = to_json['form'].as_p()
return HttpResponse(simplejson.dumps(to_json), mimetype='application/json')
else:
render_to_response("main.html", RequestContext(request, {'form':form, "result":result}))
Edit I assume that ajax.html is the template for the whole page and main.html is the template for mainDiv part of the page
So in is_ajax() part of you view you can return the data like this.
to_json = {}
to_json['form'] = render_to_string('main.html', {'form': form}, context_instance=RequestContext(request))
to_json['result'] = result
return HttpResponse(json.dumps(to_json), mimetype='application/json')
And you always return data like this, both for GET and POST AJAX calls
And in JS you get data like this:
$("#mainDiv").html(data.form);
$(".response").html(data.result);
$(".response").show();