Using Django How Can I Automatically Update Information on a Template - python

I'm not looking for exact code, but rather some guidance on my problem. On one of my pages, I want to have a small section that will tell the user the disk space left on the server and I want this information to update every 30 seconds. I have the code already written to get the disk information. However, I am unsure on how to display that information and have it update. This is what I have, just to give you a visual:
The HTML I'm working with:
{% extends "Checklist/base.html" %}
{% block main_content %}
<form action="{% url 'Checklist:run' %}" method="post"> {% csrf_token %}
<input type="submit" name="submit_button" value="Run">
</form>
<label for="disk_space">Disk Space: </label>
<input type="text" name="disk_space" value="{{ disk_space }}" id="disk_space">
{% endblock %}
The Views Function for the HTML:
def submit(request): #start scrtipt
submit.preProcess = subprocess.Popen(['chmod', '+x /home/psvuser/Desktop/test.sh'])
submit.process = subprocess.Popen(['/home/psvuser/Desktop/test.sh'])
global sem
sem = True
disk_space = request.get('disk_space', '')
disk_space = diskSpace()
start_time= int(round(time.time()))
f = open("/home/psvuser/Desktop/writer.txt", "a")
f.write(str(start_time))
f.write(", ")
return render(request, 'Checklist/stop.html')
I have an idea on how to periodically refresh the page, however, I don't know how to display 'disk_space' onto the html.

