I am trying to add a validator to django model form such that if specific value is selected then other field in the form should be entered if not entered it should give a validation error
in the below form if the user selects "Project Support Activities" from the activity_name drop down then the project id field should be mandatory
Django Form
class ActivityTrackerModelForm(forms.ModelForm):
date = forms.DateField(label='', widget=forms.DateInput(attrs={
"placeholder": "Select Date", 'id': 'datepicker', 'class': 'form-control w-100', 'autocomplete': 'off'}))
activity_name = forms.ModelChoiceField(queryset=activity.objects.all().order_by(
'activity_name'), label='', empty_label="Select Activity", widget=forms.Select(attrs={'class': 'form-control w-100'}))
system_name = forms.ModelChoiceField(queryset=system.objects.all().order_by('system_name'), label='', empty_label="Select System", widget=forms.Select(attrs={
'class': 'form-control w-100'}))
client_name = forms.ModelChoiceField(queryset=client.objects.all().order_by(
'client_name'), label='', empty_label="Select Client", widget=forms.Select(attrs={
'class': 'form-control w-100'}))
hour_choice = [('', 'Choose Hours'), (0, 0), (1, 1), (2, 2),(3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8)]
hours = forms.ChoiceField(label='', choices=hour_choice, widget=forms.Select(
attrs={'class': 'form-control w-100'}))
min_choice = [('', 'Choose Mins'), (0, 0), (15, 15), (30, 30), (45, 45)]
mins = forms.ChoiceField(label='', choices=min_choice, widget=forms.Select(attrs={'class': 'form-control w-100'}))
no_of_records = forms.IntegerField(label='', required=False, widget=forms.NumberInput(
attrs={"placeholder": "Enter no. of Records", 'class': 'form-control w-100', 'autocomplete': 'off'}))
project_id = forms.CharField(label='', required=False, widget=forms.TextInput(
attrs={"placeholder": "Project ID", 'class': 'form-control w-100'}))
user_comments = forms.CharField(
label='',
required=False,
widget=forms.Textarea(
attrs={
"placeholder": "Enter Your Comments Here...",
'rows': 6,
'class': 'form-control w-100',
'autocomplete': 'off'
}
)
)
class Meta:
model = activity_tracker
fields = ['date', 'activity_name', 'system_name', 'client_name',
'hours', 'mins', 'no_of_records', 'project_id', 'user_comments']
def clean(self):
cleaned_data = super(ActivityTrackerModelForm, self).clean()
activity = cleaned_data.get('activity_name')
project_1 = cleaned_data.get('project_id')
if re.search("^Project.*Activities$", str(activity)) or project_1 is None:
print('pass') # prints to console this is working
raise forms.ValidationError('Please Add in Project ID')#raise form error this is not working
View :
def MyTask(request):
if request.method == 'POST':
form = ActivityTrackerModelForm(request.POST or None)
if form.is_valid():
obj = form.save(commit=False)
obj.user_name = request.user
obj.approver = tascaty_user.objects.get(
username=request.user).approver
if request.user.is_team_lead:
obj.status = activity_status.objects.get(pk=3)
obj.save()
return redirect('mytask')
queryset1_PA = activity_tracker.objects.filter(
user_name=request.user).filter(status__in=[1, 2, 4]).order_by('-id')
queryset1_AP = activity_tracker.objects.filter(
user_name=request.user).filter(status=3).order_by('-date')
paginator_RA = Paginator(queryset1_AP, 10)
paginator_PA = Paginator(queryset1_PA, 10)
page = request.GET.get('page')
context = {
'title': 'TasCaty|My Task',
'activity_form': ActivityTrackerModelForm(),
'post_page_RA': paginator_RA.get_page(page),
'post_page_PA': paginator_PA.get_page(page),
}
return render(request, "tascaty/mytask.html", context)
Raising the error is working fine. But you always redirect away, even if the form is not valid, so the error will never be displayed.
You should only redirect when is_valid is True, otherwise you should redisplay the form. That means passing the invalid form back to the context - so you should only create a new one when method is not POST. So:
if request.method == 'POST':
form = ActivityTrackerModelForm(request.POST or None)
if form.is_valid():
...
obj.save()
return redirect('mytask') # indented here
else:
ActivityTrackerModelForm() # added this block, note it's aligned with the first if
...
context = {
...
'activity_form': form, # pass the existing form here
...
}
return render(request, "tascaty/mytask.html", context)
Changed MY View as mentioned by #Daniel Roseman and changed form validator
View
if request.method == 'POST':
form = ActivityTrackerModelForm(request.POST or None)
if form.is_valid():
...
obj.save()
return redirect('mytask') # indented here
else:
ActivityTrackerModelForm() # added this block, note it's aligned with the first if
...
context = {
...
'activity_form': form, # pass the existing form here
...
}
return render(request, "tascaty/mytask.html", context)
Form Validator
def clean(self):
cleaned_data = super(ActivityTrackerModelForm, self).clean()
activity = cleaned_data.get('activity_name')
project_1 = cleaned_data.get('project_id')
if re.search("^Project.*Activities$", str(activity)) and project_1 == "":
self.add_error('project_id', "Please Add Project ID")
return cleaned_data
Related
hi everyone I have a doubt with the use of forms and models.
I have to create a code that creates records in multiple tables and I don't know how to do it.
my goal is to create a page where I can enter all the data and when I save it creates the various tables filled in with the data provided by the user.
I'm a beginner I still have to learn the mechanism well =)
forms.py
from django import forms
from .models import Schede, DatiGruppi, Gruppi
class CreaSchedaForm(forms.ModelForm):
nome_scheda = forms.CharField(
required = True,
label ='Nome scheda',
widget = forms.TextInput(
attrs = {
'class': 'form-control',
'placeholder' : 'nome scheda',
'autocomplete' : 'off'
}
)
)
data_inizio = forms.DateField(
label='Data inizio',
widget = forms.DateInput(
attrs= {
'type': 'date',
'class': 'form-control',
'placeholder' : 'data inizio'
}
)
)
data_fine = forms.DateField(
label='Data fine',
widget = forms.DateInput(
attrs= {
'type': 'date',
'class': 'form-control',
'placeholder' : 'data fine'
}
)
)
class Meta:
model = Schede
fields = ['nome_scheda','data_inizio','data_fine']
class CreaDtGruppoForm(forms.ModelForm):
giorni_settimana = forms.ChoiceField(
choices = DatiGruppi.giorni_settimana_scelta
)
dati_gruppo = forms.ModelChoiceField(
queryset = Gruppi.objects.all(),
empty_label = "-",
required = True
)
class Meta:
model = DatiGruppi
fields = ['giorni_settimana', 'dati_gruppo']
views.py
#login_required
def creaScheda(request):
if request.method == "POST":
form = CreaSchedaForm(request.POST)
if form.is_valid():
scheda = form.save(commit = False)
scheda.utente = request.user
scheda.save()
else:
form = CreaSchedaForm()
context = {"form": form}
return render(request, "crea_scheda.html", context)
I had correct working form to register and login user using username. I add field email to above forms and change to login by email field. Now registered working but login is not. In view sign_in authentication have None value. I have a error 'AnonymousUser' object has no attribute '_meta'.
models
from django.db import models
from django.contrib.auth.models import User
class ClassAtSchool(models.Model):
name = models.CharField(max_length=30, unique=True,
blank=False)
def __str__(self):
return self.name
class Teacher(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
class_at_school = models.ManyToManyField(ClassAtSchool)
def __str__(self):
return self.user.email
class Student(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
class_at_school = models.ForeignKey(ClassAtSchool,
on_delete=models.SET_NULL, null=True)
class Meta:
ordering = ['user']
def __str__(self):
return self.user.email
views
def sign_up(request):
context ={}
who ={"teacher": Teacher, "student": Student}
form = UserSignUpForm(request.POST or None)
context['form'] = form
if request.method == "POST":
if form.is_valid() and request.POST.get("who"):
user = form.save(commit=False)
user.set_password(form.cleaned_data['password'])
user.save()
person = who[request.POST.get("who")]
person(user=user).save()
messages.success(request, f'{user.email} registered succesfull')
return render(request, 'registration/sign_up.html', context)
return render(request, 'registration/sign_up.html', context)
def sign_in(request):
context = {}
form = UserLoginForm(request.POST or None)
context['form'] = form
if form.is_valid():
print('test1')
email = form.cleaned_data.get('email')
password = form.cleaned_data.get('password')
user = authenticate(email=email, password=password)
print(f'email: {email}')
print(f'user: {user}')
login(request, user)
return redirect('/')
else:
print('test3')
attempt = request.session.get('attempt') or 0
request.session['attempt'] = attempt + 1
return render(request, 'registration/sign_in.html', context)
return render(request, 'registration/sign_in.html', context)
forms
class UserSignUpForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(UserSignUpForm, self).__init__(*args, **kwargs)
self.fields['email'].required = True
self.fields['first_name'].required = True
self.fields['last_name'].required = True
who = forms.ChoiceField(
choices=[('student', 'Student'), ('teacher', 'Teacher')],
label="",
required=True,
widget=forms.RadioSelect(
attrs={'style':'max-width: 20em; ', 'autocomplete':'off', })
)
password = forms.CharField(
label="Password",
validators=[MinLengthValidator(8, message="Minimum 8 characters")],
widget=forms.PasswordInput(attrs={'autocomplete':'off'}))
confirm_password = forms.CharField(
label="Confirm password",
validators=[MinLengthValidator(8, message="Minimum 8 characters"), ],
widget=forms.PasswordInput(attrs={'autocomplete':'off'}))
class Meta:
model = User
fields = ('who', 'username', 'email', 'first_name', 'last_name', 'password', )
help_texts = {"username": None}
widgets = {
'username': forms.TextInput(attrs={}),
'first_name': forms.TextInput(attrs={}),
'last_name': forms.TextInput(attrs={}),
'email': forms.TextInput(attrs={}),
}
def clean(self):
cleaned_data = super(UserSignUpForm, self).clean()
password = cleaned_data.get("password")
confirm_password = cleaned_data.get("confirm_password")
if password != confirm_password:
msg = _(f'Password and confirm password does not match')
self.add_error('password', msg)
self.add_error('confirm_password', msg)
helper = FormHelper()
helper.form_tag = 'false'
helper.attrs = {"novalidate": True, 'autocomplete':'off'}
helper.form_class = 'form-horizontal'
helper.field_class = 'col-md-8 '
helper.label_class = 'col-md-4'
helper.layout = Layout(
Row(
Column(
Field('who', css_class='form-group', style='margin-left:200px'),
Field('username', css_class='form-group ', style=''),
Field('email', css_class='form-group'),
Field('first_name', css_class='form-group'),
Field('last_name', css_class='form-group'),
Field('password', css_class='form-group'),
Field('confirm_password', css_class='form-group'),
FormActions(
Submit('save', 'Sign up', css_class="btn-primary"),
Submit('cancel', 'Cancel'),
),
)
)
)
class UserLoginForm(forms.Form):
email = forms.CharField(widget=forms.TextInput(
attrs={
"class": "form-control"
}))
password = forms.CharField(
widget=forms.PasswordInput(
attrs={
"class": "form-control",
"id": "user-password"
}
)
)
def clean_username(self):
email = self.cleaned_data.get("email")
qs = User.objects.filter(email__iexact=email) # thisIsMyUsername ==
thisismyusername
if not qs.exists():
raise forms.ValidationError("This is an invalid user.")
if qs.count() != 1:
raise forms.ValidationError("This is an invalid user.")
return email
helper = FormHelper()
helper.form_tag = 'false'
helper.attrs = {'autocomplete':'off'}
helper.form_class = 'form-horizontal'
helper.field_class = 'col-md-8 '
helper.label_class = 'col-md-4'
helper.layout = Layout(
Row(
Column(
Field('email', css_class='form-group ', style=''),
Field('password', css_class='form-group'),
FormActions(
Submit('submit', 'Sign in', css_class="btn-primary"),
Submit('cancel', 'Cancel'),
),
)
)
)
Thanks, i find answer. I must do custom Model Manager but I stay with username auth.
I have a Django model form where i want to make a form field required if some value is selected in another form field.
e.g
if activity_name = 'Project Planned Activities' is selected in activity_name form field the project_id field should be required = true
I have added a form validator for this but its not working
class ActivityTrackerModelForm(forms.ModelForm):
activity_name = forms.ModelChoiceField(
queryset=activity.objects.all().order_by('activity_name'),
label='',
empty_label="Select Activity",
widget=forms.Select(
attrs={
'class': 'form-control',
}
)
)
project_id = forms.CharField(
label='',
required=False,
widget=forms.TextInput(
attrs={
"placeholder": "Project ID",
'class': 'form-control'
}
)
)
class Meta:
model = activity_tracker
fields = ['activity_name', 'project_id']
def clean(self):
activity = self.cleaned_data.get('activity_name')
if re.search("^Project.*Activities$", str(activity)):
self.fields['project_id'].required = True
Tried using the form validator
if activity_name = 'Project Planned Activities' is selected in activity_name form field the project_id field should be required = true
Problem was first in my Validator itself and in my view. i was not passing the current form to the template instead i was passing the new form eveytime to my template.
Corerct Form Validator
class ActivityTrackerModelForm(forms.ModelForm):
date = forms.DateField(label='', widget=forms.DateInput(attrs={
"placeholder": "Select Date", 'id': 'datepicker', 'class': 'form-control w-100', 'autocomplete': 'off'}))
activity_name = forms.ModelChoiceField(queryset=activity.objects.all().order_by(
'activity_name'), label='', empty_label="Select Activity", widget=forms.Select(attrs={'class': 'form-control w-100'}))
system_name = forms.ModelChoiceField(queryset=system.objects.all().order_by('system_name'), label='', empty_label="Select System", widget=forms.Select(attrs={'class': 'form-control w-100'}))
client_name = forms.ModelChoiceField(queryset=client.objects.all().order_by(
'client_name'), label='', empty_label="Select Client", widget=forms.Select(attrs={'class': 'form-control w-100'}))
hour_choice = [('', 'Choose Hours'), (0, 0), (1, 1), (2, 2),(3, 3), (4, 4), (5, 5), (6, 6), (7, 7), (8, 8)]
hours = forms.ChoiceField(label='', choices=hour_choice, widget=forms.Select(
attrs={'class': 'form-control w-100'}))
min_choice = [('', 'Choose Mins'), (0, 0), (15, 15), (30, 30), (45, 45)]
mins = forms.ChoiceField(label='', choices=min_choice, widget=forms.Select(attrs={'class': 'form-control w-100'}))
no_of_records = forms.IntegerField(label='', required=False, widget=forms.NumberInput(
attrs={"placeholder": "Enter no. of Records", 'class': 'form-control w-100', 'autocomplete': 'off'}))
project_id = forms.CharField(label='', required=False, widget=forms.TextInput(
attrs={"placeholder": "Project ID", 'class': 'form-control w-100'}))
user_comments = forms.CharField(
label='',
required=False,
widget=forms.Textarea(
attrs={
"placeholder": "Enter Your Comments Here...",
'rows': 6,
'class': 'form-control w-100',
'autocomplete': 'off'
}
)
)
class Meta:
model = activity_tracker
fields = ['date', 'activity_name', 'system_name', 'client_name',
'hours', 'mins', 'no_of_records', 'project_id', 'user_comments']
def clean(self):
cleaned_data = super(ActivityTrackerModelForm, self).clean()
activity = cleaned_data.get('activity_name')
project_1 = cleaned_data.get('project_id')
if re.search("^Project.*Activities$", str(activity)) and project_1 == "":
self.add_error('project_id', "Please Add Project ID")
return cleaned_data
Corrrect View
def MyTask(request):
form = ActivityTrackerModelForm(request.POST or None)
if request.method == 'POST':
form = ActivityTrackerModelForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.user_name = request.user
obj.approver = tascaty_user.objects.get(
username=request.user).approver
if request.user.is_team_lead:
obj.status = activity_status.objects.get(pk=3)
obj.save()
messages.success(request, 'New Entry Created')
return redirect('mytask')
queryset1_PA = activity_tracker.objects.filter(
user_name=request.user).filter(status__in=[1, 2, 4]).order_by('-id')
queryset1_AP = activity_tracker.objects.filter(
user_name=request.user).filter(status=3).order_by('-date')
paginator_RA = Paginator(queryset1_AP, 10)
paginator_PA = Paginator(queryset1_PA, 10)
page = request.GET.get('page')
context = {
'title': 'TasCaty|My Task',
'activity_form': form,
'post_page_RA': paginator_RA.get_page(page),
'post_page_PA': paginator_PA.get_page(page),
}
return render(request, "tascaty/mytask.html", context)
I initialize a form using an instance of a model in my view like this
entry_form = EntryDetailsForm(instance=entry)
my model form is the following
class EntryDetailsForm(ModelForm):
start_date = forms.DateField(widget=TextInput(attrs={'class': 'form-control input-sm','readonly':''}))
start_hour = forms.TimeField(widget=TimeInput(attrs={'class': 'form-control input-sm input-append date', 'id':'starthour','readonly':''}))
end_hour = forms.TimeField(widget=TextInput(attrs={'class': 'form-control input-sm',
'id':'endhour','readonly':''}))
error_css_class = 'has_error'
required_css_class = 'has_warning'
def __init__(self, *args, **kwargs):
self.fields['start_date'] = self.instance.start.date()
self.fields['start_hour'] = self.instance.start.time()
self.fields['end_hour'] = self.instance.end.time()
class Meta:
model = Entry
exclude = ('start', 'end', 'creator')
widgets = {
'reason':Textarea(attrs={'class':'form-control input-sm'}),
'title': TextInput(attrs={'class': 'form-control input-sm'}),
'comment':Textarea(attrs={'class': 'form-control input-sm'}),
'patient': Select(attrs={'class':'form-control input-sm selectpicker',
'data-live-search':'true'}),
'event_category': Select(attrs={'class':'form-control input-sm'}),
'doctor': Select(attrs={'class': 'form-control input-sm selectpicker',
'data-live-search':'true'})
}
def save(self, commit=True):
print 'Form save method'
model = super(EntryDetailsForm, self).save(commit=False)
model.start = datetime.combine(self.cleaned_data['start_date'], self.cleaned_data['start_hour'])
model.end = datetime.combine(self.cleaned_data['start_date'], self.cleaned_data['end_hour'])
But I get an error that my EntryDetailsForm object doesn't have an instance attribute. Am I doing something wrong?
EDIT Using this method won't populate the value of start_date start_hour and end_hour fields. How do I do that in __init__?
EDIT2: I used the initial parameter in my view
entry_form = EntryDetailsForm(instance=entry, initial={'start_date':...etc})
and worked. Is there a way to do using the init?
You need to call super(EntryDetailsForm, self).__init__(*args, **kwargs), before self.instance usage.
I have an application which has a form in which you can specify users by name or by formset where you select groups of users or both.
I need to add some sort of validation where the form won't proceed when sum of users selected manually and/or from groups will equal 0.
How do I make a communication between form and formset to raise validation error?
Is there another way of raising an error like in typical form and formset?
def form_valid(self, form):
context = self.get_context_data()
formset = context.get('formset')
is_valid = form.is_valid()
if formset:
is_valid &= formset.is_valid()
if not is_valid:
context['form'] = form
return self.render_to_response(context)
task = form.save()
if self.has_formset():
added_groups_list = []
for formset_form in formset:
...do something...
class TaskForm(forms.ModelForm):
sms_preview = forms.CharField(
label=u"Treść wiadomości SMS", required=False, widget=forms.Textarea(attrs={
'cols': 2,
'rows': 2,
'readonly': 'readonly',
})
)
users = forms.ModelMultipleChoiceField(User.objects.none(), label=u'Użytkownicy', required=False)
class Meta:
model = Task
fields = ('procedure', 'priority', 'location', 'message', 'date_from', 'date_to',
'users')
widgets = {
'date_from': DateTimePickerInput,
'date_to': DateTimePickerInput,
'procedure': forms.Select(attrs={'class': 'chosen',
"data-placeholder": u"Wybierz procedurę",
}),
'message': forms.Textarea(attrs={'cols': 2, 'rows': 2,
'placeholder': u"Własna treść wiadomości (opcjonalnie)"
}),
'hours': forms.TextInput(attrs={'placeholder': u'Godziny'}),
'minutes': forms.TextInput(attrs={'placeholder': u'Minuty'}),
}
def __init__(self, *args, **kwargs):
self.site = kwargs.pop('site')
self.procedure = kwargs.pop('procedure', None)
self.user = kwargs.pop('user', None)
super(TaskForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
layout = Layout('procedure')
date_from = timezone.now() + timezone.timedelta(hours=1)
self.fields['date_from'].initial = date_from.strftime("%d.%m.%Y %H:%M")
date_to = date_from + timezone.timedelta(hours=1)
self.fields['date_to'].initial = date_to.strftime("%d.%m.%Y %H:%M")
self.fields['date_from'].required = True
self.fields['date_to'].required = True
self.fields['message'].label = ""
self.fields['users'].widget.attrs = {
'class': 'chosen',
'data-placeholder': u'Nie wybrano użytkowników.',
'readonly': 'readonly',
'disabled': 'disabled',
}
class TaskActionGroupFormset(forms.formsets.BaseFormSet):
def __init__(self, *args, **kwargs):
self.site = kwargs.pop('site')
self.procedure = kwargs.pop('procedure', None)
super(TaskActionGroupFormset, self).__init__(*args, **kwargs)
#cached_property
def forms(self):
return [self._construct_form(i, site=self.site, procedure=self.procedure)
for i in xrange(self.total_form_count())]
#property
def empty_form(self):
form = self.form(
auto_id=self.auto_id,
prefix=self.add_prefix('__prefix__'),
empty_permitted=True,
site=self.site,
)
self.add_fields(form, None)
return form
Since you are using Django forms, you'll want to use the clean() method and override it to run your validation.