I am trying to submit my zipped formsets using Ajax. The code works perfectly when the request is not ajax. But whenever I am trying to implement Ajax, I am getting a ValidationError: [u'ManagementForm data is missing or has been tampered with']
My views:
def App(request):
lectures = Lecture.objects.all()
TopicFormSet = modelformset_factory(Topic, extra=0)
SummaryFormSet = modelformset_factory(Summary, extra=0)
tquery = Topic.objects.all()
squery = Summary.objects.all()
#saving formsets:
if request.method == 'POST' and request.is_ajax():
t_formset = TopicFormSet(request.POST)
s_formset = SummaryFormSet(request.POST) #formset instances
if t_formset.is_valid() and s_formset.is_valid():
t_formset.save() and s_formset.save()
zipped = zip(t_formset.forms, s_formset.forms) #saving them with new data
else:
return HttpResponse("not valid formsets, dude") # for testing purposes
else: #request=GET
t_formset = TopicFormSet(queryset = tquery)
s_formset = SummaryFormSet(queryset = squery)
zipped = zip(t_formset.forms, s_formset.forms)
return render (request, "app.html", {"lectures" : lectures, "zipped" : zipped, "t_formset" : t_formset, "s_formset" : s_formset})
And my Javascript code:
$(document).ready(function(){
$(".mygt").click(function(){ // it's a button
serializedData = $("#id_form-0-name").serialize(); // I didn't know how to "catch" all forms in formset so I am only serializing the first one
$.ajax({
url: "/app/",
type: "post",
data: serializedData,
csrfmiddlewaretoken:'{{ csrf_token }}',
success: alert(serializedData)
})
event.preventDefault();
});
});
P.S. I have also included csrf cookie in from this article: https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#ajax and {% csrf_token %} is present near the forms.
Edit: my html:
<form action = "http://127.0.0.1:8000/app/" method = "POST">
{% csrf_token %}
<!-- t_formset.management_form -->
{{ t_formset.management_form }}
<!-- t_formset.management_form -->
{{ s_formset.management_form }}
<!-- formsets -->
{% for topic, summary in zipped %}
<div id="topic">{{ topic }}</div>
<br>
<input type="submit" value="Submit" class="mygt" />
<br>
<div id="summary">{{ summary }}</div>
<br>
<input type="submit" value="Submit" class="mygt" />
{% endfor %}
The problem was that I did not know how to serialize all formset and I was only serializing the first field (for testing purposes). I was not aware of the fact that Django sends all formset and not just one field like I thought. So I replaced the
serializedData = $("#id_form-0-name").serialize();
with:
serializedData = $("form").serialize();
And the code works!
Did you include {{t_formset.management_form}} and {{s_formset.management_form}} in your template as described here?I had the same problem and i got the same answer here
Related
I am trying to display a form in django and pre-filling it dynamically.
I want the user of my sample news gathering site to modify an entry.
I have my Manual Input form class
#forms.py
class ManualInputForm(forms.Form):
source = forms.CharField(label="Source:", widget = forms.TextInput(attrs={'size': 97}))
topic = forms.CharField(label="Topic:", widget = forms.TextInput(attrs={'size': 97}))
news = forms.CharField(widget = forms.Textarea(attrs={"rows":5, "cols":100}))
link = forms.CharField(label="Link (optional):", required = False, widget = forms.TextInput(attrs={'size': 97}))
In the HTML I am going manually because I would like to pre-fill all fields with data coming in from the related function in views.py.
#html file
<form method="post" class="form-group">
{% csrf_token %}
<div class="input-group mb-3">
<div class="container">
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }}
<br>
{{ field }}
</div>
{% endfor %}
</div>
<div class="input-group">
<p> </p>
<button type="submit" class="btn btn-success" name="Submit">Save</button>
</div>
</div>
</form>
How do I do it? It's driving me crazy o.O
I would like to keep using django's forms because of its integrated error manager (not all fields are required but some are and I'd like for django to keep managing it).
Thank your for your suggestions!
EDIT:
as requested I'll post the views.py related function:
#views.py
def editnews(response, id):
form = ManualInputForm(response.POST or None)
#tableToView is a dataframe retrieved by querying an external DB
#data cannot be stored in django's buit in because of reasons ;-)
#checking the dataframe is correct and it is:
#IT IS MADE OF A SINGLE LINE
print(tableToView)
#THIS IS PROBABLY NOT THE WAY TO DO IT
form.source = tableToView.loc[0, 'Source']
form.topic = tableToView.loc[0, 'Topic']
form.news = tableToView.loc[0, 'News']
form.link = tableToView.loc[0, 'Link']
return render(response, 'manual/editnews.html', {"form":form})
In the image the text should be pre-filled.
Try something like that:
def editnews(response, id):
data = {k.lower(): v for k, v in tableToView.loc[0].to_dict().items()}
form = ManualInputForm(response.POST or None, initial=data)
return render(response, 'manual/editnews.html', {'form': form})
when you are declaring or rendering form in view and sending it to template. use initial argument and pass dictionary in it with key as name of field and value as the text which you want prefilled.
like this
context['form'] = NameofForm(initial={'Source':'mysite', 'Topic':'mytopic'})
return context
Update
> def test_view(request, pk):
> template_name = 'form.html'
> form = MyForm(initial={"test":"initialize with this value"})
> if request.method == 'POST':
> form = MyForm(request.POST)
> if form.is_valid():
> form.save()
> return HttpResponseRedirect(reverse('list-view'))
>
> return render(request, template_name, {'form': form})
Try this:
#views.py
def editnews(response, id):
data = {}
data["source"] = tableToView.loc[0, 'Source']
data["topic"] = tableToView.loc[0, 'Topic']
data["news"] = tableToView.loc[0, 'News']
data["link"] = tableToView.loc[0, 'Link']
form = ManualInputForm(response.POST or None, initial=data)
return render(response, 'manual/editnews.html', {"form":form})
You could do it with jQuery if you're using one. You'll need to set the attribute of id to the form field.
jQuery code example:
$('#topic').val("Prefilled text goes here");
I am trying to add a slider with dynamic data to my django project using jQuery and ajax to do it, I received help to make the previous and next button which gives the ability to swipe through profiles, but I am now focusing on the previous one, in the process I realized that there is a NoReverseMatch error in the code and I don't know how to fix them because I am very new at jQuery and ajax.
views.py
def matesmain(request):
contents = Mates.objects.all()
context = {
'contents': contents,
'form_mates': MatesForm(),
}
print("nice3")
return render(request, 'mates.html', context)
def previous(request):
id= request.GET.get("id", None)
if id != 1:
previous_id= id-1
prev_user= Mates.objects.filter(user= previous_id)
data={
"username": prev_user.user,
"req_bio": prev_user.req_bio,
"req_image": prev_user.req_image,
}
return JsonResponse(data)
models.py
class Mates(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='usermates')
users_requests = models.ManyToManyField(User, related_name="users_requests")
req_bio = models.CharField(max_length=400)
req_image = models.ImageField(upload_to='requestmates_pics', null=True, blank=True, default=False)
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profile_pic = models.ImageField(upload_to='profile_pics', null=True, blank=True, default='default.png')
bio = models.CharField(max_length=400, default=1, null=True)
connection = models.CharField(max_length = 100, blank=True)
follower = models.IntegerField(default=0)
following = models.IntegerField(default=0)
urls.py
urlpatterns = [
path('mates', views.mates, name='mates'),
path('mates-main', views.matesmain, name='mates-main'),
path('previous', views.previous, name='previous'),
]
html
<div class="mates-grid-1-1-content">
<div class="mates-grid-2-content">
<button type="button" onclick="previous({{user.id}})" id="prev-button">prev</button>
</div>
<div class="mates-grid-1-content">
<div class="mates-item-content">
<img class="mate-pic" src="{{ user.profile.profile_pic.url }}" >
</div>
<div class="mates-item-content">
{{ content.user }}
</div>
<div class="mates-item-content">
<div class="responses">
<div class="response-item-img">
<img class="mates-image" src="{{ content.req_image.url }}" width="400px">
</div>
<div class="response-item-bio">
<p>{{ content.req_bio }}</p>
</div>
<div class="response-item-button">
<button type="submit">Submit</button>
</div>
</div>
</div>
</div>
<div class="mates-grid-3-content">
<button type="button" onclick="next({{user.id}})" id="next-button">next</button>
</div>
</div>
script
function previous(id){
$.ajax({
url: '{% url 'previous' %}',
type: "get",
data: {
'id': id,
},
dataType: 'json',
success: function (data) {
$(".mates-item-content").empty();
$(".mates-item-content").append("<img class="mate-pic" src="{{ user.profile.profile_pic.url }}" >");
$(".mates-item-content").empty();
$(".mates-item-content").append("{{ content.user }}");
$(".mates-item-content").empty();
$(".mates-item-content").append("<img class="mates-image" src="{{ content.req_image.url }}" width="400px">");
$(".mates-item-content").empty();
$(".mates-item-content").append("<p>{{ content.req_bio }}</p>");
},
error: function (data) {
alert('nope');
}
});
};
}
You're getting a NoReverseMatch exception because you're not using the {% url &} tag properly inside the $.ajax() function. Since you have that JavaScript code within the HTML itself, then you can replace what you have with the following:
url: "{% url 'previous' %}"
Also, in the success property's callback function, you're not using the data you're getting from the view how it is supposed to be used. Note that you won't be able to get that data using the Django template language inside this callback, but you need to use JavaScript to access that data. For example:
success: function(data){
console.log(data.req_bio);
}
That is because when the HTML gets rendered by Django, the information that you might want to use inside the callback function (e. g.: {{ req.bio }}), will not be there yet.
Nevertheless, there's a way to keep using the Django template language when handling an AJAX response, using the context data returned by the view. Please, refer to this answer for further information.
Hello Awesome People!
So many questions on StackOverflow are about "How to refresh dom via jquery (from the same view/url)" It's not what I'm looking for.
With a website that large of its parts are running with ajax, I wonder how to refresh a part of the HTML DOM when querying a foreign django view.
Let me be clearer with some examples:
I have that view that sends all_users to template
def usersList(request):
all_users = User.objects.all()
return render(request,'administration/users-list.html',{'all_users':all_users})
In the template I loop through all_users... The 2nd <span> reflects the activation state of the user
{% for u in all_users %}
<span>{{forloop.counter}}.- {{u.name}} <span>
<span id='user_{{u.id}}_state'>
<button data-id='{{u.id}}' type='button' class='css-btn btn-circle'>
{% if u.is_activate %} Active{% else %}Inactive{% endif %}
</button>
<span>
{% endfor %}
With jquery, I send a request to a specific view responsible only to activate or deactivate the account of the user. We can activate/deactivate user in many parts of the website, that's why I do so in a different view.
Here's the view:
def deactivateUser(request):
user = request.user
if user.has_perm('is_admin') and request.is_ajax() and request.method == 'POST':
id_user = request.POST.get('id')
targeted_user = get_object_or_deny(User,id=id_user)
# get_object_or_deny is my own function
it will get the object or raise PermissionDenied otherwise
if targeted_user.is_activate:
targeted_user.is_activate = False
state = 'deactivated'
else:
targeted_user.is_activate = True
state = 'activated'
targeted_user.date_update_activation = NOW() # own function
targeted_user.save()
return JsonResponse({'done':True,'msg':'User successfully %s' %s state})
# Here we return a JsonResponse
raise PermissionDenied
So now, how can I refresh the Dom with following jquery stuff to get the current state of each user
$(document).on('click','.btn-circle',function(){
var id = $(this).data("id");
$.ajax({
url:'/u/de-activate/?ref={{ request.path }}',
type:'post',
data:{
csrfmiddlewaretoken:"{{ csrf_token }}",
id:id,
},
success:function(response){
$("#user_"+id+"_state").replaceWith($("#user_"+id+"_state",response));
if(response.created) alert(response.msg);
},
error:function(){
alert("An error has occured, try again later");
}
});
});
Note that all_users is required to loop through. deactivateUser() return a Json response, even though it doesn't returned it, it will not matter.
You can send http response, not json.
First, just move your html that want to change. in this situation,
{% for u in all_users %}
<div id="user-part">
<span>{{forloop.counter}}.- {{u.name}} <span>
<span id='user_{{u.id}}_state'>
<button data-id='{{u.id}}' type='button' class='css-btn btn-circle'>
{% if u.is_activate %} Active{% else %}Inactive{% endif %}
</button>
<span>
</div>
{% endfor %}
Then save it i.e. user_part.html
Second, make your view return HttpResponse with that html, and context. You can use either HttpResponse or render_to_response. I recommend render_to_response.
context = {
'all_users': all_users,
}
return render_to_response(
'path_to/user_part.html',
context=context,
)
Third, you just change script for replacing your html.
success: function(response){
$('#user-part').html(response);
prevent();
}
I'm trying to implement a dictionary of forms where two fills are initialized with some values I have. I'm passing this through a Jquery function but when its going to add it to the template, the jquery function has an error that says Cannot set property 'value' of null when trying to execute this document.getElementById('example1').value = example;
My code here:
view.py
def exampleCaesar(request):
if request.is_ajax() and request.method == "GET":
form = caesarCipher(request.GET or None)
if form.is_valid:
wordToEncrypt = request.GET.get('word')
wordToEncrypt = wordToEncrypt.upper()
wordLength = len(request.GET.get('word'))
key = request.GET.get('the_key')
print(wordLength)
print(key)
equations = {}
for x in range(wordLength):
exampleForm = caesarCipher(initial={'letterOfWord' : wordToEncrypt[x], 'key' : key})
##print(exampleForm)
if exampleForm.is_valid:
equations = (exampleForm)
print(equations)
context = { 'equations' : equations
}
return render(request, "content/exampleCaesar.html", context)
Javascript file
$("#encryptButton").on({
click : function() {
var variable = document.getElementById('id_plaintext');
console.log(variable.value)
$.ajax( {
url: "/exampleCaesar",
type : "GET",
data: { CSRF: 'csrf_token',
word: $('#id_plaintext').val(),
the_key: $('#id_key').val()
},
success : function(example) {
$('#example1').show();
$('#example1').html(example);
document.getElementById('example1').value = example;
console.log(example);
}
}); //END OF Ajax
} //END OF FUNCTION
}); //END OF encryptButton
Template file
{% for letterOfWord, key in equations.items %}
<form onsubmit="return false;" method="GET" class="exaSubmit" enctype="multipart/form-data">
{% csrf_token %}
<div id="example1" type="hidden">
( {{ letterOfWord }} + {{ keyToUse }} ) MOD 26 =
{{ letterToFill }} <button name="action" class="validateButton" value="validate"> Validate </button> <br>
</div>
</form>
{% endfor %}
I believe I'm not filling the dictionary the right way. When I try to print it out in the console to see the values, only the names of the fields are but not the values. My bets are in this section but not entirely sure how's that possible when I'm appending the information to it.
I have a form in django. it's 'composing mail' form. I send this form from view to my template and i apply ckeditor to chang the body style. i want this form to be posted by ajax. and when ckeditor is used, value of body field isn't send with request.POST. i use this line of code to use ckeditor:
CKEDITOR.replace('id_body');
(without using ckeditor, every thing works fine.)
<form id="compose_form" action="compose/" method="post">
{% csrf_token %}
{{ form.non_field_errors }}
<div>
<div class="form-field">
<label for="id_recipient">{% trans 'recipient' %}:</label>
{{ form.recipient }}
{{ form.recipient.errors }}
</div>
<div class="form-field">
<label for="id_subject">{% trans 'subject' %}:</label>
{{ form.subject }}
{{ form.subject.errors }}
</div>
</div>
<div class="form-field">
{{ form.body }}
{{ form.body.errors }}
</div>
<input id="messages-submit" type="submit" value=""Send"/>
</div>
</form>
and i use this script to send form data via ajax:
<script type="text/javascript">
$(function() {
$('#compose_form').submit(function() {
var temp = $("#compose_form").serialize();
$.ajax({
type: "POST",
data: temp,
url: 'compose/',
success: function(data) {
// do s.th
}
});
return false;
});
});
</script>
with this script, body value isn't send to request.POST(i mean it sends empty string in body field), when i add the below line to my script, it sends value of body field, but it isn't ajax any more. Can you please help me what to do?
The reason that the data in the editor isn't included in the form is because the editor isn't a part of the form. It needs to update the form element you have associated it with. For this to happen you need to tell the editor to update the form element.
So in the submit function for your form you need to grab data from the editor.
This should do the trick:
$(function() {
$('#compose_form').submit(function() {
for (var instance in CKEDITOR.instances)
CKEDITOR.instances[instance].updateElement();
var temp = $("#compose_form").serialize();
etc etc...
I also had the same issue with django-ckeditor,What I tried is
<script type="text/javascript">
for (var instance in CKEDITOR.instances)
CKEDITOR.instances[instance].updateElement();
then checked the instance name by:
console.log(instance)
it gave "id_Your_Message" ,,So I did:
var temp = $("#id_Your_Message").val()
it works fine
<script type="text/javascript">
$(function () {
$('#submit_button_id').click(function () {
$.post("action post file url", $("#form_id").serialize(), function (data) {});
});
});
</script>
I hope above script may be help you