Here is one of my Django views:
def index(request):
message = request.GET.get('message', '')
context = RequestContext(request, {
'message': message
})
return render(request, 'bets/index.html', context)
My goal is to display message in template only once, and not to display it on page reload. I tried this:
request.GET['message'] = ''
but get error "This QueryDict instance is immutable". How do I remove key from QueryDict?
Even if you could remove that value from the querydict, that wouldn't help because it is based on the URL you've used to request that view, so when you refresh you're going to be using the same URL again with the existing parameters.
Rather passing the message value in the GET parameters, put it in the session, and use request.session.pop('message') in the index view.
Even better, use the built-in messages framework which does all that for you.
#Daniel Rosemans answer is correct, in that you should use session to store value.
However, to answer your original question in regards how to remove, GET and POST parameters are immutable. You can not change these querydicts. If you want to remove something from them (say, to prevent key from being iterated over) you need to make copy of the QueryDict and then pop item from it.
https://docs.djangoproject.com/en/3.1/ref/request-response/#querydict-objects
The QueryDicts at request.POST and request.GET will be immutable when
accessed in a normal request/response cycle. To get a mutable version
you need to use QueryDict.copy().
If you don't want to fix this problem in frontend, and still need Django's components like forms, this snippet may help.
fetch('', {
"method": "POST",
"headers": {
"X-CSRFToken": csrfToken,
"content-type": "application/json",
},
"body": JSON.stringify({
"csrfmiddlewaretoken": "{{ csrf_token }}",
"username": document.querySelector("#id_username").value,
"password": document.querySelector("#id_password").value,
})
}).then(res => res.json()).then(data => processData(data))
def fix_fetched_post(request):
try:
# fetch
post_data = json.loads(request.body.decode("utf-8"))
request.POST._mutable = True
for key, value in post_data.items():
request.POST[key] = value
request.POST._mutable = False
except:
# form
pass
return request
Related
I have a profile page with user posts. People can like/dislike the posts. It works well but it reloads the page, that is why I am implementing AJAX.
The route goes like this.
Inside the profile view is the "like POST" request ending with a
data = {
'likes':post.likes.all().count()
}
return JsonResponse(data,safe=False)"
When clicking on the like button, I can see the data on a blank page if I want to. So I know it is receiving it.
Unfortunately, when using AJAX, instead of returning the data. it returns the profile view's
return render(request, "profile.html", context)
Here is my AJAX code
const post_id = $(this).attr('id')
const likeText = $( `.like_button${post_id} `).attr('name')
const trim = $.trim(likeText)
const url = $(this).attr('action')
let res;
const likes = $(`.like_count${post_id}`).text() || 0
const trimCount = parseInt(likes)
$.ajax({
type: 'POST',
url: url,
data: {
'csrfmiddlewaretoken':$('input[name=csrfmiddlewaretoken]').val(),
'post_id':post_id,
},
success: function(data){
console.log('success', data)
},
error: function(data){
console.log('error', data)
}
})
Any help would be much appreciated. First time using AJAX, so as much details would be appreciated.
Not really surprising it's returning HTML since that's exactly what you're returning in the view: rendered HTML. If you want JSON you need then you want a JSONResponse object.
https://docs.djangoproject.com/en/4.1/ref/request-response/#jsonresponse-objects
So, instead of:
return render(request, "profile.html", context)
which will take the profile.html, inject the values from context and send you that as html, you should do something like:
response = JsonResponse({"mydata": "goes here", "some_more": data})
You can then parse this as JSON in your AJAX code. If it's the case that the context dictionary contains all the data you need and that is what you want, you can just swap out that one line:
response = JsonResponse(context)
Edit: To address the question in the comment. Suppressing the default form response in the frontend is not a Django thing, it's done with JS on the event using something like:
e.preventDefault()
See here for info: https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault
I am trying to pre populate the form to the edit entry, but I am having a problem to assign the right variable.
I did a test using just a text to populate it, and it works. But could somebody give a hint in how to do it retrieving the content and title from util.save_entry(title,content).
My test:
def edit_entry(request):
if request.method == "POST":
form = EditEntryForm(request.POST)
if form.is_valid():
title = form.cleaned_data["title"]
content = form.cleaned_data["content"]
util.save_entry(title,content)
args = {'form':form, 'content':content}
return render (request, "encyclopedia/edit.html", {
"name": title,
"content": content
}, args)
if request.method == "GET":
util.save_entry(title,content)
form = EditEntryForm(initial={'title': title, 'content': content})
return render(request, "encyclopedia/edit.html", {
"form": EditEntryForm(initial={'title': title, 'content': content})
})
Why not using CBV? UpdtateView handles the retriving of the data for a particular object (in this case, the one you're updating/editing). However, in both cases (function views or class views) you have to pass some idetifier (I recomend using PK or slug) for retriving that object, you do this through the URL, which might be the thing you're missing. As I told you, UpdateView already handles that, but for your case using function views:
For best practices, and in this case, for pourposes, add self, request, *args and **kwargs as parameters for your edit_entry function, then you can get the identifier of the object you want to edit by adding at the beginning of your function the following code:
identifier=request.kwargs['identifier']
So in your urls.py you should have:
path('url/to/edit/<identifier>', ...)
Then, below the identifier definition in edit_entry, define a variable to retrive the specific object you want to edit
object=MyModel.objects.get(identifier=identifier)
Pass the object through your context, just as you're doing with the form and in template:
<input type="your-field-type" class="some-class" name="field" id="id_field" value="{{object.field}}">
That should do the work since your POST method is already working.
Sorry if the answer was a little bit extensive.
I have a Django web application and I'm trying to make an ajax call for uploading a large image. If I can upload the image I am trying to upload, I'll read the image with the pythonocc library and return the volume information. Since this process takes a long time, I am trying to do it using the django-background-tasks library. According to what I'm talking about, the ajax request that I am trying to take the image and send it to the view is as follows.
var data = new FormData();
var img = $('#id_Volume')[0].files[0];
data.append('img', img);
data.append('csrfmiddlewaretoken', '{{ csrf_token }}');
$.ajax({
method: 'POST',
url: '{% url 'data:upload' %}',
cache: false,
processData: false,
contentType: false,
type: 'POST',
data: data,
}).done(function(data) {
});
my view that will work after Ajax request;
def uploadFile(request):
if request.method == 'POST':
file = request.FILES.get('img')
filename = file.name
key = 'media/' + filename
s3_resource = boto3.resource('s3')
bucket = s3_resource.Bucket('bucket')
bucket.put_object(Key=key, Body=file)
new_data = Data(author=request.user)
new_data.save()
data_id = new_data.id
initial = CompletedTask.objects.filter(verbose_name=verbose)
request.session['u_initial'] = len(initial)
verbose = str(request.user) + '_3D'
read_3D(data_id, key, filename, verbose_name = verbose) ###background-task function is called
return redirect('data:uploadStatus')
The view that calls this background task function should also redirect the view, which will show the status of the upload.
def upload_status(request):
customer= str(request.user)
verbose = customer + '_3D'
initial = request.session['u_initial']
if request.is_ajax():
running, completed = get_upload_status(verbose)
context = {
"initial": initial,
"running": running,
"completed": completed,
}
return JsonResponse(context)
return render(request, "file_pending.html")
However, when I made the Ajax request, the view gave The view data.views.uploadFile didn't return an HttpResponse object. It returned None instead. error because it doesn't enter if request.method == 'POST' condition. When I remove that condition line from view, it cannot get the img file. By the way, this uploadFile view was working without any problems. Strangely, when I added the upload_status view and redirect it, it stopped working. To revert, I deleted that part and reverted it back, but it still didn't work.
How can I fix this problem? Am I missing something?
A. You should restrict your view method to the actual method you are expecting using the require_http_methods decorator
from django.views.decorators.http import require_POST
#require_POST
def uploadFile(request):
file = request.FILES.get('img')
filename = file.name
key = 'media/' + filename
s3_resource = boto3.resource('s3')
# ....
return redirect('data:uploadStatus')
That way calls to this endpoint not using POST will receive a 405 Method not allowed response. It's easier to debug and clarifies what is going wrong.
B. Make sure that your Ajax call in the frontend follows the redirect.
The cache parameter is not necessary with POST (unless you plan for IE8)
About contentType=false and processData=false: it still looks to me like this is a regular multipart-formdata call. Don't do this unless you know why you are doing it. Your Django code looks like it will handle the regular form data just fine, no need to tinker with the default behaviour.
C. You have to implement an error callback, as well:
}).done(function(data) {
}).fail(function(xhr, text, error) {
console.log(text, error)
});
I suppose what happens is some error that you are not handling in the JS code.
You have not added your settings, so I cannot be sure, but I suppose that you have not setup your CSRF configuration for AJAX correctly. One way to fix this would be to add the CSRF cookie to the AJAX request, as well. Both the CSRF form field as well as the cookie have to be present.
See https://docs.djangoproject.com/en/3.1/ref/csrf/#ajax
Is it possible to create function without template?
Like I'm trying to create delete function and what I want to do is to delete something immediately after clicking the delete button and then redirect to other page.
I want to place a delete button on the page users can edit their post.
html is like this
<button type="submit" class="btn btn-outline-secondary">Save Changes</button>
</form>
and I want to place delete button next to this button.
def edit_entry(request, entry_id):
'''Edit a post.'''
entry = Entry.objects.get(id=entry_id)
if request.method != 'POST':
form = EditEntryForm(instance=entry)
else:
form = EditEntryForm(instance=entry, data=request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse_lazy('main:index'))
return render(request, 'main/edit_entry.html', {'entry': entry, 'form': form})
def delete_entry(request, entry_id):
'''Delete post'''
#I don't wanna create template for this function.
pass
Anyone who can give me tips?
by the docs simple-view you can use the httpresponse
from django.http import HttpResponse
def delete_entry(request, entry_id):
'''Delete post'''
#I don't wanna create template for this function.
return HttpResponse('done')
Usually it make sense to use ajax for this purpose.
In this case the handling of click splits into steps:
1) You click the button
2) jQuery (or any other javascript code) catches click event and sends AJAX request (usually post request, because post is used to for data modifications in REST agreement) to defined url on your server (like /delete/my/some/thing/) in urls.py
3) Djnago routes request to the delete_something view (or function)
4) delete_something takes request, checks what you want to check (like some permissions of current user), deletes what you actually want to delete and makes ajax response.
5) jQuery takes this response (which it actually waits on 3-4 steps) and checks response content to detect if server says everything is ok or there is some error.
So this is the code to create ajax request from jQuery:
$('#delete-button').click(function(){
var delete_id = (#delete-button).data()
$.ajax
({
url: '/delete/my/some/thing/',
data: {"id": delete_id},
type: 'post',
success: function(result)
{
// here you can write code to show some success messages
},
error: function(result) {
// here you can write code to show some error messages or re-send request
}
});
});
You also can use not $.ajax() method, but $.post(), if you want.
This is the code from this answer to make Django json response:
return HttpResponse(json.dumps(response_data), content_type="application/json")
It could look like this:
import json
from django.http import HttpResponse
def delete_something(request):
resp_data = {}
user = request.user
delete_id = request.POST.get('delete_id') # or None, if there is no delete_id
# here you can check user permissions, if your site has them
# for example check that user can delete something and can delete this entity by writing check_user_can_delete() function
if delete_id and check_user_can_delete(user):
# do your deletion
resp_data = {'status': 'ok'}
else:
resp_data.update(errors=[])
if no delete_id:
resp_data['errors'] = 'no delete_id'
if not check_user_can_delete(user):
resp_data['errors'] = 'you cave no permissions to delete delete_id'
resp = json.dumps(resp_data)
return HttpResponse(resp, content_type='application/json')
Also note that Django 1.7 has JsonResponse object, as shown in SO answer I mentioned.
In my Django app I need to refresh part of a page via an Ajax call. The associated view returns a JsonResponse object, where one key in the context is the re-rendered HTML.
Something like this:
def myview(request):
...
tmpl8 = template.loader.get_template('page-table.html')
new_body = tmpl8.render({ 'rows': MyModel.custom_query() })
context = { 'new_body': new_body,
'other_info': other_information_for_javascript }
return JsonResponse(request, context)
Now, I also have context processors which add generic information. Some of this is needed in rendering page-table.html.
Unfortunately, context-processors don't get invoked by a bare Template.render(). They are invoked on the returned JsonResponse object, but it's too late by then as I've already rendered the template.
In Django 1.9 you can supply a RequestContext to Template.render and things go well - except for a Deprecation Warning appearing in the console. Django 1.10 insists that Template.render be given a dict.
So, the best I've been able to come up with is this:
from .context_processors import my_context_processor
def myview(request):
...
tmpl8 = template.loader.get_template('page-table.html')
render_context = { 'rows': MyModel.custom_query() }
render_context.update(my_context_processor(request))
new_body = tmpl8.render(render_context)
context = { 'new_body': new_body,
'other_info': other_information_for_javascript }
return JsonResponse(request, context)
Basically explicitly invoking the processor.
What am I missing?
Let's see what the render documentation says:
Template.render(context=None, request=None)
Renders this template
with a given context.
If context is provided, it must be a dict. If it isn’t provided, the
engine will render the template with an empty context.
If request is provided, it must be an HttpRequest. Then the engine
must make it, as well as the CSRF token, available in the template.
How this is achieved is up to each backend.
Here’s an example of the search algorithm. For this example the
TEMPLATES setting is:
The second parameter is the request! So we have
new_body = tmpl8.render(render_context, request)
Try to pass request to render() method:
new_body = tmpl8.render({ 'rows': MyModel.custom_query() }, request)