Django form validation with values_list in ModelChoiceField - django

I have a form like this:
class SearchForm(forms.Form):
genus = forms.RegexField(
regex=r'^[a-zA-Z]+$',
required=False,
)
species = forms.RegexField(
regex=r'^[a-zA-Z]+$',
required=False,
)
island_group = forms.ModelChoiceField(
required=False,
queryset=Locality.objects.values_list('islandgroup', flat=True).distinct('islandgroup'),
Now, my form fails validation on the the island_group field as I am not returning model objects. I need to return the values_list to get the distinct entries. There is a bit more to this form which is why I don't want to use a model form.
My question is: what is the best way to get my form to validate?
Any help much appreciated.

Why not override the save method: call some validation function before actual save?

I had the same problem, my solution now is to use the ChoiceField instead of the ModelChoiceField. I believe this makes sense, since we do not want the user to select model instances, but distinct attribute values throughout one table column and the same attribute might well correspond to several model instances.
class SearchForm(forms.Form):
# get the distinct attributes from one column
entries = Locality.objects.values_list('islandgroup', flat=True).distinct('islandgroup')
# change the entries to a valid format for choice field
locality_choices = [(e, e) for e in entries]
# the actual choice field
island_group = forms.ChoiceField(
required=False,
choices=locality_choices)
This way Django's inbuilt validation performs exactly what we want, i.e. checking whether a member of the set of all possible attributes from one column was selected.

Related

Django: multiple different fields -> one model

I want to show different fields (a html-option-field who gets Mymodel.object.all and a textfield) and save it to one model field.
How can I build this?
MultiValueField (https://docs.djangoproject.com/en/2.1/ref/forms/fields/) doesn't help with different fields?
Has someone an example? How can I define which kind of field it is?
EDIT:
How can I determine which field I want to save in the model-field? I use a ModelForm.
You should use forms.ModelChoiceField(choices=ModelClass.objects.all()) for the choicefield, you can also set the widget to be widget=forms.CheckboxSelectMultiple.
your form can be like
class SuperForm(forms.Form):
cool_field = forms.ModelChoiceField(
choices=ModelClass.objects.all(),
widget=forms.CheckboxSelectMultiple,
)
text_area = forms.CharField(widget=forms.Textarea)

How to filter the options for a ForeignKey based on an other ForeignKey

I need to filter the options for a ForeignKey based on an other ForeignKey.
class LabValue(models.Model):
measurement = models.ForeignKey(
'LabMeasurement', on_delete=models.CASCADE)
unit = models.ForeignKey(
LabMeasurementUnit,
on_delete=models.CASCADE,
limit_choices_to={'parameter__id': self.measurement.parameter.id},
)
How can I retrieve self.measurement.parameter.id? If I manually enter an ID instead of self.measurement.parameter.id like for example "1" the query works.
def __str__(self):
return str(self.measurement.parameter.id)
also works as desired and returns e. g. 1 as result
I don't believe this is possible, not due to Django's ORM, but due to the way the database itself works. measurement is a ForeignKey, so it's a relationship between two tables. But relationships between tables don't deal with filtering.
Thus, you will have to implement this at the level of the forms which update the model, or, alternatively, override the model's save function and throw an error if the desired expectation is not satisfied.
For a very clear answer: You can't do that.
Explanation:
It's a django model when you create model object/save then only your model object is created and on that time valid foreign key of specified instance is to be passed, so giving a limit to specific choices to model directly is not possible you need to handle it from view.
So if you have handle the choices of option in view but as of alternatively you can override clean() method to raise errors.
in model you can use clean method as below:
def clean(self):
if self.measurement.parameter.id == 1:
# .. do whatever you want to write if this condition happens
pass
elif self.measurement.parameter.id in [1,2,3,4,55,6]: # or just pass set of ids, statically or with queryset
# .. code
else:
raise Exception("Unexpected selection of choice!!!")

Django 1.8 best place for custom validation of two fields on model level, where one of them is FK?

I have model like this:
class Item(models.Model):
number = models.CharField()
menu = models.ForeignKey(Menu)
class Meta:
unique_together = ('number', 'menu')
what I would like to validate is, that 'number' is unique within certain menu, but ignoring case. E.g if ('a1', 1) then don't allow ('A1', 1).
1) I want do this validation on model level, not form.
2) I want store in database values for number EXACTLY as given on input
My first approach was to do it in model's clean method, but there always value of FK field menu is None, so I got RelatedObjectDoesNotExist. Is it issue with Django 1.8 or it was never possible to handle fk fields in models clean?
I noticed that in save method I can acces FK but I can not raise ValidationError which will be handled nice for user.
What is the best strategy to validate it?
See this ticket.
In a nutshell, you can just item.number = value.lower() before saving.

