Change field value in clean() after negative validation - django

I have a problem that I don't know how to solve. I don't know if that's even possible :(
I have a form that has four fields for simplicity (check, select, time1, time2). When the checkbox is active, the time field is displayed, otherwise select (using JS).
class Form(forms.Form):
check = forms.BooleanField(required=False, initial=False, widget=forms.CheckboxInput(attrs={'style':'width:20px;height:20px;'}))
select = forms.ChoiceField(choices=[], required=False)
time1 = forms.TimeField(required=False)
time2 = forms.TimeField(required=False)
def __init__(self, *args, **kwargs):
select = kwargs.pop('select', None)
super(Form, self).__init__(*args, **kwargs)
self.fields['select'].choices = select
def clean(self):
cleaned_data = super(Form, self).clean()
time1 = cleaned_data.get("time1")
time2 = cleaned_data.get("time2")
select = cleaned_data.get("select")
check = cleaned_data.get("check")
if check:
if time1 is None or time2 is None:
raise forms.ValidationError(
{'time1': ["error!", ], 'time2': ["error!", ]}
)
else:
if select is None:
raise forms.ValidationError(
{'select': ["error!", ]}
)
I don't know how to change the value of the check field to True when the time fields are not filled.I can't use cleaned_data["check"] = True with return because it won't throw an exception

Related

ModelForm returns None even fields has value

I've been searching of why modelforms fields model returns None even it has a value given in the fields.
Form
class GoodMoralSettingForm(CrispyFormMixin, forms.ModelForm):
class Meta:
model = Student
fields = [
"requestor",
"good_moral_certificate",
"good_moral_remarks",
"good_moral_purpose"
]
def __init__(self, *args, **kwargs):
self.student = kwargs.pop('student', None)
super(GoodMoralSettingForm, self).__init__(*args, **kwargs)
student = Student.objects.get(pk=self.student)
self.fields['good_moral_certificate'].initial = student.good_moral_certificate
def clean_good_moral_certificate(self):
good_moral_certificate = self.cleaned_data.get('good_moral_certificate', None)
good_moral_remarks = self.cleaned_data.get('good_moral_remarks', None)
good_moral_purpose = self.cleaned_data.get('good_moral_purpose', None)
requestor = self.cleaned_data.get('requestor', None)
if requestor is None:
requestor = "{requestor}"
if good_moral_purpose is None:
good_moral_purpose = "{good_moral_purpose}"
if good_moral_remarks is None:
good_moral_remarks = "{good_moral_remarks}"
print("=========================")
print(good_moral_remarks) # returns None
print(good_moral_purpose) # returns None
print(requestor) # returns data I suppose to get
good_moral_certificate = good_moral_certificate.replace(
"{requestor}", requestor).replace(
"{remarks}", good_moral_remarks).replace(
"{purpose}", good_moral_purpose
)
return good_moral_certificate
I try to clean that two fields but throws again a None, can someone help me where I did wrong? Really appreciate it
this question is answered in this Django cleaned_data.get('obj') method returns none, I have to declare my clean to the last field so it will all get the self.cleaned_data.

Django: validate_date() missing 1 required positional argument: 'value'

