django model filefield having db error - django

I am using django 1.4 and using django model's filefield to upload some document via modelform. I am having following issues:
When I submit form it says:
Data truncated for column 'file_name' at row 1
Following is my model for this:
class App(models.Model):
user_name=models.CharField(max_length=50)
email=models.CharField(max_length=50)
status=models.CharField(max_length=10,choices=APPLICATIONSTATUSCHOICE)
archived=models.BooleanField()
mark_spam=models.BooleanField()
date=models.DateField()
file_name=models.FileField(upload_to=PATH+"/")
def getPath(self):
return PATH
def __unicode__(self):
return self.user_name
def send_email(self):
pass
Here is the code for model form:
class AppForm(ModelForm):
class Meta:
model=App
exclude=('status','archived','mark_spam')
email=forms.EmailField()
def save(self,commit=True):
app=super(AppForm,self).save(commit=False)
app.status='sent'
app.save()
Also it is storing file with its original name,can I have it with something unique name as I am from PHP background and in PHP I normally save it like <mysql auto id>.<filextension>, so how can I do it in django. My first impression was that all this will be automatically done via django while it just save it with name of its own choice but I need to save names into db also so want to name them according to my choice. How can it be done and what is problem in my code that is giving above mentioned error?

How long is the file_name you are trying to store?
By default, FileField instances are created as varchar(100) columns in your database. As with other fields, you can change the maximum length using the max_length argument. That might be resulting the data truncation error.
Also you can rename your file whatever you want. the upload_to can be a callable that takes the instance and the name of the uploaded file as input and then you can specify the exact name and path you want to store.
Eg.
def get_file_name(instance, filename):
return '/'.join(['blah', instance.user.username, filename])
...
file_name=models.FileField(upload_to=get_file_name)
...

Related

Passing The ID of a Field in another Field of the same model Django

Hello, I am trying to pass the ID of a Model in an Image Field URL(upload_to) and then access it Through a URL unique to The instance.
Here's What I did (Amature);
class User(models.Model):
serial = models.AutoField(primary_key=True)
profile = models.ImageField(upload_to=f"profiles/{serial}/")
But all I'm Getting is OSError.
I wanted to save the file to profiles/{serial}/ directory in the app.
So Every Instance of the Model has its own Directory. And Then access it Through host:port/api/users/{serial}/profile.jpg
My View Set is served through host:port/api/users
Is there a way I can Do it?
Any Help is Highly Appreciated. A Detailed Explaination is Even more Appreciated.
From https://docs.djangoproject.com/en/3.0/ref/models/fields/#django.db.models.FileField.upload_to
upload_to may also be a callable, such as a function. This will be called to obtain the upload path, including the filename. This callable must accept two arguments and return a Unix-style path (with forward slashes) to be passed along to the storage system. The two arguments are: instance and filename
So you can do something like this
def profile_handler(instance, file_name)
return f"profiles/{instance.id}/{file_name}"
class User(models.Model):
serial = models.AutoField(primary_key=True)
profile = models.ImageField(upload_to=profile_handler)
on model override save method like this
def save(self,*args, **kwargs):
self.fieldname=currentmodel.objects.last().id+1
super(Orders, self).save(*args, **kwargs)

Adding file upload widget for BinaryField to Django Admin

