Saving file field as None but reflects empty string in the database - django

I'm trying to submit a form with a file field. There are instances when the file is not yet available so the user will have to submit the form without the file first.
My field now looks like this
file_attachment_pdf = models.FileField(blank= True,null = True, unique=True)
I'm able to save 1 record with blank fields. When adding the 2nd record with blank file upload, it doesn't work anymore since there is an existing record with the file value.
Upon checking, it's an empty string.
So I tried to override the clean function in models.py
def clean(self):
if not self.file_attachment_doc:
print('doc is blank, will tag as Null')
self.file_attachment_doc = None
But for some reason, when I check in the DB it still stores as an empty string. (I'm using DB Browser for SQLite, it says the field of data is Text with 0 chars)
When I try to submit, here is the error that is returned to me
File with this File attachment pdf already exists.
If this question was already asked and answered before, please do let me know.

FileField store the file path as a CharField. For CharField, the null=True settings, equals to store empty string. That why you see empty string in your db. Your error come from the unique=True parameter. That means that you can store only one empty file for your model.
Plus the model clean method is called only if you user model form to save you instance. In all way, if the method is call or not, it don't make difference, the value stored would be always empty string
https://docs.djangoproject.com/en/2.1/ref/models/fields/#null

Related

How to store a file as a string in a model text field?

I am pretty new to Django. I need some help.
I am working on an admin page that allows me to edit objects. My model has a text field that I want to fill with a file contents which will be uploaded with a FileInput widget in a form.
So I want to read the file contents, put it in a string and store it in the model text field. Therefore it should be saved in the database.
Any help with this? I dont know how to get the file , read it and store it in my model as string. I am using a ModelAdmin btw.
First off, you might want to just use a model.FileField (https://docs.djangoproject.com/en/2.0/ref/models/fields/#filefield). The database only has to store a path to the file, instead of the entire contents of the file. You might not want to store the contents of a file directly in the database, especially if its a large file.
But if you do want to read a file in to your model. Try something along the lines of:
models.py
class SomeModel(Model):
textfield = TextField()
views.py or whatever script you are calling
with open('data.txt', 'r') as myfile:
data=myfile.read()
newmodel = SomeModel()
newmodel.textfield = data
newmodel.save()

Saving image file field manually in django

I am trying to save a preview image generated by "preview_generator" Python app.
But I am getting IntegrityError duplicate key value violates unique constraint "users_material_pkey". I've tried many things but nothing seems to be working.
If I call super at the end of save I don't get material_file url or path.
remove this line material = self in your code and use below way
obj = super(Material, self).save(force_update=False, using=None,update_fields=None)
material = obj

Django rest Framework, change filename of ImageField

I have an API endpoint with Django Rest Framework to upload an image.
class MyImageSerializer(serializers.ModelSerializer):
image = serializers.ImageField(source='image')
I can upload images but they are saved with the filename that is sent from the client which can result to collisions. I would like instead to upload the file to my CDN with a timestamp filename.
Generating the filename is not the problem, just saving the image with it.
Any one knows how to do that?
Thanks.
If your image is of type ImageField from django, then you don't really have to do anything, not even declare it in your serializer like you did. It's enough to add it in the fields attribute and django will handle collisions. This means django will add _index on each new file which might generate a collision, so if you upload a file named 'my_pic.jpg' 5 times, you will actually have files 'my_pic.jpg', 'my_pic_1.jpg', 'my_pic_2.jpg', 'my_pic_3.jpg', 'my_pic_4.jpg' on your server.
Now, this is done using django's implementation for FileSystemStorage (see here), but if you want it to append a timestamp to your filename, all you have to do is write a storage class where you overwrite the get_available_name(name) method. Example:
class MyFileSystemStorage(FileSystemStorage):
def get_available_name(self, name):
''' name is the current file name '''
now = time.time()
stamp = datetime.datetime.fromtimestamp(now).strftime('%Y-%m-%d-%H-%M-%S')
return '{0}_{1}'.format(name, str(stamp))
And the image field in your model:
image = models.ImageField(upload_to='your upload dir', storage= MyFileSystemStorage)
Important update
As of August 20, 2014 this is no longer an issue, since Django found a vulnerability related to this behaviour (thanks #mlissner for pointing it out) . Important excerpt :
We’ve remedied the issue by changing the algorithm for generating file
names if a file with the uploaded name already exists.
Storage.get_available_name() now appends an underscore plus a random 7
character alphanumeric string (e.g. "_x3a1gho"), rather than iterating
through an underscore followed by a number (e.g. "_1", "_2", etc.).

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.

Auto-truncating fields at max_length in Django CharFields

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.