I'm getting an issue with my Django form validation. I would like to display form errors and make all fields required. I don't know why my fields can accept blank while blank is not defined in my model file.
This is my model :
class Customer(models.Model):
email = models.CharField(max_length=150, verbose_name=_('e-mail'), null=False)
first_name = models.CharField(max_length=70, verbose_name=_('first name'), null=False)
last_name = models.CharField(max_length=70, verbose_name=_('last name'), null=False)
country = models.ForeignKey(Country, verbose_name=_('country'))
institution = models.CharField(max_length=255, verbose_name=_('institution'), null=True)
creation_date = models.DateTimeField(auto_now_add=True, verbose_name=_('creation date'), null=False)
modification_date = models.DateTimeField(auto_now=True, verbose_name=_('modification date'), null=False)
class Meta:
verbose_name = _('customer')
verbose_name_plural = _('customer')
def __str__(self):
return f"{self.email}"
This is my form :
class CustomerForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(CustomerForm, self).__init__(*args, **kwargs)
self.fields['country'].empty_label = _('Select a country')
self.fields['country'].queryset = self.fields['country'].queryset.order_by(
'name')
for key in self.fields:
self.fields[key].required = True
class Meta:
model = Customer
fields = ['email', 'first_name', 'last_name', 'country', 'institution']
widgets = {
'email': forms.TextInput(attrs={'placeholder': _('name#example.com')}),
'first_name': forms.TextInput(attrs={'placeholder': _('First Name')}),
'last_name': forms.TextInput(attrs={'placeholder': _('Last Name')}),
'institution': forms.TextInput(attrs={'placeholder': _('Agency, company, academic or other affiliation')}),
}
You can find here my view with Django CBV :
class HomeView(CreateView):
""" Render the home page """
template_name = 'app/index.html'
form_class = CustomerForm
def get_context_data(self, **kwargs):
kwargs['document_list'] = Document.objects.all().order_by('publication__category__name')
return super(HomeView, self).get_context_data(**kwargs)
def post(self, request, *args, **kwargs):
if request.method != 'POST':
return HttpResponseRedirect(self.get_success_url())
form = self.form_class(request.POST)
email = request.POST['email']
country_id = request.POST['country']
country = Country.objects.get(id=country_id)
for checkbox in request.POST.getlist('DocumentChoice'):
document = Document.objects.get(id=checkbox)
token = self.gen_token(email, document.edqm_id)
Download.objects.create(email=email, country=country, pub_id=checkbox, token=token,
expiration_date=now + timedelta(minutes=10))
if not form.is_valid():
print('form invalid')
continue
return HttpResponseRedirect(self.get_success_url())
And finally my template :
{% extends "publication/base_backend.html" %}
{% load staticfiles %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block main %}
<form method="post" id="customerform" novalidate>
{% csrf_token %}
<h3>{% trans 'Your information' %}</h3>
<hr>
<div class="col-sm-12 col-md-12 col-lg-12">
{{ form.email|as_crispy_field:"bootstrap" }}
</div>
<br />
<br />
<br />
<br />
<div class="alert alert-info col-sm-12 col-md-12 col-lg-12" role="alert">
<small>{% trans "The fields below are optional if you have already requested a publication:" %}</small>
</div>
<div class="col-sm-5 col-md-5 col-lg-5">
{{ form.first_name|as_crispy_field:"bootstrap" }}<br>
{{ form.country|as_crispy_field:"bootstrap" }}
</div>
<div class="col-sm-5 col-md-5 col-lg-5 col-sm-offset-2 col-md-offset-2 col-lg-offset-2">
{{ form.last_name|as_crispy_field:"bootstrap" }}<br>
{{ form.institution|as_crispy_field:"bootstrap" }}
</div>
<div class="col-md-12">
<br />
<br />
</div>
<input type="submit" class="btn btn-default" value="{% trans 'Save' %}"/>
{% trans 'Cancel' %}
</form>
Issues :
According to required fields, I don't know why my form doesn't display missing values errors when I want to submit it.
I have to display fields as shown in my template because I have to make bootstrap design.
In order to display form errors, I have to write {{form.email.errors}} for example but nothing appears.
Thank you by advance
Related
this is my code and I try to show the label but it is not displaying
this is my form file
forms.py
class ReviewForm(ModelForm):
class Meta:
model = Review
fields = ['value', 'body']
labels = {'value': 'Place your vote', 'body': 'Add a comment with your vote'}
def __init__(self, *args, **kwargs):
super(ReviewForm, self).__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs.update({'class': 'input'})
this is my views file and the function for this form
views.py
def project(request, pk):
projectObj = Project.objects.get(id=pk)
form = ReviewForm()
context = {
'project': projectObj,
'form': form
}
return render(request, 'projects/single-project.html', context)
this is the model for the review section
models.py
class Review(models.Model):
VOTE_TYPE = (
('up', 'Up Vote'),
('down', 'Down Vote')
)
owner = models.ForeignKey(Profile, on_delete = models.CASCADE, null=True)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
body = models.TextField(null=True, blank=True)
value = models.CharField(max_length=200, choices=VOTE_TYPE)
created = models.DateTimeField(auto_now_add=True)
id = models.UUIDField(default = uuid.uuid4, unique=True, primary_key=True, editable=False)
class Meta:
# every user can only have single comment on each project
unique_together = [['owner', 'project']]
def __str__(self):
return self.value
and the html file to display the form
html file
<form class="form" action="{% url 'project' project.owner.id %}" method="POST">
{% csrf_token %}
{% for field in form %}
<div class="form__field">
<label for="formInput#textarea">{{field.label}}</label>
{{field}}
</div>
{% endfor %}
<input class="btn btn--sub btn--lg" type="submit" value="Comments" />
</form>
I try to show form.label but it is not displaying
the label is contained in the label_tag property in the field:
<form class="form" action="{% url 'project' project.owner.id %}" method="POST">
{% csrf_token %}
{% for field in form %}
<div class="form__field">
<label for="formInput#textarea">{{field.label_tag}}</label>
{{field}}
</div>
{% endfor %}
<input class="btn btn--sub btn--lg" type="submit" value="Comments" />
</form>
Are you sure for your labels property of Meta class of ModelForm, i do not known this property. You probably need to redefine your field with label you want to, or update field label in the init function of your form.
class ReviewForm(ModelForm):
class Meta:
model = Review
fields = ['value', 'body']
labels = {'value': 'Place your vote', 'body': 'Add a comment with your vote'}
def __init__(self, *args, **kwargs):
super(ReviewForm, self).__init__(*args, **kwargs)
for name, field in self.fields.items():
field.widget.attrs.update({'class': 'input'})
I try to add a validation state like "this already exist." (like registration form, see picture) just under my form input.
But when I submit my form i'v this error 'UNIQUE constraint failed'
this is my code
model
class Company(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
siret = models.CharField(max_length=50, unique=True)
forms
class CheckoutForm(forms.Form):
siret = forms.CharField(required=True, widget=forms.TextInput(attrs={'placeholder': 'Ton SIRET'}))
class Meta:
model = Company
fields = ('siret')
def clean(self):
siret = cleaned_data.get('siret')
if siret:
raise forms.ValidationError("This siret exist.")
else:
return siret
view
def post(self, request, *args, **kwargs):
form = CheckoutForm(self.request.POST)
if form.is_valid():
siret = form.cleaned_data.get('siret')
company = Company(
user = self.request.user,
siret = siret,
)
company.save()
context = {
'company': company,
}
return redirect("core:payment")
else:
messages.info(self.request, "Please fill in the shipping form properly")
return redirect("core:checkout")
template
{% load crispy_forms_tags %}
<main>
<div class="container wow fadeIn">
<h2 class="my-5 h2 text-left">Checkout form</h2>
<div class="row">
<div class="col-md-8 mb-4">
<div class="card">
<form method="post" class="card-body">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-primary" id="checkout-button" data-secret="{{ session_id }}">
Checkout
</button>
</form>
</div>
</div>
Thanks a lot
you have to add errors_messages to your email field like this:
email = models.EmailField(
_('email address'),
blank=True,
unique=True,
null=True,
error_messages={
'unique': _("A user with that email address already exists."),
}
)
I have a date of birth field in my model but why it's not showing up in the template when I render it? The date_of_birth is inside Teacher model. And Do you have any good idea on date_of_birth field? Because right now I'm using charfield for that so I need to convert the DateInput to string so that I can add it into charfield.
here is my models.py
class Teacher(models.Model):
user = models.OneToOneField(CustomUser, on_delete=models.CASCADE, primary_key=True)
date_of_birth = models.CharField(max_length=100)
teacher_type = models.CharField(max_length=50
def __str__(self):
return self.user.email
my forms.py
class TeacherRegisterForm(UserCreationForm):
date_attr = {'class': 'form-control', 'id': 'dateofbirth-register', 'type': 'date'}
date_of_birth = forms.DateField(widget = forms.DateInput(attrs=date_attr))
teacher_type = forms.ChoiceField(label=_('Teacher Type'), choices=type_choice
class Meta(UserCreationForm):
model = CustomUser
fields = ['date_of_birth', 'teacher_type']
def save(self, commit=True):
user = super().save(commit=False)
user.is_teacher = True
user.save()
teacher = Teacher.objects.create(user=user)
teacher.date_of_birth += str(self.cleaned_data.get('date_of_birth'))
teacher.teacher_type += self.cleaned_data.get('teacher_type')
return user
views.py
#login_required
#teacher_required
def teacherInfoView(request):
template_name = "attendance/content/teacher/teacher_info.html"
teacher_info = Teacher.objects.filter(user=request.user)
context = {'teacher_info': teacher_info}
return render(request, template_name, context)
template
{% for info in teacher_info %}
<!-- firstname -->
<div class="row">
<div class="ml-5 mr-auto">
<h5>Name : {{ info.user.first_name }} {{ info.user.last_name }}</h5>
</div>
<div class="ml-5">
<h5>Email : {{ info.user.email }}</h5>
</div>
</div>
<div class="row">
<div class="ml-5">
<h5>Date Of Birth : {{ info.date_of_birth }}</h5>
</div>
<div class="ml-5">
<h5>Gender : {{ info.user.gender }}</h5>
</div>
</div>
{% endfor %}
I have created small stock web app.
I created a stock model with unique part_number field. In my update template I send all item information to be displayed. Then I get an error in the part_number field that it is already there.
How can I avoid this validation for that part_number only?
I mean for same part_number suppose validation will not work. But if I modified to another part_number that already exists I get an error that it's being duplicated.
Model:
class Stock(models.Model):
part_number = models.CharField(max_length=30, blank=False, unique=True)
part_name = models.CharField(max_length=70)
quantity = models.IntegerField(blank=False)
location = models.CharField(max_length=3, blank=True)
model = models.CharField(max_length=40, blank=True, null=True, default="")
min_quantity = models.IntegerField(unique=False, blank=True, default=0)
max_quantity = models.IntegerField(unique=False, blank=True, default=0)
class Meta:
ordering = ['part_number']
def clean(self):
self.part_number = self.part_number.upper()
def __str__(self):
return self.part_number
Form.py:
class StockUpdateModelForm(forms.ModelForm):
class Meta:
model = models.Stock
fields = ['part_name', 'quantity', 'location','part_number']
views.py:
def stock_update_form_view(request, part_id):
item = Stock.objects.get(id=part_id)
item_id = Stock.objects.get(id=part_id).pk
form = StockUpdateModelForm({
'part_number' : item.part_number,
'part_name' : item.part_name,
'quantity' : item.quantity,
'location' : item.location
})
if request.method == 'POST':
form = StockUpdateModelForm(request.POST)
if form.is_valid():
s = Stock.objects.get(pk=item_id)
s.part_name = form.cleaned_data['part_name']
s.part_number = form.cleaned_data['part_number']
s.quantity = form.cleaned_data['quantity']
s.location = form.cleaned_data['location']
print("form is valid")
s.save()
return redirect('/stock/')
return render(request, 'stock/stock_update.html', {'form': form, 'pn': item.part_number})
html:
<form class="bg-light shadow" method="POST">
<div style="margin-left:10%; margin-top:30px">
<h4>Part Number : {{ pn }}</h4>
</div>
<hr style="width:100%">
{% csrf_token %}
<div class="row" style="margin-left:30px; margin-top:40px ">
<div class="col-sm-4" style="margin-left:6%">
{{ form.part_name|as_crispy_field }}
</div>
<div class="col-sm-4" style="margin-left:15%">
{{ form.part_number|as_crispy_field }}
</div>
<div class="col-sm-4" style="margin-left:6%">
{{ form.quantity|as_crispy_field }}
</div>
<div class="col-sm-4" style="margin-left:15%">
{{ form.location|as_crispy_field }}
</div>
<div class="col-sm-4" style="height: 100px; margin-top:30px ; margin-left:6%">
<hr style="width:100%">
<input class="btn btn-primary" type="submit" value="Save"
style="width: 150px;">
</div>
</div>
</form>
try this
if request.method == 'POST':
form = StockUpdateModelForm(request.POST, instance=item)
if form.is_valid():
form.save()
I changed ModelChoiceField to be shown as a TextInput.
I have a field named 'filename' of this type: models.ForeignKey('Filename', on_delete=models.SET_NULL, null=True, blank=True) in a class as follow:
models.py
class Event(models.Model):
name = models.CharField('Name', max_length=100, blank=True, default='')
filename = models.ForeignKey('Filename', on_delete=models.SET_NULL, null=True, blank=True)
class Filename(models.Model):
name = models.CharField('Name', max_length=50)
def __str__(self):
return self.nome
This is part of my 'forms.py' I use show form in frontend after defining the template (myappname/templates/myappname/event_edit.html)
forms.py
class EventEditForm(forms.ModelForm):
def __init__(self, data=None, *args, **kwargs):
if data is not None:
data = data.copy()
if data['filename']:
f, f_created = Filename.objects.get_or_create(nome=data['filename'])
data['filename'] = m.id
super(EventEditForm, self).__init__(data=data, *args, **kwargs)
self.fields['filename'] = forms.ModelChoiceField(queryset=Filename.objects.all(), widget=forms.TextInput(attrs={'class': 'form-control', 'value':self.instance.filename}))
self.fields['filename'].required = False
class Meta:
model = Event
fields = ['name', 'filename']
and this is my event_edit.html:
<form method="post" class="form-horizontal">
{% csrf_token %}
{% for field in event_form %}
<div class="form-group form-group-lg">
<label for="{{ field.id_for_label }}" class="col-sm-2 control-label">{{field.label}}</label>
<div class="col-sm-6">
{{ field }}
</div>
<div class="col-sm-4">
{{ field.errors }}
</div>
</div>
{% endfor %}
<div class="form-group">
<div class="col-sm-10">
<button type="submit" class="btn btn-primary btn-lg center-block">Save</button>
</div>
</div>
</form>
The problem is that if I override ModelChoiceField to be a TextInput the 'filename' field shows id instead of showing field name.
Otherwise, If I leave it as a Choice Field it correctly shows value instead of id.
How can I solve this issue?
Based on your comments and the code in EventEditForm, you want a form that will:
display the Event.name and the linked Filename.name
use the submitted name to update Event.name
use the submitted filename to lookup or create a Filename and update Event.filename
This should work:
class EventEditForm(forms.ModelForm):
"""
Calling form.save() will:
* update the Event.name
* update the Event.filename with a Filename instance
found or created using the supplied name
"""
fname = forms.CharField(
label='Filename',
required=False,
widget=forms.TextInput(attrs={'class': 'form-control'}),
)
class Meta:
model = Event
fields = ['name',]
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control'}),
}
def __init__(self, *args, **kwargs):
super(EventEditForm, self).__init__(*args, **kwargs)
self.initial['fname'] = self.instance.filename
def save(self, commit=True):
instance = super(EventEditForm, self).save(commit=False)
cleaned_fname = self.cleaned_data['fname']
if cleaned_fname:
f, f_created = Filename.objects.get_or_create(name=cleaned_fname)
instance.filename = f
else:
instance.filename = None
if commit:
instance.save()
return instance