Muestra las diferencias entre dos versiones de la página.
| Ambos lados, revisión anterior Revisión previa Próxima revisión | Revisión previa | ||
|
wiki2:python:django:others [2020/10/09 15:08] alfred [Upload files] |
wiki2:python:django:others [2020/10/31 20:16] (actual) |
||
|---|---|---|---|
| Línea 27: | Línea 27: | ||
| # Delete a session value | # Delete a session value | ||
| del request.session['my_car'] | del request.session['my_car'] | ||
| + | </code> | ||
| + | |||
| + | |||
| + | ===== Sending mails ===== | ||
| + | You can send mails with: | ||
| + | <code> | ||
| + | from django.core.mail import send_mail | ||
| + | send_mail('subject', 'message', 'Dont Reply <do_not_reply@domain.com>', ['youremail@example.com']) | ||
| </code> | </code> | ||
| Línea 81: | Línea 89: | ||
| When a user uploads a file, Django passes off the file data to an **upload handler** – a small class that handles file data as it gets uploaded. Upload handlers are initially defined in the ''FILE_UPLOAD_HANDLERS'' setting. | When a user uploads a file, Django passes off the file data to an **upload handler** – a small class that handles file data as it gets uploaded. Upload handlers are initially defined in the ''FILE_UPLOAD_HANDLERS'' setting. | ||
| - | {{https://docs.djangoproject.com/en/3.1/ref/files/uploads/#custom-upload-handlers|You can write custom handlers that customize how Django handles files}}. | + | |
| + | * ''DEFAULT_FILE_STORAGE'' | ||
| + | * ''FILE_UPLOAD_HANDLERS'': Which handlers will use Django when receiving a file. | ||
| + | * ''FILE_UPLOAD_MAX_MEMORY_SIZE'': When MemoryFileUploadHandler (default) it will upload to memory the file (if it less than this value). | ||
| + | * ''FILE_UPLOAD_PERMISSIONS'' | ||
| + | * ''FILE_UPLOAD_TEMP_DIR'': Directory to store files larger than ''FILE_UPLOAD_MAX_MEMORY_SIZE'' value. | ||
| + | * ''MEDIA_ROOT'': Absolute filesystem path to the directory that will hold user-uploaded files. | ||
| + | * ''MEDIA_URL'': URL that handles the media served from MEDIA_ROOT, used for managing stored files. It must end in a slash. | ||
| + | |||
| + | |||
| + | [[https://docs.djangoproject.com/en/3.1/ref/files/uploads/#custom-upload-handlers|You can write custom handlers that customize how Django handles files.]] | ||
| By default, if an uploaded file is smaller than 2.5 megabytes, Django will hold the entire contents of the upload in memory. This means that saving the file involves only a read from memory and a write to disk and thus is very fast. However, if an uploaded file is too large, Django will write the uploaded file to a temporary file stored in your system’s temporary directory. | By default, if an uploaded file is smaller than 2.5 megabytes, Django will hold the entire contents of the upload in memory. This means that saving the file involves only a read from memory and a write to disk and thus is very fast. However, if an uploaded file is too large, Django will write the uploaded file to a temporary file stored in your system’s temporary directory. | ||
| + | Sometimes particular views require **different upload behavior**. In these cases, you can override upload handlers on a per-request basis by modifying ''request.upload_handlers''. | ||
| + | By default Django stores files locally, using the MEDIA_ROOT and MEDIA_URL settings. | ||
| + | |||
| + | When you use a FileField or ImageField, Django provides a set of APIs you can use to deal with that file. | ||
| + | |||
| + | <code python> | ||
| + | class Car(models.Model): | ||
| + | name = models.CharField(max_length=255) | ||
| + | price = models.DecimalField(max_digits=5, decimal_places=2) | ||
| + | photo = models.ImageField(upload_to='cars') | ||
| + | |||
| + | car = Car.objects.get(name="57 Chevy") | ||
| + | car.photo | ||
| + | # <ImageFieldFile: cars/chevy.jpg> | ||
| + | car.photo.name | ||
| + | # 'cars/chevy.jpg' | ||
| + | car.photo.path | ||
| + | # '/media/cars/chevy.jpg' | ||
| + | car.photo.url | ||
| + | # 'http://media.example.com/cars/chevy.jpg' | ||
| + | </code> | ||
| + | |||
| + | You can change the path in this way: | ||
| + | <code python> | ||
| + | >>> import os | ||
| + | >>> from django.conf import settings | ||
| + | >>> initial_path = car.photo.path | ||
| + | >>> car.photo.name = 'cars/chevy_ii.jpg' | ||
| + | >>> new_path = settings.MEDIA_ROOT + car.photo.name | ||
| + | >>> # Move the file on the filesystem | ||
| + | >>> os.rename(initial_path, new_path) | ||
| + | >>> car.save() | ||
| + | >>> car.photo.path | ||
| + | '/media/cars/chevy_ii.jpg' | ||
| + | >>> car.photo.path == new_path | ||
| + | True | ||
| + | </code> | ||
| + | |||
| + | To manipulate images: | ||
| + | <code python> | ||
| + | >>> from PIL import Image | ||
| + | >>> car = Car.objects.get(name='57 Chevy') | ||
| + | >>> car.photo.width | ||
| + | 191 | ||
| + | >>> car.photo.height | ||
| + | 287 | ||
| + | >>> image = Image.open(car.photo) | ||
| + | # Raises ValueError: seek of closed file. | ||
| + | >>> car.photo.open() | ||
| + | <ImageFieldFile: cars/chevy.jpg> | ||
| + | >>> image = Image.open(car.photo) | ||
| + | >>> image | ||
| + | <PIL.JpegImagePlugin.JpegImageFile image mode=RGB size=191x287 at 0x7F99A94E9048> | ||
| + | </code> | ||
| + | |||
| + | Django delegates decisions about how and where to store files to a file **storage system**. This is the object that actually understands things like file systems, opening and reading files, etc. The default file storage is given by the ''DEFAULT_FILE_STORAGE'' setting. | ||
| + | |||
| + | You can write your [[https://docs.djangoproject.com/en/3.1/howto/custom-file-storage/|own file storage]]. | ||
| + | |||
| + | You can create an instance of some custom file storage class, or – often more useful – you can use the global default storage system: | ||
| + | <code> | ||
| + | >>> from django.core.files.base import ContentFile | ||
| + | >>> from django.core.files.storage import default_storage | ||
| + | >>> path = default_storage.save('path/to/file', ContentFile(b'new content')) | ||
| + | >>> path | ||
| + | 'path/to/file' | ||
| + | >>> default_storage.size(path) | ||
| + | 11 | ||
| + | >>> default_storage.open(path).read() | ||
| + | b'new content' | ||
| + | >>> default_storage.delete(path) | ||
| + | >>> default_storage.exists(path) | ||
| + | False | ||
| + | </code> | ||
| + | |||
| + | The following code will store uploaded files under /media/photos regardless of what your MEDIA_ROOT setting is: | ||
| + | <code python> | ||
| + | from django.core.files.storage import FileSystemStorage | ||
| + | from django.db import models | ||
| + | |||
| + | fs = FileSystemStorage(location='/media/photos') | ||
| + | |||
| + | class Car(models.Model): | ||
| + | ... | ||
| + | photo = models.ImageField(storage=fs) | ||
| + | </code> | ||
| + | |||
| + | You can use a callable as the storage parameter for FileField or ImageField. This allows you to modify the used storage at runtime, selecting different storages for different environments, for example: | ||
| + | <code python> | ||
| + | def select_storage(): | ||
| + | return MyLocalStorage() if settings.DEBUG else MyRemoteStorage() | ||
| + | |||
| + | class MyModel(models.Model): | ||
| + | my_file = models.FileField(storage=select_storage) | ||
| + | </code> | ||
| + | |||
| + | ==== For using S3 or an alterantive service ==== | ||
| + | |||
| + | You need the ''boto3'' and ''django-storages'' packages. | ||
| + | |||
| + | An example of settings would be... | ||
| + | <code> | ||
| + | AWS_ACCESS_KEY_ID = '' | ||
| + | AWS_SECRET_ACCESS_KEY = '' | ||
| + | AWS_STORAGE_BUCKET_NAME = 'codi.coop.test' | ||
| + | AWS_S3_CUSTOM_DOMAIN = f's3.wasabisys.com/{AWS_STORAGE_BUCKET_NAME}' | ||
| + | AWS_S3_ENDPOINT_URL = 'https://s3.wasabisys.com' | ||
| + | AWS_DEFAULT_ACL = 'public-read' | ||
| + | DEFAULT_FILE_STORAGE = 'cc_lib.storages.MediaStorage' | ||
| + | EXTERNAL_MEDIA_PATH = 'fok/media' | ||
| + | MEDIA_FILE_OVERWRITE = True | ||
| + | # AWS_S3_OBJECT_PARAMETERS = {'CacheControl': 'max-age=86400',} | ||
| + | </code> | ||
| + | |||
| + | Being the ''cc_lib.storages.MediaStorage'' the next: | ||
| + | <code python> | ||
| + | from storages.backends.s3boto3 import S3Boto3Storage | ||
| + | from django.conf import settings | ||
| + | |||
| + | |||
| + | class MediaStorage(S3Boto3Storage): | ||
| + | location = settings.EXTERNAL_MEDIA_PATH | ||
| + | file_overwrite = settings.MEDIA_FILE_OVERWRITE | ||
| + | </code> | ||
| + | |||
| + | With this you could create other storages like ''PrivateMediaStorage'' and use it like this: | ||
| + | <code python> | ||
| + | class Document(models.Model): | ||
| + | uploaded_at = models.DateTimeField(auto_now_add=True) | ||
| + | upload = models.FileField() | ||
| + | |||
| + | |||
| + | class PrivateDocument(models.Model): | ||
| + | uploaded_at = models.DateTimeField(auto_now_add=True) | ||
| + | upload = models.FileField(storage=PrivateMediaStorage()) | ||
| + | user = models.ForeignKey(User, related_name='documents') | ||
| + | </code> | ||
| + | |||
| + | ==== So... ==== | ||
| + | |||
| + | - Install boto3 and django-storages. | ||
| + | - Create your own MediaStorage. | ||
| + | - Add the settings. | ||
| + | - Configure models. | ||
| + | - Remind that forms in template must have the attribute ''enctype=“multipart/form-data”''. | ||
| + | |||
| + | |||
| + | ==== Notes ==== | ||
| + | <code python> | ||
| + | def upload_path(instance, filename): | ||
| + | if instance.type == Resource.ResourceType.PROFILE_PICTURE.value: | ||
| + | return f'authors/{instance.owner.uuid}/avatars/{instance.uuid}.png' | ||
| + | elif instance.type == Resource.ResourceType.ZINE_COVER.value: | ||
| + | return f'zines/{str(instance.related_to)}/cover.jpg' | ||
| + | </code> | ||