====== Other things for Django ======
===== Messages =====
* [[https://simpleisbetterthancomplex.com/tips/2016/09/06/django-tip-14-messages-framework.html|Messages framework]]
===== Sessions =====
Add this to the config:
INSTALLED_APPS = [
...
'django.contrib.sessions',
....
MIDDLEWARE = [
...
'django.contrib.sessions.middleware.SessionMiddleware',
....
To use them:
# Get a session value by its key (e.g. 'my_car'), raising a KeyError if the key is not present
my_car = request.session['my_car']
# Get a session value, setting a default if it is not present ('mini')
my_car = request.session.get('my_car', 'mini')
# Set a session value
request.session['my_car'] = 'mini'
# Delete a session value
del request.session['my_car']
===== Sending mails =====
You can send mails with:
from django.core.mail import send_mail
send_mail('subject', 'message', 'Dont Reply ', ['youremail@example.com'])
===== Load data y dump data from DB =====
* https://docs.djangoproject.com/en/2.1/howto/initial-data/
===== Upload files =====
You will start having a form:
from django import forms
class UploadFileForm(forms.Form):
title = forms.CharField(max_length=50)
file = forms.FileField()
A view handling this form will receive the file data in ''request.FILES'', which is a dictionary containing a key for each FileField (or ImageField, or other FileField subclass) in the form. So the data from the above form would be accessible as request.FILES['file'].
The **form that posted the request has the attribute** ''enctype="multipart/form-data"''. Otherwise, request.FILES will be empty.
For managing this in a view:
def upload_file(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
handle_uploaded_file(request.FILES['file'])
return HttpResponseRedirect('/success/url/')
else:
form = UploadFileForm()
return render(request, 'upload.html', {'form': form})
def handle_uploaded_file(f):
with open('some/file/name.txt', 'wb+') as destination:
for chunk in f.chunks(): # Looping over UploadedFile.chunks() instead of using read() ensures that large files don’t overwhelm your system’s memory.
destination.write(chunk)
If you’re saving a file on **a Model with a FileField**, using a ModelForm makes this process much easier. The file object will be saved to the location specified by the upload_to argument of the corresponding FileField when calling form.save()
If you are **constructing an object manually**, you can assign the file object from request.FILES to the file field in the model:
def upload_file(request):
if request.method == 'POST':
form = UploadFileForm(request.POST, request.FILES)
if form.is_valid():
instance = ModelWithFileField(file_field=request.FILES['file'])
instance.save()
return HttpResponseRedirect('/success/url/')
else:
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.
* ''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.
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.
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
#
car.photo.name
# 'cars/chevy.jpg'
car.photo.path
# '/media/cars/chevy.jpg'
car.photo.url
# 'http://media.example.com/cars/chevy.jpg'
You can change the path in this way:
>>> 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
To manipulate images:
>>> 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()
>>> image = Image.open(car.photo)
>>> image
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:
>>> 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
The following code will store uploaded files under /media/photos regardless of what your MEDIA_ROOT setting is:
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)
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:
def select_storage():
return MyLocalStorage() if settings.DEBUG else MyRemoteStorage()
class MyModel(models.Model):
my_file = models.FileField(storage=select_storage)
==== For using S3 or an alterantive service ====
You need the ''boto3'' and ''django-storages'' packages.
An example of settings would be...
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',}
Being the ''cc_lib.storages.MediaStorage'' the next:
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
With this you could create other storages like ''PrivateMediaStorage'' and use it like this:
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')
==== 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 ====
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'