I try to implement validators for Django project
I've defined a file validators.py in myapp
I add validators=[validate_test] for my ran_num field
but got an error
I have another question: is it possible to control field BEFORE form submission?
validators.py
from django.core.exceptions import ValidationError
from django.utils.translation import gettext_lazy as _
def validate_test(self, value):
if value == 'PPP': # just test
raise ValidationError(
_("Test"),
code='Test',
params={},
)
forms.py
from .validators import validate_date
class EditForm(forms.ModelForm):
def __init__(self, request, *args, **kwargs):
super(EditForm, self).__init__(*args, **kwargs)
self.request = request
self.fields["asp_ent_loc"] = forms.ChoiceField(label = _("Site concerned by the operation"), widget=forms.Select, choices=SITE_CONCERNE)
self.fields["med_num"] = forms.CharField(label = _("Trial bacth number"), required=True,validators=[validate_test] )
self.fields["asp_ent_dat"] = forms.DateField(
label = _("Entry date"),
required = True,
initial = datetime.datetime.now(),
)
self.fields["asp_ent_pro_pay"] = forms.ChoiceField(label = _("Country of treatment origin in case of entry"), widget=forms.Select, choices=PAYS)
self.fields["asp_ent_pro_sit"] = forms.ChoiceField(label = _("Processing source site in case of entry"), widget=forms.Select, choices=SITE_PROVENANCE)
def clean(self):
cleaned_data = super(EditForm, self).clean()
class Meta:
model = Entree
fields = ('asp_ent_loc','med_num','asp_ent_dat','asp_ent_pro_pay','asp_ent_pro_sit',)
def clean_med_num(self):
data = self.cleaned_data['med_num']
if len(data) != 3:
raise forms.ValidationError(_("Error on Batch number format (3 letters)"))
if not Medicament.objects.filter(med_num = data).exists():
raise ValidationError(_('Batch number does not exist'))
return data
def clean_asp_ent_dat(self):
data = self.cleaned_data['asp_ent_dat']
entrydate = datetime.datetime.strptime(str(data), "%Y-%m-%d")
currentdate = datetime.datetime.now()
if entrydate > currentdate:
raise forms.ValidationError(_("Please control entry date"))
return data
Your Validation function is not a model's method which means it doesn't accept the self. Your Validation func only accept value argument. In your scenario-
def validate_test(value): # remove self from here
if value == 'PPP':
raise ValidationError(
_("Test"),
code='Test',
params={},
)

Django: request.user in form

