Followed below code to add and display etra fields to forms dynamically with "Add another" button. Code is working but problem is on "Add another" button click (when form in invalid) additional form fields are added to form but not displayed. Want to make sure when form is invalid extra fields are not added to form.
Forms
class MyForm(forms.Form):
original_field = forms.CharField()
extra_field_count = forms.CharField(widget=forms.HiddenInput())
def __init__(self, *args, **kwargs):
extra_fields = kwargs.pop('extra', 0)
super(MyForm, self).__init__(*args, **kwargs)
self.fields['extra_field_count'].initial = extra_fields
for index in range(int(extra_fields)):
# generate extra fields in the number specified via extra_fields
self.fields['extra_field_{index}'.format(index=index)] = \
forms.CharField()
View
def myview(request):
if request.method == 'POST':
form = MyForm(request.POST, extra=request.POST.get('extra_field_count'))
if form.is_valid():
print "valid!"
else:
form = MyForm()
return render(request, "template", { 'form': form })
HTML
<form>
<div id="forms">
{{ form.as_p }}
</div>
<button id="add-another">add another</button>
<input type="submit" />
</form>
JS
<script>
let form_count = Number($("[name=extra_field_count]").val());
// get extra form count so we know what index to use for the next item.
$("#add-another").click(function() {
form_count ++;
let element = $('<input type="text"/>');
element.attr('name', 'extra_field_' + form_count);
$("#forms").append(element);
// build element and append it to our forms container
$("[name=extra_field_count]").val(form_count);
// increment form count so our view knows to populate
// that many fields for validation
})
</script>
Related
I've created a text input form in html that sends user input words via POST method to Django views.py file. Here is the code for this form
<form method="POST" action="search">
{% csrf_token %}
<input type="search" name="search" value="{{form.search.value}}" placeholder="Search here..." autofocus x-webkit-speech/>
</form>
The value attribute takes the value returned by Django after a submit. Here is the code of my Django views.py file:
from django.shortcuts import render
import requests
from bs4 import BeautifulSoup as bs
from search.models import MyForm
def index(request):
return render(request, 'index.html')
def search(request):
if request.method == 'POST':
search = request.POST['search']
form = MyForm(request.POST)
max_pages_to_scrap = 15
final_result = []
for page_num in range(1, max_pages_to_scrap+1):
url = "https://www.ask.com/web?q=" + search + "&qo=pagination&page=" + str(page_num)
res = requests.get(url)
soup = bs(res.text, 'lxml')
result_listings = soup.find_all('div', {'class': 'PartialSearchResults-item'})
for result in result_listings:
result_title = result.find(class_='PartialSearchResults-item-title').text
result_url = result.find('a').get('href')
result_desc = result.find(class_='PartialSearchResults-item-abstract').text
final_result.append((result_title, result_url, result_desc))
context = {'final_result': final_result, 'form':form}
return render(request, 'index.html', context)
else:
return render(request, 'index.html')
When I submit the form, the value of the input field is well preserved but the cursor goes from the final position to the initial position. To solve this problem, I inserted a javascript script in my index.html file which contains my form:
<script>
/* Creating a function called PosEnd
in JavaScript to place the cursor
at the end */
function PosEnd(end) {
var len = end.value.length;
// Mostly for Web Browsers
if (end.setSelectionRange) {
end.focus();
end.setSelectionRange(len, len);
} else if (end.createTextRange) {
var t = end.createTextRange();
t.collapse(true);
t.moveEnd('character', len);
t.moveStart('character', len);
t.select();
}
}
</script>
and I modified the form like this:
<input type="search" name="search" value="{{form.search.value}}" placeholder="Search here..." autofocus x-webkit-speech/>
became:
<input type="search" name="search" PosEnd(value="{{form.search.value}}") placeholder="Search here..." autofocus x-webkit-speech/>
but now when I submit the form the input values are lost. I look forward to your responses, thank you.
I have this simple hidden field in an HTML form:
<input type="hidden" id="response" name="response">{{ form.response}}</div>
and I want to change the it's value so that I can use it using flask and WTForms later on.
I tried this:
function(token){
document.getElementById('response').value = token
}
and the function is being called with a valid token, but no success.
Any suggestions?
The input field for a form is created as follows, where additional arguments like a label or validators are possible.
class ExampleForm(FlaskForm):
response = HiddenField()
submit = SubmitField()
The following code is required to request the value on the server side.
#app.route('/', methods=['GET', 'POST'])
def index():
form = ExampleForm(request.form)
if form.validate_on_submit():
print(form.response.data)
return render_template('index.html', **locals())
If you now render within the template, the attributes name and id are set automatically. The value corresponds to the identifier of the variable to which the input field was assigned. To query and set the value, you can either use a selector with the name attribute or the id of the input field.
<form method="post">
{{ form.csrf_token }}
{{ form.response }}
{{ form.submit }}
</form>
<script type="text/javascript">
(() => {
const token = 'your token here';
let responseField;
// Selecting the input field based on the id attribute,
responseField = document.getElementById('response');
responseField.value = token;
// or select based on the name attribute.
responseField = document.querySelector('input[name="response"]');
responseField.value = token;
})();
</script>
I am trying to submit a Django form with AJAX and Vanilla JavaScript however the form is not actually submitting unless I click the submit button twice.
I have an event listener on the form that stops the default submission to avoid page reload and then I open a XMLHttpRequest. On the first submission I get a 200 response but the data hasn't actually been sent to the database. However if I click the submit button again I get the desired 201 (item created) response from the server and it reloads my posts and adds the new one perfectly.
I am still a bit unfamiliar on working with asynchronous data and cannot figure out why it's not working. If I remove the e.preventDefault the form submits correctly and the new post shows up after the page reloads.
relevant JS snippet:
const postCreateFormEl = document.getElementById("post-create-form")
const postsEl = document.getElementById("posts")
const handlePostSubmit = function(e){
e.preventDefault()
const myForm = e.target
const myFormData = new FormData(myForm)
const url = myForm.getAttribute("action")
const method = myForm.getAttribute("method")
const xhr = new XMLHttpRequest()
xhr.open(method, url)
xhr.setRequestHeader("HTTP_X_REQUESTED_WITH", "XMLHttpRequest")
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")
xhr.onload = function() {
const serverResponse = xhr.response
console.log(serverResponse, xhr.status)
const postsEl = document.getElementById("posts")
loadPosts(postsEl)
}
xhr.send(myFormData)
}
postCreateFormEl.addEventListener("submit", handlePostSubmit)
django views snippet:
def home_view(request, *args, **kwargs):
form = PostForm()
return render(request, "pages/home.html", context={'form': form})
def post_create_view(request, *args, **kwargs):
form = PostForm(request.POST or None)
next_url = request.POST.get("next") or None
if form.is_valid():
obj = form.save(commit=False)
obj.save()
if request.is_ajax():
return JsonResponse({"success": "Object created"}, status=201)
if next_url != None and is_safe_url(next_url, ALLOWED_HOSTS):
return redirect(next_url)
form = PostForm()
return render(request, 'components/form.html', context={"form": form})
On the first submit in the console it returns the event target and on the second on it returns a jsonresponse as intended. Any direction is appreciated!
edit:
home.html:
<form class='form' id='post-create-form' method='POST' action='/create-post'>
<input type='hidden' value='/' name='next'/>
{% csrf_token %}
{{ form.media }}
{{ form.as_p }}
<input type='submit' value='submit'>
</form>
urlpatterns:
urlpatterns = [
path('admin/', admin.site.urls),
path('ckeditor/', include('ckeditor_uploader.urls')),
path('', home_view),
path('posts/<int:post_id>', post_detail_view),
path('posts', post_list_view),
path('create-post', post_create_view),
]
forms.py:
class PostForm(forms.ModelForm):
content = forms.CharField(widget=CKEditorUploadingWidget())
class Meta:
model = Post
fields = '__all__'
def clean_content(self):
content = self.cleaned_data.get("content")
if len(content) > MAX_POST_LENGTH:
raise forms.ValidationError("This post is too long")
return content
Most WYSIWYG editors don't edit the actual input element that will be submitted when the user gives input. Instead what they do is that they attach an event on the submission of such forms and then when the form is being submitted they set the value of the input. CKEditor is no exception to this and does the same.
Hence what is happening here is that you submit the form and your on submit handler fires first. Unfortunately this means that the input is never filled when you submit the form by ajax, but the input does get filled after your ajax call. Which is why your second submit is successful.
What you can do to resolve this is to make CKEditor update the values of your input on submission yourself. Modify your script like so and also make sure that the your script is somewhere below where you load the forms media:
const handlePostSubmit = function(e){
e.preventDefault()
for (instance in CKEDITOR.instances) {
CKEDITOR.instances[instance].updateElement();
}
// Rest of your function
}
I want to get the following goal: I have two forms in one view, form1 and form2. The model are the following:
Class Model1(models.Model):
var_1=models.CharField()
var_2=models.CharField
Class Model2(models.Model):
var_1=models.CharField()
var_3=models.CharField
I have just set both form in the same view with a single submit button. Now I want to have the possibility to set var_1 from the form1 also for the var_1 of the Model2 (becouse are equal) when the client fill the form1. It's possible to get it?
This one my views.py
def example(request):
if request.method == 'POST':
form1 = Model1Form(request.POST)
form2 = Model2Form(request.POST)
if form1.is_valid() and form2.isvalid():
print("Il form è valido")
new_input1 = form1.save()
new_input2=form2.save()
else :
form1 = Model1Form()
form2 = Model2Form()
context= {
'form1': form1,
'form2':form2,
}
return render(request, "", context)
Suggest that you set the value of the field from the other form before saving.
if form1.is_valid() and form2.isvalid():
form2.cleaned_data['var_1'] = form1.cleaned_data['var_1']
form1.save()
form2.save()
I assume that you are not showing the var_1 field on both forms? If you were showing both this would overwrite the value of var_1 for form2.
Building a site so users can add topics onto a site and ask then Q&A. Using AJAX, the "Add Media" button on the home page loads my Add Media Form (text and an image) to the site. The box is rendering fine with the correct forms, but after hitting Submit the form doesn't save in the database. When just rendering the form onto a new HTML page without jquery, the model works just fine and the input is saved. How do I get the information submitted in a jquery lightbox to be saved in the database too?
So this is the html page
#home.html
Add Media
<div id="login-box" class="addmovie-popup">
This is the jquery that goes with it
#home.js
$(document).ready(function() {
$('.block3 a').click(function(ev) {
ev.preventDefault();
var url = $(this).attr('href');
$('#login-box').load('http://127.0.0.1:8000/home/add_movie/');
});
});
The URL conf
#urls.py
url(r'^add_media/$', 'add_media'),
The view for adding media
#views.py
def add_media(request):
if request.method == "POST":
form = MediaForm(request.POST, request.FILES)
if form.is_valid():
form.save(user = request.user)
return HttpResponseRedirect("/home//")
else:
form = MediaForm()
return render_to_response("qanda/add_media.html", {'form': form}, context_instance = RequestContext(request))
And the HTML form that it is rendering
#add_media.html
<h1> Add Media:</h1>
<form enctype = "multipart/form-data" action = "" method = "post">{% csrf_token %}
{{ form.as_p }}
<input type = "submit" value = "Add" />
<input type = "hidden" name = "next" value = "{{ next|escape }}" />
</form>
If you're loading HTML into your page dynamically action = "" would point to the current page, which clearly doesn't handle your POST requests.
Set the action to the correct URL.