I am trying to read from Django Filefield, as can be seen in my Django Model:
import os
import win32api
from django.db import models
from custom.storage import AzureMediaStorage as AMS
class File(models.Model):
'''
File model
'''
file = models.FileField(blank=False, storage=AMS(), null=False)
timestamp = models.DateTimeField(auto_now_add=True)
remark = models.CharField(max_length=100, default="")
class File_Version(File):
"""
Model containing file version information
"""
version = models.CharField(max_length=25, default="")
#property
def get_version(self):
"""
Read all properties of the given file and return them as a dictionary
"""
props = {'FileVersion': None}
# To check if the file exists ?
### This returns FALSE
print("Is the file there? ", os.path.isfile(str(File.file)) )
# To get file version info
fixedInfo = win32api.GetFileVersionInfo(str(File.file), '\\')
print("FixedInfo: ", fixedInfo)
But os.path.isfile() keeps returning False. How do I read from FileField, into my custom model ?
And moreover, the line fixedInfo, gives me the error:
pywintypes.error: (2, 'GetFileVersionInfo:GetFileVersionInfoSize',
'The system cannot find the file specified.')
os.path.isfile returns whether the filepath is pointing to a file (as opposed to a directory, for instance). File.file is pointing to a models.FileField object; the current code will always return False. I suppose you would want File.file.path to get the actual absolute filepath for the file.
In your model definition you may add:
class File(models.Model):
file = models.FileField()
...
...
def filename(self):
return os.path.basename(self.file.name)
Or you may try:
from django.core.files.storage import default_storage
Use:
1) FieldFile.name:
The name of the file including the relative path from the root of the Storage of the associated FileField.
2) default_storage.exists(path)
Return True if a file referenced by the given name already exists in the
storage system, or False if the name is available for a new file.
Hope this works!
As you are using a different storage provider for your files, you need to use the methods of that storage provider to query the file object.
os.path.isfile and win32api.GetFileVersionInfo only work for the local file system.
Related
what else do I need to add to that "file = models.FileField()"
this is what I have done but am still not getting any results, why that?
class Course(models.Model):
TOPIC_CHOICES = (
("History", "History"),
("Chemistry", "Chemistry"),
("Computer", "Computer")
)
lecturer = models.ForeignKey(Lecturer, on_delete=models.CASCADE)
category = models.CharField(choices=TOPIC_CHOICES, max_length=100)
topic = models.CharField(max_length=250)
file = models.FileField()
date_created = models.DateTimeField(default=datetime.now)
def __str__(self):
return f"{self.lecturer}: {self.topic}"
According to Django documentation, FileField takes two optional arguments.
upload_to: Sets the upload directory. The value of this argument can have several types. It can be String, Path, or a callable function. Here is an example:
upload = models.FileField(upload_to='uploads/')
If you want to define a function for this argument which returns the upload directory, you have to define it based on Django's specification of such function. The function should have the instance and filename arguments. Here is an example:
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)
storage: A storage object, or a callable which returns a storage object. This argument is used to specify a storage setting for your file-upload field. This argument enables you to choose the appropriate storage environment at runtime.
from django.conf import settings
from django.db import models
from .storages import MyLocalStorage, MyRemoteStorage
def select_storage():
return MyLocalStorage() if settings.DEBUG else MyRemoteStorage()
class MyModel(models.Model):
my_file = models.FileField(storage=select_storage)
Another use-case of this argument is having different storage environments for different types of files.
from django.conf import settings
from django.db import models
from .storages import LargeFilesStorage
class MyModel(models.Model):
my_file = models.FileField(storage=LargeFilesStorage())
As these arguments are optional, you can instantiate a FileField without them. The default values for these arguments are: upload_to='', storage=None
I´m trying to upload file into database, but when i want put the function uoload to and i want to that function store the file in a root with the data the user submit in the for, for example year, course, section, I get that information and the file they uploaded I want to store in that subfolders, the code that I´m using only store the data in the Media_root, but not in the carpets how can I do in order to store the files in the subfolder I want. I'n my model document I store the information of the document, like the autor, the title,etc.
class File(models.Model):
titulo_file=models.ForeignKey(Document,on_delete=models.CASCADE,null=True,verbose_ name='Título de la Tesis')
file = models.FileField(null=True,blank=True, upload_to=generate_path)
Here the function I use to upload the file, Int only works to store the file into a folder with it´s same name.
def generate_path(instance, filename):
path =os.path.join(instance.file.name,filename)
return path
In my view, after the user submit the information, I use a os.path.join, that do the path with the information submited like this:
year/course/section
I want to send that path to my generate_path and store in that location the file, I tried with session but it doesn't work, what can I do?
models.py
class Year(models.Model):
year_number=models.CharField(max_length=10,primary_key=True)
class Course(models.Model):
name=models.CharField(max_length=50,primary_key=True)
class Section(models.Model):
numberSection=models.IntegerField(null=True,blank=True)
year = models.ForeignKey(Year, on_delete=models.CASCADE, null=True)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
thopic_documents=(
('CS','CS'),
('SE','SE'),
('IS','IS'),
('IT','IT'),
)
class Document(models.Model):
title=models.CharField(max_length=200,primary_key=True)
thopic=models.CharField(max_length=50,choices=thopic_documents, default=None,null=True)
class Historial(models.Model):
id_section=models.ForeignKey(Section,on_delete=models.CASCADE)
title_document=models.ForeignKey(Document,on_delete=models.CASCADE,)
To get the year/course/section you need to be able to navigate from a File to a Section. However, the many-many relationship that exists between Document and Section through Historical makes it unclear how you would do that. Which Historical would you choose?
It may be better if File was linked directly to Historical:
class File(models.Model):
titulo_file=models.ForeignKey(Historical, on_delete=models.CASCADE,null=True,verbose_name='Título de la Tesis')
file = models.FileField(null=True,blank=True, upload_to=generate_path)
If that was the case, you could then implement your generate_path() such as:
def generate_path(instance, filename):
section = instance.titulo_file.id_section
year = section.year.year_number
course = section.course.name
return os.path.join(str(year), course, str(section.numberSection), filename)
To do the same thing with the model as it currently stands, you would have to do something like this:
def generate_path(instance, filename):
section = instance.titulo_file.historical_set.first().id_section
year = section.year.year_number
course = section.course.name
return os.path.join(str(year), course, str(section.numberSection), filename)
That example uses historical_set.first() to get the first Historical linked the Document. Maybe that would be ok, but otherwise you'd need to know which Historical to use.
Where a file with the same name is uploaded and you don't want to overwrite it, you could implement your own storage:
from django.core.files.storage import FileSystemStorage
class UseExistingStorage(FileSystemStorage):
def save(self, name, content, max_length=None):
if not self.exists(name):
return super().save(name, content, max_length)
return name # Don't save when the file exists, just return the name
Then reference the UseExistingStorage from your File model.
file = models.FileField(null=True,blank=True, upload_to=generate_path, storage=UseExistingStorage())
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.
I have a Django app where users upload photos and descriptions. Here's a typical model which facilitates that user behavior:
class Photo(models.Model):
description = models.TextField(validators=[MaxLengthValidator(500)])
submitted_on = models.DateTimeField(auto_now_add=True)
image_file = models.ImageField(upload_to=upload_to_location, null=True, blank=True )
Notice the image_file attribute has upload_to argument, which is fed the upload directory and file name of the image_file. The upload_to_location method takes care of that; assume it works correctly.
Now I want to upload each image to Azure Cloud Storage. The python snippet to do that is explained here. Using that, I tried to write my own custom storage that saves images to Azure. It's buggy though, and I need help in cleaning it up. Here's what I've done:
Changed the image_file attribute in models.py to:
image_file = models.ImageField("Tasveer dalo:",upload_to=upload_to_location, storage=OverwriteStorage(), null=True, blank=True )
And then created a separate storage.py in my app folder that has:
from django.conf import settings
from django.core.files.storage import Storage
from azure.storage.blob import BlobService
class OverwriteStorage(Storage):
def __init__(self,option=None):
if not option:
pass
def _save(name,content):
blob_service = BlobService(account_name='accname', account_key='key')
PROJECT_ROOT = path.dirname(path.abspath(path.dirname(__file__)))
try:
blob_service.put_block_blob_from_path(
'containername',
name,
path.join(path.join(PROJECT_ROOT,'uploads'),name),
x_ms_blob_content_type='image/jpg'
)
return name
except:
print(sys.exc_info()[1])
return 0
def get_available_name(self,name):
return name
This set up doesn't work, and returns the error: _save() takes exactly 2 arguments (3 given). Exception Location: /home/hassan/.virtualenvs/redditpk/local/lib/python2.7/site-packages/django/core/files/storage.py in save, line 48
How do I make this work? Has anyone used Azure-Storage python SDK with their Django projects in this way? Please advise.
Note: Originally, I was using the django-storages library, which obfuscated storage details from me, reducing everything to just some configuration to be entered in settings.py. But now, I need to remove django-storages from the equation, and solely use the Azure-Storage python SDK for the purpose.
Note: Ask for more information in case you need it
According your error message, you missed parameter in function _save() which should be complete in the format like _save(self,name,content).
And additionally, it seems that you want put the images directly to Azure Storage which are uploaded from client forms. If so, I found a repo in github which builds a custom azure storage class for Django models. We can get leverage it to modify your application. For more details, refer to https://github.com/Rediker-Software/django-azure-storage/blob/master/azure_storage/storage.py
And here are my code snippets,
models.py:
from django.db import models
from django.conf import settings
from django.core.files.storage import Storage
from azure.storage.blob import BlobService
accountName = 'accountName'
accountKey = 'accountKey'
class OverwriteStorage(Storage):
def __init__(self,option=None):
if not option:
pass
def _save(self,name,content):
blob_service = BlobService(account_name=accountName, account_key=accountKey)
import mimetypes
content.open()
content_type = None
if hasattr(content.file, 'content_type'):
content_type = content.file.content_type
else:
content_type = mimetypes.guess_type(name)[0]
content_str = content.read()
blob_service.put_blob(
'mycontainer',
name,
content_str,
x_ms_blob_type='BlockBlob',
x_ms_blob_content_type=content_type
)
content.close()
return name
def get_available_name(self,name):
return name
def upload_path(instance, filename):
return 'uploads-from-custom-storage-{}'.format(filename)
class Photo(models.Model):
image_file = models.ImageField(upload_to=upload_path, storage=OverwriteStorage(), null=True, blank=True )
I have some images inside the static directory and want to create a model that has a ImageField. I want to set the default field to any one of those images. I have tried using this -
def randomImage():
return ImageFile('media/blog/image/' + str(random.randrange(1, 15, 1)) + '.jpg')
# ----------------------- Model for each post in the blog-------------------
class Post(models.Model):
heading = models.CharField(max_length=150)
author = models.ForeignKey(User)
postBody = models.TextField()
postDate = models.DateTimeField('posting date')
postImage = models.ImageField(upload_to='media/blog/image/'+str(int(time.time())), default=randomImage)
Here I'm taking some assumptions,
1. Your default images are inside the static directory
2. Inside the static directory, all files are images
What is the major trick here ?
Django needs a valid file path only to create or update a file entry (image is also a file). So, what we're doing here is, list out all the files (assuming those are only images) and picking up one entry randomly using random.choice() and retun an absoulute path (something like static/my_img.jpg) to the default argument
import time
from random import choice
from os.path import join as path_join
from os import listdir
from os.path import isfile
def random_img():
dir_path = 'static'
files = [content for content in listdir(dir_path) if isfile(path_join(dir_path, content))]
return path_join(dir_path, choice(files))
class Post(models.Model):
# other fields
postImage = models.ImageField(
upload_to='media/blog/image/' + str(int(time.time())),
default=random_img)
UPDATE-1
I've created a minimal example in Django 2.1.1 which can be found in follwing git repo
Repopsitory link -> django2X
1. clone the repository,create a virtual env and install the dependencies (provided a requirements.txt file)
2.create a new superuser or use mine (username -> admin, pass-> jerin123#)
3.run the server and login to django admin : (screenshot-1)
4. Create a new Post instance (again and again)
That's it
UPDATE-2
I've made few changes in my minimal example, which are
1. Created a simple object creation (here, the Post model object) while every django startup (checkout sample/app.py)
2. Added MEDIA_ROOT and MEDIA_URL to the settings.py file
Now, start your project (it will create 3 objects per every startup) and go to http://127.0.0.1:8000/admin/sample/post/. Then open any instance and click on the image link (screenshot-2) and it will open the image (screenshot-3)
My solution is to override the model save method and check if the model is being created for the first time and also check if the postImage image field is empty. If so populate the postImage field with contents of a Radom image. Django will handle the rest
If we use the path of the Radom image directly in our model we will end up like serving some of the post model files from the media folder and some other from the static directory which is not recommended. Instead, we feed the image file content to the postImage field and Django will save the image to media folder and thus we can serve all our model images from media folder itself. Wola
Code
The following code is tested in Python 3.6
Please add the code to your models.py
from pathlib import Path
from random import randint
import time
from django.core.files import File
from django.db import models
allowed_image_extensions = ['.jpg', '.png', '.jpeg']
def get_file_extension(file_path):
return Path(file_path).suffix
def get_files_in_directory(directory, absolute_path=False):
from os import listdir
from os.path import isfile
only_files = [f for f in listdir(directory) if isfile("{}/{}".format(directory, f))]
if not absolute_path:
return only_files
else:
return ["{}/{}".format(directory, file_) for file_ in only_files]
def get_random_image_from_directory(directory_path, image_extension=None):
files_in_directory_path = get_files_in_directory(directory_path, absolute_path=True)
if image_extension:
allowed_file_types = [image_extension]
else:
allowed_file_types = allowed_image_extensions
# filter out files of type other than required file types
filtered_files_list = [_file for _file in files_in_directory_path if
get_file_extension(_file).lower() in allowed_file_types]
random_index = randint(0, len(filtered_files_list) - 1)
random_file_path = filtered_files_list[random_index]
random_file_content = File(open(random_file_path, 'rb'))
return random_file_content
def get_post_image_path(instance, filename):
path_first_component = 'posts'
ext = get_file_extension(filename)
current_time_stamp = int(time.time())
file_name = '{}/posts_{}_{}{}'.format(path_first_component, instance.id, current_time_stamp, ext)
full_path = path_first_component + file_name
return full_path
class Post(models.Model):
heading = models.CharField(max_length=150)
author = models.ForeignKey(User)
postBody = models.TextField()
postDate = models.DateTimeField('posting date')
postImage = models.ImageField(blank=True, null=True, upload_to=get_post_image_path)
# override model save method
def save(self, *args, **kwargs):
# check model is new and postImage is empty
if self.pk is None and not self.postImage:
random_image = get_random_image_from_directory(settings.STATIC_ROOT)
self.postImage = random_image
random_image.close()
super(Post, self).save(*args, **kwargs)
Also no need to set ‘/media’ in upload_to path. Django will read media path from settings variable
The best practice is to move those set of default images out of static directory to another folder probably another folder with name resources or any another meaningful name since the static directory contents will change frequently as the project grows