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.
Related
I'm currently grabbing a variable 'my_var' via GET when my view function is called, as shown. I need to query by this variable in my POST method.
def article_delete_view(request, pk):
my_var = request.GET.get('my_var') #GET THE VARIABLE from referring data-url
obj = Listing.objects.get(type=type) # I could query here if I wanted (illustration only)
article = get_object_or_404(Article, pk=pk)
data = dict()
if request.method == 'POST':
article.delete()
data['form_is_valid'] = True
articles = Article.objects.all()
obj = Listing.objects.get(name=my_var) #THIS DOES NOT WORK, since 'type' is not passed on submit.
context = {'articles':articles, 'obj':obj}
data['article_table'] = render_to_string('article_table.html', context)
else:
context = {'article':article}
data['html_form'] = render_to_string('article_delete.html', context, request=request)
return JsonResponse(data)
Is there a best-practice way to make that variable persist when POST is called on a submit? I know declaring a global is a bad idea. Thought about writing it to memory (attached to article) but that feels like a hack. Appending it to the pk argument then splitting it out feels even worse. Yuck. I could get my <input> method to pass my_var but to do so I'd have to refactor a lot of other things to make it happen. Are django session variables the answer here? Any perspective is helpful. Thanks in advance.
UPDATE & SOLUTION:
So to contextualize, the issue here is that I want to query a specific model instance within a view that does not have that model instance info (pk or id) passed to it as an argument. I can GET that variable through the data-url that fires the view, but only once. It was not usable in POST, or on any subsequent call to article_delete_view.
Imagine you're trying to make a peanut butter sandwich, but you can only retrieve one item at a time from the cupboard. You get the peanut butter on the first visit to the cupboard, but when you open the cupboard again to get the bread, the peanut butter disappears. Session variables turned out to be a solution for saving the peanut butter, you know, like a little shelf. A rolling wheelie shelf. Anyway:
def article_delete_view(request, pk):
my_var = request.GET.get('my_var')
if my_var is not None: #This prevents my_var from being lost subsequent function calls
request.session['session_var'] = my_var = request.GET.get('my_var') # create session variable
else:
pass
article = get_object_or_404(Article, pk=pk)
data = dict()
if request.method == 'POST':
article.delete()
data['form_is_valid'] = True
articles = Article.objects.all()
session_var = request.session.get('session_var', None) # grab the session variable here
obj = Listing.objects.get(name=session_var) # NOW THIS WORKS!
context = {'articles':articles, 'obj':obj}
data['article_table'] = render_to_string('article_table.html', context)
else:
context = {'article':article}
data['html_form'] = render_to_string('article_delete.html', context, request=request)
return JsonResponse(data)
The if that follows getting my_var prevents the session variable from being written as null on each subsequent call to the view, since GET only gets the variable the first time. If anyone is curious where that session_var is actually coming from, here's the source (obj.slug):
<button type="button" data-url="{% url 'article_delete' article.id %}?my_var={{obj.slug}}">Name</button>
I'm using GET to grab it from a template where it's in scope, so I can query with it in article_delete_view. Passing it in as a URL parameter did not work, since that pattern doesn't match anything in my app. So far this is working great. If anybody has any other ideas and/or can tell me why this is a bad idea, please post.
I have a simple(I think) question, about Django context rendering.
I'll step right into it -
Basically what I need is, some temp table, which in my case, I called Locked. And when a user presses a button, which Is a form, that object goes straight to the table Locked(just a simple insert). And inside that table there is a field called is_locked, and if its True, that object needs to go gray, or to have some lock icon inside the html table.
Just some kind of a viewable sign, that an object is inside the table Locked, and that another user can't access it.
But, my problem is, since in my views.py, my lock function is not returning exact html where I want to render that locker icon, instead, it returns another html.
Is there any way, to render same context, on 2 html pages? Thank's.
This is my code :
views.py
def lock(request, pk):
# Linking by pk.
opp = get_object_or_404(OpportunityList, pk=pk)
opp_locked = get_object_or_404(Locked, pk=pk)
# Taking two parametters for 2 fields.
eluid = Elementiur.objects.get(eluid=pk)
user = User.objects.get(username=request.user)
# Dont bother with this one! Just pulling one field value.
field_name = 'optika_korisnik'
obj = OpportunityList.objects.get(pk=pk)
field_object = OpportunityList._meta.get_field(field_name)
field_value = getattr(obj, field_object.attname)
# This is the main part! This is where i'm inserting data into Locked table.
if opp_locked.DoesNotExist:
opp_locked.id = int(eluid.eluid)
opp_locked.locked_eluid = eluid
opp_locked.locked_comment = field_value
opp_locked.locked_user = user
opp_locked.locked_name = 'Zaključao korisnik - ' + request.user.username
opp_locked.is_locked = True
opp_locked.save()
# This is what has to be returned, but i need context on the other page.
return render(request, 'opportunity/detalji/poziv.html',
{'opp': opp, 'locked': opp_locked})
else:
# This return has the context that i need(from the first opp_locked variable)
return render(request, 'opportunity/opp_optika.html', {'locked_test': opp_locked})
I can provide more code, but i think that it's not important for this type of question, because all of the logic is happening inside the lock finction, and last two returns.
I just had a quick overview of your snippet sorry if this not help you but you need to review it a little bit.
You call DoesNotExist on an instance of a Locked model
if opp_locked.DoesNotExist: [...]
that's not how you should use this exception.
You have a method .exists() that is available but only for Querysets.
Also if your instance does not exists you are alredy returning an Http404 response when you use get_object_or_404() method.
And perhaps you should avoid sharing primary keys between instances and replace them with models.OneToOneField (OneToOnefield)
Since i got no answers, i added a new field, is_locked, into my Locked model and that solved it.
I am using django restless for an ajax POST request, which took almost 10 to 20 seconds.
Here is my code.
class testEndPoint(Endpoint):
def post(self, request):
testForm = TestEmailForm(request.data)
if testForm.is_valid():
sometable = EmailTable.object.get(**condition)
if sometable.is_email_sent == false:
#Send Email
#Took around 15 seconds
sometable.is_email_sent = true
sometable.save()
else:
result = testForm.errors
return serialize(result)
i am calling it via $.ajax, but the problem is if two request hit this url with milliseconds time difference, both request passed through if sometable.is_email_sent = false: condition.
How can i prevent multiple submission. Right now i have moved sometable.is_email_sent = true;sometable.save(); before email send part, but i need more generic solution as there are dozen more places where this is happening. I am on django 1.5
django restless
You should disable the originating input element before you start your ajax call (that will prevent the majority of these issues).
The remaining problems can be solved by using select_for_update
class testEndPoint(Endpoint):
#transaction.commit_manually
def post(self, request):
testForm = TestEmailForm(request.data)
if testForm.is_valid():
condition['is_email_sent'] = False
try:
rows = EmailTable.objects.select_for_update().filter(**condition)
for row in rows:
row.is_email_sent = True
row.save()
#Send Email
except:
transaction.rollback()
raise
else:
transaction.commit()
else:
result = testForm.errors
return serialize(result)
select_for_update will lock the rows until the end of the transaction (i.e. it needs to be inside a transaction). By adding is_email_sent=False to the condition, we can remove the if. I've moved the changing of is_email_sent above the "Send Email", but it is not strictly necessary -- in any case it will be undone by the transaction rolling back if there is an exception.
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
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.