AngularJS/Django & Forms server-side validation result - python

I have an html form that is generated automatically in Django, displayed as a modal window using Bootstrap UI and needs to be posted via ajax with Angular.
The problem is that some validations happen on the server-side and in case of an error I need to display any errors the same way I would if it were full http post (e.g. return the form with the invalid user input in red highlighting and errors next to each field).
I'm trying to avoid to write a custom response in my view and custom parsing in my script per case as I have quite a lot of forms that require validation and need to be submitted in a modal window.
Any help really appreciated, thanks!
Here is my code:
Form
from django import forms
from djangular.forms import NgFormValidationMixin
from djangular.styling.bootstrap3.forms import Bootstrap3Form
class FormBase(Bootstrap3Form, NgFormValidationMixin, forms.Form):
form_name = None
class AppRequestForm(FormBase):
form_name = 'app_request_form'
class_type = AppRequest
name = forms.RegexField(
r'^.*$',
max_length=100,
widget=forms.TextInput(attrs={'ng-model': 'request.name'}),
error_messages={'invalid': 'Please enter a valid name.'}
)
email = forms.RegexField(
r'^.*$',
label='Email',
max_length=100,
widget=forms.TextInput(attrs={'ng-model': 'request.email'}),
error_messages={'invalid': 'Please enter a valid email.'}
)
View
from django.views.generic import FormView, TemplateView
from public.forms import AppRegisterForm, AppRequestForm
class ViewBase(TemplateView):
def get_context_data(self, **kwargs):
kwargs.update(theme=THEME_PATH)
kwargs.update(page=self.request.resolver_match.url_name)
return super(ViewBase, self).get_context_data(**kwargs)
class FormViewBase(ViewBase, FormView):
def get_context_data(self, **kwargs):
kwargs.update(form=self.get_form())
return super(FormViewBase, self).get_context_data(**kwargs)
def form_valid(self, form):
self.success_action(form)
return super(FormViewBase, self).form_valid(form)
class AppRequest(FormViewBase):
template_name = 'public/app_request.html'
form_class = AppRequestForm
def success_action(self, form):
# persist data
Template:
<form name="{{ form.form_name }}" method="post" action="/app/request" class="form" validate>
<div class="modal-header">
<h3 class="modal-title">Request details</h3>
</div>
<div class="modal-body">
<div class="well">
<fieldset>
{% csrf_token %}
{{ form.as_div }}
</fieldset>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" ng-click="submit(request)" ng-disabled="{{ form.form_name }}.$invalid">
Submit
</button>
<button type="button" class="btn btn-warning" ng-click="cancel()">Cancel</button>
</div>
</form>
Script:
$scope.apps.request = function (id) {
var url = '/app/request';
var modalInstance = $modal.open({
templateUrl: url,
controller: function ($scope, $http, $modalInstance, state) {
$scope.submit = function (request) {
$http({
method: 'POST',
url: state.url,
data: $.param(request),
headers: {'Content-Type': 'application/x-www-form-urlencoded'}
}).success(function (data, status, headers, config) {
$modalInstance.close();
}).error(function (data, status, headers, config) {
// Display validation errors on the modal window and allow user to re-submit
});
};
$scope.cancel = function () {
$modalInstance.dismiss();
};
},
resolve: {
state: function () {
return {
scope: $scope,
url: url,
id: id
};
}
}
});
};

Related

Ajax form not sending data in django