We need to store a few smallish files to the database (yes, I'm well aware of the counterarguments, but setting up e.g. FileField to work in several environments seems very tedious for a couple of files, and having files on the database will also solve backup requirements).
However, I was surprised to find out that even though BinaryField can be set editable, Django Admin does not create a file upload widget for it.
The only functionality we need for the BinaryField is the possibility to upload a file and replace the existing file. Other than that, the Django Admin fulfills all our requirements.
How can we do this modification to Django Admin?
You will want to create a custom Widget specifically for BinaryField which has to read the file contents before putting them into the database.
class BinaryFileInput(forms.ClearableFileInput):
def is_initial(self, value):
"""
Return whether value is considered to be initial value.
"""
return bool(value)
def format_value(self, value):
"""Format the size of the value in the db.
We can't render it's name or url, but we'd like to give some information
as to wether this file is not empty/corrupt.
"""
if self.is_initial(value):
return f'{len(value)} bytes'
def value_from_datadict(self, data, files, name):
"""Return the file contents so they can be put in the db."""
upload = super().value_from_datadict(data, files, name)
if upload:
return upload.read()
And then you need to use it in admin in the following way:
class MyModelAdmin(admin.ModelAdmin):
formfield_overrides = {
models.BinaryField: {'widget': BinaryFileInput()},
}
fields = ('name', 'your_binary_file')
Note:
BinaryField doesn't have a url or a file name so you will not be able to check what's in the db
After uploading the file you will be able to see just the byte size of the value stored in the db
You might want to extend the widget to be able to download the file
by reading it's contents

UniqueValidator with source field

I have model UserProfile:
class UserProfile(models.Model):
user = models.OneToOneField(User)
and serializer:
email = serializers.CharField(source='user.email', required=False,
validators=[UniqueValidator(queryset=User.objects.all())])
Serialization works fine, but deserialization doesn't — it tries to file 'user.email' field in User model, and, of course, fails.
If I change User to UserProfile in the queryset, it fails with another error:
invalid literal for int() with base 10: 'admin#localhost'
Is it possible to set different sources for serialization and deserialization?
By default, the UniqueValidator expects that the source (or field name, if no source is given) can be used in the queryset that is provided to filter out the existing object. So by using a source that spans a relation (and has a dot), it will try to use that name when filtering the queryset which fails, as you've noticed.
You can fix this by subclassing UniqueValidator to override the filter_queryset method to filter it differently.
class CustomUniqueValidator(UniqueValidator):
def filter_queryset(self, value, queryset):
"""
Filter the queryset to all instances matching the given attribute.
"""
filter_kwargs = {"email": value}
return queryset.filter(**filter_kwargs)
This hard codes email as the filter, one possible option for not hard coding it would be to split self.field_name and get the last part of the dotted source.

Python & Django, UUID not unique

I'm developing an application using Django. I have a form in which the user uploads 3 different files (at least one). Then those files are sended to a home script that generates some result files. I want to store all those files in one directory, each directory name unique by form submission. I've look around on the Internet and I find the UUID technology. I installed the module of Django named django-uuid-upload-path. But when I submit my form, it is always the same uuid that is returned, looking like a UUID string. Here is my model where I'm using this module :
from django.db import models
from uuid_upload_path import uuid
class Analysis(models.Model):
uidDir = uuid()
dirFile = 'documents/%Y/%m/%d/' + str(uidDir)
structureFile = models.FileField(upload_to = dirFile)
I've tried to use upload_to from this module in this way :
from uuid_upload_path import upload_to
class Analysis(models.Model):
structureFile = models.FileFiels(upload_to = upload_to)
I've done this for my 3 FileFields and it gave me 3 different UUID on one form submission. The problem is now that my files are not in the same directory.
Here is my controller, where I upload the files submitted by the user :
def analysis(request):
if request.method == 'POST':
documents = Analysis.objects.all()
form = AnalysisForm(request.POST, request.FILES)
if form.is_valid():
newdoc = Analysis(structureFile = request.FILES['structureFile'])
newdoc.save()
I've tried with the uuid module from Python but I got the same problem. I've tried to refresh the web page and to delete the cookies but nothing worked.
P.S : I'm using Safari on OS X 10.9.4.
Thanks in advance
I think you should't hardcode your UUID in model section. Django ORM will use it once while creating database.
In your model you should keep field with UUID, but this UUID should be generated in views.py while uploading files.
So there are steps, how upload should look like:
You feel in form and send it.
Your controller gets the form with data.
In your controller you generate UUID
Your controller saves data in database (and there you should store path to files along with generated UUIDs)
From the upload_to documentation :
This may also be a callable, such as a function, which will be called
to obtain the upload path, including the filename. This callable must
be able to accept two arguments, and return a Unix-style path (with
forward slashes) to be passed along to the storage system. The two
arguments that will be passed are:
What you need to do is create this uuid function and pass it to upload_to.
I found the answer on the mailing list of Django. Here is how you get the value of a field :
def dir_file(analysis, file_name): # should live outside your model
return 'documents/%s/%s' % ( analysis.uuidDir, file_name)
In the model, you put :
uuidDir = models.CharField(max_length = 36)
structureFile = models.FileField(upload_to = dir_file)
Now my directory are UUID and generated at each form submission !

How to use save_model in an AdminForm containing a M2M field?

i'm having an annoying issue with django model system + its default admin.
Let's assume i have a very simple model like:
class Note(models.Model):
text = models.CharField(max_length=200)
def __unicode__(self):
return self.text
and a container like:
class NoteCollection(models.Model):
notelist = models.ManyToManyField(Note)
title = models.CharField(max_length=20)
def __unicode__(self):
return self.title
What i want do to it's update all the "Note" elements when a NoteCollection gets Added.
I read that m2m models have complex save mechanism, so what i was thinking is, let's read the form object, and just save the Note elements by myself!!
But when i make something like this in APPNAME/admin.py:
from models import Note,NoteCollection
from django.contrib import admin
class NoteCollectionAdmin(admin.ModelAdmin):
def save_model(self, request, obj, form, change):
# Something USEFUL HERE
pass
admin.site.register(Note)
admin.site.register(NoteCollection, NoteCollectionAdmin)
Django pops me an error: ('NoteCollection' instance needs to have a primary key value before a many-to-many relationship can be used.)
I don't even want to use the NoteCollection object at all, i'm interested in the form object, actually..
I've also found on internet some examples that use save_model with a M2M field, so i can't understand why i keep getting this error; for reference, i've just made a new-from-scrap project and i'm using an sqlite db for testing
By overriding save_model() in NoteCollectionAdmin you're preventing Django from saving your notecollection. After handling everything, Django saves the m2m table, but fails because the notecollection doesn't have an automatic ID as you didn't save it in the database.
The main problem is that Django saves m2m files after saving the objects. I tangled with that a few days ago, see http://reinout.vanrees.org/weblog/2011/11/29/many-to-many-field-save-method.html
Somewhat related question: Issue with ManyToMany Relationships not updating inmediatly after save