I used Django to develop a web app.
In one page there's a form, once filled, a duplicate button should open a new tab of the page with the same data filled.
I already finished the new tab js function, but I do not know how to make the form in new tab filled with the data in previous tab(the same isbn filled in previous tab in my case).
<form>
<label> iSBN: </label> <input type="text" id="iSBN" name="iSBN"><br>
</form>
<button onclick="Duplicate()">Duplicate</button><br>
{% endblock %}
<script>
{% block script %}
function goBack() {
window.history.back();
}
function Duplicate(){
document.getElementById("demo").innerHTML = "Duplicate Copy for editing";
window.open(
'/new-content-checklist/',
'_blank' // <- This is what makes it open in a new window.
);
}
{% endblock %}
</script>
You can pass data from url parameters and in another tab you can then access url parameters and fill the form like this.
// const url = new URL(window.location);
const url = new URL('http://example.com/new-form.html?isbn=12345')
const previousData = url.searchParams.get('isbn')
console.log(previousData)
//output: 12345
A working demo code: jsFiddle
In short:
const data = new URL(window.location).searchParams.get('isbn')
Related
I want to modify Django admin interface.
There is delete button already has a confirmation page. But I want to add this confirmation to save or change buttons. Actually not exactly the same delete button.
I want to change default admin buttons like this JS or I want to add JS to admin buttons.
<input type="submit" onclick="linkSubmit('http://www.google.com')" value="Submit">
<p id="demo"></p>
<script>
function linkSubmit(link) {
let text = "Press a button!\nEither OK or Cancel.";
if (confirm(text) == true) {
window.location.href = link;
} else {
}
document.getElementById("demo").innerHTML = text;
}
</script>
We found the solution in the files of the delete command. We took copies of the files confirm to the delete function and connected them to the confirm button.
We still can't give it as a alert. Gives confirmation on a another page.
Assuming there is already some type of event listener for the button I would add my own custom function as an additional listener for the on click event. Then I would put in my if(confirm) logic and call event.stopImmediatePropagation() as needed to prevent the original functionality from occuring.
Create templates/admin/change_form.html in your project:
{% extends "admin/change_form.html" %}
{% block admin_change_form_document_ready %}{{ block.super }}
<script id="django-admin-form-change-constants"
data-model-name="{{ opts.model_name }}">
let modelName = document.getElementById('django-admin-form-change-constants').dataset.modelName;
let form = document.getElementById(modelName + '_form');
form.addEventListener('submit', (event) => {
let text = "Press a button!\nEither OK or Cancel.";
if (!confirm(text)) {
event.preventDefault();
}
});
</script>
{% endblock %}
Set DIRS in myproject/settings.py to point to your project's templates directory:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates'],
'APP_DIRS': True,
...
}
]
References:
https://docs.djangoproject.com/en/4.1/ref/contrib/admin/#overriding-admin-templates
https://docs.djangoproject.com/en/4.1/howto/overriding-templates/#overriding-from-the-project-s-templates-directory
https://github.com/django/django/blob/4.1/django/contrib/admin/templates/admin/change_form.html#L66-L74
https://github.com/django/django/blob/4.1/django/contrib/admin/static/admin/js/change_form.js#L5
I have a page that makes ajax calls, and my view checks if the call is ajax with .is_ajax function. However, when the page is initially loaded (for example if I stop running the server and then restart it) the is_ajax function seems to return True, which causes a MultiValueDictKeyError at /alltext, because the request does not contain the key "alltext", which contains the data from my other ajax call.
The page is a e-commerce product page, and the product has different variants (i.e. size and color), and when the user chooses variants from a dropdown menu (i.e. Large, Blue), it makes an ajax call to the backend to retrieve the price of this specific variant from the database.
Here is my code:
views.py
def product_info(request):
if request.method == "GET" and not request.is_ajax: # this is supposed to be loaded on page load
return render(request, "product_info.html")
elif request.is_ajax and request.method == "GET":
print(request.is_ajax)
'''When a user chooses a product variant on the page, this makes an ajax call
to retrieve the price of this combination'''
print("request was ajax")
combinations = request.GET["alltext"]
combinations_list = combinations.split(";")
product = Product.objects.all().latest("id")
var_names = Variation.objects.filter(product=product)
corresponding_values = []
for i in range(len(combinations_list)):
# finding this variant in database
var_name = var_names[i]
var_values = VariationValue.objects.filter(variation_name=var_name)
for val_obj in var_values:
val = val_obj.value
if val == combinations_list[i]:
corresponding_values.append(val_obj)
found_price = None
for i in range(len(corresponding_values)):
val = corresponding_values[i]
if i == 0:
combo_query = VariationCombination.objects.filter(variation_value=val)
else:
combo_query = combo_query.filter(variation_value=val)
price = combo_query[0].price
return HttpResponse("You chose: " + combinations + "price: " + price)
And here is the relevant part of the product_info page - the forms where the user chooses the variants and the script that sends the variants to the backend:
product_info.html
<form class="variations-form">
{% for name in variation_names %}
<div class="form-group">
<label class="btn-label">{{ name.variation_name }}:</label>
<!--<button type="button" class="btn btn-danger dropdown-toggle" data-toggle="dropdown">
<span class="selection"></span><span class="caret"></span>
</button>-->
<select name="variation{{ forloop.counter }}" class="form-control variations" id="variation{{ forloop.counter }}">
<option value="" selected disabled>{{ name.variation_name }}</option>
{% for key, value_list in variation_values.items %}
{% if key == name.variation_name %}
{% for value in value_list %}
<option value="{{ value }}">{{ value }}</option>
{% endfor %}
{% endif %}
{% endfor %}
</select>
</div>
<br>
{% endfor %}
</form>
Script tag with ajax call
<script>
$(document).ready(function () {
$('.form-control').change(function(){
if ($('#variation1').val()) {
var valueFrom = $('.variations option:selected').map(function () {
return $(this).val();
}).get();
var alltext = valueFrom.join(";")
$('#chosen-options').html(alltext).show('fast');
$.ajax(
{
type:"GET",
data:{
'action':'options_chosen',
alltext: alltext
},
success: function( data )
{
$('#chosen-options').html(data).show('fast');
}
});
};
});
});
</script>
Still not entirely sure what was wrong but I fixed it! At first, the page was setup so that it loads the template "homepage.html" at first, and then when you click on a product it opens in the same page, by loading the template "product_info.html". So I just changed it so that the product_info opens up on a new page when you click a product (and added the new page to urls.py), and now it works!
I would like to pass a list of ids to a view when i click on a link:
<a href="{% url 'app:manage_ids' %}?ids={{ids}}">
So i have created a template var and pass it to js:
{% with ids="" %}
<script type="text/javascript">
window.ids = "{{ ids }}";
</script>
{% endwith %}
I manage object ids when click on checkbox:
<input type="checkbox" class="checkbox" value="{{ object.id }}">
Adding or removing it to a list:
$('.checkbox').change(function() {
var id = $(this).val();
// if django template var is empty
var list_ids = [];
// else
var list_ids = window.ids;
if ($(this).is(':checked')) {
list_ids.push(id);
// here i have to save it on a django template var to use it on the click link
}
else {
const index = list_ids.indexOf(id);
if (index > -1) {
list_ids.splice(index, 1);
// here i have to save it on a django template var to use it on the click link
}
}
});
Anybody could help me about how to do it ?
Thanks you so much.
You cant update the django variable and expect it to update your href value of the link.
Moreover, you should not send the value of a GET query parameter as a list datastructure, instead you'll send it as ?ids=<value1>&ids=<value2>
To do this you'll have to first construct such string of url and then dynamically update the href of your hyperlink with it.
You can first decalre the string variable for you href in javascript somehwere as:
var id_link = "{% url 'app:manage_ids' %}?ids=";
Then whenever the click checkbox function is called you can add new value to this and update the href of link:
id_link += "&ids=" + id;
Add an id to your href and get it and update it
var a = document.getElementById('yourlinkId');
a.href = id_link
Now when someone clicks this link, you can get these in query parmeters in view as:
request.GET.getlist('ids')
and you shall get list of all IDs.
Note (My two cents): Ideally in a good archiecture this should happen by clicking a button with a post request from template in django.
I've created a page, where user can make an order. So they can fill some fields like description,notes etc. including "language", which is a one field of formset.
So in my view, there is a job_creation_form which contains almost all fields, then there is a Formset consists of Language forms.
I would like to use JQuery validate plugin to validate fields without refreshing the page. The problem is that I can see one form tag in HTML, which doesn't have any id. How to set the id?
def create_order(request):
LanguageLevelFormSet = formset_factory(LanguageLevelForm, extra=5, max_num=5)
language_level_formset = LanguageLevelFormSet(request.POST or None)
job_creation_form = JobCreationForm(request.POST or None, request.FILES or None)
context = {'job_creation_form': job_creation_form,
'formset': language_level_formset}
if request.method == 'POST':
if job_creation_form.is_valid() and language_level_formset.is_valid():
cleaned_data_job_creation_form = job_creation_form.cleaned_data
cleaned_data_language_level_formset = language_level_formset.cleaned_data
for language_level_form in [d for d in cleaned_data_language_level_formset if d]:
language = language_level_form['language']
level = language_level_form['level']
job = Job(
customer=request.user,
text_to_translate=cleaned_data_job_creation_form['text_to_translate'],
file=cleaned_data_job_creation_form['file'],
short_description=cleaned_data_job_creation_form['short_description'],
notes=cleaned_data_job_creation_form['notes'],
language_from=cleaned_data_job_creation_form['language_from'],
language_to=language,
level=level,
)
job.save()
return HttpResponseRedirect('/order-success')
else:
print job_creation_form.errors
print language_level_formset.errors
return render(request, 'auth/jobs/create-job.html', context=context)
return render(request, 'auth/jobs/create-job.html', context=context)
In Django django.forms.Form only deals with the internal (i.e. fields, validation, saving) parts of a form, not any of the details relating to submission(submit buttons, actions, method).
That is why when you render a form in a template it is like:
{% extend base.html %}
{% block content %}
<form action='' method='post'>
{{ form }}
</form>
{% endblock %}
If you want you can set an id property on the form and access it in the template like <form ... id='{{ form.id }}'>, or just put an id on the form.
I have a simple html page which renders with a number of nearly identical forms for the user to submit. Upon submit, the view is intended to add a row to the database, recreate the list of forms with slightly updated data, and send it back to the browser ('/route/complete/' maps to add_completed_route_view in urls.py).
This works perfectly the first time. Once the page has been redrawn with the new list of forms, however, the next submit will fail the request.is_ajax() test I have in the view. That causes it to skip to request.REQUEST['next'] and subsequently to home_view.
I've commented it out below, but I've also tried appending c['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest' to the view but it hasn't helped.
I'm looking for help in ensuring that the headers continue to have the appropriate XMLHttpRequest param while the user submits through AJAX. Code is below, and help is much appreciated.
script.js
<script>
$(document).ready(function() {
$(".doneForm").submit(function() {
var route_id = $(this).find('input[name=route_id]').val()
var next = $(this).find('input[name=next]').val()
var reqData = {route_id:route_id,next:next}
$.ajax({
type: "post",
url: "/route/complete/",
data: reqData,
success: function(data) {
$("#routeTable").html(data);
}
});
return false;
});
});
</script>
and
template.html
<div id="routeTable">
{% for route in route_list %}
<div id="routeDone">
<form class="doneForm" action="/route/complete/" method="post">
<input type="hidden" name="route_id" value="{{ route.route_id }}" />
<input type="hidden" name="next" value="{{ request.get_full_path }}" />
<input type="submit" value="Done" class="doneButton" />
</form>
</div>
{% endfor %}
</div>
and
views.py
def add_completed_route_view(request):
if request.method == 'POST' and request.user.is_authenticated():
add_completed_route(request)
if request.is_ajax():
wall_slug = get_wall_slug_from_route_id(request.REQUEST['route_id'])
c = get_context_for_wall_page(request, wall_slug)
# c['HTTP_X_REQUESTED_WITH'] = 'XMLHttpRequest'
return render_to_response('m/js_route_table.html', c, context_instance=RequestContext(request))
else:
return HttpResponseRedirect(request.REQUEST['next'])
else:
return HttpResponseRedirect(reverse('home_view'))
The problem is that once the Ajax is completed, it replaces the original form with a new one - and this one no longer has the javascript event handler attached, so the next time the form submits via the normal POST method.
Luckily, jQuery has a couple of methods that handle this for you - live and delegate. So instead of $(".doneForm").submit(function() ..., do this:
$(".doneForm").live("submit", function() {...