I've built dynamic forms using django formsets and javascript, but unfortunately on submitting the form only the first form is submitted. I'd like all dynamically added forms to be submitted also. Any help would be appreciated.
Views:
def routestepinfo(request):
class RequiredFormSet(BaseFormSet):
def __init__(self, *args, **kwargs):
super(RequiredFormSet, self).__init__(*args, **kwargs)
for form in self.forms:
form.empty_permitted = False
RouteStepFormSet = formset_factory(RouteStepForm, formset=RequiredFormSet, extra=1, can_order=False, can_delete=True)
if request.method == 'POST':
formset = RouteStepFormSet(request.POST)
if formset.is_valid():
for form in formset.forms:
form.save()
print 'apple'
return redirect("/buildpage/")
else:
formset = RouteStepFormSet()
return render(request, "buildpage/routestepinfo.html", {'formset' :formset})
HTML
<form id= "myForm" method = 'POST' action="{% url 'buildpage:routestepinfo' %}" enctype="multipart/form-data">{% csrf_token %}
{{ formset.management_form }}
<div id="form_set">
{% for form in formset %}
<table class='no_error'>
<tbody>.
{{form.as_table}}
</tbody>
</table>
{% endfor %}
</div>
<p><input type = "button" value = "Add another step" id = "add_more" ></p>
<div id="empty_form" style="display:none">
<table class='no_error'>
{{ formset.empty_form.as_table }}
</table>
</div>
<div id="forms"></div>
<p> </p>
<p> </p>
<input type = "submit" name = "Submit Steps">
</form>
JS Clone:
<script>
$('#add_more').click(function() {
var form_idx = $('#id_form-TOTAL_FORMS').val();
$('#form_set').append($('#empty_form').html().replace(/__prefix__/g, form_idx));
$('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1);
});
</script>
Fixed it ! Only took a week. Here's how it worked eventually. Views are largely unmodified, just added commit=False to save the list.
def routestepinfo(request, slug=None):
RouteStepFormSet = formset_factory(RouteStepForm, formset = RequiredFormSet, extra=1)
if request.method == 'POST':
formset = RouteStepFormSet(request.POST)
print formset
if formset.is_valid():
for form in formset.forms:
form_item = form.save(commit=False)
print form
form_item.save()
messages.success(request, "Record created")
return redirect ("/buildpage/")
else:
formset = RouteStepFormSet()
return render(request, "buildpage/routestepinfo.html",{'formset':formset})
Javascript was the main thing that changed, with quite a few additions and quite a bit of SO help. The prefix was the first issue, and that was fixed by using formset.empty_form and modifying it from there. Another vital part was updating the total forms, which fixed the saving issue.
<script>
$(document).ready(function() {
$('.add-item').click(function(ev) {
ev.preventDefault();
var count = $('#items-form-container').children().length;
var tmplMarkup = $('#item-template').html();
var compiledTmpl = tmplMarkup.replace(/__prefix__/g, count);
$('div#items-form-container').append(compiledTmpl);
// update form count
$('#id_form-TOTAL_FORMS').attr('value', count+1);
$('html, body').animate({
scrollTop: $("#add-item-button").position().top-200
}, 800);
});
});
</script>
Finally the html. This combined some of my own stuff with a very helpful SO post(forgot the question it came from), where formset is generated as an empty form and cloned from there.:
<div type="text/html" id="item-template">
<table>
{{ formset.empty_form.as_table }}
</table>
</div>
<font face = Flexo>
<form id= "myForm" method = 'POST' action="{% url 'buildpage:routestepinfo' %}" enctype="multipart/form-data">{% csrf_token %}
{{ formset.management_form }}
<div id="items-form-container">
{% for form in formset.forms %}
<div id="item-{{ forloop.counter0 }}">
<table>
{{form.as_table}}
<table>
</div>
{% endfor %}
</div>
Add Step
<input type = "submit" name = "Submit Steps">
</form>
Hope this helps some people. Cheers
Related
I am trying to change a Django form's data by using:
document.getElementById("id_role").innerHTML = "developer"
The CustomUser model has a "role" field that is referenced in the function. By testing the output (with the displayField() function, it appears that document.getElementById("id_role").innerHTML actually references all of the available fields ("choices" given in the models.py).
The goal is for the second function, changeField(), to change the selected data on the form (my goal isn't to change the database's stored data at this point, just the selected form's input).
My question: How do I use document.getElementById().innerHTML to access the specific value that is shown in the form, instead of all of the options for the field?
models.py
TECH_OPTIONS = ( ('developer','DEVELOPER'), ('manager','MANAGER'), ('testing','TESTING'), )
class CustomUser(AbstractUser):
career = models.CharField(max_length=30)
role = models.CharField(choices=TECH_OPTIONS,blank = True, max_length=30)
def __str__(self):
return self.username
html page
{% extends "base.html" %}
{% load bootstrap3 %}
{% block content %}
<h1 id="testTag">{{user.username}}'s Info</h1>
<input onclick="displayField(); changeField();" type="submit" name="" value="TESTING">
<form method="post">
{% csrf_token %}
{% bootstrap_form form %}
<input type="submit" value="Save" />
</form>
<script type="text/javascript">
function displayField(){
var myFormFields = document.getElementById("id_role").innerHTML
document.getElementById("testTag").innerHTML = myFormFields;
}
function changeField(){
document.getElementById("id_role").innerHTML = "developer"
}
</script>
{% endblock %}
You need to use value rather than innerHTML to change/read the value of a field:
document.getElementById("id_role").value = "developer"
Currently working on my first django web application so still quite rusty.
Trying to create a page that allows a lecturer to create a new class, the class is then added to the lecturers list of classes.
As far as I can tell, from following a tutorial, the request.method in my views.py should return a POST, but is currently returning GET.
Because of this, the new class created is never saved to the database.
here is my views.py
def create_class(request):
if request.user.is_lecturer:
print(" ")
print("user is lecturer")
form = classForm()
print(request.method)
if request.method == 'POST':
print("")
print("request method equals post")
form = classForm(request.POST)
if form.is_valid():
lecturer = LecturerProfile.objects.get(lecturer=request.user)
subject = Class.objects.get_or_create(class_name=form.cleaned_data["class_name"],
class_description=form.cleaned_data["class_description"],
lecturer=lecturer)
LecturerProfile.Classes.add(subject)
form.save(commit=True)
return index(request)
else:
print("")
print(form.errors)
else:
print("")
print("request method fail")
else:
return HttpResponse("You are not allowed here")
return render(request, 'student_feedback_app/create_class.html', {'form':form})
forms.py
class classForm(forms.ModelForm):
subject = forms.CharField(max_length=40, help_text="Class Name", required=False)
class_description = forms.CharField(max_length=200, required=False, help_text="Class Description")
slug = forms.CharField(widget=forms.HiddenInput(), required=False)
class Meta:
model = Class
fields = ('subject', 'class_description',)
my create_class.html
<!DOCTYPE html>
<html>
<head>
Create a new class
<h1> Create a new class</h1>
</head>
<body>
<div>
<form id="" method="post" action="/lecturer/classes/">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
<br>
{{ field.errors }}
{{ field.help_text }}
{{ field }}
{% endfor %}
<br>
<input type="submit" name="submit" value="Create Class" /> </form>
</div>
</body>
</html>
The errors that I get out at the terminal are
user is lecturer GET
request method fail
[11/Nov/2018 16:27:24] "GET /lecturer/create-class/ HTTP/1.1" 200 923
Not Found: /lecturer/create-class/js/plugins/jqBootstrapValidation.js
[11/Nov/2018 16:27:24] "GET /lecturer/create-class/js/plugins/jqBootstrapValidation.js HTTP/1.1" 404 4369
From the errors it would seem like it must be something to do with Bootstrap that the error is coming from, although i haven't used this for anything.
I even took out the base_template.html and wrote the create_class.html normally incase something from it was using javascript and bootstrap, but got the same output.
Any help is very appreciated
Doubt:
Here I have mentioned Html and output Image.
If I click select all option (checkbox) it will select all image but my doubt was how to delete all images once I click delete button.
Html
{% for j in img %}
<h4>
Select All
<input type="checkbox" onclick="$('input[name*=\'selected\']').prop('checked', this.checked);" />
</h4>
<button class="btn btn-danger" action=" ">Delete</button>
<div class="col">
{% if i.placename == j.gallery_place %}
<div class="show-image">
<img src="{{j.gallery_image.url}}" style="height:130px; width:130px;" />
<tag class="one" style="margin:8%">
<input type="checkbox" name="selected[]" value="{{j.id}}" />
</tag>
</div>
{% endif %}
</div>
{% endfor %}
image
enter image description here
You can use a FormView in Django and process them as follows. You need to create a form with the checkboxes first:
# forms.py
class CheckboxesForm(forms.Form):
checkboxes = forms.ModelMultipleChoiceField(
MyModel.objects.all(),
widget=forms.CheckboxSelectMultiple)
Then you need to write your own FormView and override the form_valid() method to perform the deletion of the selected objects. If you want to define objects for which you have checkboxes, you can override the get_form() method.
# views.py
class MyListView(ListView):
"""
View displaying a list of objects, you will redirect here after successfully deleting the objects you want
"""
model = MyModel
class MyView(FormView):
"""
Class based view taking care of rendering the form and processing it after posting. Finally, redirects you to your list view defined above.
"""
form_class = MyForm
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context['objects'] = MyModel.objects.all() # Customize this queryset to your liking
return context
def get_form(self, form_class=None):
form = super().get_form(form_class)
form.fields['checkboxes'].queryset =
MyModel.objects.all() # Customize this queryset to determine for which objects you want to display checkboxes
return form
def form_valid(self, form):
qs = myModel.objects.filter(
pk__in=list(map(int, self.request.POST.getlist('checkboxes'))))
qs.delete()
return HttpResponseRedirect(reverse_lazy('someurl'))
Your template will look like this (adapted from what you posted)
# template.html
{% for j in objects %}
<h4>
Select All
<input type="checkbox" onclick="$('input[name*=\'selected\']').prop('checked', this.checked);" />
</h4>
<button class="btn btn-danger" action=" ">Delete</button>
<div class="col">
{% if i.placename == j.gallery_place %}
<div class="show-image">
<img src="{{j.gallery_image.url}}" style="height:130px;
width:130px;" />
<tag class="one" style="margin:8%">
<div class="form-checkbox"><input type="checkbox" name="checkboxes" value="{{ j.pk }}">
</tag>
</div>
{% endif %}
</div>
{% endfor %}
Finally, hook up the urls.
# urls.py
from django.conf.urls import include, url
from.views import MyView, MyListView
urlpatterns = [
url(r'^listdelete/$', MyView.as_view(), name="delete-list"),
url(r'^mylist/$, MyListView.as_view(), name="someurl"),
]
I'm working on a website on which users can comment something below the posts, running on Python & Django.
As soon as a user comments something, then I'm updating comments without refreshing the web-page. Here's the code,
In views.py
postType1 = sorted(Posts.objects.filter( . . . ), key=lambda x: random.random())
postType2= Posts.objects.filter( . . . )
In template,
// BLOCK - 1
{% for post in postType1 %}
<p>{{ post }}</p>
<div class="comm_update" action="{% url 'comment:create' post.id %}">
<form class="comm_form" action="{% url 'comment:create' post.id %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<textarea id="id_comment"></textarea><br /><br />
<button type="submit">Submit</button>
</form>
</div>
{% endfor %}
<hr />
// BLOCK - 2
{% for post in postType2 %}
<p>{{ post }}</p>
<div class="comm_update" action="{% url 'comment:create' post.id %}">
<form class="comm_form" action="{% url 'comment:create' post.id %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<textarea name="comment_text" id="id_comment"></textarea><br />
<button type="submit">Submit</button>
</form>
</div>
{% endfor %}
Auto updating comments without refreshing webpage,
$(document).on("submit", ".comm_form", function(t) {
t.preventDefault();
var o = $(this),
e = $.post(o.attr("action"), o.serialize()),
n = o.attr("action");
e.done(function(t) {
var e = $(t).find(".comm_update[action='" + n + "']");
o.closest(".comm_update").html(e), o[0].reset()
})
});
On localhost everything is working fine.
*But on live server the first block BLOCK - 1 is not updating the comments, instead when user presses submit entire comment section disappears.
How can we fix this issue?
Thank You!
I'm making a workout calendar website where a user can add workouts with varying amounts of lift, sets and reps, etc. Thus, I need a form that adds a field when a user clicks a button. I've made a template and some javascript to describe what it is I want to achieve exactly:
url:
url(r'^add/(?P<year>[0-9]+)/(?P<month>[0-9]+)/(?P<day>[0-9]+)/$', views.add_workout, name = 'add_workout')
template:
{% block hidden %}
{% include "workoutcal/liftrow.html" %} {# To be used by Javascript #}
{% include "workoutcal/cardiorow.html" %}
{% endblock %}
<form action="{% url 'add_workout' date.year date.month date.day %}" method="post">
<div class="row">
<div class="col-xs-2">
<p id="date">{{ date.year }}-{{ date.month }}-{{ date.day }}</p>
<input type="hidden" name="date" value="{{ date }}">
</div>
</div>
<h2 class="col-xs-12">Lifts</h2>
<div id="liftrows">
{% for i in range %}
{% include "workoutcal/liftrow.html" %}
{% endblock %}
</div>
<div class="row">
<div class="col-xs-0"></div>
<label class="col-xs-2"><button type="button" id="addliftbutton">One more lift</button></label>
</div>
<h2 class="col-xs-12">Cardio</h2>
<div id="cardiorows">
{% include "workoutcal/cardiorow.html" %}
</div>
<div class="row">
<label class="col-xs-2"><button type="button" id="addcardiobutton">One more cardio</button></label>
</div>
<div class="row">
<div class="col-xs-10"></div>
<label class="col-xs-2"><input type="submit" id="submitbutton" value="Save Workout"></label>
</div>
</form>
javascript:
//Adding onclick to buttons
document.getElementById('addliftbutton').onclick = addLiftRow;
document.getElementById('addcardiobutton').onclick = addCardioRow;
for (var i=0; i<setsBoxes.length; i++){
setsBox = setsBoxes[i];
setsBox.onchange = insertRepFields;
}
function addLiftRow(){
var liftRowElements = document.getElementById('liftrows');
var hidden_liftrow = document.getElementById('hidden').getElementsByClassName('lift')[0];
var new_liftrow = hidden_liftrow.cloneNode(true);
liftRowElements.appendChild(new_liftrow);
}
function addCardioRow(){
var cardiorows = document.getElementById('cardiorows');
var hidden_cardiorow = document.getElementById('hidden').getElementsByClassName('cardio')[0];
var new_cardiorow = hidden_cardiorow.cloneNode(true);
cardiorows.appendChild(new_cardiorow);
}
function insertRepFields(){} // big function that inserts as many input fields as the number inside the box whose event called the function.
2 questions:
1. Is there a better way to do this in Django?
2. If this is the best way, how do I go about sending the data of my massive form back to django? Since I don't know exactly how many fields there will be, I don't know how to create a form that accepts a variable amount of fields, and fields within fields.
Here's how a filled-in form could look:
The best way to accomplish that is inserting inputs with the same name and then in Django get all those inputs as a list like:
def view(request):
inputs = request.POST.getlist('your_input_name')
for i in inputs:
Model.objects.create() # Save your model