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.
Related
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
}
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>
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.
I have a problem that has been bugging me for days now, and I know it might be trivial for some but please bear with me. In my twig I have an href that reveals a modal with a checkbox, the href also contains a path to an action in my controller
Twig
Action in Controller
/**
*
* #Route("/{id}/delete", name="delete_image")
*/
public function deleteAction(Request $request, CategoryImage $CategoryImage, $id)
{
$em = $this->getDoctrine()->getManager();
$CategoryImage = $em->getRepository('AcmeBundle:entity')->find($CategoryImage->getId());
$em->remove($repo);
$em->flush();
return $this->redirect($this->generateUrl('page_main', array("id"=>$id)));
}
Modal
<div id="deleteModal" class="reveal-modal large">
<h6>Delete Image</h6>
<br/>
{{form_start(DeleteImageForm, {'attr': {'id': 'DeleteImageForm'}})}}
Delete Image
{{form_widget(DeleteImageForm.remove)}}
{{form_errors(DeleteImageForm.remove)}}<br/><br/>
admin password <br/>
<input type="password" id="DeletePass"/>
<br/>
<button id = "Delete">Submit</button>
{{form_end(DeleteImageForm)}}
<a class="close-reveal-modal">×</a>
</div>
Jquery
$('#Delete').on('click', function(e){
var data = $(this).serialize();
if ($('#DeletePass').val() == 'password'){
$( "#DeleteImageForm" ).submit();
}else{
alert('invalid password');
e.preventDefault();
}
});
Is there any way to pass the id / value of the path to the modal so when checkbox is checked and is submitted it goes to the action?
Any help would be much appreciated.
Generate your delete form in twig, by passing a placeholder for the dynamic parameter i.e id. You can do it in controller where you generate the deleteForm.
The idea is to have a unique identifier in action so that we can replace via jQuery.
return $this->createFormBuilder()
->setAction($this->generateUrl('image_delete', array('id' => 'image_id')))
->setMethod('DELETE')
->getForm()
;
Now, when your modal gets open, Through jQuery, replace the placeholder i.e image_id to your required value, , as below :
var currentUrl = jQuery("form#DeleteImageForm").attr('action');
// suppose 154 is your new image id.
var newHref = currentUrl.replace('image_id', '154');
// change url of the form action.
jQuery("form#DeleteImageForm").attr('href', newHref);
Hope this helps.
Note : The code is not tested!
I've a problem trying to submit a formset through Ajax. A little informaton of what I'm doing is that the user enters a word and through Ajax I get the length of of it and produce a formset according to that number. I then print it out with a for loop and each form has a valid button that its suppose to submit that specific format to validate it. So its a single form submit for each button. Here is my code:
<div id="example1" type="hidden">
{{ exampleForm.management_form }}
{% for form in exampleForm %}
<form onsubmit="return false;" method="GET" id="{{ form.prefix }}" >
( {{ form.letterOfWord }} + {{ form.keyToUse }} ) MOD 26 =
{{ form.letterToFill }} <button name="action" id="validateButton" value="validate"> Validate </button> <br>
</form>
{% endfor %}
And my javascript file:
$("#validateButton").on({
click : function() {
// var variable = document.getElementById('id_plaintext');
// console.log(variable.value)
console.log("Inside validate button function")
var serializedData = $('form').serialize();
console.log(serializeData);
$.ajax( {
url: "/exampleCaesar",
type : "GET",
data: { CSRF: 'csrf_token',
serializedData
},
success : function(exampleData) {
console.log(exampleData)
}
}); //END OF Ajax
} //END OF FUNCTION
}); //END OF validateButton
Thing is that when I click any of the validate buttons, nothing is submitted. I know this because I got a console.log in the javascript to know when it goes in. If that doesnt print out then it didn't actually go in.
Any tips? Been breaking my head all day for this. Thanks
you have multiple IDs validateButton. This might be source of your problems. As far as I know, or would guess, jquery will only trigger on the first button of the first form in this case.
Also, I'm not sure if jquery will serialize proper form when you use this code
var serializedData = $('form').serialize();
as again, you have multiple form in your html
Also, the managment_form should be inside of <form>, otherwise it won't get sent to Django. Check out the docs https://docs.djangoproject.com/en/1.9/topics/forms/formsets/#using-a-formset-in-views-and-templates