Since you want to do regular asynch calls to your server while a page is already loaded, I would use AJAX to solve this problem.
I'd usually do something like this
def your_django_view(request):
# magic
server_data = '98% left!'
response_data['data'] = server_data
return HttpResponse(json.dumps(response_data), content_type="application/json")
Then in your view, use some jquery (or javascript..whatever) to deal with all of the data
$.ajax({type: 'POST',
url: '/function_url/',
data: {
},
success: function(response_data) {
var server_mem = response_data['data']
$('#disk_space').val(server_mem);
})

Use this return statement at the end of your submit view:
return render_to_response('Checklist/stop.html',
{'disk_space':disk_space},
context_instance=RequestContext(request))
The second argument of render_to_response is a dictionary of {'key':value, 'key2':value2} you can use in the template.

Related

Django pagination of filtered results using AJAX

I've been trying to figure this out for days and was unable to find any useful information online.
What I am trying to do is paginate objects from my model after filtering them using a drop down menu and supplying the data to python via AJAX. I know where the problem is but I am not sure how to solve it. I have two templates, first one is:
entry_index.html:
{% extends 'main/base.html' %}
<form action="" method="get" accept-charset="utf-8">
<select class="selectpicker" name="times" onchange="FilterCategories()" id="times">
<option value="1">last 24 hours</option>
<option value="30">past month</option>
<option value="365">past year</option>
<option value="10000">all time</option>
</select>
</form>
<ul id="all-games" class="list-unstyled">
{% include page_template %}
</ul>
The template that is being included in the above template is entry_index_page.html:
{% if objects %}
{% for object in objects %}
do something
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if objects.has_previous %}
previous
{% endif %}
<span class="current">
Page {{ objects.number }} of {{ objects.paginator.num_pages }}.
</span>
{% if objects.has_next %}
next
{% endif %}
</span>
</div>
urls.py:
url(r'^$', views.entry_index, name='index')
views.py:
def entry_index(
request,
template='entry_index.html',
page_template='entry_index_page.html'):
date_from = timezone.now() - timezone.timedelta(days=1)
obj_list=Object.objects.filter(submitted__gte = date_from).order_by('-votes')
message=[]
context = {
'objects': obj_list,
'page_template': page_template}
if request.is_ajax():
template = page_template
message = []
if request.method == "GET":
time_range = request.GET.get('time_range')
if time_range is not None and time_range != u"":
time_range = request.GET['time_range']
date_from = timezone.now() - timezone.timedelta(days=int(time_range))
obj_list= Object.objects.filter.filter(submitted__gte=date_from)
paginator = Paginator(obj_list, 2)
page = request.GET.get('page')
try:
objects= paginator.page(page)
except PageNotAnInteger:
objects= paginator.page(1)
except EmptyPage:
objects= paginator.page(paginator.num_pages)
context.update({"message":message,"objects":objects})
return render_to_response(
template, context, context_instance=RequestContext(request))
ajax.js:
function FilterCategories() {
var timePosted = document.getElementById('times');
$.ajax({
type: "GET",
url: "",
data: {
'time_range': timePosted.value,
'csrfmiddlewaretoken': $("input[csrfmiddlewaretoken]").val()
},
success: filterResults,
dataType: 'html'
});
}
Now to explain what I think is going on and hopefully someone can help me find a solution.
When the home page is loaded (entry_index.html) the model gets filtered based on the first option in the drop down menu (i.e. value="1", which is filtering for the data entries submitted within the last day). The obj_list variable gets populated and is passed to the paginator and everything works as expected. I get a certain number of pages and can navigate through pages. Now lets assume we are on the home page again and I select "all time" from the drop down menu. This will trigger the onchange callback and it will call the FilterCategories() function. Note the url in AJAX is "" (an empty string, so pointing to my index page). According to urls.py, it will call my entry_index() view. Because request is ajax, the template used will change (page_template becomes the new template, page_template = entry_index_page.html). Because the new time range specified with the drop down menu and passed on with ajax, I get a new obj_list which is then paginated and produces "objects" which are then passed as context onto the template. Up until this point everything works as expected. I get right amount of pages etc. However, the problem starts when I try to go to the next page with the newly selected filter. When I click the next page button, the request that is being made is not an ajax request so everything that is in the request.is_ajax() conditional is not executed. So in another words a click to the next page is calling my entry_index view again and the template being used this time around is entry_index.html and my filter is reset back to the default, which is the "last 24 hours" filter. Therefore, when I click the next page what I end up getting is actually the default home page again instead of getting the next page of objects with my newly selected drop down filter.
My question is, is there an easy way to fix this so that I can scroll through the pages of my filtered model? Or should I completely abandon this approach and there is an easier way to do this? I apologize for a long post and I hope someone out there will be able to help me out. Thank you for taking your time to read this.
Here's an example of an approach I've taken to having dynamic content displayed on a page using Django and Ajax:
I was tooling a little browser Sci-fi game just to practice this specific technique. Everything took place in a single view:
class GameViewport(TemplateView):
template_name = "game_viewport.html"
#cached_property
def slug(self):
return self.kwargs['slug']
#cached_property
def game(self):
return Game.objects.get(url=self.kwargs['slug'])
#cached_property
def player(self):
return Player.objects.get(game=self.game)
#cached_property
def current_planet(self):
return self.player.current_planet
#cached_property
def left_column(self):
player = self.player
if player.current_location:
node = player.current_node
if len(Location.objects.filter(node=node)) == 0:
spawn_locations(node)
locations = Location.objects.filter(node=node)
else:
locations = Location.objects.filter(planet=node)
html = "Other Sites in ".format(str(node))
for location in locations:
html += '<li>{} ({})</li>'.format(location.name, location.type.name)
return html
elif player.current_node:
planet = player.current_planet
if len(Node.objects.filter(planet=planet)) == 0:
spawn_nodes(planet, get_name_choices())
nodes = Node.objects.filter(planet=planet)
else:
nodes = Node.objects.filter(planet=planet)
html = '<h4><b>Other Starports on {}:</b></h4>'.format(planet.name)
for node in nodes:
html += '<li> {} ({})</li>'.format(node.name, node.type.name)
return html
elif player.current_planet:
system = player.current_system
html = '<h4><b>Known Planets in {}:</b></h4>'.format(system.name)
for known_planet in player.known_planets.filter(solar_system=system):
html += '<li> {} ({})</li>'.format(
known_planet.name,
known_planet.classification.name
)
return html
else:
html = '<h4><bShip Status</b></h4>'
html += '<p><b>Fuel:</b> 100%</p>'
return html
So as you can see, the left column would generate different html data based on what the player's current settings are. This would be plugged into the template like so:
<div class="col-md-3">
<div class="leftColumn">
{% autoescape off %}
{{ view.left_column }}
{% endautoescape %}
</div>
</div>
If the user clicked on a new location, I would send her decision through AJAX:
$(".planetChoice").click(function(){
event.preventDefault();
var submission_data = {planet: $(this).text()};
console.log(submission_data);
$.ajax({
url: $('#visitPlanet').attr('href'),
type: 'GET',
dataType: "json",
data: submission_data,
success: function(html_data) {
window.location.reload();
},
failure: function(data) {
alert('Something went wrong. Please refresh the page.');
}
});
});
All this would do is update the player's state and reload the page according to her new settings:
def visit_planet(request, slug):
player = Game.objects.get(url=slug).player_1
planet = Planet.objects.get(name=request.GET.get('planet', "").strip())
if planet:
player.current_location = None
player.current_node = None
player.current_planet = planet
player.save()
response = {'status': 1, 'message': "Ok"}
return JsonResponse(response)
Thus displaying the new data, as determined by the left_column property.
I went about this by changing the state in the database, but it could just as easily be accomplished with session variables. I found it to be a relatively clean and DRY way of cycling dynamic content. It also has the advantage of giving Django an opportunity to generate or modify data in between clicks.
Not sure if this applies to your situation, but hopefully it sparks an idea!
EDIT: You don't even necessarily need to output HTML. Here's an approach I am using in a different application:
<!--Product Tile #1-->
{% if view.tile_data.0 %}
<div class="col-md-4">
<div class="card hoverable">
<!--Card content-->
<div class="card-block" id="tile_{{ view.tile_data.0.invoice }}_id">
<!--Title-->
<h4 class="card-title">Shipment {{ view.tile_data.0.invoice }}</h4>
<!--Text-->
<p class="card-text">{{ view.tile_data.0.supplier.name }}
<br>
<b>{{ view.tile_data.0.lbs|floatformat }} Lbs # {{ view.tile_data.0.price }} USD</b>
<br>
{{ view.tile_data.0.variety.commodity }} {{ view.tile_data.0.variety }} {{ view.tile_data.0.inshell|shell_display }}</p>
</div>
<!--/.Card content-->
</div>
</div>
{% endif %}
<!--./Product Tile #1-->
This data is directly fed through the view from a model manager:
def tile_data(self, status, first, last):
return self.model.objects.filter(status=status)[first:last]