Customizing a date field in a model form

Let me start out by saying this question is basic, and almost certainly has been answered elsewhere if I only could find exactly what I need.
I have users who will be entering dates in some screwy formats, which won't be recognized by the usual validation code for the DateField (for instance, 2014/4 which I will want to convert to 2014-4-01 internally). Do I need to mess with the code to clean, validate, or both for this, and if I redefine those functions do I need to explicitly call super?
My thought was that maybe I should give the model field and the form field different names, and somehow fill the model field with the form field data when I process the form... but the how of that is vague.
class Person(models.Model):
(some other fields)
date_of_arrival = models.DateField(blank=True, null=True)
class ClientForm(forms.ModelForm):
(some other fields)
date_of_arrival = forms.DateField(required=False, help_text="Date of arrival in town")
class Meta:
model = Person
fields = (..., date_of_arrival, ...)
$date = date('Y-m-d',$urvalue);
You need to implement a _clean() method in your form, where the name of the field is before the _, so in your example it would be date_of_arrival_clean().
In this method, do all your validations. I would recommend the dateutil package, which provides parse method that is designed to take a string that could possibly be a date, and convert it to a date. Using this method you will not have to write the mundane logic, instead your entire method would be:
def date_of_arrival_clean(self):
user_input = self.cleaned_data['date_of_arrival']
try:
d = parse(user_input)
except (ValueError, TypeError):
raise forms.ValidationError('{} is not a valid date'.format(user_input))
return user_input
My thought was that maybe I should give the model field and the form
field different names
You don't have to do this ... you can keep the same fields as in your model form and still implement the _clean() method as described above.
Keep in mind that if your form is a ModelForm, then django will also do database validation when try to validate the form.

ModelChoiceField , removing the blank choice

I want to get rid of the "-------------" choice Django adds in a select input representing a Foreign Key on a ModelForm
It's been answered that you can use the empty_label=none option, but I have a ModelForm, not a regular form and overriding the field is not allowed.
I know that I can override the __init__() method of the ModelForm in order to modify a ModelChoiceField's queryset using
self.fields['my_foreign_key'].queryset = ....
But this would be really ugly, as this happens over +10 foreign_keys on the "Main" model, and there's more than a Modelform based on this model
The whole context :
each one of these foreign_key points to the same kind of models : they are particular lists of choices, many models to ease their modification via the admin.
all these models are related to the Main model via a "to_field=code" relation, based on a Charfield which contains a three-letter code (hey, not my fault, I had to use a legacy MS Access DB), this CharField has the blank=True, unique=True options, so I could, for each list, create a "---No information yet---" record which has, you bet, a blank value instead of a three letter code...
The situation is now : I get a select input field with two "blank value" choices : the django one and mine, one after the other.
I just miss a 'empty_label=none` option here too...
If you want to remove the blank choice for all ModelChoiceField you could do something like..
class Form(forms.ModelForm):
class Meta:
model = MyModel
def __init__(self, *args, **kwargs):
super(Form, self).__init__(*args, **kwargs)
modelchoicefields = [field for field_name, field in self.fields.iteritems() if
isinstance(field, forms.ModelChoiceField)]
for field in modelchoicefield:
field.empty_label = None
But that would replace all ModelChoiceFields, so maybe something more fine grained like:
for fieldname in ('field1','field2','field3','field4','field5','field6'):
self.fields[fieldname].empty_label = None
Certainly easier than the __init__ overwrite you mentioned!
Update from Moritz
You can use 2017+ use:
forms.ModelChoiceField(... empty_label=None