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ů)

  1. Django: Úvod a instalace 14.8.2009
  2. Django: Nastavení projektu a první pokusy 21.8.2009
  3. Django: Databázový model 28.8.2009
  4. Django: Databázový model podruhé 4.9.2009
  5. Django: Administrace 11.9.2009
  6. Django: Prezentace dat 18.9.2009
  7. Django: Prezentace dat podruhé 25.9.2009
  8. Django: Zpracovávání formulářů 2.10.2009
  9. Django: Autentizace a autorizace 9.10.2009
  10. Django: Nahrávání souborů 16.10.2009
  11. Django: Zabudované aplikace 23.10.2009
  12. Django: Rozšiřování možností Djanga 30.10.2009
  13. Django: Internacionalizace 6.11.2009
  14. Django: Nasazování projektu 13.11.2009
  15. Django: Kešování a škálování 20.11.2009
  16. 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.

Django 10

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.

Django 10

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ů.

Django 10

Související odkazy

Příště se podíváme na některé zabudované aplikace v Djangu.

Autor je dlouhodobým studentem Fakulty informatiky, webový nadšenec a programátor — nejraději programuje v jazycích Haskell a Python.

Věděli jste, že nám můžete zasílat zprávičky? (Jen pro přihlášené.)

Komentáře: 2

Přehled komentářů

limu Prima plugin
PMD photologue
Zdroj: https://www.zdrojak.cz/?p=3102