Using Django FormPreview the right way

My Goal
I have a django project with a form, and I want to display a preview page before the user submits.
The problem
I can display a preview page using a Django FormPreview, but not all form data is displayed properly. Specifically, if I have a field with choices, the string values of these choices aren't displayed. I'm also having problems applying template filters to date fields. The end result is that some data on the preview page is visible but other data is blank:
However, if I display the same data for posts that have actually been submitted, then everything displays properly:
My Code
models.py:
class Game(models.Model):
# Game Choices
FOOTBALL = 0
BASKETBALL = 1
TENNIS = 2
OTHER = 3
GAME_CHOICES = (
(FOOTBALL, 'Football'),
(BASKETBALL, 'Basketball'),
(TENNIS, 'Tennis'),
(OTHER, 'Other')
)
game_id = models.AutoField(primary_key=True)
location = models.CharField(max_length=200, verbose_name="Location")
game = models.IntegerField(choices=GAME_CHOICES, default=FOOTBALL)
game_date = models.DateField(verbose_name='Game Date')
forms.py
class GameForm(ModelForm):
class Meta:
model = Game
fields = (
'location',
'game',
'game_date'
)
I'm pretty sure that the problem is in my views.py: I'm not sure that I'm processing the POST request the right way to feed all data to the preview page.
views.py
def form_upload(request):
if request.method == 'GET':
form = GameForm()
else:
# A POST request: Handle Form Upload
form = GameForm(request.POST) # Bind data from request.POST into a GameForm
# If data is valid, proceeds to create a new game and redirect the user
if form.is_valid():
game = form.save()
return render(request, 'games/success.html', {})
return render(request, 'games/form_upload.html', {
'form': form,
})
preview.py
class GameFormPreview(FormPreview):
form_template = 'games/form_upload.html'
preview_template = 'games/preview.html'
def done(self, request, cleaned_data):
# Do something with the cleaned_data, then redirect
# to a "success" page.
return HttpResponseRedirect('/games/success')
form_upload.html
...
<form method="post">
{% csrf_token %}
<ul><li>{{ form.as_p }}</li></ul>
<button type="submit">Preview your post</button>
</form>
...
preview.html
{% load humanize %}
...
<h1>Preview your submission</h1>
<div>
<p>Location: {{ form.data.location }}</p>
<p>Game Date: {{ form.data.game_date|date:"l, F d, Y" }}</p>
<p>Game Type: {{ form.data.get_game_display }}</p>
</div>
<div>
<form action="{% url 'form_upload' %}" method="post">
{% csrf_token %}
{% for field in form %}
{{ field.as_hidden }}
{% endfor %}
<input type="hidden" name="{{ stage_field }}" value="2" />
<input type="hidden" name="{{ hash_field }}" value="{{ hash_value }}" />
<!-- Submit button -->
<button type="submit">Submit your post</button>
<!-- Go back button -->
<button type="submit">
<a href="{% url 'form_upload' %}"
onClick="history.go(-1);return false;" >
Go back and edit your post
</a>
</button>
</div>
</form>
</div>
...
Two issues
Essentially, I'm having these two issues:
String values for choices are not displayed. If I use the get_FOO_display() method in my preview.html template, it returns blank. However, if I use this in a page after the post has been submitted, it displays properly.
The humanize date filter doesn't work. If I apply a humanize filter ({{ form.data.game_date|date:"l, F d, Y" }}) in preview.html, it also displays blank. Again, this works for submitted posts.
My question essentially is: what's the right way to use the FormPreview here?
form.data does not have get_FOO_display attributes. When you access {{ form.data.get_game_display }} in the template, it fails silently and doesn't display anything.
The get_FOO_display are methods of the instance, so try this instead.
{{ form.instance.get_game_display }}
Wherever possible you should access data from form.cleaned_data (which is validated and 'cleaned') instead of form.data, which is the raw data submitted to the form.
The filters don't work with form.data.game_date because it's a raw string. They should work with form.cleaned_data.game_date, which has been converted to a python date object.
Finally, you haven't implemented anything in your done method, you've just copied the comment from the docs. You could create a new game using cleaned_data as follows:
def done(self, request, cleaned_data):
game = Game.objects.create(**cleaned_data)
return HttpResponseRedirect('/games/success')

