I have a view like this:
def form1(request):
if request.method == 'POST':
form = SyncJobForm(request.POST)
if form.is_valid():
# do something
in_progress = True
return render_to_response('form1.html', {'in_progress': in_progress})
I would like to know how to set it to refresh the template after is done with the view process. Like rendering the page after its done:
in_progress = True
return render_to_response('form1.html', {'in_progress': in_progress})
# after its finished
finished = True
return render_to_response('form1.html', {'finished': finished})
How can I implement something like this? Thanks in advance.
You can't maintain state between page calls on a global basis, so you'll need to store your data in the database. In addition, a view can't negotiate anything with the browser after it has returned a page, so you need to split this into multiple views and spawn a separate thread for the job. Here's a general outline that might help:
def do_something():
my_job = Jobs.get(id=blah)
my_job.in_progress = True
my_job.save()
# Do some stuff here....
my_job.in_progress = False
my_job.save()
def step1(request):
in_progress = Jobs.get(id=blah).in_progress
if not in_progress:
if request.method == 'POST':
form = SyncJobForm(request.POST)
if form.is_valid():
thread.start_new_thread(do_something)
return HttpResponseRedirect(step2)
else:
return render_to_response('form.html', 'form': form)
else:
form = SyncJobForm()
return render_to_response('form.html', 'form': form)
else:
return HttpResponseRedirect(step2)
def step2(request):
in_progress = Jobs.get(id=blah).in_progress
if in_progress:
return render_to_response('in_progress.html')
else:
return HttpResponseRedirect(finished)
def finished(request):
return render_to_response('finished.html')
Then have your page in_progress.html periodically refresh the page. When the job is completed, you can display a status message in finished.html.
There are more sophisticated ways to do this (write Javascript to poll the server periodically), but you're still going to need to write separate views to respond with the appropriate information. In addition, you could use a job management framework like Celery to create and execute jobs, but again you'll still have to create separate views to handle status information.
I am not able to think of doing this without some sort of call back from the client (unless you use some asynchronous server mechanism, something I am not familiar with). One way would be to have the client poll the server periodically after receiving the "in_progress" notification to see if the processing has finished. The server side can be split into two calls, first to handle the POST as shown in your question and another one to find out and report if a given task is finished.
Related
Currently, I have a view that essentially closes a lead, meaning that it simply copies the information from one table (leads) to another (deals), now what I really would like to do is that after clicking close, the user is redirected to another page where the user can update some entries (sales forecast), I have a view that updates the lead, so I thought that I can do something like below:
#login_required
def close_lead(request):
id = request.GET.get('project_id', '')
keys = Leads.objects.select_related().get(project_id=id)
form_dict = {'project_id': keys.project_id,
'agent': keys.agent,
'client': keys.point_of_contact,
'company': keys.company,
'service': keys.services,
'licenses': keys.expected_licenses,
'country_d': keys.country
}
deal_form = NewDealForm(request.POST or None,initial=form_dict)
if request.method == 'POST':
if deal_form.is_valid():
deal_form.save()
obj = Leads.objects.get(project_id=id)
obj.status = "Closed"
obj.save(update_fields=['status'])
## Changing the Forecast Table Entry
forecast = LeadEntry.objects.filter(lead_id=id)
for i in forecast:
m = i
m.stage = "Deal"
m.save(update_fields=['stage'])
messages.success(request, 'You have successfully updated the status from open to Close')
update_forecast(request,id)
else:
messages.error(request, 'Error updating your Form')
return render(request,
"account/close_lead.html",
{'form': deal_form})
This view provides the formset that I want to update after closing the lead
#login_required
def update_forecast(request,lead_id):
# Gets the lead queryset
lead = get_object_or_404(Leads,pk=lead_id)
#Create an inline formset using Leads the parent model and LeadEntry the child model
FormSet = inlineformset_factory(Leads,LeadEntry,form=LeadUpdateForm,extra=0)
if request.method == "POST":
formset = FormSet(request.POST,instance=lead)
if formset.is_valid():
formset.save()
return redirect('forecast_lead_update',lead_id=lead.project_id)
else:
formset = FormSet(instance=lead)
context = {
'formset':formset
}
return render(request,"account/leadentry_update.html",context)
As you can see I’m calling this function update_forecast(request,id) after validating the data in the form, and I would have expected to be somehow redirected to the HTML page specified on that function, however, after clicking submit, the form from the first view is validated but then nothing happens, so I'm the function doesn't render the HTML page
My question how can I leverage existing functions in my views?, obviously, I will imagine that following the DRY principles you can do that in Django, so what am I doing wrong ?, how can I call an existing function within another function in views?
A view returns a response object. In your current code, you're calling a second view but not doing anything with its response. If you just wanted to display static content (not a form that might lead to an action that cares about the current URL) you could return the response object from the second view - return update_forecast(request, id).
But since your second view is displaying a form, you care about what the action for the view from the second form is. The typical Django idiom is to have forms submit to the current page's URL - that wouldn't work if you just call it and return its response object. You could customize the action in the second view, say adding an optional parameter to the view, but the usual idiom for form processing is to redirect to the view you want to show on success. Just as you do in the update_forecast view. Something like this:
messages.success(request, 'You have successfully updated the status from open to Close')
return redirect('update_forecast', lead_id=id)
At a Django server, I have a page with a single button that starts a function.
This function takes a while to complete, and I tried writing updates on the process to a request.session variable with the intent of checking on its contents from a separate page.
It seems however that request.session variables are not updated until the function they are included in are done. At least the variable does not update until then.
Am I right, and if so, is there a way to write to request.session variable before the function's completion?
The sessions are set up properly, I can write and read variables with other examples. For now I'll also just make a temporary db record to store the status update info and read it from there, but I'm curious as of this request.session thing - is my guess correct, and is there a way around?
update:
views.py
#login_required
def autolink(request):
result, time = access_check(request, 'super')
if not result:
return redirect('index')
result = f_autolink(request)
if result is None:
result = request.session.get('technical', '')
return render(request, 'autolink.html', {'result': result, })
functions.py
def f_autolink(request):
if request.method == 'GET':
result = None
elif request.method == 'POST':
request.session['technical'] = 'starting the job'
result = f_kzd_autolink(request)
else:
result = None
return result
def f_kzd_autolink(request):
homeless = Kzd.objects.filter(payment__isnull=True, returned=False).distinct()
result = []
count = homeless.count()
counter = 0
request.session['technical'] = 'Starting link job - {c} records to process'.format(c=count)
for h in homeless:
counter += 1
request.session['technical'] = 'Checking record {c1} of {c}'.format(c1=counter, c=count)
/* long code that makes the h in homeless cycle run for about 3 minutes, unrelated to the question */
so basically, the view shows request.session.get('technical', ''), but neither function writes to it until they are done (it then writes about processing the last record).
The session is saved on a per-request basis when it was modified or when the setting settings.SESSION_SAVE_EVERY_REQUEST is set to True in your settings.
So the simple answer yes, the session is saved by the session middleware when processing the response created by a view. But you could do it manually by calling request.session.save() inside your view.
If you have code, that runs very long it would be better to imediately create a response and use tools like celery to asynchronously process your task.
And you should consider storing your data into an own database table/ own model if it is not really related to the user session.
I have a form on web page where an authenticated user can create an order. When they create the order, they are redirected to order_review page. On the order_review page, there is a confirm button and cancel button.
If cancel button is clicked, order is removed. But I can't rely on that user either clicks on confirm or cancel so I would like to handle situations when user leaves this page.
There are two ways in my opinion. Either the order would be deleted when users session expired or when it wasn't confirmed for for example 5 minutes.
def reviewOrder(request, id):
order = get_object_or_404(models.Order, id=id)
if order.type == 'retail':
total = models.Price.calculate_price(order.dry_wood, order.wet_wood, order.briquette, order.stelinka)
context = {'order': order}
context.update(total)
return render(request, 'reviewOrder.html', context=context)
return render(request, 'reviewOrder.html', context={'order': order})
def delete_order(request, id):
order = get_object_or_404(models.Order, id=id)
order.delete()
return HttpResponseRedirect('/')
def confirm_order(request, id):
order = get_object_or_404(models.Order, id=id)
order.confirmed = True
order.save()
if not order.backuped:
models.OrderBackup.objects.create(original_id=order.id,**model_to_dict(order, exclude=['id','customer','backuped' ]))
order.backuped = True
notifications.AdminNotifications.new_order(order)
notifications.CustomerNotifications.orderHasBeenCreated(order)
return HttpResponseRedirect('/')
What would you do?
So one way I've handled this before is to use onbeforeunload (in conjunction with other methods). The idea is to fire off an ajax call to some url in your website that will handle things as the user leaves the current page.
window.onbeforeunload = function(e) {
$.ajax({
url: "/remove_order/",
type: "post",
async: false,
data:{
'orer_id':order_id
}
});
return undefined;
};
So when the user leaves the page by clicking a link or typing a new url it will fire off this ajax call to remove the order or it can check the current state of the order and then do what you need etc.
I last used this to decrement some inventory in a system when it was temporarily taken this helped to free up inventory immediately in most cases but i also had a periodic task that would cleanup unused inventory after some time.
I've had things clean up on session end or another way I've done this before is to use http://www.celeryproject.org/celery task queues and fire off an async task after a period of time
cleanup_stuff.apply_async((somemodel.id,), countdown=timedelta(minutes=15).seconds)
#classmethod
def cleanup_stuff(cls, id=None):
# blah blah create conditions for deletion
cls.objects.filter(*conditions).delete()
Is it possible to submit a django form to run 2-3 large tasks at the backend and display the user a page for status/progress of the tasks running and display the output on completion.
I have a function as below in my view.py file which executes large tasks. I want that while the tasks are been running in the back-end on my server(ubuntu), instead of showing the user with the page/browser loading (which hangs for huge tasks), I want to show a page to show the tasks status/progress.
views.py
def test(request) :
if request.method == 'POST': # If the form has been submitted...
form = TestForm(request.POST)
if form.is_valid(): # All validation rules pass
form_data = form.cleaned_data
## Get the form data and use it
output, error = executeCommand("##Perform the required action with the form data")
if error == '' :
return HttpResponse('Work Done !! Thanks')
else :
return HttpResponse('Error Occurred')
else:
form = TesForm() # An unbound form
return render_to_response("test.html", { 'form' : form } , context_instance=RequestContext(request))
Any help/advice is greatly appreciated.
I think your webserver will get stucked if your "executeCommand" take very long time to finish.
You might use message queues as suggested by toabi.
You might have a look on django-celery.
I want to check if a user has any new messages each time they load the page. Up until now, I have been doing this inside of my views but it's getting fairly hard to maintain since I have a fair number of views now.
I assume this is the kind of thing middleware is good for, a check that will happen every single page load. What I need it to do is so:
Check if the user is logged in
If they are, check if they have any messages
Store the result so I can reference the information in my templates
Has anyone ever had to write any middleware like this? I've never used middleware before so any help would be greatly appreciated.
You could use middleware for this purpose, but perhaps context processors are more inline for what you want to do.
With middleware, you are attaching data to the request object. You could query the database and find a way to jam the messages into the request. But context processors allow you to make available extra entries into the context dictionary for use in your templates.
I think of middleware as providing extra information to your views, while context processors provide extra information to your templates. This is in no way a rule, but in the beginning it can help to think this way (I believe).
def messages_processor(request):
return { 'new_messages': Message.objects.filter(unread=True, user=request.user) }
Include that processor in your settings.py under context processors. Then simply reference new_messages in your templates.
I have written this middleware on my site for rendering messages. It checks a cookie, if it is not present it appends the message to request and saves a cookie, maybe you can do something similar:
class MyMiddleware:
def __init__(self):
#print 'Initialized my Middleware'
pass
def process_request(self, request):
user_id = False
if request.user.is_active:
user_id = str(request.user.id)
self.process_update_messages(request, user_id)
def process_response(self, request, response):
self.process_update_messages_response(request, response)
return response
def process_update_messages(self, request, user_id=False):
update_messages = UpdateMessage.objects.exclude(expired=True)
render_message = False
request.session['update_messages'] = []
for message in update_messages:
if message.expire_time < datetime.datetime.now():
message.expired = True
message.save()
else:
if request.COOKIES.get(message.cookie(), True) == True:
render_message = True
if render_message:
request.session['update_messages'].append({'cookie': message.cookie(), 'cookie_max_age': message.cookie_max_age})
messages.add_message(request, message.level, message)
break
def process_update_messages_response(self, request, response):
try:
update_messages = request.session['update_messages']
except:
update_messages = False
if update_messages:
for message in update_messages:
response.set_cookie(message['cookie'], value=False, max_age=message['cookie_max_age'], expires=None, path='/', domain=None, secure=None)
return response