Django Form, form is not being submitted - python

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

Related

How to properly render form fields with django?

I am currently working on a login page for a django webapp. I am trying to include the login form within the index.html file. However, the form fields are not being rendered. My urls are correct I believe but I'm not sure where I am going wrong. Here is my views.py, forms.py and a snippet of the index.html. (I do not want to create a new page for the login I'd like to keep it on the index page)
# Home view
def index(request):
form = LoginForm()
if form.is_valid():
user = authenticate(
username=form.cleaned_data['username'],
password=form.cleaned_data['password'],
)
if user is not None:
login(request, user)
messages.success(request, f' welcome {user} !!')
return redirect('loggedIn')
else:
messages.info(request, f'Password or Username is wrong. Please try again.')
return render(request, "index_logged_out.html")
class LoginForm(forms.Form):
username = forms.CharField(max_length=63)
password = forms.CharField(max_length=63, widget=forms.PasswordInput)
<!-- Login -->
<section class="page-section" id="login">
<div class="container">
<div class="text-center">
<h2 class="section-heading text-uppercase">Login</h2>
</div>
<form>
{% csrf_token %}
{{form}}
<center><button class="btn btn-primary btn-block fa-lg gradient-custom-2 mb-3" type="submit" style="width: 300px;">Login</button></center>
</form>
<div class="text-center pt-1 mb-5 pb-1">
<center><a class="text-muted" href="#!">Forgot password?</a></center>
</div>
<div class="d-flex align-items-center justify-content-center pb-4">
<p class="mb-0 me-2">Don't have an account?</p>
<button type="button" class="btn btn-outline-primary">Create New</button>
</div>
</form>
</div>
</section>
To add to A.S's answer, you should initialize a context variable at the start of the view.
def index(request):
context = {}
form = LoginForm()
context['form'] = form
if form.is_valid():
# ...
return render(request, "index_logged_out.html", context)
It makes it easier to work with when you start to pass in more variables to the context later on, otherwise it'll complicate your view.

Django form not showing up in template

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})

How to save changed fields with ModelForm?

I want to save changed values of ModelForm to database. I'm having problems even I follow this documentation if I'm right that it can be possible with initial values: Documentation- providing initial values
models.py:
class Settings(models.Model):
url = models.URLField(max_length=100, default='https://website.com')
maxCount = models.SmallIntegerField(default=30)
views.py:
def Settings(request):
settingsObj = Settings.objects.get(id=1)
form = SettingsForm(initial={'url': settingsObj.url, 'maxCount':settingsObj.maxCount}, instance=settingsObj)
if form.is_valid():
form.save()
forms.py:
class SettingsForm(forms.ModelForm):
class Meta:
model = Settings
fields = ['url', 'maxCount']
templates
<form class="form-horizontal" role="form" method="POST">{% csrf_token %}
<div class="form-group">
<div class="col-sm-offset-1 col-sm-6">
{{ form.as_p }}
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-1 col-sm-6">
<button type="submit" class="btn btn-primary btn-lg btn-block">Accept</button>
</div>
</div>
</form>
Currently the form is showing the current values from database, but isn't saving changed data. form.is_valid() returns True, but form.save() seems to do nothing.
The initial argument and the instance argument in your call to SettingsForm() serve the exact same purpose, because you are using the fields of the instance individually in each field of initial.
The save() method is not working because you need to populate the form with data from request.POST.
This view should work:
def settings(request):
settingsObj = Settings.objects.get(id=1)
if request.POST:
form = SettingsForm(request.POST, instance=settingsObj)
if form.is_valid():
form.save()
else:
form = SettingsForm(instance=settingsObj)
context = { ..., 'form': form, ... }
return render(request, 'template-address', context)

Why is my django form sending as a GET and then giving a 405 error at POST

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/

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