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.
Related
I am pretty new to django and I am having some issues with my form. It is not submitting anything. I don´t have any idea why, no issue appears in the terminal. It displays the form correctly, but when filling it out and submitting, it just redirects me to the same form but blank. I check the database and nothing´s been added. My code below:
#views.py
def ContractView(request):
form=contractsform(request.POST)
if form.is_valid():
con =form.save()
return redirect("{% url 'contracts' %}", con.id)
else:
form = contractsform()
return render(request, 'contform.html', {'form': form})
#contform.html
<div class="card-body">
<form action="" method="POST" class="row g-3">
{% csrf_token %}
<label for="{{ form.subject.id_for_label }}">Name:</label>
{{form.name}}
<div class="col-md-6">
<label for="{{ form.subject.id_for_label }}">Contractor:</label>
<div class="input-group input-group-sm mb-3">
{{form.contractor}}
<button id="new-vendor" class="btn btn-outline-secondary" type="button">+</button>
</div>
</div>
<div class="col-md-6">
<label for="{{ form.subject.id_for_label }}">Contractee:</label>
{{form.contractee}}
</div>
...
<div class="col-md-6">
<button type="button" onclick="javascript:history.back()">Cancel</button>
</div>
<div class="col-md-6">
<input type="submit" value="Submit" class="btn btn-primary" style="float: right;">
</div>
</form>
#forms.py
class contractsform(forms.ModelForm):
class Meta:
model = Contratos
fields = '__all__'
widgets = {
'name': forms.TextInput(attrs ={'class': 'form-control'}),
'contractee': forms.TextInput(attrs={'class': 'form-control'}),
'contractor': forms.Select(attrs={'class': 'form-control', 'id': 'contractor_view' }),}
#urls.py
urlpatterns = [
path('contracts/', views.contratostabla, name='contracts'),
path('contracts/add/', ContractView, name='new-contract'),
]
You are currently redefining the form as an empty form when it is not valid, you need to change you logic to use a form filled with POST data when the request method is POST and an empty form when the method is GET. You still need to use the filled form for POST when it's invalid so that you get the errors and the previous data
def ContractView(request):
if request.method == 'POST':
form = contractsform(request.POST)
if form.is_valid():
con = form.save()
return redirect("{% url 'contracts' %}", con.id)
else:
form = contractsform()
return render(request, 'contform.html', {'form': form})
FYI on readability, the convention is to use CamelCase for classes so your form would be named ContractsForm and lowercase-with-underscores for functions so your view would be contract_view
You can update your view like this
def ContractView(request):
form = contractsform(request.POST or None)
if form.is_valid():
con = form.save()
con.save()
return redirect("{% url 'contracts' %}", con.id)
else:
form = contractsform()
return render(request, 'contform.html', {'form': form})
I think this will solve your problem
My problem is not showing up form in the Django template.
I'm using python 3.7.6
Django 3.2
Here is my code
....................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
forms.py
from django import forms
from tasks.models import Task, TaskType
class TaskForm(forms.ModelForm):
name = forms.CharField(max_length=100,
required=True, widget=forms.TextInput(attrs={'class': 'form-control'}))
input_image = forms.ImageField(widget=forms.FileInput(
attrs={'class': 'form-control-file'}))
task_type = forms.ModelChoiceField(queryset=TaskType.objects.name.all(), widget=forms.Select(
attrs={'class': 'form-control'}))
class Meta:
model = Task
fields = ['name', 'input_image', 'task_type']
view.py
from django.shortcuts import render, redirect
from tasks.forms import TaskForm
def create_task(request):
if request.method == 'POST' and 'submit-task' in request.POST:
task_form = TaskForm(request.POST, request.FILES, instance=request.user)
if task_form.is_valid():
task_form.save()
return redirect(to='dashboard')
return render(request, 'users/dashboard.html', {'task_form': task_form})
dashboard.html
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<div class="modal fade" id="myModal">
<div class="modal-dialog modal-fullscreen-lg-down">
<div class="modal-content">
<!-- Modal Header -->
<div class="modal-header">
<h4 class="modal-title">Upload your image</h4>
<button
type="button"
class="btn-close"
data-dismiss="modal"
></button>
</div>
<!-- Modal body -->
<div class="modal-body">
<div class="form-group">
<label class="">Task name</label>
{{task_form.name}}
<div class="input-group">
<select class="custom-select" id="inputGroupSelect04">
<option selected>Choose your model</option>
{{task_form.task_type}}
</select>
<span class="input-group-btn">
<span class="btn btn-outline-dark btn-file">
Browse… {{task_form.image_input}}
</span>
</span>
<input type="text" class="form-control" readonly />
</div>
<img id="img-upload" />
</div>
</div>
<!-- Modal footer -->
<div class="modal-footer">
<button
type="button"
class="btn btn-secondary"
data-dismiss="modal"
>
Close
</button>
<button type="button" class="btn btn-primary" name="submit-task">
Save changes
</button>
</div>
</div>
</div>
</div>
</form>
So, in the template, the form is not showing up. Please help me to fix it. Thank you so much
def create_task(request):
if request.method == 'POST' and 'submit-task' in request.POST:
task_form = TaskForm(request.POST, request.FILES, instance=request.user)
...
return render(request, 'users/dashboard.html', {'task_form': task_form})
I pretty sure that you want to return dashboard.html on GET method,
however task_form is creating only when POST method. In other words task_form does not exists.
You should define it before using:
def create_task(request):
if request.method == 'POST' and 'submit-task' in request.POST:
task_form = TaskForm(...)
...
else:
task_form = TaskForm(...) # task form defined for non POST methods
# or place it here
# task_form = TaskForm(...) # task form defined for non POST methods
return render(request, 'users/dashboard.html', {'task_form': task_form})
the first thing in your code is you are passing an instance for a create method but the instance does not exist when you didn't create the record yet, the other problem is you are not supporting the get method.
there is an example :
from django.shortcuts import render, redirect
from tasks.forms import TaskForm
from .models import Task
def create_task(request):
instance = Task.objects.filter(user=request.user)
if request.method == 'POST':
if instance:
task_form = TaskForm(request.POST, request.FILES, instance=request.user)
else:
task_form = TaskForm(request.POST, request.FILES)
if task_form.is_valid():
task_form.save()
return redirect(to='dashboard')
return render(request, 'users/dashboard.html', {'task_form': task_form})
else:
if instance:
task_form = TaskForm(instance=request.user)
else:
task_form = TaskForm()
return render(request, 'users/dashboard.html', {'task_form': task_form})
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.
I have html form which I want to send and save to django model. When I try to send message I get an error:
ValueError at /account/userinfo/akylson/
"<Mail: hhh>" needs to have a value for field "id" before this many-to-many relationship can be used.
Request Method: POST
Request URL: http://localhost:8000/account/userinfo/akylson/
Django Version: 1.11.3
Exception Type: ValueError
Exception Value:
"<Mail: hhh>" needs to have a value for field "id" before this many-to-many relationship can be used.
You can see my code below.
Here is my html form below:-
<form role="form" class="form-horizontal" method="post">
{% csrf_token %}
<div class="form-group">
<input type="checkbox" id="id_receiver" name="receiver" value="{{ user.username }}" checked hidden>
<label class="col-lg-2 control-label">Тема</label>
<div class="col-lg-10">
<input type="text" placeholder="" id="id_subject" name="subject" value="{{ subject }}" class="form-control">
</div>
</div>
<div class="form-group">
<label class="col-lg-2 control-label">Сообщение</label>
<div class="col-lg-10">
<textarea rows="10" cols="30" class="form-control" id="id_message" name="message"></textarea>
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-lg-10">
<span class="btn green fileinput-button"><i class="fa fa-plus fa fa-white"></i>
<span>Приложение</span><input type="file" name="files[]" multiple=""></span>
<button class="btn btn-send" value="submit" type="submit">Send</button>
</div>
</div>
</form>
Here is my view.py:
#login_required()
def userinfo(request, username):
username = User.objects.get(username=username)
args = {}
args['user'] = username
if request.method == 'POST':
sender = request.user
receiver = request.POST['receiver']
subject = request.POST['subject']
message = request.POST['message']
b = Mail.objects.create(sender=sender, receiver=receiver, subject=subject, message=message)
b.save()
return render(request, 'account/userinfo.html', args)
Here is my models.py:
class Mail(models.Model):
sender = models.ForeignKey(User, related_name='mail_sender')
receiver = models.ManyToManyField(User, related_name='mail_receiver')
subject = models.CharField(max_length=200)
message = RichTextUploadingField()
date = models.DateTimeField(auto_now=True, null=False, blank=False)
class Meta():
ordering = ['-date']
def __str__(self):
return self.subject
Here is my forms.py:
class NewMailForm(forms.ModelForm):
class Meta:
model = Mail
fields = (
'sender',
'receiver',
'subject',
'message',
)
widgets = {'receiver': forms.CheckboxSelectMultiple()}
You have to pass user instances to your views.py.
Change your views.py as showed below,
views.py:
#login_required()
def userinfo(request):
user = request.user
form = NewMailForm(request.POST or None)
if request.method == 'POST':
if not form.is_valid():
print form.errors
return render(request,'')
else:
sender = user
receiver = form.cleaned_data.get("receiver")
subject = form.cleaned_data.get("subject")
message = form.cleaned_data.get("message")
b = Mail.objects.create_user(
sender=sender,
receiver=receiver,
subject=subject,
message=message)
b.save()
return render(request, 'account/userinfo.html')
and forms.py:
<form action="." method="POST">{% csrf_token %}
{{ form.as_p }}
</form>
This will create a new mail objects with requested user.
In your views.py create an instance of your model
for example m = Mail()
then post each of the field using the instance for example
m.receiver = request.POST.get('receiver')
then save with
m.save()
Before a Many2many field can be linked Django needs the id of the record on the other side of the relationship (in this case your Mail) model.
So you have to actually create it before setting the receiver like this:
b = Mail.objects.create(sender=sender, subject=subject, message=message)
b.receiver = receiver
b.save()
You have made several mistakes:
forms.py is not required if you have made an HTML form and linked to project.
You have not defined b. Just written b.save
Just debug these errors and you are Done!
I am trying to create a 'change email' form in Django. The form is showing in the template but when I submit the form this happens in the terminal
[09/Feb/2016 09:19:55] "GET /change_email/ HTTP/1.1" 200 3775
[09/Feb/2016 09:19:55] "GET /static/css/styles.css HTTP/1.1" 304 0
[09/Feb/2016 09:20:09] "POST /account/ HTTP/1.1" 405 0
And it sends to a blank page. I've also looked in the view and noticed, with some print statements, that it's going to the last else statement rather than going through if request.POST:
Here's my code:
View:
def change_email(request):
if request.POST:
print "POST"
form = ChangeEmailForm(request.POST)
if form.is_valid():
email = request.POST['current_email']
email_new = request.POST['new_email']
email_confirmed = request.POST['confirm_email']
form.save()
if email_new == email_confirmed:
User.objects.get(email=email).update(email=request.POST['new_email'])
else:
return HttpResponse("These emails don't match")
else:
return render(request, 'registration/email_change_form.html', {'form': form})
else:
print "GET"
form = ChangeEmailForm()
return render(request, 'registration/email_change_form.html', {'form': form})
Form:
class ChangeEmailForm(forms.ModelForm):
current_email = forms.EmailField()
new_email = forms.EmailField()
confirm_email = forms.EmailField()
class Meta:
model = User
fields = ('current_email', 'new_email', 'confirm_email')
Template:
<form action="{% url 'account' %}" method="post">
{% csrf_token %}
<div class="form-group">
<label for="Current Email">Current Email:</label>
<div class="col-sm-10">
{% render_field form.current_email type="email" class+="form-control" placeholder="Current Email" %}
</div>
</div>
<div class="form-group">
<label for="New Email">New Email:</label>
<div class="col-sm-10">
{% render_field form.new_email type="email" class+="form-control" placeholder="New Email" %}
</div>
</div>
<div class="form-group">
<label for="Confirm New Email">Confirm New Email:</label>
<div class="col-sm-10">
{% render_field form.confirm_email type="email" class+="form-control" placeholder="Confirm New Email" %}
</div>
</div>
<div class="form-group" style="padding-top: 40px; text-align:right">
<button type="submit" class="btn btn-primary">Save changes</button>
</div>
</form>
URLs:
urlpatterns = [
url(r'^change_email/$', views.change_email, name="change_email"),
url(r'^account/$', TemplateView.as_view(template_name='account_details.html'), name="account"),
]
form action=""
This will send the form back to the change_email and go through the POST method part (currently it sends to another url). Also, use
if request.method == 'POST':
TemplateView will not have post method defined. It is a basic class which makes template rendering easy.
Django will throw 405 METHOD NOT ALLOWED on class based views if appropriated method is not defined for corresponding HTTP verb.
You should implement your own class:
class AccountView(TemplateView):
template_name='account_details.html'
def post(self):
# your code to handle HTTP post
# return response
def get(self):
# your code to handle HTTP get
# return response
and in urlpatterns:
url(r'^account/$', AccountView.as_view()),
More information at : https://docs.djangoproject.com/es/1.9/topics/class-based-views/