Django FileField with upload_to determined at runtime
You've probably read the documentation, so here's an easy example to make it make sense:
def content_file_name(instance, filename):
return '/'.join(['content', instance.user.username, filename])
class Content(models.Model):
name = models.CharField(max_length=200)
user = models.ForeignKey(User)
file = models.FileField(upload_to=content_file_name)
As you can see, you don't even need to use the filename given - you could override that in your upload_to callable too if you liked.
Access upload_to of a Model's FileFIeld in Django?
You can access this with:
Video.file.field.upload_to # 'videos/'
or through the _meta
object:
Video._meta.get_field('file').upload_to # 'videos/'
The upload_to=…
parameter [Django-doc] can however also be given a function that takes two parameters, and thus in that case it will not return a string, but a reference to that function.
Django model with FileField -- dynamic 'upload_to' argument
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)
Does the 'upload_to' callback in the FileField hit the db for related content?
No, it will not hit the DB, because you're already passing existing and saved(!) Owner
and Chat
objects to Attachment.objects.create
, so the upload handler does not need to fetch them from the database.
However, extra queries for related objects will be made when you get the attachment fresh from the database:
attachment = Attachment.objects.get(id=some_id)
attachment.file.save(...)
In this case using select_related
can remove the extra queries.
You can always verify the SQL queries that are run (only in DEBUG
mode):
from django.db import connection
def get_attachment_path(instance, filename):
print('Queries before', connection.queries)
path = f'{instance.owner.name}/{instance.chat.id}/{filename}'
print('Queries after', connection.queries)
return path
Alternatively, Django Debug Toolbar is a great addition to a Django project to optimize SQL queries.
How to get the fieldname of a FileField in the upload_to method, for a field translated with modeltranslation in Django?
Something like this should work.
from modeltranslation.translator import translator
from django.db.models import FileField
import os
class TranslationMeta(type):
def __init__(self, name, bases, attrs):
for attrname, attrvalue in attrs.items():
if self.is_translated_field(name, attrname):
field = attrvalue
if isinstance(field, FileField):
self.update_upload_to(field, attrname)
super().__init__(name, bases, attrs)
def is_translated_field(self, class_name, attr_name):
opts = translator.get_options_for_model(self)
return attr_name in opts.get_field_names()
def update_upload_to(self, field, attr_name):
opts = translator.get_options_for_model(self)
translated_fields = opts.fields[attr_name]
for trans_field in translated_fields:
# print(trans_field.name)
# print(trans_field.language)
trans_field.upload_to = self.custom_upload_to(field.upload_to, trans_field.language)
def custom_upload_to(self, base_upload_to, language):
# If the original upload_to parameter is a callable,
# return a function that calls the original upload_to
# function and inserts the language as the final folder
# in the path
# If the original upload_to function returned /path/to/file.png,
# then the final path will be /path/to/en/file.png for the
# english field
if callable(base_upload_to):
def upload_to(instance, filename):
path = base_upload_to(instance, filename)
return os.path.join(
os.path.dirname(path),
language,
os.path.basename(path))
return upload_to
# If the original upload_to parameter is a path as a string,
# insert the language as the final folder in the path
# /path/to/file.png becomes /path/to/en/file.png for the
# english field
else:
return os.path.join(
os.path.dirname(base_upload_to),
language,
os.path.basename(base_upload_to))
# This is how you would use this class
class MyModel(models.Model, metaclass=TranslationMeta):
field = FileField()
m = MyModel(models.Model)
print(m.field.upload_to)
It uses introspection to dynamically override the upload_to
parameter of every language-specific FileField generated by django-modeltranslation behind the scenes.
With this model for instance:
class MyModel(models.Model):
field = FileField(upload_to=...)
if you have defined field
as a translatable field by adding
from modeltranslation.translator import register, TranslationOptions
from . import models
@register(models.MyModel)
class MyModelTranslationOptions(TranslationOptions):
fields = ("field",)
in translation.py
, django-modeltranslation will generate something like
class MyModel(models.Model):
field = FileField(upload_to=...)
field_en = FileField(upload_to=...)
field_fr = FileField(upload_to=...)
if you have en
and fr
defined in your LANGUAGES
settings.
If the upload_to
parameter passed to the FileField was a path as a string, it is overridden with the same path in which a folder for the language is inserted.
If it is a function, then the folder for the language is inserted in the path returned by this function.
For instance if you have
class MyModel(models.Model):
field = FileField(upload_to="/path/to/file.png")
or
def get_upload_path(instance, filename):
return "path/to/file.png"
class MyModel(models.Model):
field = FileField(upload_to=get_upload_path)
then, in both cases:
- the English version of the file will be stored under /path/to/en/file.png
- the French version of the file will be stored under /path/to/fr/file.png
Related Topics
Datetime Dtypes in Pandas Read_Csv
Tensorflow Only Running on 1/32 of the Training Data Provided
Pandas - Slice Large Dataframe into Chunks
How to Debug in Django, the Good Way
How to Shift a Column in Pandas Dataframe
Cartesian Product of Two Lists in Python
Pandas: Setting No. of Max Rows
Remove and Replace Printed Items
How to Find the Last Occurrence of an Item in a Python List
How to Scroll the Background Surface in Pygame
Function for Factorial in Python
How to Print to Stderr in Python
"Cannot Open Include File: 'Config-Win.H': No Such File or Directory" While Installing MySQL-Python