Minimal Example for dynamic HTML pages with Django and AJAX - python

I refer to the following posts:
Reload table data in Django without refreshing the page
Django dynamic HTML table refresh with AJAX
Despite the two post and nice answers, I am still struggling to construct a minimal working example for dynamic HTML pages resorting to Django and AJAX.
I have to following code:
models.py
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
urls.py
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^get_more_tables', views.get_more_tables, name='get_more_tables')
]
views.py
from django.shortcuts import render
from .models import Question
def index(request):
a = Question.objects.order_by('-pub_date')
context = {'questions': a}
return render(request, 'polls/index.html', context)
def get_more_tables(request):
a = Question.objects.order_by('-pub_date')
context = {'questions': a}
return render(request, 'polls/get_more_tables.html', context)
index.html
<html>
<body>
<table id="_appendHere">
<tr><td> text </td></tr>
{% for a in questions %}
<tr><td> {{ a.question_text }} </td></tr>
{% endfor %}
</table>
</body>
</html>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
var append_increment = 0;
setInterval(function() {
$.ajax({
type: "GET",
url: "{% url 'get_more_tables' %}",
data: {'append_increment': append_increment}
})
.done(function(response) {
$('#_appendHere').append(response);
append_increment += 10;
});
}, 1000)
get_more_tables.html
{% for a in questions %}
<tr><td> {{ a.question_text }} </td></tr>
{% endfor %}
I have the following issues:
According to Console Error with Ajax: ReferenceError: $ is not defined, I need to set up the js.file in the js-script. If I do not do that, I get the "ReferenceError: $ is not defined" error. Why is that, in particular, as this is not necessary for the previous above mention posts?
If I run http://localhost:8000/polls/, nothing happens. I was assuming that, when I use
q2 = Question(question_text="What's up4?", pub_date=timezone.now())
q2.save()
by python manage.py shell, the entire internal database should be shown. However, nothing is happening. When I refresh the site by hand, all entries are shown.
The inspector console of Mozilla does not show any entry. The network console of Mozilla does show that /pools and the external js file is accessed. However, no continuous access in 1s intervals is shown (not sure if that should be the case).

Your HTML is not valid, for a couple of reasons.
First, you put the script block outside the closing </html> tag. That means it's outside the document itself, and may not be read by the browser.
More importantly, you haven't got your code inside a proper script element. You have an opening tag, but you use that to reference the external jQuery library via the src attribute. You don't have a closing tag at all
You need to put the jQuery reference in its own element, and use proper opening and closing tags for your own script.
<html>
<body>
<table>
...
</table>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script type="text/javascript">
var append_increment = 0;
setInterval(function() {
$.ajax({
type: "GET",
url: "{% url 'get_more_tables' %}",
data: {'append_increment': append_increment}
})
.done(function(response) {
$('#_appendHere').append(response);
append_increment += 10;
});
}, 1000)
</script>
</body>
</html>

You have to externalyze your jquery in another file (without any tags, just the jquery). And add a ready function:
$(document).ready(function(){
// Your JS code here
});
In the html, do as follow:
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
<script src="<relative_path_to_your_js>">
The route /polls/ doesn't exist. So nothing happens. You only have routes / and /get_more_tables defined.
I did not understand the last question, what do you enter in the Interactive Console ? (After entering ./manage.py shell)

Related

Why is my base navbar doesn't show the groups that I have at /groups/? [duplicate]