I am trying to send data to Django without page refresh. So I am using ajax.
Created a Django model and form
class MyModel(models.Model):
text=models.CharField(max_length=100)
class MyForm(forms.ModelForm):
class Meta:
model=MyModel
fields = "__all__"
Then send the form to the HTML page via views.py
def home(request):
print(request.POST.get('text',False))
form = MyForm(request.POST)
if request.method=='POST':
print(request.POST.get('text',False))
if form.is_valid():
data=form.save()
return render(request,'home.html',{'form':form})
Create a form in HTML template
<form action = "" id="post-form" method = "post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="submit" id="submit-button">
</form>
This is the javascript file
$(document).on('submit','#post-form',
function(x){
x.preventDefault();
console.log("button clicked")
$.ajax({
type:'POST',
url:'/',
data:{
text:$("id_text").val(),
csrfmiddlewaretoken:$('input[name=csrfmiddlewaretoken]').val()
},
success:function(){
alert('Saved');
}
})
}
)
I think it might be the issue with URL, if i set url like this way
url:'{% url "home" %}',
Then in console, i got this error
XHR POST http://127.0.0.1:8000/%7B%%20url%20%22home%22%20%%7D
I am unable to find, where is the issue.
You should use # for selecting id, so use text:$("#id_text").val().
For using dynamic url, use like this:
url:"{% url 'home' %}",
For more information, about using dynamic urls refer this question.
Edit:
One way to use dynamic url is to pass it with onsubmit event, so try this:
Template file or home.html
<form id="post-form" method="POST" onsubmit="checking('{% url 'home' %}')">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="submit" id="submit-button">
</form>
Js file:
function checking(dynamicUrl) {
$(document).on("submit", "#post-form", function (x) {
x.preventDefault();
console.log("button clicked");
$.ajax({
type: "POST",
url: dynamicUrl,
data: {
text: $("#id_text").val(),
csrfmiddlewaretoken: $("input[name=csrfmiddlewaretoken]").val(),
},
success: function () {
alert("Saved");
},
});
});
}

I am trying to inject an already rendered Django template into a section of my page using AJAX

I am following by this tutorial on how to get live updates on django without refreshing the page.
The tutorial uses flasks render_template to get the html rendered which is then injected to a page section.
I am trying to do the same in Django, But django just directly renders it in the browser... I don't want that. I just want django to send the rendered html response to AJAX which could then inject that to a section on my live page.
Here is the code :
views.py
class ManageView(LoginRequiredMixin, View):
template_name = "dashboard/manage.html"
context = {}
def get(self, request, app_id, folder_id=None):
app = App.objects.get(pk=int(app_id))
self.context["app"] = app
if folder_id:
try:
self.context["folder"] = Folder.objects.get(id=folder_id)
except:
self.context["folder"] = app.folder
else:
self.context["folder"] = app.folder
return render(request, self.template_name, self.context)
def post(self, request, app_id, folder_id=None):
try:
files = request.FILES.getlist('files_to_upload')
folder_name = request.POST.get("folder")
master = request.POST.get("master")
if master:
master = Folder.objects.get(id=master)
if folder_name:
Folder.objects.create(name=folder_name, owner=request.user.customer, folder=master)
if files:
for file in files:
if file.size < settings.MAX_FILE_SIZE:
File.objects.create(folder=master, item=file, name=file.name, size=file.size)
app = App.objects.get(pk=int(app_id))
self.context["app"] = app
if folder_id:
try:
self.context["folder"] = Folder.objects.get(id=folder_id)
except:
self.context["folder"] = app.folder
else:
self.context["folder"] = app.folder
return render(request, 'dashboard/filesection.html', self.context)
except DatabaseError:
return render(request, "dashboard/index.html", self.context)
urls.py
urlpatterns = [ url(r'^manage/(?P<app_id>[0-9]+)/(?P<folder_id>.+)', test, name='browse'), ]
dashboard/manage.html
<div class="modal-body">
<form id="app-launch" enctype="multipart/form-data" method="post">
{% csrf_token %}
<div class="form-row">
<div class="input-group mb-3">
<div class="custom-file">
<input type="hidden" value="{{ folder.id }}" name="master">
<input type="hidden" value="{{ app.id }}" name="file_app_id">
<input type="file" class="custom-file-input" name="files_to_upload" id="file_upload" accept=".py,.js,.json,.txt,.css,.html,.pdf,.htm,.doc,.docx,.log,.ppt,.pptx" multiple>
<label class="custom-file-label" for="inputGroupFile02">Choose file</label>
</div>
<div class="input-group-append">
<button class="input-group-text btn btn-primary" id="">Upload</button>
<button class="input-group-text btn btn-primary fileButton" id="">Upload</button>
</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-danger" data-dismiss="modal">Cancel</button>
</div>
</div>
app.js AJAX calls
$(document).ready(function() {
$(document).on('click','fileButton', function(e) {
e.preventDefault()
// const axios = require('axios');
var formData = new FormData();
var ins = document.getElementById('file_upload').files.length;
for (var x = 0; x < ins; x++) {
formData.append("files_to_upload", document.getElementById('file_upload').files[x]);
}
const csrftoken = getCookie('csrftoken');
var app_id = $('input[name="file_app_id"]').val();
var folder_id = $('input[name="master"]').val();
formData.append('master', folder_id);
req = $.ajax({
type: 'POST',
url: `/manage/${app_id}/${folder_id}`,
data: formData,
processData: false,
contentType: false,
headers: {
"X-CSRFToken": csrftoken,
}
});
req.done(function (data) {
$('#refreshSection').html(data)
})
});
});
AJAX POST and everything works, it just that the django is refreshing and rendering that section template on the browser which i don't want.
[Solved]
Its was a mistake from my side. I missed e.preventDefault()
which is really dumb.

