Django: Nahrávání souborů

Kromě textových dat můžeme poskytnout možnost uživatelům vkládat do aplikace binární data v podobě souborů. V dnešním pokračování seriálu o frameworku Django se zaměříme právě na tato data, konkrétně na obrázky a jejich náhledy.
Seriál: Hrajeme si s Djangem (16 dílů)
- Django: Úvod a instalace 14. 8. 2009
- Django: Nastavení projektu a první pokusy 21. 8. 2009
- Django: Databázový model 28. 8. 2009
- Django: Databázový model podruhé 4. 9. 2009
- Django: Administrace 11. 9. 2009
- Django: Prezentace dat 18. 9. 2009
- Django: Prezentace dat podruhé 25. 9. 2009
- Django: Zpracovávání formulářů 2. 10. 2009
- Django: Autentizace a autorizace 9. 10. 2009
- Django: Nahrávání souborů 16. 10. 2009
- Django: Zabudované aplikace 23. 10. 2009
- Django: Rozšiřování možností Djanga 30. 10. 2009
- Django: Internacionalizace 6. 11. 2009
- Django: Nasazování projektu 13. 11. 2009
- Django: Kešování a škálování 20. 11. 2009
- Django: Závěr 27. 11. 2009
Základním způsobem, jak v aplikaci napsané v Djangu ukládat soubory, je využít databázový typ FileField. Podle názvu by se dalo čekat, že se binární data ukládají přímo do databáze, to však bývá pomalé a není to příliš transparentní. Proto Django ukládá data do souboru v předem určeném adresáři a do databáze zapíše pouze cestu k tomuto souboru.
Rozšíříme si náš malý projekt o nahrávání obrázků k filmům registrovanými uživateli. Django má sice k tomuto účelu zabudovaný typ ImageField, ale ten neumí upravovat velikost obrázků a generovat náhledy. Mohli bychom si tuto funkcionalitu napsat sami (např. s využitím Python Imaging Library), ale není k tomu důvod, když můžeme použít existující řešení.
Náhledy obrázků pomocí sorl-thumbnail
Asi nejpoužívanější aplikace pro generování náhledů v Djangu má název sorl-thumbnail a najdeme ji na adrese http://code.google.com/p/sorl-thumbnail/. Z těchto stránek si stáhneme aktuální verzi instalačního archivu, který rozbalíme. Poté je potřeba se přihlásit jako uživatel root, přejít do nově vytvořeného adresáře a v něm napsat instalační příkaz python setup.py install
.
Po instalaci je potřeba tuto aplikaci začlenit do našeho projektu. K tomu stačí otevřít soubor settings.py
a připsat nový záznam do konfigurační konstanty INSTALLED_APPS
:
INSTALLED_APPS = ( # ... 'sorl.thumbnail', )
Od této chvíle budeme mít možnost v šablonách generovat náhledy. Nejprve si však vytvoříme vhodný databázový model pro připojování obrázků k záznamům o filmech. Do souboru video_store/models.py
vložíme tento kód:
from sorl.thumbnail.fields import ThumbnailField class FilmImage(models.Model): film = models.ForeignKey('Film', verbose_name='Film') image = ThumbnailField('Obrázek', upload_to='img/movies/', size=(800, 600)) def __unicode__(self): return self.image.name class Meta: verbose_name = 'obrázek' verbose_name_plural = 'obrázky'
Atribut image
zastupuje obrázek s náhledem. Parametr upload_to
specifikuje adresář pro ukládání souborů. Tento adresář je relativní k adresáři se statickými soubory (konstanty MEDIA_ROOT a MEDIA_URL). V dokumentaci se můžeme dočíst, že další parametr, size
, určuje nejvyšší dovolenou šířku a výšku nahraného obrázku. Jestliže je obrázek větší, automaticky se zmenší. Při zmenšování jsou proporce obrázku zachovány; nedojde tak k jeho deformaci. Metoda __unicode__
vrací název souboru obrázku včetně adresáře, v němž je uložený.
Pokud se nahrávaný soubor bude jmenovat stejně jako jiný již uložený soubor, nově nahraný soubor se automaticky přejmenuje (k jeho názvu se připojí podtržítko). V případech, kdy očekáváme, že k tomu bude docházet často, je dobrý nápad v parametru upload_to
uvést specifikaci času (např. 'img/movies/%Y/%m/'
), která vytvoří adresářovou strukturu podle aktuálního času.
Model je potřeba synchronizovat s databází (python manage.py syncdb
) a vytvořit formulář, pomocí něhož uživatelé budou nahrávat obrázky. Do souboru video_store/forms.py
napíšeme kód, který odvodí z modelu odpovídající formulář:
from models import FilmImage class ImageForm(forms.ModelForm): class Meta: model = FilmImage exclude = ('film')
Na posledním řádku je použit meta atribut exclude
, který vynechá z našeho modelu cizí klíč film
. Ten při nahrávání souboru nebude potřeba, stačí nám pouze atribut image
.
Trochu si to teď usnadníme a formulář pro nahrávání souborů zabudujeme do stránky zobrazující detail filmu. V souboru video_store/views.py
modifikujeme pohled film_detail
:
from forms import ImageForm from models import FORMAT_CHOICES, FilmImage from django.http import HttpResponseRedirect def film_detail(request, film_id): try: film = Film.objects.filter(id=film_id)[0] except IndexError: return HttpResponseRedirect('/nabidka/') film.format = FORMAT_CHOICES[film.format][1] if request.method == 'POST' and request.user.is_authenticated(): form = ImageForm(request.POST, request.FILES) if form.is_valid(): film_image = FilmImage(film=film) if request.FILES.has_key('image'): file = request.FILES['image'] film_image.image.save(file.name, file) else: form = ImageForm() images = FilmImage.objects.filter(film=film) return render_to_response('film_detail.html', {'film': film, 'images': images, 'form': form}, context_instance=RequestContext(request))
Nový kód se ve výpise nachází na řádcích 13-25, změněn byl rovněž kód na řádku 27. Jestliže přihlášený uživatel odeslal formulář (řádek 13), zpracujeme odeslaný obsah. O řádek dále si všimněte, že je potřeba formuláři poskytnout kromě parametru request.POST
i objekt request.FILES
, který reprezentuje přiložené soubory. Pokud je všechno v pořádku (řádek 16) a objekt request.FILES
opravdu obsahuje klíč image
(řádek 19), uložíme soubor do jeho umístění (řádek 21). Zároveň se uloží záznam do databáze. První argument ukládací metody je název souboru a druhý je samotný soubor. Na řádku 25 vybíráme z databáze všechny obrázky, které souvisejí s daným filmem.
Kromě pohledu musíme upravit i odpovídající šablonu (templates/film_detail.html
). Na druhý řádek tohoto souboru přípíšeme značku {% load thumbnail %}
, který načte aplikaci sorl-thumbnail, budeme tak moct generovat náhledy obrázků. Dále bychom měli v šabloně uvést kód pro zobrazení náhledů a formuláře pro nahrávání:
{% for image in images %} {% thumbnail image.image 100x75 as img %} <a href="{{ image.image.url }}"><img src="{{ img.absolute_url }}" alt="{{ film.name_czech }}" width="{{ img.width }}" height="{{ img.height }}"></a> {% endfor %} {% if user.is_authenticated %} <h2>Nahrát obrázek</h2> <form action="." method="POST" enctype="multipart/form-data"> <table> {% include "form.html" %} <tr> <td></td> <td><input type="submit" value="Nahrát"></td> </tr> </table> </form> {% endif %}
Na druhém řádku je použita značka pro generování náhledu obrázku. Ta obsahuje objekt obrázku, požadovanou velikost náhledu a v našem případě i proměnnou (img
), se kterou budeme dále pracovat. Kromě toho můžeme použít některý ze zabudovaných filtrů a generovat tak např. pomocí filtru bw
černobílé náhledy. Dále zobrazíme náhled obrázku včetně udání jeho šířky a výšky. Náhled je klikatelný, odkaz směřuje na obrázek ve větší velikosti.
Pokud je uživatel přihlášen, zobrazí se mu i formulář pro nahrávání obrázků. Kód tohoto formuláře se podobá formulářům z předchozích dílů, jenom HTML značka <form>
obsahuje navíc atribut enctype="multipart/form-data"
. Bez něj by nahrávání souborů nefungovalo.
Aplikace sorl-thumbnail podporuje tradiční obrázkové formáty (JPEG, PNG, GIF…) a po instalaci programu ImageMagick i některé méně tradiční (EPS, PSD, PDF…). Když se pokusíme nahrát nepodporovaný soubor, zobrazí se nám chybová hláška.
Zbývá dodat, že se náhledy kešují, takže jsou generovány pouze jednou, při prvním načtení stránky. Při odstranění záznamu z databáze pomocí metody delete
jsou smazány obrázky včetně náhledů.
Související odkazy
- Souborový objekt a nahrávání souborů na Djangoproject.com
- Projekt sorl-thumbnail a jeho dokumentace
- Ukázkový projekt ke stažení.
Příště se podíváme na některé zabudované aplikace v Djangu.
Diky za typ na sorl-thumbnail. Pouziti jednoduche a pritom spousta optionu.
Tohle je taky užitečné.