Hantera filer¶
Detta dokument beskriver Djangos API:er för filåtkomst för filer som till exempel de som laddas upp av en användare. API:erna på lägre nivå är tillräckligt generella för att du ska kunna använda dem för andra ändamål. Om du vill hantera ”statiska filer” (JS, CSS, etc.), se Hur man hanterar statiska filer (t.ex. bilder, JavaScript, CSS).
Som standard lagrar Django filer lokalt med hjälp av inställningarna MEDIA_ROOT
och MEDIA_URL
. Exemplen nedan förutsätter att du använder dessa standardinställningar.
Django tillhandahåller dock sätt att skriva anpassade ”fillagringssystem” som gör att du helt kan anpassa var och hur Django lagrar filer. I andra halvan av detta dokument beskrivs hur dessa lagringssystem fungerar.
Använda filer i modeller¶
När du använder en FileField
eller ImageField
tillhandahåller Django en uppsättning API:er som du kan använda för att hantera den filen.
Tänk på följande modell, som använder en ImageField
för att lagra ett foto:
from django.db import models
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")
specs = models.FileField(upload_to="specs")
Varje Car
-instans har ett photo
-attribut som du kan använda för att få fram detaljerna i det bifogade fotot:
>>> 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
'https://media.example.com/cars/chevy.jpg'
Detta objekt - car.photo
i exemplet - är ett File
-objekt, vilket innebär att det har alla de metoder och attribut som beskrivs nedan.
Observera
Filen sparas som en del av att modellen sparas i databasen, så det faktiska filnamnet som används på disken kan man inte lita på förrän efter att modellen har sparats.
Du kan till exempel ändra filnamnet genom att ställa in filens name
till en sökväg i förhållande till fillagringsplatsen (MEDIA_ROOT
om du använder standard FileSystemStorage
):
>>> import os
>>> from django.conf import settings
>>> initial_path = car.photo.path
>>> car.photo.name = "cars/chevy_ii.jpg"
>>> new_path = os.path.join(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
Så här sparar du en befintlig fil på disken till en FileField
:
>>> from pathlib import Path
>>> from django.core.files import File
>>> path = Path("/some/external/specs.pdf")
>>> car = Car.objects.get(name="57 Chevy")
>>> with path.open(mode="rb") as f:
... car.specs = File(f, name=path.name)
... car.save()
...
Observera
Medan ImageField
icke-bilddataattribut, såsom höjd
, bredd
och storlek
är tillgängliga på instansen, kan de underliggande bilddata inte användas utan att öppna bilden igen. Exempel på detta:
>>> 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>
Objektet ”Fil¶
Internt använder Django en django.core.files.File
-instans varje gång den behöver representera en fil.
För det mesta använder du en Fil
som Django har gett dig (dvs. en fil som är kopplad till en modell enligt ovan, eller kanske en uppladdad fil).
Om du behöver konstruera en File
själv är det enklaste sättet att skapa en med hjälp av ett Python-inbyggt file
-objekt:
>>> from django.core.files import File
# Create a Python file object using open()
>>> f = open("/path/to/hello.world", "w")
>>> myfile = File(f)
Nu kan du använda alla dokumenterade attribut och metoder i File
-klassen.
Tänk på att filer som skapas på detta sätt inte stängs automatiskt. Följande metod kan användas för att stänga filer automatiskt:
>>> from django.core.files import File
# Create a Python file object using open() and the with statement
>>> with open("/path/to/hello.world", "w") as f:
... myfile = File(f)
... myfile.write("Hello World")
...
>>> myfile.closed
True
>>> f.closed
True
Att stänga filer är särskilt viktigt när man använder filfält i en loop över ett stort antal objekt. Om filer inte stängs manuellt efter åtkomst kan det uppstå risk för att filbeskrivare tar slut. Detta kan leda till följande fel:
OSError: [Errno 24] Too many open files
Lagring av filer¶
Bakom kulisserna delegerar Django beslut om hur och var filer ska lagras till ett fillagringssystem. Detta är det objekt som faktiskt förstår saker som filsystem, öppning och läsning av filer etc.
Djangos standard fillagring är '
django.core.files.storage.FileSystemStorage
'
. Om du inte uttryckligen anger ett lagringssystem i default
-nyckeln i inställningen STORAGES
, är det detta som kommer att användas.
Se nedan för mer information om det inbyggda standard fillagringssystemet, och se Så här skriver du en anpassad lagringsklass för information om hur du skriver ditt eget fillagringssystem.
Förvaringsobjekt¶
Även om du för det mesta vill använda ett File
-objekt (som delegerar till rätt lagring för filen) kan du använda fillagringssystem direkt. Du kan skapa en instans av någon anpassad fillagringsklass, eller - ofta mer användbart - du kan använda det globala standardlagringssystemet:
>>> 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
Se API för fillagring för API:et för fillagring.
Den inbyggda lagringsklassen för filsystem¶
Django levereras med en django.core.files.storage.FileSystemStorage
-klass som implementerar grundläggande lokal filsystemlagring.
Till exempel kommer följande kod att lagra uppladdade filer under /media/photos
oavsett vad din MEDIA_ROOT
-inställning är:
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)
Custom storage systems fungerar på samma sätt: du kan skicka in dem som argumentet storage
till en FileField
.
Använda en callable¶
Du kan använda en callable som storage
parameter för FileField
eller ImageField
. Detta gör att du kan ändra den använda lagringen vid körning, till exempel välja olika lagring för olika miljöer.
Din callable kommer att utvärderas när dina modellklasser laddas, och måste returnera en instans av Storage
.
Till exempel:
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)
För att ställa in en lagring som definieras i inställningen STORAGES
kan du använda storages
:
from django.core.files.storage import storages
def select_storage():
return storages["mystorage"]
class MyModel(models.Model):
upload = models.FileField(storage=select_storage)
Eftersom den anropsbara utvärderas när dina modellklasser laddas, om du behöver åsidosätta inställningen STORAGES
i tester, bör du använda en LazyObject
-underklass istället:
from django.core.files.storage import storages
from django.utils.functional import LazyObject
class OtherStorage(LazyObject):
def _setup(self):
self._wrapped = storages["mystorage"]
my_storage = OtherStorage()
class MyModel(models.Model):
upload = models.FileField(storage=my_storage)
LazyObject
fördröjer utvärderingen av lagringen tills den faktiskt behövs, vilket gör att override_settings()
kan träda i kraft:
@override_settings(
STORAGES={
"mystorage": {
"BACKEND": "django.core.files.storage.InMemoryStorage",
}
}
)
def test_storage():
model = MyModel()
assert isinstance(model.upload.storage, InMemoryStorage)