How may I get the user details to use within a from? I know in the view I can just do:
currentUser=request.user
But if I use it in the form as so I get the following error "'request' is not defined".
class SelectTwoTeams(BootstrapForm):
currentUser=request.user
date_joined = currentUser.date_joined.replace(tzinfo=pytz.utc)
timeless30 = datetime.datetime.now() - datetime.timedelta(seconds=3610)
timeless30 = timeless30.replace(tzinfo=pytz.utc)
if date_joined > timeless30:
currentCharities = forms.ModelChoiceField(queryset=Charity.objects.filter(enabled=1), empty_label=None, widget=forms.Select(attrs={"class":"select-format"}))
team1 = forms.ModelChoiceField(queryset=StraightredTeam.objects.none(), empty_label=None,
widget=forms.Select(attrs={"class":"select-format"}))
team2 = forms.ModelChoiceField(queryset=StraightredTeam.objects.none(), empty_label=None,
widget=forms.Select(attrs={"class":"select-format"}))
Many thanks for any help in advance.
Below shows the init of this form just incase it may help. I know how to get access to the user data using kwargs for this part:
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
self.currentSelectedTeam1 = kwargs.pop('currentSelectedTeam1', None)
self.currentSelectedTeam2 = kwargs.pop('currentSelectedTeam2', None)
self.currentfixturematchday = kwargs.pop('currentfixturematchday', None)
self.currentCampaignNo = kwargs.pop('currentCampaignNo', None)
super(SelectTwoTeams, self).__init__(*args, **kwargs)
cantSelectTeams = UserSelection.objects.select_related().filter(~Q(fixtureid__fixturematchday=self.currentfixturematchday),campaignno=self.currentCampaignNo, )
if not cantSelectTeams:
queryset = StraightredTeam.objects.filter(currentteam = 1).order_by('teamname')
else:
queryset = StraightredTeam.objects.filter(currentteam = 1).exclude(teamid__in=cantSelectTeams.values_list('teamselectionid', flat=True)).order_by('teamname')
teamsAlreadyPlaying = StraightredFixture.objects.filter(soccerseason=1025, fixturematchday=self.currentfixturematchday, fixturedate__lte = timezone.now())
postponedGames = StraightredFixture.objects.filter(soccerseason=1025, fixturematchday=self.currentfixturematchday,fixturestatus = "P")
queryset = queryset.exclude(teamid__in=teamsAlreadyPlaying.values_list('home_team_id', flat=True)).order_by('teamname')
queryset = queryset.exclude(teamid__in=teamsAlreadyPlaying.values_list('away_team_id', flat=True)).order_by('teamname')
queryset = queryset.exclude(teamid__in=postponedGames.values_list('home_team_id', flat=True)).order_by('teamname')
queryset = queryset.exclude(teamid__in=postponedGames.values_list('away_team_id', flat=True)).order_by('teamname')
self.fields['team1'].queryset = queryset
self.fields['team2'].queryset = queryset
self.fields['team1'].initial = self.currentSelectedTeam1
self.fields['team2'].initial = self.currentSelectedTeam2
self.fields['team1'].label = False
self.fields['team2'].label = False
date_joined = user.date_joined.replace(tzinfo=pytz.utc)
timeless30 = datetime.datetime.now() - datetime.timedelta(seconds=3610)
timeless30 = timeless30.replace(tzinfo=pytz.utc)
if date_joined > timeless30:
self.fields['currentCharities'].label = False
The form class is defined when the module is loaded. That means that you can't set currentUser = request.user, since you don't have access to the request object yet. You should remove that line from your code.
The correct approach is to override the __init__ method so that it takes the user. If your field definitions depend on the user then you need to move them into the __init__ method.
class SelectTwoTeams(BootstrapForm):
def __init__(self, *args, **kwargs):
self.user = kwargs.pop('user')
super(SelectTwoTeams, self).__init__(*args, **kwargs)
date_joined = self.user.date_joined.replace(tzinfo=pytz.utc)
timeless30 = datetime.datetime.now() - datetime.timedelta(seconds=3610)
timeless30 = timeless30.replace(tzinfo=pytz.utc)
if date_joined > timeless30:
self.fields['currentCharities'] = forms.ModelChoiceField(queryset=Charity.objects.filter(enabled=1))
...
You should only use None as the default when popping if the user is not required. It is required in your case, since you access self.user.date_joined in the __init__ method. By storing the user as self.user, you can access it in other methods if required.
Finally, you need to change your view to pass the user when you instantiate the form.
if request.method == "POST"
form = SelectTwoTeams(request.POST, user=request.user)
...
else:
form = SelectTwoTeams(user=request.user)
You can overwrite the save method and send the request there.
viewys.py
if request.method == "POST"
if forms.is_valid():
form.save(request=request.user)
and in your forms.py:
def save(self, request=None, *args, **kwargs
self.currentUser = request.user
super(SelectTwoTeams, self).save(*args, **kwargs)
instance.save()
return instance
To get any variable in forms you must pass as kwarg argument then get-it in init
In the Form:
class someForm(forms.ModelForm):
...code...
def __init__(self, *args,**kwargs):
self.Name = kwargs.pop('SomeName')
in your views:
yourform = someForm(request.POST or None, initial={'foo': foo}, SomeName= someVar)

Validate formset according master's form field value

I have a admin master detail, using tabular inline forms.
I have some special validations to accomplish:
If "field_type" is "list", validate that at least one item in the formset has been added.
But if not (field_type has another value), do not validate.
If "field_type" is "list", then, make visible the formset, otherwise hide it. This is javascript. I also must validate that on the server. I do that on the clean() of ValueItemInlineFormSet. The problem is that is now validating formset always and it should just happen when field_type = "list". How can I get the value of my master field into the formset?
class ValueItemInlineFormSet(BaseInlineFormSet):
def clean(self):
"""Check that at least one service has been entered."""
super(ValueItemInlineFormSet, self).clean()
if any(self.errors):
return
print(self.cleaned_data)
if not any(cleaned_data and not cleaned_data.get('DELETE', False) for cleaned_data in self.cleaned_data):
raise forms.ValidationError('At least one item required.')
class ValueItemInline(admin.TabularInline):
model = ValueItem
formset = ValueItemInlineFormSet
class MySelect(forms.Select):
def render_option(self, selected_choices, option_value, option_label):
if option_value is None:
option_value = ''
option_value = force_text(option_value)
data_attrs = self.attrs['data_attrs']
option_attrs = ''
if data_attrs and option_value:
obj = self.choices.queryset.get(id=option_value)
for attr in data_attrs:
attr_value = getattr(obj, attr)
option_attrs += 'data-{}={} '.format(attr, attr_value)
if option_value in selected_choices:
selected_html = mark_safe(' selected="selected"')
if not self.allow_multiple_selected:
# Only allow for a single selection.
selected_choices.remove(option_value)
else:
selected_html = ''
return format_html('<option value="{}" {}{}>{}</option>', option_value, option_attrs, selected_html, force_text(option_label))
class RequirementFieldForm(forms.ModelForm):
field_type = forms.ModelChoiceField(queryset=FieldType.objects.all(),
widget=MySelect(attrs={'data_attrs': ('identifier', 'name')}))
def __init__(self, *args, **kwargs):
self.qs = FieldType.objects.all()
super(RequirementFieldForm, self).__init__(*args, **kwargs)
class Meta:
model = RequirementField
fields = ['field_type', 'name', 'description', 'identifier', 'mandatory', 'order_nr', 'active']

Django formset validating all forms

I have made a formset like this.
class MixedObsCollsForm(forms.Form):
def __init__(self, *args, **kwargs):
project_id = kwargs.pop('project_id')
super(MixedObsCollsForm, self).__init__(*args, **kwargs)
project = get_object_or_404(Project, pk=project_id)
self.fields['photos_upload'] = forms.ImageField(label="Photos", required=False)
taxon = forms.CharField(max_length=80, required=False)
growth_form = forms.ChoiceField(choices = growthFormChoice, required=False)
height = forms.DecimalField(max_value=99, min_value=0, decimal_places=2, required=True)
density = forms.IntegerField(max_value=100, min_value=1, required=False)
And I have a weird way to display it in a view using a class to handle all the processing. But its like this...
class ObservationFormView(object):
def __init__(self, request=None, project_id=None, observation_id=None):
self.request = request
self.ObservationFormSet = formset_factory(form=MixedObsCollsForm, extra=5)
self.ObservationFormSet.form = staticmethod(curry(MixedObsCollsForm, project_id=project_id))
self.project = get_object_or_404(Project, pk=project_id)
self.user = request.user
self.use_type = 'create'
self.formset = self.ObservationFormSet(self.request.POST or None)
def isValid(self):
return self.formset.is_valid() & self.locationForm.is_valid()
def render(self):
return render(self.request, 'observation_form.html', {'use_type': self.use_type, 'formset': self.formset, 'locationForm':self.locationForm, 'project_photos': self.project.photos.all(),})
def processForm(self):
for form in self.formset:
if form.is_valid() == True:
if form.cleaned_data['is_collection'] == True:
collection = self.getOrMakeCollection(form.cleaned_data, self.locationForm.cleaned_data['location'], False)
if form.cleaned_data['is_collection'] == False:
collection = self.getOrMakeCollection(form.cleaned_data, self.locationForm.cleaned_data['location'], True)
observation = self.saveObservation(form.cleaned_data, self.locationForm.cleaned_data['location'], collection)
return observation
The problem is when I render this form in a view even the forms within the formset which are empty don't validate. Reporting "This field is required." next to all the height fields. Even the empty ones.
It was my understanding that the empty forms should always pass validation. I have looked at the management form data and it all looks fine.
If I change required to False it means I end up with IntegrityErrors when I try to save the formset to the database.
I finally worked out what it was so here it is for anyone else who has this problem, its easy to overlook.
It wasn't even included in the code in the question so there wasn't much help anyone could offer.
This line:
growth_form = forms.ChoiceField(choices = growthFormChoice, required=False)
Used the growthFormChoice variable which was a tuple of choices like this.
growthFormChoice = (('0', 'Select one'),
('T', 'tree'),
('S', 'shrub'))
Because of this the default value ('0':'Select One') resulted in passing a 0 to every form which wasn't (intentionally) altered and triggering validation for the rest of the fields. Simply changing it to ('':'Select One') was all it needed.