I am trying to create a dependable dropdown on Django but since my JavaScript/ajax knowledge is not great, I have hit rock bottom. Note: I have read previous questions on this matter but none of them fully solved my problem.
Problem Description:
Due to my database size, I am retrieving partial data from the server whenever a view is requested. This makes my job of using forms harder since I am using the username of the user to filter my server. Here is a simplified version of my code.
urls.py
urlpatterns = [
url(r'^SpecificVessel', views.SpecificVessel, name="goSpecificVessel"),
]
views.py
#login_required
def SpecificVessel(request):
#Get the username to filter the tables from SQL Server:
username = None
if request.user.is_authenticated:
username = request.user.username
#Get the shipnames.
cursor.execute("select distinct SHIPNAME from Table where [GROUP]=" + "'" + username + "'")
row = cursor.fetchall()
df_listofships = pd.DataFrame(data=row, columns=['SHIPNAME'])
shipnames = list(df_listofships['SHIPNAME'].tolist()) # LIST FOR SHIP SELECTION
#Get All the data from database.
cursor.execute("select * from Table2 where [GROUP]=" + "'" + username + "'")
row = cursor.fetchall()
df = pd.DataFrame(data=row)
colnames = list(dftrans.columns.values.tolist()) #LIST FOR YEAR DROPDOWN SELECTION
#getting the dropdown selections:
Dropdown_shipname = request.POST.get('Dropdown_shipname')
Dropdown = request.POST.getlist('Dropdown')
return render(request, 'SpecificVessel.html',
{'colnames': colnames, 'Dropdown': Dropdown, 'shipnames': shipnames, 'Dropdown_shipname': Dropdown_shipname,})
SpecificVessel.html
<form method="post">
{% csrf_token %}
<div class="form-group col-md-4">
<label for="Dropdown_shipname"><b>Select Vessel</b></label>
<select name="Dropdown_shipname" id="Dropdown_shipname" data-style="btn-default" class="selectpicker form-control" >
{% for i in shipnames %}
<option value="{{ i }}" {% if Dropdown_shipname == i %} selected {% endif %}>{{ i }} </option>
{% endfor %}
</select>
</div>
<div class="form-group col-md-4">
<label for="Dropdown"><b> Select Month </b></label>
<select name="Dropdown" id="Dropdown" data-style="btn-default" class="selectpicker form-control" multiple>
{% for i in colnames %}
<option value="{{ i }}" {% if Dropdown == i %} selected {% endif %} >{{ i }} </option>
{% endfor %}
</select>
</div>
<div class="form-group col-md-1 margin_top_25">
<input type="submit" value="Submit" />
</div>
</form>
What is the problem?
The solution I have in the code above provides me with independent dropdowns. That is, whenever there is a mismatch, it throws me an error. I have been trying to approach this in different way, however, after long research online, I found out that javascript or ajax may be the way to go about this. My question is this: Is there any way in which I could get what the user has selected in Dropdown_shipname before he submits the results? If yes, how would you solve this problem?
I hope I was clear enough. Please let me know if I should explain the problem any better.
There's a lot I feel I need to address before answering your main question.
The if request.user.is_authenticated bit is unnecessary; you already decorate the view with #login_required, so there's no way the user isn't authenticated.
Where does cursor come from? It doesn't look like you're using Django's database stuff (the ORM, or even raw cursors), but something else? Why is that?
Having a global cursor may lead to trouble down the line in production, when it's being shared between requests in a multithreaded situation. (Using Django's database functionality the database connections are correctly reset between requests, and each thread gets its own connection.)
Your SQL queries are vulnerable to SQL injection attacks, since you're just concatenating strings together. You need to use placeholders (parametrized queries) instead. How that's done depends on the database and database driver you're using.
You definitely don't need Pandas and a Pandas dataframe to extract the data from your database result! (My pet peeve: useless use of Pandas.)
The first retrieval would be shipnames = [row[0] for row in cursor].
The second retrieval would be colnames = [d[0] for d in cursor.description] (or similar; depends on your database). (However, you really don't want to fetch a number of rows just to get the column names; one row, e.g. LIMIT 1 in standard SQL, would do.)
You should be using Django forms to manage, well, forms. That way you don't need to manage rendering the <select>s and <option>s and selecteds manually.
This view would likely become a FormView subclass.
You say "This makes my job of using forms harder since I am using the username of the user to filter my server.", but that's a non-issue. You can well pass in your Django request, or just the User, or an username, to a custom form class, and have it modify or even add fields dynamically to the form based on it.
That said, the most minimal solution here is a tiny bit of JavaScript, to refresh the page with an added query string argument for the first selection. That is, when the user changes the shipname field, you'd refresh the page with e.g. ?shipname=selection-here, and deal with figuring out the correct choices for the other field in your view code.
The most minimal way I can think of is
<script>
document.getElementById("Dropdown_shipname").addEventListener("change", (event) => {
location.href = `?shipname=${event.target.value}`;
}, false);
</script>
Beyond that, you could use an AJAX request to selectively refresh only part of the page, and beyond that, maybe refactor the form into, say, a React.js or Vue.js component that deals with the form.
But either way, no, you're not going to be able to dynamically change the other field without JavaScript.
Related
I have a Django localhost web server that I've been experimenting with, and I'm doing the infamous "todo app" as my intro to a few topics.
Conceptually, the user puts their "todo item" into a text input field (which is a Django form that's inside of the Django HTML template). This action takes the text and puts it in an SQLite3 database and at the same time, the action creates a div container that has a paragraph tag and a button inside of that div. The P tag gets filled with the contents of the text given in the input. The button is meant to do two things; firstly, it deletes the parent div, which removes that button and the todo item. So, it's like a "this item is done" button, which deletes that item. That is working properly.
The SECOND function of the button, though, has got me stumped. The idea is to delete that item from the database as well, as that's where I store and retrieve the todo items from when the page is generated. I have attempted to do this by passing the {item} variable (which is a Django Template Language variable made inside of the Django view, which I will show below) into a delete function, which I am expecting should delete the associated database entry, by filtering for any item that contains the text within {item}. Now, I know this isn't a perfect method, but it's my first try. Please don't nitpick it too hard. It's not meant to be a customer-facing application, it's only an experiment to be used by me.
The problem: The button that should be deleting the associated database entry bypassing the {item} variable into a filter delete method for the database is throwing errors and is not working, despite me messing with the syntax endlessly. I'm clearly doing something wrong, but I don't know what, and the chrome dev tools error is REALLY not helping or making sense. I'd greatly appreciate any help.
The code:
relevant django template section for the todo page/app:
<div class="root-div container">
<div class="app-div container">
<div class="form-div container">
<form method="post">
{% csrf_token %}
{{ form }}
<button type="submit" class="btn">Post</button>
</form>
</div>
<div class="tutorial-container container">
<p class="container">Items will show up below.</p><hr></br>
</div>
<div class="todo-items-container container">
{% for item in all_todo_items %}
<div class="spawner container">
<p class="item-container container">>: {{ item }}</p>
<button class="delete-item-button btn" onclick="this.parentNode.remove();Todoitem.objects.filter(item={{ item }}).delete();">Del</button>
</div>
{% endfor %}
</div>
The view associated with this app:
from django.shortcuts import render
from django.http import HttpResponse, HttpResponseNotFound, HttpResponseRedirect #for handling basic responses
from .forms import TodoForm
from .models import Todoitem
def todo_view(request, *args):
all_todo_items = Todoitem.objects.all #grabbing items from the db and putting them in a variable
if request.method == "POST": #block for when info is being pushed thru view
form = TodoForm(request.POST)
if form.is_valid():
form.save() #posts to db hopefully
form = TodoForm()
return render(request, 'todo/base.html', context={
"form": TodoForm,
"all_todo_items": all_todo_items})
The associated model:
# always makemigration and migrate when models are changed
class Todoitem(models.Model):
#the modelform will pull from this list to populate the form fields/types/elements
item = models.CharField(max_length=200, blank=True)
def __str__(self):
return self.item
# when objects are called manually, they return their strings instead because of this
The error:
When I run this application and test it in chrome dev tools, this is what I get.
This makes little to no sense to me, and it doesn't really give me an obvious source of what the issue is, although I obviously suspect it involves the inline JS function I'm attempting to do, AKA
Todoitem.objects.filter(item={{ item }}).delete();
Again, I'd really appreciate any insight on what on earth is going wrong, whether it's a syntax thing, or I'm calling a variable in a method that doesn't work that way, or anything. Although, please be aware that I am a novice to this, and I haven't heavily used Javascript yet, and I am still quite new to the DOM and advanced Python stuff.
Thank you.
edit: I'm running Windows as my main OS, and the Django webserver is running Python 3.8.2, and Django 3.0.4. This is contained within a virtual environment. All other parts of the Django webserver work perfectly. It also boots up normally.
Since I'm new to Django and have absolutely no knowledge of JS/AJAX, I'm trying to code in the most simple possible way dependent dropdowns. I managed to get my dropdowns populated with values from my database (data_immo). Now I need to have the second dropdown updated after user selection in the first one.
Here is my home.html file with the two dropdowns (regions and departements):
<select name="regions" id="regions">
{% for item in query_results_dict %}
<option value={{ item.nom_reg }}>{{ item.nom_reg }}</option>
{% endfor %}
</select>
<select name="departements" id="departements">
{% for item in departements %}
<option val="{{ item.insee_dep }}"> {{ item.insee_dep }} </option>
{% endfor %}
</select>
views.py:
def MyView(request):
query_results = data_immo.objects.all()
regions = data_immo.objects.values_list("nom_reg", flat=True).distinct()
departements = data_immo.objects.values_list("insee_dep", flat=True).distinct()
query_results_dict = {
'query_results': query_results,
'regions': regions,
'departements': departements,
}
return render(request,'home.html', query_results_dict)
models.py:
class data_immo(models.Model):
id = models.AutoField(primary_key=True)
insee_dep = models.IntegerField(db_column='INSEE_DEP', blank=True, null=True)
nom_reg = models.TextField(db_column='NOM_REG', blank=True, null=True)
class Meta:
managed = True
db_table = 'immo'
My understanding is that I need to retrieve the selected value from the "regions" dropdown and use an onChange function to trigger an action that will use that variable and inspect the db so only matching depatements are selected. Unfortunately I don't know how to proceed. I know there are a few examples out there, but none of them were really satisfying, and those I tried failed due to my lack of JS/AJAX knowledge.
Let me know if need more details.
EDIT 1: added models.py; also, the website that has been recommended in the comments has values stored in different tables whereas mine are all in the same one. Surely this must have an impact on my code and cannot be replicated per se.
I have a web form created that requires template creation by the user. Calling all previous entries that are templates isn't an issue, but passing the information to the form when called seems to be tricky. Basically I want the user to be able to select a template from the dropdown (See the screenshots and stuff below), then based on their selection, update the variables in the script to autofill form data. Right now, it only selects the most recent entry. Is this possible just using python/flask/javascript or am I missing something? Any help would be appreciated! Thanks!
Template Dropdown
<label for="template_select">Select Template</label>
<select class="form-control" name="template_select" id='template_select' onchange='changeTemplate()'>
<option value=''></option>
{% for template_info in template_search %}
<option value='{{template_info.client_name}}'>{{template_info.client_name}}</option>
{% endfor %}
</select>
Javascript to change values
{% for template_info in template_search %}
<script>
function changeTemplate(){
var template = document.getElementById('template_select').value;
document.getElementById('client_name').value='{{template_info.client_name}}';
document.getElementById('client_name').innerHTML='{{template_info.client_name}}';
}
</script>
{% endfor %}
Python Passing in the Query
template_search = newmatter_model.NewClientMatter.query.filter_by(
creator=verifier, is_template='on').all()
Your mistake is to create Javascript code in a loop. You don't need to do this.
What you want to do is think of the data sent to the browser as independent. Make it work first without Flask and Jinja2. Create a static page that works and does what you want.
The following code would work with static data:
function changeTemplate(){
var template = document.getElementById('template_select').value;
document.getElementById('client_name').innerHTML = template;
}
<label for="template_select">Select Template</label>
<select class="form-control" name="template_select" id="template_select" onchange="changeTemplate()">
<option value=""></option>
<option value="Client 1">Client 1</option>
<option value="Client 2">Client 2</option>
<option value="Client 3">Client 3</option>
</select>
<div id="client_name"><i>No client set</i></div>
That's HTML for a select box, a separate <div> element, and Javascript code to copy the selected option value into the <div>. Note that the Javascript code doesn't know anything about what data is involved; no client names are stored in the code. All that the small function does is to copy the value from the currently selected option, to somewhere else.
Once that works on its own you can start thinking about how you are going to insert the values from your application. In the above code, all that needs replacing is the dropdown options, because the Javascript code can then access everything it needs from the <select> element value.
So the Javascript code doesn't need to be generated at all, you only generate the <option> elements, as you already did in your question.
You rarely need to generate dynamic Javascript code, and it would be much better for your app if you don't. Static Javascript code can be served by a CDN and cached in the browser, removing the need for your app to keep serving that again and again for all clients. Try to minimise that whenever you can.
Instead, generate just the data that the code needs to operate on.
You can put that information in HTML tags. In the above example, your data is put in the repeated <option> tags. Or
you could add data attributes to your HTML, which are accessible both to Javascript code and to CSS. Or
use AJAX to load data asynchronously; e.g. when you pick an option in the <select> box, use Javascript and AJAX to call a separate endpoint on your Flask server that serves more data as JSON or ready-made HTML, then have the Javascript code update your webpage based on that. Or
generate JSON data and put it directly into your HTML page. Just a <script>some_variable_name = {{datastructure|tojson|safe}};<script> section is enough; then access that some_variable_name from your static Javascript code to do interesting things on the page. JSON is a (almost entirely a) subset of Javascript, and the way the tojson filter works is guaranteed to produce a Javascript-compatible data structure for you. The browser will load it just like any other embedded Javascript code.
I am struggling with something that is probably very basic: I need to generate a form with marks for my University database application. Each student in each module has a class got "Performance" that stores all the marks for the module. There are different assessments and the Performance class calculates the average for them.
I need to be able to enter, for example, all the marks for the first assessment, and I did that with a dynamically generated Django Form as a table in the template:
{% for performance in performances %}
<tr>
<td>
{{ performance.student }}
</td>
<td>
{% if to_mark == 1 %}
<input type="text" class="input-mini" name="{{ student.student_id }}" value="{{ performance.assessment_1 }}">
{% else %}
{{ performance.assessment_1 }}
{% endif %}
</td>
And the same for the other assessments (to_mark gets passed on by views.py to indicate which assessments needs to be marked here)
I have failed to use Inlineformfields and therefore decided to generate a form dynamically and validate it with Javascript, also because the validation is simple (has to be a number between 1 and 100), and also because I want to use Javascript to calculate the average on the fly.
My problem is that I have no clue about Javascript. All the tutorials (like this one http://www.w3schools.com/js/js_form_validation.asp) use the name of the form field in the Javascript function, but in my case that name is dynamically generated, so that I can access it easily in the views.py:
if student.student_id in request.POST:
tmp = request.POST[student.student_id]
try:
mark = int(tmp)
if mark in range(0, 100):
performance = Performance.objects.get(student=student, module=module)
if to_change == 1:
performance.assessment_1 = mark
...and so on for the other assessments
except ValueError:
pass (in case someone accidentally enters a letter and not a number)
Is there a way I can use Javascript to address my form fields? Or should I use a different approach than taking the student_id as the name to read it out? How could I do that?
Thanks,
Tobi
There are at least 3 ways to get to the form fields using JavaScript:
By ID, by CSS class or by DOM traversal. If you're not familiar with JavaScript, I would suggest finding a django-friendly client-side form validator like: https://code.google.com/p/django-ajax-forms/
I am creating a form, where people can either choose a new team and then input its location or if they select a team from the list, I would like the input to be disabled - and the selected team's location to be given in the input box. This is my code so far. It doesn't work. :(
<select id="chooseTeam" name="chooseTeam" data-placeholder="Select Team">
<option></option>
{% for team in teams %}
<option>{{team.name}}</option>
{% endfor %}
<option>New Team</option>
</select>
<input type="text" id="input_location" name="input_location"/>
In a separate JS File:
$(document).ready(function() {
$('#chooseTeam').on('blur', function() {
if (form.chooseTeam.value != "New Team" && form.chooseTeam.value != "Select Team") {
$("#input_location").html("{{team.location}}");
document.getElementById("input_location").disabled = true;
}
});
});
MINI-UPDATE
Apparently, AJAX is needed for this. I am brand new to Javascript (3 days old) so I don't know how AJAX works yet but I am at this moment googling it to try and figure it out.
Oh, and Merry Christmas, everybody!
"#code-on-christmas"
It seems that you are using Django and jQuery. Is that true? I cannot give you a fully detailed answer at the moment, but the code in the following line cannot work because you are mixing client-side and server-side code.
$("#input_location").html("{{team.location}}");
The expression {{team.location}} cannot be evaluated by Django because it only exists on the client-side in your browser. If you want Django to evaluate what has been chosen on the client-side, you should use AJAX calls between server and client by using $.ajax().
Also, why don't you use jQuery selectors for all of your code? Do something like this:
$(document).ready(function() {
$('#chooseTeam').on('blur', function() {
var currentTeam = $(this).val();
if (!$.inArray(currentTeam, ['New Team', 'Select Team'])) {
$("#input_location").attr('disabled', 'disabled');
}
});
});