How to update a ModelForm's initial values dynamically?

I'm trying to create a dynamic form which pre-populates the initial attribute of a certain field (title) when another field (checkin_type) is selected from a drop-down menu.
I'm using Django's generic CreateView, and the way I'd like to go about it is by overriding the view's post() method such that if it receives an AJAX request (which is triggered by a jQuery .change() in the aforementioned drop-down menu and contains the id of the selection option), it updates the initial property of the form's title.
Here is the view I've tried so far (in views.py):
from django.views import generic
from .models import CheckIn, CheckInType
class CheckInCreate(generic.CreateView):
model = CheckIn
fields = '__all__'
def post(self, request, *args, **kwargs):
if request.is_ajax():
checkin_type = CheckInType.objects.get(pk=request.POST['id'])
form = self.get_form()
form['title'].initial = checkin_type.title
self.object = CheckIn.objects.create(checkin_type=checkin_type)
return self.render_to_response(self.get_context_data(form=form))
else:
return super().post(request, *args, **kwargs)
Here is how the AJAX request is made in the form's template, called templates/dashboard/checkin_form.html in accordance with Django's naming convention (dashboard is the name of the app):
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/js-cookie#2/src/js.cookie.min.js"></script>
<script>
$(document).ready(function(){
var csrftoken = Cookies.get('csrftoken');
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
$(".auto-submit").change(function() {
$.post({
url: "{% url 'checkin-create' %}",
data: {id: $(".auto-submit option:selected").val()}
})
});
});
</script>
<form action="" method="post">{% csrf_token %}
{% for field in form %}
<div class="{% if field.name == 'checkin_type' %}auto-submit{% endif %}">
{{ field.errors }}
{{ field.label_tag }}
{{ field }}
</div>
{% endfor %}
<input type="submit" value="Send message" />
</form>
Here are the corresponding models (in models.py):
from django.db import models
class CheckInType(models.Model):
title = models.CharField(blank=True, max_length=255)
description = models.TextField(blank=True)
def __str__(self):
return self.title
class CheckIn(models.Model):
checkin_type = models.ForeignKey(CheckInType, null=True, on_delete=models.CASCADE)
title = models.CharField(blank=True, max_length=255)
description = models.TextField(blank=True)
notes = models.TextField(blank=True)
# Scheduling
requested_date = models.DateField(blank=True, null=True)
completed_date = models.DateField(blank=True, null=True)
The problem is that when I select an option from the drop-down menu for checkin_type, I see no change in the form:
I would have expected the Title field to become pre-populated with '1-week check-in' in this example. Any ideas why this is not working?
I think it will work if you change
form['title'].initial = checkin_type.title
to
form.cleaned_data['title'] = checkin_type.title