Form-View Mapping Issue in Django

This is becoming very frustrating, like all of my Django form endeavors have been thus far...
I have a search bar form that is supposed to send the user to a url '/project/search/<query>/' and the url works fine if I type in a url but my form is not mapping correctly. I am implementing this first in the search results page, which will still have a search bar, and whenever I type in a value to the search bar I get redirected to '/project/search/'. Where have I gone wrong? I have spent a solid two days on this to no avail.
I am really struggling with this and I have no idea what I am doing wrong. I wish I had at least an error or something to fix but this is just not working.
Here is my form class and view:
from django import forms
class SearchForm(forms.Form):
search_string = forms.CharField(initial='Search Article Text',max_length=100)
def search(request, search_query):
form = SearchForm()
context = RequestContext(request)
search_string = search_query.replace('_',' ')
search_terms = search_query.split('_')
search_results = Article.objects.all()
for term in search_terms:
search_results = search_results.filter(article__icontains=term)
context_dict = {
'search_string':search_string,
'search_results':search_results,
'form':form,
}
if request.method == 'POST':
form = SearchForm(request.POST)
context_dict['form'] = form
if form.is_valid():
search_string = form.cleaned_data['search_string']
search_query = search_string.replace(' ','_')
###return HttpResponseRedirect(reverse('search', args=(search_query,)))
search_url = '/project/search/' + search_query + '/'
return HttpResponseRedirect(search_url)
return render_to_response('search.html', context_dict, context)
The html:
<form action='/beacon/search/' class="navbar-form navbar-right" method='POST'>
<div class="form-group">
{% csrf_token %}
{{ form.search_string }}
</div>
<input type='submit' value='Submit' class='btn btn-default'/>
</form>
I don't really understand your question. You're getting redirected because that's what you've told the view to do: you explicitly return an HttpResponseRedirect. If you don't want to redirect, don't do that.
Ok after troubleshooting for probably 5 or so days I have realized what my issue was (I also had a little help from the Django Users Google group).
I am answering this in case anybody also has my problem in the future btw. I'm obviously not an expert on the forms part of Django.
This all had to do with the actual HTML writeup I had. In my form tag the action was to '/project/search/' which just redirected me to that URL because django thought /project/search/ was different than project/search/query . Therefore all I needed to do for this part was change the action to refer to any URL that would validate my search view- so I picked /project/search/search_query/ but anything after /project/search/ would have worked.
My second issue was with my input. I needed to include a name in my input -'search_string'- so my search view would understand what values the form itself was carrying.
Therefore my html in the end looks like:
<form action='/beacon/search/search_query/' class="navbar-form navbar-right" method='POST'>
<div class="form-group">
{% csrf_token %}
<input type="text" name='search_string' class="form-control" placeholder="Search Article Text"/>
</div>
<!--<button type="submit" class="btn btn-default" value='Submit'>Submit</button>-->
<input type='submit' value='Submit' class='btn btn-default'/>
</form>
Credit to Branko Majic to helping my on Dj Users Group as well. Seriously.

