I am creating a 'Forms Management' system for my application.
I am creating a forms dynamically using a custom form 'factory' method.
Form data is in a json file.
I can create a forms.CharField and set the label, required, initial and help_text properties.
When I try to set the max_length property I do not get any error message, but the resulting HTML does not contain the max_length attribute.
In static (class) forms defined as
class SearchAccountForm(forms.Form):
provider_code = forms.CharField(
label='Provider:',
max_length=100,
required=True,
widget=forms.TextInput(attrs={'class': 'form-control'}))
The resulting HTML contains the max_length attribute.
<label for="id_provider_code">Provider:</label>
</th><td><input type="text" name="provider_code" class="form-control" maxlength="100" required id="id_provider_code">
So what's up with max_length??
Json file
{
"form1": [
{
"fld_name": "customer_name",
"fld_type": "CharField",
"fld_label": "Cust Name",
"fld_required": "False",
"fld_maxLength": 5,
"initial": "Dr John"
},
{
"fld_name": "customer_number",
"fld_type": "CharField",
"fld_label": "Cust #",
"fld_required": "True",
"fld_maxLength": 15,
"help_text": "Enter account number"
},
{
"fld_name": "customer_type",
"fld_type": "CharField",
"fld_label": "Customer Type",
"fld_required": "False"
}
]
}
and the forms.py factory method
from django import forms
import json
def dynfrm():
f = open('blog/frmJson/frm1.json')
data = json.load(f)
fields = {}
for i in data['form1']: ## form1 = form name in json file
print(i)
## add to fields list
if i['fld_type'] == 'CharField':
fields[i["fld_name"]] = forms.CharField()
if 'fld_label' in i:
fields[i["fld_name"]].label = i["fld_label"]
if 'fld_required' in i:
if i["fld_required"] == 'False':
fields[i["fld_name"]].required = False
else:
fields[i["fld_name"]].required = True
if 'initial' in i: fields[i["fld_name"]].initial = i["initial"]
if 'help_text' in i: fields[i["fld_name"]].help_text = i["help_text"]
## next line not working
if 'fld_maxLength' in i: fields[i["fld_name"]].max_length = i["fld_maxLength"]
fields[i["fld_name"]].widget = forms.TextInput()
return type('DynForm', # form name is irrelevant
(forms.BaseForm,),
{'base_fields': fields})
my view
def vdynfrm(request):
if request.method == 'POST':
form = dynfrm(request.POST)
if form.is_valid():
pass
## all good
else:
form = dynfrm()
##return render(request, "blog/dfrm.html",{'form': form})
return render(request, "blog/searchAccount.html",{'form': form})
and the resulting HTML
<div class="form-group">
<form action="/searchAccount/" method="post">
<table>
<tr>
<th><label for="id_customer_name">Cust Name:</label></th>
<td><input type="text" name="customer_name" value="Dr John" id="id_customer_name">/td>
</tr>
<tr>
<th><label for="id_customer_number">Cust #:</label></th>
<td><input type="text" name="customer_number" required id="id_customer_number"><br>
<span class="helptext">Enter account number</span></td>
</tr>
<tr>
<th><label for="id_customer_type">Customer Type:</label></th>
<td><input type="text" name="customer_type" id="id_customer_type"></td>
</tr>
</table>
<input type="submit" value="Submit">
</form>
</div>
The max_length property only works when you send the context correctly to your template file.
your forms.py
class SearchAccountForm(forms.Form):
provider_code = forms.CharField(
label='Provider:',
max_length=100,
required=True,
widget=forms.TextInput(attrs={'class': 'form-control'}))
With function based view:
def home(request):
if request.method == 'POST':
form = SearchAccountForm(request.POST)
if form.is_valid():
provider_c= form.cleaned_data['provider_code']
print('Provider Code :',provider_c)
return HttpResponseRedirect('/thanks/')
else:
form = SearchAccountForm()
return render(request, 'home/index.html', {'form': form})
def thanks(req):
return render(req, 'home/thanks.html')
If you forget to give else condition for get request method, so you will not receive django's inbuild error messages as well as max_length etc.
With Class based view it can be handled easily:
from django.views.generic.edit import FormView
class Home(FormView):
template_name = 'home/index.html'
form_class = SearchAccountForm
success_url = '/thanks/'
def form_valid(self, form):
print(form)
print('Provider Code : ', form.cleaned_data['provider_code'])
return super().form_valid(form)
def thanks(req):
return render(req, 'home/thanks.html')
From both the examples above max_length property is working properly because its get request got handled.
Check your views.py, it may help.
Related
I have my model.py file as below. I've created a conjugate primary key date & station.
Models.py
from django.db import models
from django import forms
# Create your models here.
from django.db import models
from django.conf import settings
from django.contrib.auth.models import User
# Create your models here.
class ManHour(models.Model):
class Meta:
unique_together = (('date', 'station'),)
station_choices = (
('KHI','Station1'),
('ISB', 'Station2'),
('LHE','Station3'),
)
station = models.CharField(
max_length=3,
choices=station_choices,
)
date = models.DateField()
date_time = models.DateTimeField(auto_now=True)
imports_airside = models.DecimalField(max_digits= 5, decimal_places= 3, default = 0)
imports_landside = models.DecimalField(max_digits= 5, decimal_places= 3, default = 0)
exports = models.DecimalField(max_digits= 5, decimal_places= 3, default = 0)
Form.py
from django import forms
from manhourapp.models import ManHour
from datetime import date
class DateInput(forms.DateInput):
input_type = 'date'
class InputForm(forms.ModelForm):
class Meta:
model = ManHour
fields = ['date','station', 'imports_airside', 'imports_landside', 'exports']
widgets = {
'date':forms.widgets.DateInput(attrs={'type': 'date', 'max':str(date.today())})
}
Views.py
def form_page(request):
context = {}
try:
man_hour = ManHour.objects.get(pk=request.GET.get("pk"))
except ManHour.DoesNotExist:
man_hour = None
if man_hour:
context["Total_Imports"] = man_hour.imports_airside + man_hour.imports_landside
if man_hour:
context["Total_Hours"] = man_hour.imports_airside + man_hour.imports_landside + man_hour.exports
if request.method == 'POST':
properties_Form = InputForm(request.POST, instance=man_hour)
if properties_Form.is_valid():
obj = properties_Form.save()
return redirect("%s?pk=%s" % (reverse('form'), obj.pk))
else:
context['form']= InputForm(instance=man_hour)
return render(request, "form.html", context)
HTML
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js" async></script>
</head>
<body>
<form target="upload_frame" action="." method="post" enctype="multipart/form-data" >
{% csrf_token %}
{{ form.as_p }}<br>
<input type="text" name="Total_Imports" value="{{ Total_Imports }}" class="form-control" disabled><br>
<input type="text" name="Total_Hours" value="{{ Total_Hours }}" class="form-control" disabled><br>
<input type="submit" name="submit" value="Upload" id="submit">
<div class="user_panel">
logout
</div>
</form>
</body>
</html>
I am new to Django and I want to understand how can I show a pop up that would tell user that record already exists on this date and station.
I need to understand how can I add an exception handling and show pop up to user?
I've tried to add exception handling using code below but it is not working.
try:
if request.method == 'POST':
properties_Form = InputForm(request.POST, instance=man_hour)
if properties_Form.is_valid():
obj = properties_Form.save()
return redirect("%s?pk=%s" % (reverse('form'), obj.pk))
except IntegrityError as e:
error_message = e.__cause__
print(error_message)
A Form class in Django will perform validation and cleaning for you. It will not raise errors but catch them itself so using a try-except does not make much sense as the form itself has caught the error.
Instead you just need to return a response in case .is_valid() returns False. Furthermore the form itself will render the errors to display it to the user when you render it so you should simply render the same page again:
if request.method == 'POST':
properties_Form = InputForm(request.POST, instance=man_hour)
if properties_Form.is_valid():
obj = properties_Form.save()
return redirect("%s?pk=%s" % (reverse('form'), obj.pk))
context['form'] = properties_Form
return render(request, "form.html", context) # form was not valid so returning a response here
If you want to customize the error message that is generated you can do it by setting the error_messages attribute on the Meta class of the form (See Considerations regarding model’s error_messages [Django docs]):
from django.core.exceptions import NON_FIELD_ERRORS
class InputForm(forms.ModelForm):
class Meta:
model = ManHour
fields = ['date','station', 'imports_airside', 'imports_landside', 'exports']
widgets = {
'date':forms.widgets.DateInput(attrs={'type': 'date', 'max':str(date.today())})
}
error_messages = {
NON_FIELD_ERRORS: {
'unique_together': "%(model_name)s's %(field_labels)s are not unique.",
}
}
I'm trying to have user input the data and store into DB and map with the other data.
Model:
class Code(models.Model):
name = models.CharField(max_length=4, default=None, blank=True, unique=True)
Within the Model, there is another class
class Pull(models.Model):
code_pull = models.ForeignKey(Code, on_delete=models.SET_NULL, null=True)
How to display to call in the Form and View, so that data is pass when user input the data in the input field.
Form
class Code_Form(forms.ModelForm):
class Meta:
model = Code
fields = ('name',)
class Pull_Form(forms.ModelForm):
class Meta:
model = Pull
fields = ('code_pull', 'data1', 'prefix',)
#Inital Value is NULL
def __init__(self, *args, **kwargs):
super(Pull_Form, self).__init__(*args, **kwargs)
self.fields['code_pull'].queryset = CODE.objects.none()
if 'code_pull' in self.data:
c = self.data.get('code_pull')
self.fields['code_pull'].queryset = CODE.objects.filter(name=c)
#print(self.fields['code_pull'].queryset)
I updated the code for the FORM, so that it initial the value from the CODE_form, Still Error, as the code field is empty
Here is the VIEW:
def InputData(request, *args, **kwargs):
form = Pull_Form(request.POST or None)
if request.method == 'POST':
if form.is_valid():
data_add = form.save(commit=False)
data_add.code = form.cleaned_data['code_pull']
data_add.save()
messages.success(request, 'Successfully')
else:
messages.error(request, form.errors)
return render(request, template_name, {'form': form })
ERROR: Not able to add the data as the field for the code is not selected when submitting the form.
ERROR CODE: code - Select a valid choice. That choice is not one of the available choices.
{{ messages }}
<form id="form1" class="post-form" role=form method="POST" action=".">{% csrf_token %}
<input id="code_pull" class="form-control" type="text" maxlength="4" required></input>
<label for="code_pull">Code</label>
<button type="submit" class="btn">Save</button>
</form>
Thank you for the help in advance.
Django forms use the name attribute in HTML controls to capture form data.
<input id="code" name="code" class="form-control" type="text" maxlength="4" required></input>
I only added name="code". this should make it work.
I'm a beginner in django and I want to put two diferent registers in the same view. But also I want to make my own forms and put diferent url at the action tag. I did it in one form, but when I puy the second form, this doesn't work.
This is my models.py:
from django.db import models
class userProfile(models.Model):
usermail = models.CharField(max_length=264)
username = models.CharField(max_length=264)
userpass = models.CharField(max_length=264)
class companyProfile(models.Model):
companymail = models.CharField(max_length=264)
companyname = models.CharField(max_length=264)
companypass = models.CharField(max_length=264)
This is my forms.py:
from django import forms
from Pruebas_app.models import companyProfile, userProfile
class registerCompany(forms.Form):
companypassconf = forms.CharField()
class Meta():
model = companyProfile
fields = ['companymail','companyname', 'companypass']
labels = {'companymail': '', 'companyname': '', }
widgets = { 'companypass': forms.PasswordInput(),}
class registerUser(forms.Form):
userpassconf = forms.CharField()
class Meta():
model = companyProfile
fields = ['usermail','username', 'userpass']
labels = {'usermail': '', 'username': '', }
widgets = {'userpass': forms.PasswordInput(),}
And this is my template:
<form action="{ url 'user_register'}" method="post">
<input type="text" name="username">
<input type="email" name="usermail">
<input type="password" name="userpass">
<input type="password" name="userpassconf">
<input type="submit" value="Register">
</form>
<form action="{ url 'company_register'}" method="post">
<input type="text" name="companyname">
<input type="email" name="companymail">
<input type="password" name="companypass">
<input type="password" name="companypassconf">
<input type="submit" value="Register">
</form>
And this is my views.py:
from django.shortcuts import render
from Pruebas import forms
from Pruebas.forms import registerCompany, registerUser
from django.http import HttpResponse
def user_register(request):
form = forms.registerUser()
regd = False
passmatch = True
if request.method == "POST":
form = registerUser(request.POST)
if form.is_valid():
form_data = form.cleaned_data
print (form_data.get("userpass"))
if form_data.get("userpass") == form_data.get("userpassconf"):
passmatch = True
form.save()
regd = True
print("saved")
else:
passmatch = False
else:
print("error")
red = 'Pruebas/register.html'
regd = False
return render(request, 'Pruebas/register.html', {'registered': regd, "matchPass": passmatch})
I tried to send the data from my forms to one unique view, but I only can recive the data from the user register. I don't know what I was doing wrong or what I have to do to make this works, please help!
In your forms, don't define meta class and just display the fields like you're doing with the userpassconf. You can even combine the forms into a single form, and then just handle the data in your view like you're already doing, but create two instances. Something like:
if request.method == "POST":
form = registerUser(request.POST)
if form.is_valid():
form_data = form.cleaned_data
if form_data.get("userpass") == form_data.get("userpassconf"):
passmatch = True
new_user = userProfile()
new_user.usermail = form_data.get("usermail")
new_user.username = form_data.get("username")
new_user.save()
So basically you're just creating an instance of whatever model you want to save to, and assigning the form data to it, and then saving it. And don't forget to import your models into the views file.
Django 1.8 / Python 3.4
I wanna add data from an html-form via a Django view named "add" to my database. However, for some reason this just doesn't happen and I can't figure out what's wrong. Presumably the mistake is in the view's code, but what exactly do I need to change?
models.py
from django.db import models
from model_utils import Choices
class Entry(models.Model):
user = models.CharField(max_length=50)
title = models.CharField(max_length=200)
description = models.TextField()
due_date = models.DateField('Date')
due_time = models.TimeField('Time')
STATUS = Choices('Open', 'Done')
status = models.CharField(choices=STATUS, default=STATUS.Open, max_length=4)
def __unicode__(self):
return u"%s %s %s %s %s" % (self.user, self.title, self.description, self.expiry, self.status)
def expiry(self):
return u"%s %s" % (self.due_date, self.due_time)
The interesting part of my add.html
<td><input type="text" name="title"></td>
<td><input type="text" name="description"></td>
<td><input type="text" name="due_date"></td>
<td><input type="text" name="due_time"></td>
<td>
<select name="status" size="1" selected value="Open">
<option>Open</option>
<option>Done</option>
</select>
</td>
forms.py
from django import forms
from django.forms.widgets import TextInput
class EntryForm(forms.Form):
title = forms.CharField(max_length=200)
description = forms.widgets.TextInput()
due_date = forms.DateField()
due_time = forms.TimeField()
status = forms.ChoiceField(choices=[(x, x) for x in range(1, 2)])
And the relevant view in my views.py
from django import forms
from django.shortcuts import render
from django.shortcuts import redirect
from website.list.forms import EntryForm
def add(request):
if request.method == "POST":
form = EntryForm(request.POST)
if form.is_valid():
new_entry = form.save()
new_entry.save()
return redirect('website')
else:
form = EntryForm()
return render(request,'add.html', {'form': form})
Any help is appreciated!
[EDIT]
So, this is what my add.html looks like now:
<form action="." method="post">
{% csrf_token %}
{{ form }}
<br><input type="submit" value="Send"/>
<br><br>Cancel
</form>
And the slightly edited views.py again:
from django import forms
from django.shortcuts import render
from django.shortcuts import redirect
from www.todolist.forms import EntryForm
from django.contrib.auth.models import User
def add(request):
if request.method == "POST":
form = EntryForm(request.POST)
if form.is_valid())
form.save()
return redirect('website')
else:
form = EntryForm()
return render(request,'add.html', {'form': form})
Figured it out ...
This is what forms.py has to look like in order for the save() function to work in the view:
class EntryForm(forms.ModelForm):
CHOICES = (
('1', 'Open'),
('2', 'Done'),
)
title = forms.CharField(max_length=200)
description = forms.CharField(widget=forms.Textarea)
due_date = forms.DateField()
due_time = forms.TimeField()
status = forms.ChoiceField(choices=CHOICES)
class Meta:
model = Entry
fields = '__all__'
The important things to notice are "ModelForm" instead of just "Form" and the class Meta information.
I am guessing you are getting an error here; form = EntryForm(request.POST) but because you are manually writing out the form html instead of using the Django form to do it, you aren't seeing the error.
https://docs.djangoproject.com/en/1.8/topics/forms/#the-template is how you should use Django to render your html for your Django form; and this by default will display any errors the happened when trying to validate your data.
I have a simple form where there is a username and a message. Upon clicking the submit button, I want the data for user and message to be stored separately into the database. I am currently receiving an IntegrityError on m.save()
"Exception Value: SimpleMessage_message.content may not be NULL"
and was told to instead use forms to accomplish this. However, I am confused as to how to use a form to pass in form data to the individual User and Message Models so that the input data is saved in the database.
Models
class User (models.Model):
name = models.CharField(max_length=20)
def __unicode__(self):
return self.name
class Message (models.Model):
content = models.TextField(max_length=140, null=True, blank=True)
user = models.ForeignKey(User)
time = models.DateTimeField()
def __unicode__(self):
return self.content
views.py
def index (request):
if request.method == 'POST':
u = User(name=request.POST.get('user'))
u.save()
m = Message(content=request.POST.get('text'), user = u)
m.save()
return render_to_response('index.html', {
'user': u,
'message': m,
}, RequestContext(request))
else:
u = User()
m = Message()
return render_to_response('index.html', {
'user': u,
'message': m,
}, RequestContext(request)
)
index.html
<form action="{% url 'index' %}" method = "post">
{% csrf_token %}
<input type="text" name="user" id="user" maxlength="20" placeholder = "Username">
<br>
<br>
<textarea rows="4" cols="35" id="text" name="text" maxlength="140" placeholder = "Message goes here"></textarea><br>
<input type="submit" value="Submit">
</form>
Try changing your model so that content can be null:
class Message (models.Model):
content = models.TextField(max_length=140, null=True, blank=True)
Or give a default value in the form:
m = Message(content=request.POST.get('text', ' '), user = u)