validate and submit Django form (django-crispy-forms) with Ajax

I'm very new at django and web development. I need help submitting Django form (using django-crispy-forms) with Ajax How do I:
validate input
submit without reloading
display errors in case of validation failure
Right now i can submit the form and it saves the entries into the database but reloads the entire page in the process.
i have included relevant snippets of my code below
// forms.py
class SubscriptionForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(SubscriptionForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.template_pack = 'bootstrap3'
self.helper.form_tag = True
self.helper.form_id = 'sub-form-1'
self.helper.form_class = 'form-inline'
self.helper.field_template = 'bootstrap3/inline_field.html'
self.helper.layout = Layout(
Div(Div(css_class='sub-form-notifications-content'),
css_class='sub-form-notifications'),
InlineField('name', id='subName'),
InlineField('email', id='subEmail'),
FormActions(Submit('submit', 'Notify me', css_class='form-control')),
)
class Meta:
model = Sub
fields = "__all__"
def clean_email(self):
"""
Validate that the supplied email address is unique for the
site.
"""
if User.objects.filter(email__iexact=self.cleaned_data['email']):
raise forms.ValidationError(
_("This email address is already in use. Please supply a different email address."))
return self.cleaned_data['email']
// views.py
from django.shortcuts import render, redirect
from .forms import SubscriptionForm
from .models import Sub
def index(request):
if request.method == 'POST':
sub_form = SubscriptionForm(request.POST)
if sub_form.is_valid():
sub_form.save()
# return redirect('landing:landing')
else:
sub_form = SubscriptionForm()
return render(request, 'landing/index.html', {'sub-form': sub_form})
// template
...
{% crispy sub-form %}
...
// rendered form HTML
<form class="form-inline" id="sub-form-1" method="post">
<input type='hidden' name='csrfmiddlewaretoken'
value='tdiucOssKfKHaF7k9FwTbgr6hbi1TwIsJyaozhTHFTKeGlphtzUbYcqf4Qtcetre'/>
<div class="sub-form-notifications">
<div class="sub-form-notifications-content">
</div>
</div>
<div id="div_id_name" class="form-group">
<label for="subName" class="sr-only requiredField">Name</label>
<input type="text" name="name" maxlength="30" required placeholder="Name"
class="textinput textInput form-control" id="subName"/>
</div>
<div id="div_id_email" class="form-group"><label for="subEmail" class="sr-only requiredField">Email address</label>
<input type="email" name="email" maxlength="60" required placeholder="Email address"
class="emailinput form-control" id="subEmail"/>
</div>
<div class="form-group">
<div class="controls ">
<input type="submit" name="submit" value="Notify me" class="btn btn-primary" id="submit-id-sub-form"/>
</div>
</div>
</form>
I'll try to give you an idea of how to do it easily.
Add the onsubmit event listener to the form and error-block e. g. bellow the form where errors will be displayed.
template
<form class="form-inline" id="sub-form-1" method="post" onsubmit="sendData();">
...
</form>
<div class="error-block">
<!-- Here is the space for errors -->
</div>
now the handler which will send the data to the view to be validated and saved
<script>
function sendData(e) {
e.preventDefault(); // don not refresh the page
var form_data = {
name: $('input[name="name"]').val(),
... other field values ...
}
$.ajax({
url: "{% url 'url-you-want-send-form-to' %}",
method: "POST",
data: form_data,
success: function(response) {
// here are the success data in the response
// you can redirect the user or anything else
//window.location.replace("{% url 'success-url' %}");
},
error: function(response) {
// here are the errors which you can append to .error-block
//$('.error-block').html(response);
}
})
}
</script>
In the view you'll receive the data in the same form as the form would be submitted but you will not have to render whole template to the response, but only the erros from the validated form, so the view you will send your ajax POST request to will have to be different from the view which renders the form. You can create another which will handle it.

405 error with AJAX for submission

I am trying to perform ajax for my form in my Django project. I am pretty new to webdev in general so what I think I am supposed to do is have an ajax call POST in a specific url which I chose as /message/. When a POST request is sent, my view should respond to this request and update my database and also render some text within /message/. However I am getting 405 error.
urls.py:
from home.views import HomeView, contact_request_view
urlpatterns = patterns('',
url(r'^$', HomeView.as_view(), name="home"),
url(r'^message/', contact_request_view)
)
views.py:
class HomeView(generic.TemplateView):
template_name = "home/home.html"
def contact_request_view(request):
if request.method == 'POST':
form = ContactForm()
if form.is_valid():
obj, created = User.objects.get_or_create(email=form.cleaned_data['email'],
first_name=form.cleaned_data['first_name'],
last_name=form.cleaned_data['last_name'])
ContactRequest.objects.create(user=obj,
message=form.cleaned_data['message'])
return render(request, "message", "testing")
return render(request, "message", "FAIL")
form.py:
class ContactForm(forms.Form):
first_name = forms.CharField(required=True, widget=forms.TextInput(attrs={'class': 'form-control'}))
last_name = forms.CharField(required=True, widget=forms.TextInput(attrs={'class': 'form-control'}))
email = forms.EmailField(required=True, widget=forms.EmailField())
message = forms.CharField(required=True, widget=forms.Textarea(attrs={'class': 'form-control contact-margin', 'rows': '8', 'placeholder': 'Message...'}))
JS:
var contactForm = document.getElementById("contact-form");
var firstName = contactForm.getElementById("firstname");
var lastName = contactForm.getElementById("lastname");
var email = contactForm.getElementById("email");
var message = contactForm.getElementById("message");
contactForm.submit(function() {
var contactInfo = {
first_name: firstName.val(),
last_name: lastName.val(),
email: email.val(),
message: message.val()
};
$.ajax({
type: 'POST',
url: "/message/",
data: contactInfo,
success: function() {
console.log("posted");
},
error: function() {
console.log("failed")
}
});
return false;
});
form:
<section id="contact">
<div class="container">
<div class="title-container">Contact Us</div>
<div class="title-caption">Reach us at (415)-911-9999</div>
<form class="contact-input" id="contact-form" method="post">
{% csrf_token %}
<div class="col-md-12">
<div class="col-md-6">
<div class="contact-input-margin form-group">
<input id="firstname" class="form-control" placeholder="First name">
</div>
<div class="contact-input-margin form-group">
<input id="lastname" class="form-control" placeholder="Last name">
</div>
<div class="contact-input-margin form-group">
<input id="email" class="form-control" placeholder="Email">
</div>
<div class="contact-input-margin form-group">
<input class="form-control" placeholder="Phone number">
</div>
</div>
<div class="contact-input-margin col-md-6">
<div class="form-group">
<textarea id="message" rows="8" class="form-control contact-margin" placeholder="Message...">
</textarea>
</div>
</div>
</div>
<input type="submit" value="Submit" class="btn btn-xl">
</form>
</div>
</section>
Method Not Allowed because you have not defined the POST in your view, the error make sence. Your view needs the POST method to be declared:
class HomeView(generic.TemplateView):
def post(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data(object=self.object)
return self.render_to_response(context)
You should send the csrf token with the post request in your JS code or you have to turn off the csrf protection on your message view with csrf_exempt() method like this url(r'^message/', csrf_exempt(contact_request_view)).
You would need to create a form instance and populate it with data from the request:
def contact_request_view(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
# if form is valid, you can process the data in form.cleaned_data
...
return render(request, "message", "testing")
else:
do_some_stuff() # if you `print(form.errors)` you can see what cause the errors
return render(request, "message", "FAIL")
Also, you might want to take a look at ModelForm
, so you can redeclare your ContactForm like this:
class ContactForm(ModelForm):
class meta:
model = User
def __init__(self, *args, **kwargs):
super(ContactForm, self).__init__(*args, **kwargs)
After that, if form is valid, you can call form.save(), and django will create the User instance automatically for you.

Categories