Auto-truncating fields at max_length in Django CharFields - django

I have a field that has a max_length set. When I save a model instance, and the field's value is greater than max_length, Django enforces that max_length at the database level. (See Django docs on models: http://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.CharField.max_length)
However, since I am using Postgres, I receive a DatabaseError exception like this:
DatabaseError: value too long for type character varying(1000)
I would prefer to instead auto-truncate the value (so I don't have an exception). Now, I can do this manually, but what I would really want is to have all of my models auto-truncate the value. (Not necessarily intelligently. Just cutting it off at the 999th character is fine.)
Should I just write a custom class that imports from models.Model and override the save() method, looping through each _meta.field, checking for the max_length, and then truncating? That seems inelegant and there must be a better way.

You could create a custom field that auto-truncates the field (I think this code should work, but double-check it):
class TruncatingCharField(models.CharField):
def get_prep_value(self, value):
value = super(TruncatingCharField,self).get_prep_value(value)
if value:
return value[:self.max_length]
return value
Then, instead of using models.CharField in your models.py file, you'd just use TruncatingCharField instead.
get_prep_value prepares the value for a field for insertion in the database, so it's the ideal place to truncate.

Why don't you use a TextField? From the manual:
For large amounts of text, use
TextField.

Why don't you use ModelForm. ModelForm enforces a validation, setting its default max_length to model field's max_length property, and raising proper validation error when form.is_valid() is called. That way you don't have to save the form, until form is validated.
Or, if you want to silently pass the validation and truncate suits best to you, write a simple django form, and write a clean method that truncates input string to the max_length and return stripped data. Take data from form.cleaned_data after form is validated and save the object.
All considering the fact, Forms are designed to validate data before going to DB.

That seems inelegant and there must be a better way.
The only reason the truncate behavior ever happens in the first place is because of MySQL's failure to adhere to the SQL Standard. The throwing of an exception is the correct response when attempting to INSERT a string into a VARCHAR field that is not wide enough to hold it. MySQL truncates and inserts instead.
If you want to silently corrupt your data, then you'll have to do it manually somehow--PostgreSQL will never do it for you.

Related

Django: Order of validation in Models

Recently I found out that its possible to define Django form validation directly in the models.py file. This can be done the following way:
fev1_liter = models.DecimalField(validators=[MaxValueValidator(8.2),
MinValueValidator(0.3)],
max_digits=3, decimal_places=2)
This is an awesome alternative to validation in forms.py, but I do have a very annoying problem:
How can I control in which order the validation is executed?
In this example Django will first validate if the inputs digits is in the format x.xx and thereafter min and max value. This results in some very confusing error messages.
Thanks in advance!
For each model field, field.clean() first performs field validation via field.validate(), then via field.run_validators(), validators are called in order they are returned from the field.validators iterator.
This makes sense, because in the general case you can expect your validators to fail if the field validation failed, so it makes for easier debugging. Remember that field validators are non-obligatory, so field.validate() takes precedence. If you want to change the behavior, you'll have to create your own Field classes and override the field.clean() behavior.
You can inspect the field sources for more details.

Django call cleaned_data on all fields

Is there a way to call cleaned_data on all fields with some function instead of individually calling it for each field?
Also, why do we even need to call cleaned_data?
I am not sure if I need it here... I am using a for loop to save a formset, but it only saves the last one. Here is the code
for instance in form:
tmp = instance.save(commit=False)
# it throws an error when I try to do tmp[foreign_key] = other_model
setattr(tmp, foreign_key, other_model)
tmp.save()
What are you hoping for? You don't ever call it. cleaned_data gets populated upon validating the form.
form.is_valid() populates form.cleaned_data, which is a dictionary storing all data "cleaned" i.e. validated and converted to their python types.
I don't think one can make data much more accessible than a dictionary of keys mapped to field names.
As for your latest update, that itself is pretty confusing.
You appear to be setting an attribute on a foreign key in your modelform instance based on a local variable named 'gen_house_form_saved' (which I don't understand as well: if it's in the locals() namespace, and you're not using a dynamic name, why use locals at all).

In Django, how can I calculate or update certain model fields BEFORE any validation happens?

So I'm new to Django...
First some background on how we do things now. We have a custom php system but I am constructing an improved inventory management system in django using only the admin interface. We store part numbers, and it is essential that we do not store duplicates. Part numbers can sometimes be entered with hypens, periods, spaces, etc. We need to be sure that duplicate parts are not added no matter what kind of formatting is entered. With our existing non-django system, we use a regex to strip anything from the string that is not a-zA-Z0-9. The actual entered part number is persisted, and the cleaned number is persisted to the db as well. Then when someone is adding a new part or even searching for a part, this cleaned version of the part number helps to avoid this ambiguity. We do the same for the manufacturer name.
My way of emulating this in django was to add the part_number_clean field along with the part_number field to the model. Then I overrode the save method to calculate the clean part number like so (manufacturer as well):
def save(self, *args, **kwargs):
self.manufacturer_clean = re.sub(r'[^a-zA-Z0-9]', '', self.manufacturer).lower()
self.part_number_clean = re.sub(r'[^a-zA-Z0-9]', '', self.part_number).lower()
super(CatalogProduct, self).save(*args, **kwargs)
The problem is, I need to unique on a combination of part number and manufacturer:
class Meta:
unique_together = ('part_number_clean ', 'manufacturer_clean ')
When I try to save a duplicate record, I get a database integrity violation. So it seems like django is evaluating the unique fields before the save function is called (which makes sense). I just need to know how or which method I should override to calculate these fields BEFORE any validation.
Additionally, I am interested in adding a third field to the unique_together mix that may or may not be filled out. If it is not filled it will just have an empty default value. I hope this will not cause any issues.
It would also be great if when the user tabbed-out of the manufacturer and part number fields, and both were not empty, some js would see if that product exists already, and offer the user the option to click a button and be whisked away to that record, before they waste their time filling out the rest of the data only to find that it already exists. I'm guessing this lies way outside the realm of the admin interface without serious hacking. Is there any way to somehow integrate this with the admin interface? Its working great for me up till now...
I figured it out. I'm posting the answer for anyone else that is curious. This was actually very simple in the end to implement in the model. All one needs to do is implement (override?) the clean() method of the model. In the method, I calculate and set my special fields, then be sure to call self.validate_unique() after. Works like a charm! No need to raise any exceptions, the form will display the error on top perfectly. Doing this in the save method will not work, as the exception cannot be thrown by your code or django at that point. Here is the code:
class CatalogProduct(models.Model):
manufacturer = models.CharField(max_length=100)
manufacturer_clean = models.CharField('Manufacturer',max_length=100,blank=True,editable=False)
part_number = models.CharField(max_length=100)
part_number_clean = models.CharField('Part number',max_length=100,blank=True,editable=False)
def clean(self):
# Calculate manufacturer_clean and part_number_clean
self.manufacturer_clean = re.sub(r'[^a-zA-Z0-9]', '', self.manufacturer).lower()
self.part_number_clean = re.sub(r'[^a-zA-Z0-9]', '', self.part_number).lower()
self.validate_unique()
The model is only responsible for describing data and how that data should be represented between your Python and database environment. It's because of this atomic role that models don't care about validation and what you've just went in there and introduced it.
You need a model form. It can clean the manufacturer and part number and also ensure that uniqueness constraints are satisfied as part of the validation process.

Invalid keyword argument on new model entry

I have the following model:
class mark(models.Model):
title=models.CharField(max_length=35)
url=models.URLField(max_length=200)
user=models.ManyToManyField(User,blank=True)
and then I use a form to save some data to the db. My code inside the view that saves the data is:
new_mark= mark(url=request.POST['url'],
title=request.POST['title'],
user=request.user)
new_mark.save()
Of course I have all the data validation, login required validation, etc.
When I run this it throws me an unexpected
'user' is an invalid keyword argument for this function
on theuser=request.user) line. Any ideas what might be wrong?
Please provide the whole traceback and make sure your view has no function named "mark" etc (You probably also want to change mark to Mark to follow Python and Django style guides.) test via print type(mark) before the "new_mark = …" line.
Also I am not 100% sure if a ManyToMany field allows settings data like that, eg try:
new_mark= mark(url=request.POST['url'],
title=request.POST['title'])
new_mark.user.add(request.user)
new_mark.save()
And since it's an m2m field you probably want to rename the field to users.

In a Django form clean function, how can one reliably test if an optional field was filled out?

In Django, I have a form class with a clean function. In this clean function, I check to see if an optional select box was filled out.
def clean_session_1(self):
# Check if session_1 is filled out.
if self.cleaned_data['session_1']:
# more validation
return self.cleaned_data['session_1']
If the select box was filled out, then more validation of the field ensues.
For some reason, this code is not testing whether the field was filled out and runs "more validation" every time.
I was wondering how can one reliably test if this field "session_1" was filled out? Thanks!
Access self._raw_value('key') to get the raw entry in the form field (i.e. just text, not processed to a python object).
Update: As Ahsan says, do it in the clean method. You should probably call the superclass clean method also.
You can check it in form's clean method
def clean(self):
data = self.data # data contains all fields, optional or required both
optional_field = data.get('optional_field', None)
# more validation
return optional_field