Use model attribute as field parameter - python

I'm trying to create a folder and upload an image to in it. My model is similar to this:
class XYZ(models.Model):
def code(self):
syms = ['#','#','$','%','&','*','+','-']
return ''.join(x+random.choice(syms) for x in [self.name[-2:],self.name[2:]])
def make_folder(self):
os.mkdir(os.getcwd()+'/XYZ/'+folder)
def save(self):
self.make_folder()
super(XYZ,self).save(*args,**kwargs)
name = models.CharField(max_length=20)
folder = property(code)
image = models.ImageField(upload_to='HELP_HERE')
I've tried using folder, self.folder, property(code) but it says it isn't defined, how do I access this attribute? Is this the correct approach?
I've also set in the configs.py my MEDIA_URL and MEDIA_ROOT

I think a more simple and understandable way to do this would be to avoid using the inner class function and doing something like this:
# Reference this function outside of your model class.
def xyz_directory_path(instance, filename):
# file will be uploaded to MEDIA_ROOT/xyz_<id>/<filename>
return 'xyz_{0}/{1}'.format(instance.id, filename)
Then, on your models, reference the function in your upload_to field like so:
class XYZ(models.Model):
image = models.ImageField(upload_to=xyz_directory_path)
With your media config set up, this should work. There's a really nice and simple explanation about file handling here.

Related

Django FileField upload_to assistance

I'm new to python and trying to adapt to the OOP of Python. Could someone explain why the following is saving in a folder called 'None'? I want to upload an audio file in the admin page. This file gets stored in its own folder with the 'Vocab name'
class Vocab(models.Model):
module = models.ForeignKey(Modules, on_delete=models.CASCADE)
number = models.CharField(max_length = 250)
name = models.CharField(max_length = 250)
class VocabContent(models.Model):
vocab = models.ForeignKey(Vocab, on_delete=models.CASCADE)
audio = models.FileField(upload_to=vocab.name)
Running the following on shell.
>>> from module.models import Modules, Vocab, VocabContent
>>> vocab = VocabContent.objects.get(pk=1)
>>> vocab.vocab.name
'Numbers'
Numbers is the value i am looking for.
It's probably because the way you reference vocab.name is not defined when your model migration is run. I can't explain precisely why this happens but a solution would be to use a callable as your upload_to to evaluate it at runtime and get the value correctly, much like this other answer: Dynamic File Path in Django
So, for you, you could have something like:
import os
def get_upload_path(instance, filename):
return os.path.join("%s" % instance.vocab.name, filename)
class VocabContent(models.Model):
vocab = models.ForeignKey(Vocab, on_delete=models.CASCADE)
audio = models.FileField(upload_to=get_upload_path) # Important to NOT put the parenthesis after the function name
Which would result in a path that concatenates the vocab.name to your file name for every new file.

Django: how to stop Django from altering filenames (e.g. transforming a space into a _ )?

I'm working on a little Django project and I have a filefield in my models.py.
Everything works fine, but I don't like that Django is editing the filenames after upload. It changes spaces into underscores and it removes (square) brackets and stuff like that.
Is there any way to stop Django from doing this?
I can see that it is to make the website safer, more secure and also just avoid errors. But, I'm the only one who's going to be able to upload files anyway.
Hopefully someone knows if (and how) this is possible :)
Thanks!
edit:
Here's the FileField in the models.py:
file = models.FileField(upload_to=file_path)
Here's my upload_to:
def file_path(instance, filename):
extension = filename.split('.')[-1]
new_filename = '%s - %s.%s' % (str(instance.model.object_number), str(instance.model.object_name), str(extension))
return '/'.join(['files', str(instance.model.object_theme), str(instance.model.object_number), new_filename])
Yes, you can write your own upload_to function, as explained in the documentation: https://docs.djangoproject.com/en/1.9/ref/models/fields/#django.db.models.FileField.upload_to
def user_directory_path(instance, filename):
# file will be uploaded to MEDIA_ROOT/user_<id>/<filename>
return 'user_{0}/{1}'.format(instance.user.id, filename)
class MyModel(models.Model):
upload = models.FileField(upload_to=user_directory_path)
If you want to avoid overwriting files with identical names, you have to add that functionality back. The default storage uses this function django.core.file.storage.get_availble_name()
You can read the source code on github
Edit: It looks as though your upload_to function doesn't return anything. It should return a pathname as a string.
import os
def file_path(instance, filename):
base, extension = os.path.splitext(filename)
return '{model.object_number} - {model.object_name}{extension}'.format(
model=instance.model, extension=extension)
You need to create your own storage class
mystorage.py
import re
from django.utils.encoding import force_text
from django.utils.functional import keep_lazy_text
from django.core.files.storage import FileSystemStorage
#keep_lazy_text
def get_valid_filename(s):
"""
>>> get_valid_filename(" tawanda's portrait in 2019.jpg ")
'tawandas portrait in 2019.jpg'
"""
s = force_text(s).strip()
return re.sub(r'(?u)[^-\w. ]', '', s)
class CleanFileNameStorage(FileSystemStorage):
def get_valid_name(self, name):
"""
Return a filename, based on the provided filename, that's suitable for
use in the target storage system.
"""
return get_valid_filename(name)
If you want to apply this storage to all models you can assign the Django setting DEFAULT_FILE_STORAGE with your custom class e.g
DEFAULT_FILE_STORAGE = 'mystorage.CleanFileNameStorage'
If this is only for a particular model then:
models.py
def myfile_save_to(instance, filename):
return 'my_files/{filename}'.format(filename=filename)
class MyFileModel(models.Model):
my_file = models.FileField(
upload_to=myfile_save_to,
storage=CleanFileNameStorage(),
)
Remember if you specify the storage on a model level you must initialize the class e.g. CleanFileNameStorage() if you don't you will get argument errors