I'm developing an internal application and I would like to be able to nest my views to keep everything nice and organized. I plan on doing this by keeping different parts of the page in their own HTML files with their own Views (separate sidebar and navbar, separate charts, etc).
views.py
from django.shortcuts import render
from django.views.generic import TemplateView
import Recall.data_logger.models as DLM
class ReportHome(TemplateView):
template_name = 'data_logger/index.html'
class SelectorSidebar(TemplateView):
template_name = 'data_logger/sidebar.html'
def get(self, request, *args, **kwargs):
companies = DLM.Company.objects.order_by('company_name').all()
return render(request, self.template_name, {'companies':companies,})
index.html
<html>
<head></head>
<body data-gr-c-s-loaded="true">
{% include 'data_logger/navbar.html' %}
<div class="container-fluid">
<div class="row">
{% include 'data_logger/sidebar.html' %} <!-- This is the part I need help with-->
</div>
</div>
</body>
</html>
sidebar.html
<div class="col-sm-3 col-md-1 sidebar">
<ul class="nav nav-sidebar">
{% for company in companies %}
<li>{{ company.company_name }}</li>
{% endfor %}
</ul>
</div>
I understand that by just using {% include 'data_logger/sidebar.html' %} it's just loading the HTML and bypassing SelectorSidebar, how do I direct it through the View?
I'd like a solution that allows me to access anything from a simple list of names to relitively large datasets being fed into a D3 chart.
Solution
This is what I ended up using:
index.html
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"
integrity="sha384-vk5WoKIaW/vJyUAd9n/wmopsmNhiy+L2Z+SBxGYnUkunIxVxAv/UtMOhba/xskxh"
crossorigin="anonymous"></script>
<script>
$.get("_sidebar", function(data, status){
$("#_sidebar").html(data);
});
</script>
</head>
<body data-gr-c-s-loaded="true">
{% include 'data_logger/navbar.html' %}
<div class="container-fluid">
<div class="row" id="_sidebar"></div>
</div>
</body>
</html>
Where _sidebar is the URL to SelectorSidebar:
urlpatterns = [
path('', v.ReportHome.as_view(), name='ReportHome'),
path('_sidebar', v.SelectorSidebar.as_view(), name='SelectorSidebar'),
]
I think you are making some confusion on how Django templates and views work together.
In very simple terms a Django template is what defines the HTML code that makes up a page. You can keep your templates very modular and organized; to do this you can use the include template tag or you can use template inheritance, which is a very powerful way to have "modular" templates.
A Django view is basically a function (or a class of you are using class based views) that receive an HTTP request and build an HTTP response.
It doesn't make much sense to have "nested" views because usually you have just one HTTP request and you want to build just a response with the HTML needed to display the page.
So I think that you can happily use Django templates to put together all the modules that make up your page (header, sidebar, etc.), but each page should correspond to a single Django view.
Another approach could use AJAX and Javascript to make different HTTP requests and build up the page client-side, but I think that this is not the approach you are considering here.
As #baxeico answered, you can't have multiple views to serve a page, because one HTTP request is one view.
If you have content that needs to appear on a lot of pages, like your sidebar, and that content also requires some context information to render (like a list of companies to fetch from the db), you have two options:
If the stuff required to add to the sidebar is fairly limited, create a template context processor that you add to the list of context processors in your settings (TEMPLATES setting).
def sidebar_context(request):
return {'companies': DLM.Company.objects.order_by('company_name').all()}
and in your settings, you'd add something like 'myapp.custom_contexts.sidebar_context' at the top of the list.
Now, every single template has access to the context variable companies, including your sidebar template.
If the stuff shown in the sidebar is more dynamic, or more complex, you should consider fetching the data from within the browser using AJAX. You would create a view that returns JSON instead of HTML and in your sidebar template add javascript to fetch the data and populate the sidebar.
The view is as simple as your current one:
def sidebar(request):
return JsonResponse({'companies': Company.objects.all().values('name', 'id')})
which will return a list of dicts containing name and id of each company. In your AJAX handler for the successful response (which receives the data), you can then loop through data and access data[i].name and data[i].id which you can use to populate your list.
I won't go as far as posting the full javascript (please search for jQuery, ajax and django) but here's a bit to give you an idea, assuming jQuery:
$(window).on('load', function() {
$.ajax({
url: "{% url 'sidebar' %}", // assuming this is inside a template, if not {% url %} won't work and you'll have to get it in a different way
success: function(data) {
if (data.length > 0) {
for (var i=0; i<data.length; i++) {
var elem = $("<li>" + data[i].name + "</li>")
$("#companies").append(elem)
}
}
})
})

JSignature Field Not Appearing in Django

First time posting on the site, apologies before hand as I am a newbie.
Building a Django project in Visual Studio for a class and need a signature form to appear on one of the pages. Currently been following this guide: https://pypi.org/project/django-jsignature/ but have hit a roadblock as all I can get to show on the page is a save button. Below I've listed what I've got.
forms.py
from django import forms
...
from jsignature.forms import JSignatureField
from jsignature.widgets import JSignatureWidget
...
class SignatureForm(forms.Form):
signature = JSignatureField()
template.html
{% extends "app/layout.html" %}
{% block content %}
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
{{form.media }}
<form action="." method="POST">
{% for field in form %}
{{ field.label_tag }}
{{ field }}
{% endfor %}
<input type="submit" value="Save"/>
{% csrf_token %}
</form>
</body>
{% endblock %}
views.py
from jsignature.utils import draw_signature
from app.forms import SignatureForm
...
def signature(request):
assert isinstance(request, HttpRequest)
return render(
request,
'app/template.html',
{
'title':'About',
'message':'Your application description page.',
'year':datetime.now().year,
}
)
def my_view(request):
form = SignatureForm(request.POST or None)
if form.is_valid():
signature = form.cleaned_data.get('signature')
if signature:
#as an image
signature_picture = draw_signature(signature)
#or as a file
signature_file_path = draw_signature(signature, as_file=True)
Again, when taken to my template page all that populates is a lone save button. I included this in the body of my layout html page as I had read it could be an issue with running the script on the page but still no luck.
<script src="ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
Can anyone point me in the right direction? Hopefully I have provided sufficient info.
I had the same problem. I fixed it by changing where I put {{ form.media }}.
Documentation says to put it above the form, I instead inserted it bellow all my other JS imports.
base.html
.
.
.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.4.0/Chart.bundle.min.js"</script>
{% block extra_javascript %}{% endblock %}
form-template.html
{% extends "base.html" %}
{% crispy form main_helper%}
{% block extra_javascript %}
{{form.media}}
{% endblock %}
I know this is old, but I just installed django-jsignature and had the same issue. I am new to Django and would like to share the solution to those that are also new to Django and want to use django-jsignature in their project.
There are a few things you have to check.
First, use Chrome, then go to Developer Tools. Check your console and see if you have Javascript errors. If you do, you have to modify 2 files.
The 2 files you have to modify can be found at https://github.com/fle/django-jsignature/pull/11/commits
Reload your template page, and see if you still have javascript errors on your page.
I am using jQuery Core 3.4.1. and this is working great for me with no errors. I have placed the jquery script link on the head section of my html page.
Remove all other javascript dependencies just to make sure they are not conflicting.
Now if you no longer have javascript errors and you still don't have a signature pad, move to step 2.
Load your html template in Chrome and View Page Source. On where you're supposed to have the signature section, you should see a "hidden" div with in the form. That means that the form loaded correctly in html, but your CSS may cause this section not to display correctly.
If that is the case, try creating a new template and just have the jsignature template code in it without any CSS just to test.
If you do not see a "hidden" div on your signature template html when clicking on View Page Source, that means you're page did not render correctly.
a. On your views.py, make sure you add {'form': form } in your context. This instruction was not included in the Readme.
example: return render(request, 'your_signature_template.html', {'form': form }
b. On your signature_template html file, make sure you have {{ form.media }} on top of your form.
Follow exactly what's in the django jsignature tutorial on the pypi site and just make sure you have the latest version of jquery in the head tag of your template. And also dont forget to pass an empty instance of the Signature form to your render method through to your template
I made a demo. Checkout https://djsign.herokuapp.com
And you can find the codes at https://github.com/mnnlthmpsn/djsignature.git
Same Problem, different solution:
I tried to embed the sign-field in a modal, where AGBs should be shown and signed.
Beforehand i made sure that the signaturefield would show up correctly when using nothing but the example-case by fle plus my own base with header and sidebar. There, everything was working allright.
When trying to embed it into another page (with or without being in a modal), the Signaturefield would not show up, but DevTools (Chrome) showed that it loaded correctly.
I saw the size properties of the field being "ratio" for "height" and "width", and fixed "height" to 200px. Then everything worked all right.
forms.py:
from django import forms
from .models import Signature
from jsignature.widgets import JSignatureWidget
from jsignature.forms import JSignatureField
class SignatureForm(forms.ModelForm):
signature = JSignatureField(widget=JSignatureWidget(jsignature_attrs={'color': '#e0b642', 'height': '200px'}))
class Meta:
model = Signature
fields = ['signature']

Is it possible to target the value of a html element in Django template language?

<h1 class='username'>something</h1>
{% if request.user|stringify == .username %}
Is something like this possible or not?
No, you can't do this. You're getting confused on what is client side and what is server side. Your dynamic html code is handled through Javascript - which is client side. Where as Django Template Languange is used server side where it produces a rendered html page (which is what the javascript sees)
From your use case I'd imagine that
{% if request.user.is_authenticated %}
might be enough since your log out button should hopefully be obvious enough that it is to log out the user seeing the page.
Alternatively, you can just hide it with javascript and then secure the view more to make sure that the request user does have permission to do the logging out.
Suppose your html code of Logout button is like this:
<h1 class='username'>something</h1>
<button class='logoutbtn'>Logout</button>
Then you can hide the logout button with the javascript like this:
<script type="text/javascript">
$('.username').html(data.username);
if (data.username != "{{ request.user }}" )
{
$('.logoutbtn').hide();
}
</script>
Or based on the data.logged_in_user like this :
<script type="text/javascript">
$('.username').html(data.username);
if (data.username != data.logged_in_user )
{
$('.logoutbtn').hide();
}
</script>

How to autocomplete a search form?

I have a search form and it works fine. Now I would like my search form autocomplete.I tried using django autocomplete light but I have a problem implementing it.
Is it possible to use my existing code, add JavaScript and make it work?
I tried to do it myself but I came to a wall.
I would really appreciate if someone could help me with this, give me a hint or a link for a working demo or tutorial.
This is my current code. Thanks for your time.
views.py
def search(request):
if 'q' in request.GET and request.GET['q']:
q = request.GET['q']
search_list = Book.objects.filter(
Q(title__icontains=q) | Q(description__icontains=q))
return render_to_response('books/search_results.html', {'search_list': search_list, 'query': q}, context_instance=RequestContext(request))
else:
return render_to_response('books/please_submit.html', {}, context_instance=RequestContext(request))
urls.py
urlpatterns = patterns('',
url(r'^search/$','papers.views.search', name='search'),
)
search.html
<form method='get' action='/search/'>
<input type='text' name='q' class="btn btn-theme btn-sm btn-min-block biggerForm">
<input type='submit' value='Search' class="btn btn-theme btn-sm btn-min-block">
</form>
Django-autocomplete-light is tricky to set up and in my opinion its easier using other autocompletes.
Here is how I got it working using bootstrap 2. (There is a bootstrap 3 compatible library as well, and the configuration is more or less the same https://github.com/bassjobsen/Bootstrap-3-Typeahead).
You need a few things to work together.
1: Create a view that will process the autocomplete request and return suggestions.
so in views.py
def book_autocomplete(request, **kwargs):
term = request.GET.__getitem__('query')
books = [str(book) for book in book.objects.filter(Q(title__icontains=q) | Q(description__icontains=q))]
return HttpResponse(json.dumps(books))
And in urls.py add an entry:
url(r'^autocomplete/book_autocomplete/' , booking.ebsadmin.book_autocomplete , name='book_autocomplete'),
2: Load the bootstrap typeahead/autocomplete code into your page. The project I inherited was already using bootstrap 2, so this code was already there.
So in your template in the head section (this will probably differ depending on the the directory structure of your static files):
<script type="text/javascript" src="{{ STATIC_URL }}bootstrap/js/bootstrap.min.js"></script>
<link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}bootstrap/css/bootstrap.min.css" />
3: Connect the bootstrap typeahead/autcomplete to your view.
I created a file book_autocomplete.js, and added it to my static files folder. This tells it to attach the autocomplete to the element with id book-search (you will have to give the search box on your form and id equivalent to the '#book-search' that I have used here. An example on how to do this in your form definition https://stackoverflow.com/a/5827671/686016):
book_search_typeahead.js
$(document).ready(function() {
$('#book-search').typeahead({source: function (query, process) {
return $.getJSON(
'/autocomplete/book_autocomplete/', // this is the url for the view we created in step 1
{ query : query },
function (data) {
console.log(data) ;
return process(data);
});
}});
});
back to your template and add this line to load the javascript that we just created:
<script type='text/javascript' src='{{ STATIC_URL }}book_search_typeahead.js' ></script>
I think that is everything.
#wobbily_col answer works obviously but generally you want to avoid hitting a relational database to create autocomplete results. I have made an autocomplete library that works fast and is easy to use. But all it does is to give you text results back. It is up to you to feed it data and make its responses in your API. Checkout: https://github.com/wearefair/fast-autocomplete
And here is an explanation of how it works: http://zepworks.com/posts/you-autocomplete-me/

How to use Django views and template tag simultanosly?

In my Django-template:
<div class="A">
{% url renders_data object.id %}
</div>
<div class="B">
{% render_data object.id %}
</div>
Div A is common way to call a method in views.py whereas Div B is for template tags.
User will open a link. Let's say: /myapp/test/ a page will open contain two template tag section at the page. Name of this tag is render_data I want to load the data into each template tag with Ajax. To work with it we need request.ajax:. That is why i thought to write views method. I thought to complete it with the following way:
I wrote the exact copy of template tag in views.py (renders_data with passing object_id parameter) and render it to the right template. When i open the maypp/test after removing the div A from template. It shows the URL (myapp/test/<object_id>) in each template tags section (each corner) except the data. Is their any possibility to show the context except this URL See the image when i choose this option
Second i also thought to import views method (renders_data) in template tag (render_data). So that data will display in each corner and request.Ajax: will also work. if this can be possible then how?
I am not able to solve this issue. please help me :(
See how the render_data looks like:
#register.simple_tag
def render_widget(object_id):
from myapp.plugins.model import Widgetsetting
widget_setting = Widetsetting.objects.get(id = object_id)
widget = widget_settings.get_widget()
template_name = widget.template_name
context = widget.context(widget=widget_settings)
t = get_template("widgets/%s" % template_name)
return t.render(Context(context))
From the code you've posted something like below should work...
in views.py:
from django.http import HttpResponse
from netadmin.plugins.templatetags.widgets import render_widget
def ajax_render_data(request, object_id):
return HttpResponse(render_widget(object_id))
in your django template:
(you said you want several of these on the page, so I'm going to use a loop in the django template, I don't know what your widget collection is called but I'm sure you can work it out for your particular case)
<div class="widgets-container">
{% for widget_settings in widgetsettings.objects.all %}
<div class="widget-content" id="widget-content-{{ widget_settings.pk }}">
not loaded yet
</div>
{% endfor %}
</div>
<script>
// here we use django to render the ajax urls into an object
// in javascript so we can use it on the client side
var ajax_urls = {
{% for widget_settings in widgetsettings.objects.all %}
"{{ widget_settings.pk }}: "{% url ajax_render_data widget_settings.pk %}"{% if not forloop.last %},{% endif %}
{% endfor %}
};
// (I'll assume you're using jQuery)
// start the ajax calls when the page is loaded:
$(document).ready(loadWidgets());
function loadWidgets() {
// loop over the urls we provided from django:
jQuery.each(ajax_urls, function(widget_id, url) {
// do the ajax call:
$.get(url, function(data) {
// put the content into the widget div:
$('#widget-content-'+widget_id).html(data);
});
});
}
</script>
in urls.py:
urlpatterns += patterns('netadmin.plugins.ajax_view',
url(r'^ajax/(?P<object_id>\d+)/$', 'ajax_render_data', name='ajax_render_data'),
)

Categories

Resources