Python & Django, UUID not unique - django

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 !

Related

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

Indexing Taggit tags with Algolia for Django: '_TaggableManager' object has no attribute 'name'

I'm having some issues using the Algolia Django integration with one of my models which contains a TaggitManager() field. I'm currently being thrown back the following error when running this command:
$ python manage.py algolia_reindex
AttributeError: '_TaggableManager' object has no attribute 'name'
I've had a look at the Taggit documentation, but I'm just not sure exactly how I would marry the method outlined with the Algolia search index method.
index.py:
import django
django.setup()
from algoliasearch_django import AlgoliaIndex
class BlogPostIndex(AlgoliaIndex):
fields = ('title')
settings = {'searchableAttributes': ['title']}
index_name = 'blog_post_index'
models.py:
from taggit.managers import TaggableManager
class Post(models.Model):
...some model fields...
tags = TaggableManager()
To index the taggit tags with your Post fields, you will need to expose a callable that returns a Blog Post's tags as a list of strings.
The best option is to store them as _tags, which will let you filter on tags at query time.
Your PostIndex would look like this:
class PostIndex(AlgoliaIndex):
fields = ('title', '_tags')
settings = {'searchableAttributes': ['title']}
index_name = 'Blog Posts Index'
should_index = 'is_published'
As for Post:
class Post(models.Model):
# ...some model fields...
tags = TaggableManager()
def _tags(self):
return [t.name for t in self.tags.all()]
Following these instructions, your records will be indexed with their respective tags:
You can check the taggit branch of our Django demo, which demonstrates these steps.
To answer my own question. I have now passed in both the model and the model index so Algolia now knows what to index and what not to index. Although I would like a method to allow Algolia to index taggit tags, alas, it is probably not possible.
My apps.py file:
import algoliasearch_django as algoliasearch
from django.apps import AppConfig
from .index import PostIndex
class BlogConfig(AppConfig):
name = 'blog'
def ready(self):
Post = self.get_model('Post')
algoliasearch.register(Post, PostIndex)
My index.py file:
from algoliasearch_django import AlgoliaIndex
class PostIndex(AlgoliaIndex):
fields = ('title')
settings = {'searchableAttributes': ['title']}
index_name = 'Blog Posts Index'
should_index = 'is_published'
And that should pretty much work! Simple when you know how, or after trying about 10 different options!
So since nobody is answering I tell you how I solved this issue but I have to say that it is not a nice Way and not a "clean" Solution at all. So what I did is went into "taggit managers" in the site-packages (env->lib->python2.x/3.x-> site_packages->taggit->managers.py) In the managers.py file you will find at line 394 this beautiful piece of code:
def __get__(self, instance, model):
if instance is not None and instance.pk is None:
raise ValueError("%s objects need to have a primary key value "
"before you can access their tags." % model.__name__)
manager = self.manager(
through=self.through,
model=model,
instance=instance,
prefetch_cache_name=self.name, # this is the line I comment out when building the index,
name=self.name #this is the line I added and needs to be commented out after the index is build.
)
return manager
So what I do when I want to rebuild the search index is comment out (putting"#" infront of the line) prefetch_cache_name=self.name, and replace it with name=self.name. So building the index will work. After the Index is finished building, you have to bring everything back as it was before (switch the "#" to name=self.name again and leave prefetch_cache_name=self.name, visible again).
As already mentioned this is probably not the best way but I had the same pain and this is working for me. It takes one minute when you have the routine. Since I have to rebuild the Index maybe once every two weeks, that isn't such a deal for me but if you have to do it very often this might be annoying...
Anyway I hope that helps you.
It can help you if you using django==2+
The problem is in get_queryset() method of TaggableManager
Open file with it (my path was: Pipenv(project_name)/lib/site-packages/taggit/manager.py)
Find _TaggableManager class and change method name get_queryset to get_query_set
Done. I wish taggit's developers will fixed this in future updates

is it possible to have path string as the argument to django filefield?

I have to create and save django model containing filefield with a downloaded attachment.
somefield = models.FileField(upload_to='somefolder/%Y/%m/%d')
I have downloaded the attachment but when I give path to the downloaded attachment as filefield upload to argument, attachment is not getting saved. I checked the folder and could not find the file.
My procedure is as follows
1.Download attachment and save it.
2.Call Model with filefield.
path='path_to_downloaded_attachment'
obj = somemodel.objects.create(param1=value, param2=value, somefield=path, param3=value)
obj.save()
Is django expecting any arguments other than path?
You can pass a lambda to upload_to:
def generate_filename(instance, filename):
return find_filename(instance, filename) # based on your rules
class yourmodel(models.Model):
somefield = models.FileField(upload_to=generate_filename)

Django model with FileField -- dynamic 'upload_to' argument

I am using the model with FileField to deal with file uploading. Now the files can be uploaded successfully. However, there is one more small improvement I want to make, which is to create folder for the user with the username.
Here is the code I've tried
class UserFiles(models.Model):
user = models.OneToOneField(User)
file = models.FileField(upload_to='files/users/user.username/%Y_%m_%d/')
this would give the folder of 'user.username' instead of 'John'(one example of username)
I have also tried other ways like files/users/%user.username/%Y_%m_%d/
,but it would not give the folder with the user name. Not sure how the syntax should be or whether this is possible.
Can you give some suggestions on this?
Thank you very much for your help and explanation.
Instead of a string try passing a function:
def generate_filename(self, filename):
url = "files/users/%s/%s" % (self.user.username, filename)
return url
class UserFiles(models.Model):
user = models.OneToOneField(User)
file = models.FileField(upload_to=generate_filename)

django model filefield having db error

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)
...