how to get datetime instance?

Well it is look like simple question. but i am in the learning stage can`t figure out.
This is my model class
class Store(models.Model):
file = models.FileField(upload_to=content_file_name)
file_name = models.CharField(max_length=100)
created_date = models.DateTimeField(auto_now_add=True)
userfild = models.ForeignKey(user,null=True, blank=True)
Ok for some clarification i just included my content_file_name function here
def content_file_name(instance, filename):
return os.path.join(
"user_%d" % instance.created_date, filename)
but the output of the folder structure looks like this
`None`
----filename
i can get the file_name instance like this --> intance.file_name
but when i enter instance.created_date it is return 'None' value.
What i am missing.? And my another doubt is i set it DateTimeField as auto_now_add=True So it`s saved into the db when form is submitted. But why None return?
auto_now_add is not populated until the object is saved. If you want a default that is applied as soon as the object is instantiated, use the default parameter with a callable:
created_date = models.DateTimeField(default=datetime.datetime.now)
Not quite sure that this is what you're asking, but here goes anyway:
You create an instance of any class by instantiating it:
Store()
In Python, this calls the class's init() function. Since you don't have one, it will use the 'constructor' from the class you're inheriting from, models.Model.
So, for example, you could do something like this:
my_store = Store()
my_store.file_name = 'some new name!'

Dynamic File Path in Django and South

I'm having a problem setting a dynamic path to my imageField.
This is my models.py
class Imagen(models.Model):
def get_upload_path(self, filename):
return os.path.join(
"img/experiencias/experiencia_%d" % self.id_experiencia.id, 'ficha' + '_' + filename)
nombre = models.CharField(max_length=50)
id_experiencia = models.ForeignKey(TipoExperiencia)
imagen = models.ImageField(upload_to= get_upload_path)
caption = models.CharField(max_length=150,blank=True)
alt = models.CharField(max_length=100)
This is a solution that I've found here
This actually works fine when updating objects, but when I try to insert new elements, the inserts fail because in that moment self does not exists.
I've tried another solution here whose proposal is overriding the ImageField method to customize upload_to.
The problem is that I use South and it's quite difficult to manage custom fields
I use Django 1.5. I would like to know if exists any easy way to manage dynamic file path in django
Thanks
Alternatively you can override the save method to move the file to the correct path.
class Model(models.Model):
def save(self, *args, **kwargs):
instance = super(Model, self).save(*args, **kwargs)
# your logic here to change the file location
return instance
I think you can get away here with Unipath.
Unipath usage

Accessing model from FileSystemStorage

I have custom FileSystemStorage.
The idea is to pass optional filename parameter.
My custome storge code:
class SalesMapFileStores(FileSystemStorage):
def __init__(self, location=None, base_url=None, filename=None):
if filename:
self.filename = filename
super(SalesMapFileStores, self).__init__(location=location, base_url=base_url)
def get_available_name(self, name):
return name
def get_valid_name(self, name):
if self.filename:
return self.filename
return name
def _save(self, name, content):
if self.exists(name):
self.delete(name)
return super(SalesMapFileStores, self)._save(name, conten
What i whant is to pass this filename parameter from model.
Somethin like this:
class SalesMapImage(models.Model):
name = models.CharField(max_length=254, verbose_name='Filename')
image = SalesMapImageField(upload_to='SalesMap/Test', storage=SalesMapFileStores(filename=name), verbose_name='Test Image',
content_types=('image/jpeg', 'image/png'))
But in this case, Django passes as a parameter itself model.CharField (it's obvious :)).
The question is: how can I get access to my model instance from Storage?
Thanks in advance!
Well, it is a bit crazy idea but you can try to override the assignment to that field in that class so the SalesMapFileStores instance always keep in sync with the name field like this:
class SalesMapImage(models.Model):
name = models.CharField(max_length=254, verbose_name='Filename')
image = SalesMapImageField(upload_to='SalesMap/Test', storage=SalesMapFileStores(), verbose_name='Test Image',
content_types=('image/jpeg', 'image/png'))
def __setattr__(self, key, value):
self.__dict__[key]=value
if key=='name':
self.image.storage.filename = value
And the general idea is to hook the assignment of the value to the update of the filename field.
This is supposing that you don't want to update it manually from within your view. Because it wouldn't take much effort to do model_instance.storage.filename = self.name or even add a method to your custom storage class to update the filename.
UPDATE: Heads up for the storage=SalesMapFileStores(). There you are passing a instance of SalesMapFileStores. Not the class, so it might be possible you'll be using the same instace for storing all files and this my bring conflicts with filename. You can try it like this: storage=SalesMapFileStores
Hope this helps!

Categories