Submitting contents of file to a Django view in HTML

I'm working with Django v1.4 (Python v.2.7.3) and I'm trying to build a proofchecking application. My proofchecker has an "Examples" page with a list of links to example proofs for the user, and these are rendered on the screen one after the other. A small sample is shown below:
These example files are saved at MEDIA_ROOT on the server, and what I want is a way to make it so that clicking the link will pass the contents of the file in a POST message to a particular view. That view already has code designed to handle the user uploading a proof file directly from their file system, so essentially what I want to do is make it so that the examples.html template (shown below) passes the same kind of information except for files already stored on the server.
The code for examples.html is:
{% load staticfiles %}
<html>
<head>
<title>Anaconda</title>
<style>
body
{
overflow-y: scroll;
}
</style>
</head>
<body>
<center>
<img src="{% static "AnacondaTitleText.png" %}" alt="Title" height="40%" width="40%"/>
<div align="left" style="width:800px;">
<h2>Anaconda Examples</h2>
<p>You can click the button beside each example on this page to load it into the proof window.
{% if examples %}
The following examples are included with Anaconda:</p>
<br>
{% for example in examples %}
<p>{{ example.exampleFile.name|cut:"./" }}: link</p>
<br>
{% endfor %}
{% else %}
There are no examples currently included with Anaconda.</p>
{% endif %}
</div>
</center>
</body>
</html>
The "a href..." part will need to be changed because currently, clicking it will bring up a "Save file" dialog which is not what I want.
In my server's views.py file, I have the following code capable of handling uploaded files:
def proof(request):
if request.method == 'POST':
defaultText = request.FILES['docfile'].read()
proofText = ProofForm(request.POST)
else:
defaultText = ""
proofText = ProofForm()
c = {}
c.update(csrf(request))
c['form'] = proofText
c['default_text'] = defaultText
return render_to_response('server/proof.html', c)
I suppose what I want is a way to do the following:
The user clicks the link next to a particular example proof
All the necessary information gets loaded into request.FILES
This gets sent to the server as a POST request to proof(request)
proof(request) treats it like a regular uploaded file and reads the file contents
My models.py file looks like this:
from django.db import models
class Example(models.Model):
exampleFile = models.FileField(upload_to='.')
I'd be happy to provide additional information if necessary.
I found a solution to this, but I suspect it's a bit hacky.
Inside examples.html I include the following:
{% for example in examples %}
<form name="example" action="/server/proof" method="post">
{% csrf_token %}
<p>{{ example.exampleFile.name|cut:"./" }}</p>
<input type="hidden" name="example" value="{{ example.exampleFile.name|cut:"./" }}">
<input type="submit" value="Select">
</form>
<br>
{% endfor %}
I associate the name of the file with a hidden input element, and then in views.py I have the following code:
def proof(request):
if request.method == 'POST':
print "DATA"
print str(request.POST)
try:
defaultText = request.FILES['docfile'].read()
except:
examplePath = request.POST.get('example')
with open(join(MEDIA_DIR, examplePath), 'r') as f:
defaultText = f.read()
proofText = ProofForm(request.POST)
else:
defaultText = ""
proofText = ProofForm()
c = {}
c.update(csrf(request))
c['form'] = proofText
c['default_text'] = defaultText
return render_to_response('server/proof.html', c)
It's patchwork, but it does what I want. Hopefully I'll have time to improve the code at some later time.

