I have a form so an user can ask for a loan and it will tell them if it´s approved or not. The problem is not the logic, it´s the submit input that doesn't work. It will not save the form in the database or show me the errors because of the submit input. Maybe is something wrong with the succes_url? I don't know, but here's my code:
views.py:
#don't worry about the logic part of the form, it's just to show how it´s supposed to work
class LoanRequest(LoginRequiredMixin, generic.CreateView):
form_class = LoanForm
success_url = reverse_lazy('Prestamos')
template_name = 'Prestamos/template/Prestamos/prestamos.html'
def form_valid(self, form):
user = self.request.user
cliente = Cliente.objects.get(user_id = user.id)
if not cliente.approve_loan(form.cleaned_data.get('loan_total')):
form.add_error(field=None, error='loan not approved')
return self.form_invalid(form)
else:
form.instance.customer_id = cliente
super(LoanRequest, self).form_valid(form)
return render(self.request, 'Prestamos/template/Prestamos/prestamos.html', context={'form': form, 'success_msg': 'loan approved!'})
urls.py:
urlpatterns = [
path('prestamos/', views.LoanRequest.as_view(), name = 'prestamos'),
]
forms.py:
class LoanForm(forms.ModelForm):
class Meta:
model = Prestamo #loan in English
fields = ['loan_type', 'loan_total', 'loan_date']
and the template:
<div class="container">
{%if success_msg%}
<p class="alert alert-success">{{success_msg}}</p>
{%endif%}
<form action="" method="POST">
{%csrf_token%}
{%for field in form%}
<div class="form-group">
<label for="{{field.label}}">{{field.label}}</label>
{{field}}
</div>
{%for error in field.errors%}
<p>{{error}}</p>
{%endfor%}
{%endfor%}
<input type="submit" value="request"></input>
</form>
</div>
models.py:
class Prestamo(models.Model):
loan_id = models.AutoField(primary_key=True)
loan_type = models.CharField(max_length=20,
choices = [('PERSONAL', 'PERSONAL'), ('HIPOTECARIO', 'HIPOTECARIO'), ('PRENDARIO', 'PRENDARIO')])
loan_date = models.DateField()
loan_total = models.IntegerField()
customer_id = models.IntegerField()
class Meta:
db_table = 'prestamo'
Well, <input> is an empty tag, it does not contain anything, so don't close it.
Additionally, I'd recommend you to make gaps between template tags, like it should be {% endfor %} not {%endfor%}.
Also, remove the empty action attribute from form, as Django always take current page route if not mentioned or empty string.
Also use novalidate on form for rendering custom errors.
Try this template:
<div class="container">
{% if success_msg %}
<p class="alert alert-success">{{success_msg}}</p>
{% endif %}
<form method="POST" novalidate>
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label for="{{field.label}}">{{field.label}}</label>
{{field}}
</div>
{% for error in field.errors %}
<p>{{error}}</p>
{% endfor %}
{% endfor %}
<input type="submit" value="request">
</form>
</div>
Edit:
One mistake I could see the name for the view is prestamos and you have mentioned it as Prestamos, which is wrong.
So:
class LoanRequest(LoginRequiredMixin, generic.CreateView):
form_class = LoanForm
success_url = reverse_lazy('prestamos')
template_name = 'Prestamos/template/Prestamos/prestamos.html'
Related
I have searched the site for a solution, many related questions, but no direct response given. I have 3 apps on my projects, and 2 worked pretty well, except for this app, which is not writing to the database.
Yes, the other 2 apps write to the postgres database, and I can view the table, but this return empty rows, and I don't see any problem. I hope someone can help me, see below my Model, Form and View.py.
MY View.py
from django.shortcuts import render, redirect
from .forms import EngForm
def EngineerList(request):
return render(request, "Engineers/elist.html")
def EngineerForm(request):
if request.method == "GET":
form = EngForm()
return render(request, "Engineers/eform.html", {'form':form})
else:
form = EngForm(request.POST)
if form.is_valid():
form.save()
return redirect('/engineer/') #pointing to urls.py paths
My Forms.py
from django import forms
from .models import Engineers
class EngForm(forms.ModelForm):
class Meta:
model = Engineers
fields = '__all__'
labels = {
'workarea' : 'Zone',
'face' : 'Picture',
}
def __init__(self, *args, **kwargs):
super(EngForm, self).__init__(*args, **kwargs)
self.fields['position'].empty_label='Select'
self.fields['workarea'].empty_label='Select'
self.fields['face'].required = False
My Model.py
from django.db import models
class Engineers(models.Model):
position = (
('NOC', 'NOC'),
('Supervisor', 'Supervisor'),
('Site Manager','Site Manager'),
('Site Engineer', 'Site Engineer'),
)
region = (
('SS','South-South'),('SW','SW'),('SE','SE'),
('NE','NE'),('NW','NW'),('NC','NC'),
)
firstname = models.CharField(max_length=20)
lastname = models.CharField(max_length=20)
email = models.EmailField(max_length=50)
username = models.CharField(max_length=20)
phone = models.IntegerField()
position = models.CharField(max_length=20, choices=position)
workarea = models.CharField(max_length=20, choices=region)
face = models.ImageField(upload_to='', height_field=15, width_field=9)
My HTML
{% extends "Dashboard/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<form method="POST" action="" autocomplete="off">
{% csrf_token %}
<div class="row">
<div class="col-md-12">
{{form.face|as_crispy_field}}
</div>
<p>
<hr>
<p>
<div class="col-md-6">
{{form.firstname|as_crispy_field}}
</div>
<div class="col-md-6">
{{form.lastname|as_crispy_field}}
</div>
<div class="col-md-6">
{{form.username|as_crispy_field}}
</div>
<div class="col-md-6">
{{form.position|as_crispy_field}}
</div>
<div class="col-md-6">
{{form.phone|as_crispy_field}}
</div>
<div class="col-md-6">
{{form.workarea|as_crispy_field}}
</div>
<div class="col-md-12">
{{form.email|as_crispy_field}}
</div>
</div>
<hr>
<div>
<button type="submit" class="btn btn-success"><i class="fas fa-save"></i> Submit</button>
</div>
</form>
{% endblock content %}
My problem is, the database table is returning empty row.
Solved. Applied #GwynBleidD suggest, I reviewed the doc, and then rearranged the code as such.
def EngineerForm(request):
if request.method == "POST":
form = EngForm(request.POST)
if form.is_valid():
form.save()
return redirect('/engineer/') #pointing to urls.py paths
else:
form = EngForm()
return render(request, "Engineers/eform.html", {'form':form})
I'm struggling to set an initial value in a form instance based on the URL parameter in Django 3.0.
I have a Claim model:
# models.py
class Claim(models.Model):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
text = models.TextField()
date_added = models.DateTimeField(default=timezone.now)
member = models.ForeignKey(User, on_delete=models.CASCADE)
I have a NewClaimForm based on ModelForm:
# forms.py
class NewClaimForm(forms.ModelForm):
class Meta:
model = Claim
fields = ['product', 'text']
I have a NewClaimView based on CreateView:
# views.py
class NewClaimView(LoginRequiredMixin, CreateView):
model = Claim
form_class = NewClaimForm
template_name = 'portal/new_claim.html'
def form_valid(self, form):
form.instance.member = self.request.user
return super(NewClaimView, self).form_valid(form)
And using the following template fragment on the index page...
# index.html
<div class="card-deck">
{% for product in products %}
<div class="card text-center">
<div class="card-header">
<h5 class="card-title text-primary">{{ product }}</h5>
</div>
<div class="card-body">
<ol class="card-text text-left">
<li>Fill in the {{ product }} form</li>
<li>Attach your medical records</li>
<li>Get your claim reviewed within 48 hours</li>
</ol>
Online Form
</div>
</div>
{% endfor %}
</div>
...I pass the product_id parameter to the URL:
# urls.py
app_name = 'portal'
urlpatterns = [
path('new_claim/<int:product_id>/', NewClaimView.as_view(), name='new_claim_product'),
]
And lastly, this is what my new_claim template looks like:
# new_claim.html
{% extends "portal/base.html" %}
{% load bootstrap4 %}
{% block content %}
<p>Submit a new claim</p>
<form action="{% url 'portal:new_claim' %}" method='post' class="form" enctype="multipart/form-data">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button name="submit">Submit claim</button>
{% endbuttons %}
</form>
{% endblock content %}
I would like to now use the product_id to set the initial value of the product field in the form instance according to product_id. How can I achieve this?
Figured out how to do this by modifying the get method for my class-based view:
# views.py
class NewClaimView(LoginRequiredMixin, CreateView):
model = Claim
form_class = NewClaimForm
template_name = 'portal/new_claim.html'
def get(self, request, product_id=None, *args, **kwargs):
form = self.form_class(initial={'product': product_id})
return render(request, self.template_name, {'form': form})
def form_valid(self, form):
form.instance.member = self.request.user
return super(NewClaimView, self).form_valid(form)
Here's a link to the relevant documentation.
Is this an optimal way to solve this?
Do I need the *args and **kwargs in the modified get method? I'm not using them in my code but perhaps it would be useful to keep them there for other purposes in the future?
I have the following models defined:
class Item(models.Model):
rfid_tag = models.CharField()
asset = models.OneToOneField('Assets', default=None, null=True,
on_delete=models.SET_DEFAULT,)
date = models.DateTimeField(name='timestamp',
auto_now_add=True,)
...
class Assets(models.Model):
id = models.AutoField(db_column='Id', primary_key=True)
assettag = models.CharField(db_column='AssetTag', unique=True, max_length=10)
assettype = models.CharField(db_column='AssetType', max_length=150)
...
class Meta:
managed = False
db_table = 'Assets'
ordering = ['assettag']
def __str__(self):
return f"{self.assettag}"
def __unicode__(self):
return f"{self.assettag}"
For which I have created the following form and formset:
class ItemDeleteForm(forms.ModelForm):
asset = forms.CharField(required=True,
help_text= "Item asset tag",
max_length=16,
label="AssetTag",
disabled=True,
)
delete = forms.BooleanField(required=False,
label="Delete",
help_text='Check this box to delete the corresponding item',
)
class Meta:
model = Item
fields = ['asset']
ItemDeleteMultiple = forms.modelformset_factory(model=Item,
form=ItemDeleteForm,
extra=0,
)
managed by the view:
class DeleteMultipleView(generic.FormView):
template_name = '*some html file*'
form_class = ItemDeleteMultiple
success_url = reverse_lazy('*app_name:url_name*')
def form_valid(self, form):
return super().form_valid(form)
And rendered in the template:
{% extends "pages/base.html" %}
{% block title %}
<title>Delete Multiple</title>
{% endblock %}
{% block content %}
<h1>Delete Multiple Items</h1>
<br>
<form class="ManualForm" action ="." method="POST"> {% csrf_token %}
{{ form.management_form }}
<table border="2">
<tr><th colspan="3" scope="row">Select Items to Delete</th></tr>
{% for item_form in form %}
<tr>
<td><label for="{{ item_form.asset.id_for_label }}">AssetTag {{forloop.counter}}:</label>
{% if item_form.non_field_errors %}
{{ item_form.non_field_errors }}
{% endif %}
{% if item_form.asset.errors %}
{{item_form.asset.errors}}
{% endif %}
</td>
<td>{{item_form.asset}}</td>
<td>{{item_form.delete}}
{% if item_form.delete.errors %}
{{item_form.delete.errors}}
{% endif %}
</td>
</tr>
{% endfor %}
</table>
<br>
<input class = "btn btn-success" type="submit" value="Delete Selected" />
Cancel
</form>
<form class="AutoForm" action ="." method="POST"> {% csrf_token %}
{{form.as_table}}
<input class = "btn btn-success" type="submit" value="Delete Selected" />
Cancel
</form>
{% endblock %}
When I submit AutoForm, everything is great. It takes me to app_name:url_name, but if I sumbit ManualForm I don't get redirected. It will simply clear all data and reload the form page with empty fields.
The HTTP POST response status code for AutoForm is 302, while for ManualForm is 200.
I don't understand how the template could influence the behavior of the url redirection. What am I doing wrong in the manual rendering of the formset?
It seems that adding:
{% for field in item_form.hidden_fields %}
{{field}}
{% endfor %}
under {% for item_form in form %} will solve the issue.
I didn't understand very well from the docs:
Looping over hidden and visible fields
If you’re manually laying out a form in a template, as opposed to
relying on Django’s default form layout, you might want to treat
< input type="hidden"> fields differently from non-hidden fields. For
example, because hidden fields don’t display anything, putting error
messages “next to” the field could cause confusion for your users – so
errors for those fields should be handled differently.
I just thought this is about errors, so I didn't care. But one of the first thing it says about forms is this:
As an example, the login form for the Django admin contains several
< input> elements: one of type="text" for the username, one of
type="password" for the password, and one of type="submit" for the
“Log in” button. It also contains some hidden text fields that the
user doesn’t see, which Django uses to determine what to do next.
It also tells the browser that the form data should be sent to the URL
specified in the < form>’s action attribute - /admin/ - and that it
should be sent using the HTTP mechanism specified by the method
attribute - post.
Maybe it will help someone else.
I am working on my first Django project.
But I get following errors:
edit_file template
<form method="POST" action="{% url 'edit_file' file.id %}">
{% csrf_token %}
{{ form.errors }}
{{ form.non_field_errors }}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field.errors }}
{{ hidden_field }}
{% endfor %}
<div class="form-group row">
<label for="id_name" class="col-sm-3 col-form-label"> File Name </label>
<div class="col-sm-4">
{% render_field form.name|add_class:"form-control" %}
</div>
</div>
<div class="form-group row">
<label class="col-sm-3 col-form-label">File Path</label>
<div class="col-sm-4">
{% render_field form.directory_path|add_class:"form-control" %}
</div>
</div>
<div class="form-group">
{% render_field form.script_code|add_class:"form-control" %}
<pre id="id_script_code" style="height: 40pc;">{{ form.script_code }}</pre>
</div>
<button type="submit" class="btn btn-success mr-2">Save Changes</button>
Back
</form>
Views.py
def edit_file(request, id):
instance = get_object_or_404(File, id=id)
if request.method == "POST":
form = EditFileForm(request.POST, instance=instance)
if form.is_valid():
print('Form validation => True')
form.save()
return HttpResponse('<h1> database updated! </h1>')
else:
print('Form validation => False')
file = File.objects.latest('id')
context = {'file': file, "form": form}
return render(request, 'edit_file.html', context)
else:
instance = get_object_or_404(File, id=id)
form = EditFileForm(request.POST or None, instance=instance)
file = File.objects.latest('id')
context = {'file': file, "form": form}
return render(request, 'edit_file.html', context)
forms.py
class EditFileForm(ModelForm):
# field_order = ['field_1', 'field_2']
class Meta:
print("forms.py 1")
model = File
fields = ('name', 'script_code', 'directory_path','version')
def clean(self):
print("forms.py 2")
cleaned_data = super(EditFileForm, self).clean()
name = cleaned_data.get('name')
print("cleaned data: ", cleaned_data)
Models:
Version id point to a version which contains multiple files.
class File(models.Model):
# Incrementing ID (created automatically)
name = models.CharField(max_length=40)
script_code = models.TextField() # max juiste manier?
directory_path = models.CharField(max_length=200)
version = models.ForeignKey('Version', on_delete=models.CASCADE)
class Meta(object):
db_table = 'file' # table name
class Version(models.Model):
# Incrementing ID (created automatically)
version_number = models.CharField(max_length=20)
pending_update = models.BooleanField(default=False)
creation_date = models.DateTimeField(auto_now_add=True, null=True, editable=False)
modification_date = models.DateTimeField(auto_now_add=True, null=True)
connecthor = models.ForeignKey('ConnecThor', on_delete=models.CASCADE)
def __str__(self):
return self.connecthor_id
The problem:
form.is_valid() keeps failing. My view returns one error.
*version: This field is required. But I don't know how to fix this. Users should only be able to update 3 of the 5 data fields. So there is no reason to show the PK or FK in the template.
You've included version in the list of fields in your form, but you aren't outputting it in the template so there is no means of providing it. Since the model field does not specify blank=True, it is a required field, hence the error.
If you don't want users to be able to modify this field, you should remove it from that list of fields under Meta.
You do not have version in your template. Your model field for version does not say it can have null values. Hence your form validation fails. Include it in your template or remove the field from EditFileForm class's Meta class in forms.py
Form.is_valid() always returns false. Here's my code.
#urls.py
url(r'^create/', "app.views.createpost", name="createpost"),
My models.py
class Post(models.Model):
"""docstring for Post"""
post_image = AjaxImageField(upload_to='posts', max_width=200, max_height=200, crop=True, null= False, default='site_media/media/BobMarley/bob1.jpg')
poster = models.ForeignKey(User, null= False, default=User.objects.get(username="admin")
Here's my forms.py
#forms.py
class AjaxImageUploadForm(forms.Form):
image = forms.URLField(widget=AjaxImageWidget(upload_to='posts'))
view.py
#views.py
def createpost(request):
if request.method == 'POST':
form = AjaxImageUploadForm(request.POST, request.FILES)
if form.is_valid():
newpost = Post(post_image = request.FILES['image'])
newpost.poster = request.user
newpost.save()
return HttpResponseRedirect('/create/')
else:
form = AjaxImageUploadForm() # An empty, unbound form
posts = Post.objects.all()
return render_to_response('create.html',{'posts': posts, 'form': form},context_instance=RequestContext(request))
The Template
#create.html
{% block extra_head %}
{{ form.media }}
{% endblock %}
{% block body %}
<form method="POST" enctype="multipart/form-data" action="{% url "createpost" %}">
{% csrf_token %}
{{ form.errors}}
{{ form.as_p }}
<button type="submit" name='image'>Upload</button>
</form>
{% endblock %}
The form is never valid and the error printed is "This field is required."
And this is the form widget begin created
<tr><th><label for="id_image">Image:</label></th><td><ul class="errorlist"><li>This field is required.</li></ul>
<div class="ajaximage"><a class="file-link" target="_blank" href=""> <img class="file-img" src=""></a> <a class="file-remove" href="#remove">Remove</a>
<input class="file-path" type="hidden" value="" id="id_image" name="image" /> <input type="file" class="file-input" name="image"/> <input class="file-dest" type="hidden" name="image" value="/ajaximage/upload/posts/0/0/0"> <div class="progress progress-striped active"> <div class="bar"></div></div>
</div>
</td></tr>
I am using the django package called django-ajaximage and their custom widget (AjaxImageWidget) is probably a custom text widget
class AjaxImageWidget(widgets.TextInput):
Thank you for you help
You do not need the init on your model class. Also, null=False is the default. I'm not sure the text 'admin' will default to the user with username admin. You would need to do:
default=User.objects.get(username="admin")
You can also include the poster assignment in the creation of the object like so:
newpost = Post(post_image=request.FILES['image'], poster=request.user)
To make the form validate you need to put blank=True in the field like so:
poster = models.ForeignKey(User, blank=True, default=User.objects.get(username="admin")
I would also make the form be a ModelForm:
class AjaxImageUploadForm(forms.ModelForm):
class Meta:
model=Post
exclude=['poster',]