How to realize a dynamic sidebar with Django?

I plan on creating a sidebar with changing elements (depending on the current url and authentication-status).
For example: The default sidebar shows a login and a tag cloud.
If a user is already logged in, I want to display a user menu.
If the current url is /tagcloud, I want to hide it from the sidebar.
Actually, I need a way which enables me to do something like this in a view:
def some_view(request):
if request.user.is_authenticated():
sidebar.remove('login')
sidebar.add('user_menu')
def tag_cloud(request):
sidebar.remove('tag_cloud')
Afterwards, I want to pass the sidebar (implicitly, without passing it to render_to_response) to the template where I have in mind to do something like this:
<div id="sidebar">
{{ sidebar }}
</div>
Is this possible?
You'd better do this in a context_processors.py file
That also mean you have to use a RequestContext when returning your views
def include_tagcloud(request):
if request.path == '/tagcould/':
tagcloud = Tags.objects.filter(active=True) #whatever
return {'tagcloud': tagcloud}
def include_login(request):
if request.user.is_authenticated():
loginform = MyLoginForm(request.POST)
#passing a Django form + POST data in the case of re-submit
return {'loginform' : loginform}
And then in your template :
{% if loginform %}
<form action="accounts/login/">
{{form.as_p}}
<input type="submit" name="Login">
</form>
{% endif %}
{% if tagcloud %}
{%for tag in tagcloud %}.....{%for}
{% endif %}
In this example the login form points to a fixed view,
if you want to catch the login form post on everyview, I don't know how to do
EDIT : if you don't use the CSRF features of Django, you could simply insert the login form in the base template without using any django form and pointing to a login view :
{% if user.is_authenticated %}
<form action="accounts/login/">
<input type="text" name="username"><input type="password" name="password">
<input type="submit" name="Login">
</form>
{% endif %}
Yeah, but you can use inheritance of templates as well as composition. Then include your sidebar in a parent template that is used/inherited from in all of your templates. Then it is easy to find the template for the sidebar: it's in a separate file.
Answer of #Dominique is correct but When you write something in context_processors that's load at any page of the website. That maybe makes a performance issue.
I think the right way to handle dynamic sidebar is simpletag and use where you need.
def get_sidebar():
tags = Tags.objects.filter(active=True)
latest_posts = Post.objects.all().order_by('-create_at')[:10]
html = render_to_string("sidebar.html", {
"tags": tags,
"latest_posts": latest_posts
})
return html
And now just use in template files:
<div class="col-md-4 sidebar">
{% get_sidebar %}
</div>
Also, you can pass request to simpletag to use user.is_authenticated for authenticated user access.

Categories

Resources