Django'da Dosya ve Resim Yükleme (File Upload)

Web uygulamaları geliştirirken kullanıcıların sisteme profil fotoğrafı, belge veya ürün/kurs görselleri yüklemesi çok sık karşılaştığımız bir senaryodur. Django, dosya yükleme (file upload) işlemlerini yönetmek için son derece güçlü ve güvenli araçlar sunar.

Basit bir HTML formundan başlayarak, Django form sınıflarına ve veritabanı modellerine kadar adım adım dosya ve resim yükleme işlemlerini inceleyeceğiz.


Ön Hazırlık: Medya Ayarları (Çok Önemli!)

Dosya yükleme işlemlerine geçmeden önce, Django'nun yüklenen dosyaları nereye kaydedeceğini ve bu dosyalara tarayıcıdan nasıl erişileceğini bilmesi gerekir.

settings.py dosyanızın en altına şu iki ayarı ekleyin:

Python:
import os

# Tarayıcıdan dosyaya erişmek için kullanılacak URL öneki
MEDIA_URL = '/media/' 

# Dosyaların sunucuda fiziksel olarak kaydedileceği klasör
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

urls.py (Ana proje klasöründeki) dosyanıza, geliştirme aşamasında medya dosyalarına erişebilmek için şu eklemeyi yapın:

Python:
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    # ... mevcut url'leriniz ...
]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

(Not: Django'da resim dosyalarıyla (ImageField) çalışabilmek için terminalden pip install Pillow komutu ile Pillow kütüphanesini kurmayı unutmayın.)


Image Upload ve Dosyanın Kayıt Edilmesi (Temel Yöntem)

En temel seviyede, bir HTML formu kullanarak dosya yükleyebilir ve bunu Django'nun dahili FileSystemStorage sınıfı ile kaydedebiliriz.

HTML Formu (upload.html):

Dosya yüklerken form etiketinde enctype="multipart/form-data" kullanmak zorunludur. Aksi halde dosya sunucuya iletilmez, sadece dosyanın adı metin olarak gider.

HTML:
<form method="POST" enctype="multipart/form-data">
    {% csrf_token %}
    <input type="file" name="document">
    <button type="submit">Yükle</button>
</form>

View (views.py):

Python:
from django.shortcuts import render
from django.core.files.storage import FileSystemStorage

def upload_image(request):
    if request.method == 'POST' and request.FILES.get('document'):
        uploaded_file = request.FILES['document'] # HTML'deki name niteliği
        
        # Dosyayı kaydetme işlemi
        fs = FileSystemStorage()
        name = fs.save(uploaded_file.name, uploaded_file)
        url = fs.url(name)
        
        return render(request, 'upload.html', {'uploaded_file_url': url})
    
    return render(request, 'upload.html')

Çoklu Dosya Yükleme (Multiple File Upload)

Kullanıcıdan aynı anda birden fazla dosya almak isterseniz, HTML tarafında multiple özelliğini kullanmalı ve View tarafında dosyaları bir döngü ile yakalamalısınız.

HTML:

HTML:
<input type="file" name="documents" multiple>

View (views.py):

Python:
def upload_multiple(request):
    if request.method == 'POST':
        files = request.FILES.getlist('documents') # getlist() kullanımı kritik!
        fs = FileSystemStorage()
        
        for f in files:
            fs.save(f.name, f)
            
        return render(request, 'success.html')
    return render(request, 'upload_multiple.html')

Form Class ile Upload

Manuel işlemler yerine Django'nun kendi Form yapılarını kullanmak, güvenlik ve doğrulama (validation) açısından her zaman daha iyidir.

forms.py:

Python:
from django import forms

class UploadForm(forms.Form):
    title = forms.CharField(max_length=50, label="Başlık")
    image = forms.ImageField(label="Resim Seçiniz")

View (views.py):

Python:
def form_upload(request):
    if request.method == 'POST':
        # Dosyalar her zaman request.FILES içinde gelir!
        form = UploadForm(request.POST, request.FILES) 
        if form.is_valid():
            # Dosyayı işleme/kaydetme adımları...
            image = form.cleaned_data['image']
            # ...
            return render(request, 'success.html')
    else:
        form = UploadForm()
    return render(request, 'form_upload.html', {'form': form})

Model Class ile Upload (En İyi Yöntem)

Gerçek dünya projelerinde yüklenen dosyalar genellikle veritabanındaki bir tabloyla (Model) ilişkilendirilir.

models.py:

Python:
from django.db import models

class Document(models.Model):
    title = models.CharField(max_length=100)
    # upload_to parametresi, dosyanın media/ klasörü altında nereye kaydedileceğini belirler.
    uploaded_file = models.FileField(upload_to='documents/') 
    uploaded_at = models.DateTimeField(auto_now_add=True)

Bu modeli bir ModelForm ile birleştirdiğimizde, form doğrulandığı an dosya otomatik olarak media/documents/ klasörüne kaydedilir ve veritabanına yolu (path) yazılır. Manuel kayıt işlemine gerek kalmaz.


Kurs Resminin Yüklenmesi (Örnek Senaryo)

Diyelim ki bir eğitim platformu tasarlıyoruz ve her kursun bir kapak resmi olması gerekiyor. Modeli ve formu buna göre kurgulayalım.

models.py:

Python:
class Course(models.Model):
    name = models.CharField(max_length=200, verbose_name="Kurs Adı")
    # %Y/%m/%d ile resimler yıl/ay/gün klasörlerine düzenli şekilde kaydedilir
    image = models.ImageField(upload_to='course_images/%Y/%m/', verbose_name="Kurs Resmi") 

forms.py:

Python:
from django import forms
from .models import Course

class CourseForm(forms.ModelForm):
    class Meta:
        model = Course
        fields = ['name', 'image']

View (views.py):

Python:
def add_course(request):
    if request.method == 'POST':
        form = CourseForm(request.POST, request.FILES)
        if form.is_valid():
            form.save() # Hem veritabanı kaydı atılır hem de resim sunucuya kaydedilir!
            return render(request, 'success.html')
    else:
        form = CourseForm()
    return render(request, 'add_course.html', {'form': form})

Resimlerin Gösterilmesi

Veritabanına kaydettiğimiz bir kursun resmini HTML şablonunda (Template) göstermek oldukça basittir. Veritabanında resmin kendisi değil, adresi tutulur. Bu adrese .url özelliği ile erişiriz.

Template (courses.html):

HTML:
<h2>Mevcut Kurslar</h2>
<div class="course-list">
    {% for course in courses %}
        <div class="course-card">
            <h3>{{ course.name }}</h3>
            
            {% if course.image %}
                <img src="{{ course.image.url }}" alt="{{ course.name }}" style="max-width: 300px;">
            {% else %}
                <p>Bu kurs için resim yüklenmemiş.</p>
            {% endif %}
            
        </div>
    {% endfor %}
</div>

Önemli Hatırlatma: {{ course.image.url }} yapısını kullanabilmeniz için makalenin en başında anlattığımız MEDIA_URL ve MEDIA_ROOT ayarlarının eksiksiz yapılmış olması şarttır!



🚀 BONUS – Profesyonel İpuçları

🔒 Dosya Türü Kontrolü

def clean_image(self):
image = self.cleaned_data.get('image')
if not image.name.endswith('.jpg'):
raise forms.ValidationError("Sadece JPG yükleyebilirsin!")
return image

📏 Dosya Boyutu Limiti

if file.size > 2*1024*1024:
raise ValidationError("Max 2MB")

Best Practices

✔ Dosyaları UUID ile kaydet
✔ Cloud storage kullan (AWS S3)
✔ CDN kullan
✔ Kullanıcıya progress bar göster


🎯 ÖZET

KonuAçıklama
ImageField        Resim yükleme
FileField        Genel dosya
request.FILES        Dosyaya erişim
MEDIA_ROOT        Fiziksel klasör
MEDIA_URL        Tarayıcı yolu
multipart/form-data        Upload için zorunlu

Teorik bilgileri pekiştirmek için bütün bu anlattıklarımızı bir araya getiren, laboratuvar ortamında  baştan sona çalışan bir Ders Materyali Paylaşım uygulaması

Bu örnek uygulamada, hem bir kapak resmi (ImageField) hem de bir belge (FileField) yüklenebilen bir yapı kuracağız.

Uygulamamızın adının materyal_app olduğunu varsayarak adımlara geçelim:

1. Ayarlar (settings.py)

Öncelikle projenin medya ayarlarının yapıldığından emin olalım. En alta şunları ekliyoruz:

Python:
import os

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')

2. Veritabanı Modeli (materyal_app/models.py)

Bir ders materyali için gerekli alanları tanımlıyoruz. Hem resim hem de dosya yükleme işlemini aynı modelde birleştiriyoruz.

Python:
from django.db import models

class DersMateryali(models.Model):
    baslik = models.CharField(max_length=200, verbose_name="Materyal Başlığı")
    aciklama = models.TextField(verbose_name="Açıklama", blank=True)
    # Kapak resmi (Örn: projenin ekran görüntüsü)
    kapak_resmi = models.ImageField(upload_to='kapaklar/%Y/%m/', verbose_name="Kapak Resmi", null=True, blank=True)
    # Asıl dosya (Örn: PDF, ZIP veya Python scripti)
    dosya = models.FileField(upload_to='belgeler/', verbose_name="Ders Dosyası")
    yuklenme_tarihi = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.baslik

3. Form Sınıfı (materyal_app/forms.py)

Modelimizle doğrudan konuşan, güvenlik ve doğrulama işlerini halleden formumuzu oluşturuyoruz. Bu dosyayı kendin oluşturmalısın.

Python
from django import forms
from .models import DersMateryali

class MateryalForm(forms.ModelForm):
    class Meta:
        model = DersMateryali
        fields = ['baslik', 'aciklama', 'kapak_resmi', 'dosya']
        widgets = {
            'baslik': forms.TextInput(attrs={'class': 'form-control'}),
            'aciklama': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
            'kapak_resmi': forms.FileInput(attrs={'class': 'form-control'}),
            'dosya': forms.FileInput(attrs={'class': 'form-control'}),
        }

4. Görünümler (materyal_app/views.py)

İki adet view fonksiyonuna ihtiyacımız var: Biri formu gösterip kaydetmek için, diğeri ise yüklenen materyalleri listelemek için.

Python:
from django.shortcuts import render, redirect
from .models import DersMateryali
from .forms import MateryalForm

# Yükleme Formu Görünümü
def materyal_yukle(request):
    if request.method == 'POST':
        # Dosyalar request.FILES ile alınır!
        form = MateryalForm(request.POST, request.FILES)
        if form.is_valid():
            form.save()
            return redirect('materyal_listesi') # Başarılıysa listeye yönlendir
    else:
        form = MateryalForm()
        
    return render(request, 'materyal_app/yukle.html', {'form': form})

# Listeleme Görünümü
def materyal_listesi(request):
    materyaller = DersMateryali.objects.all().order_by('-yuklenme_tarihi')
    return render(request, 'materyal_app/liste.html', {'materyaller': materyaller})

5. URL Yönlendirmeleri (Proje Klasörü / urls.py)

Hem uygulamamızın URL'lerini hem de Django'nun yüklenen medya dosyalarını sunabilmesi için gerekli ayarları yapıyoruz.

Python:
from django.contrib import admin
from django.urls import path
from materyal_app import views
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', views.materyal_listesi, name='materyal_listesi'),
    path('yukle/', views.materyal_yukle, name='materyal_yukle'),
]

# Geliştirme ortamında medya dosyalarına erişim izni
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

6. HTML Şablonları

templates/materyal_app/yukle.html (Form Sayfası):

Form etiketindeki enctype kısmına özellikle dikkat çekmekte fayda var, en sık unutulan kısımdır.

HTML:
<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <title>Yeni Materyal Yükle</title>
</head>
<body>
    <h2>Yeni Ders Materyali Ekle</h2>
    
    <form method="POST" enctype="multipart/form-data">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Sisteme Yükle</button>
    </form>
    
    <br>
    <a href="{% url 'materyal_listesi' %}">Listeye Dön</a>
</body>
</html>

templates/materyal_app/liste.html (Gösterim Sayfası):

Burada hem resmin hem de dosyanın .url özelliği ile nasıl çağrıldığını görebiliriz.

HTML:
<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <title>Ders Materyalleri</title>
</head>
<body>
    <h2>Yüklenen Ders Materyalleri</h2>
    <a href="{% url 'materyal_yukle' %}">+ Yeni Yükle</a>
    <hr>

    <div style="display: flex; flex-wrap: wrap; gap: 20px;">
        {% for materyal in materyaller %}
            <div style="border: 1px solid #ccc; padding: 15px; border-radius: 8px; width: 300px;">
                <h3>{{ materyal.baslik }}</h3>
                <p><small>{{ materyal.yuklenme_tarihi|date:"d M Y" }}</small></p>
                <p>{{ materyal.aciklama }}</p>

                {% if materyal.kapak_resmi %}
                    <img src="{{ materyal.kapak_resmi.url }}" alt="Kapak" style="max-width: 100%; height: auto; border-radius: 4px;">
                {% endif %}

                <br><br>
                {% if materyal.dosya %}
                    <a href="{{ materyal.dosya.url }}" download style="padding: 5px 10px; background-color: #007bff; color: white; text-decoration: none; border-radius: 4px;">
                        Dosyayı İndir
                    </a>
                {% endif %}
            </div>
        {% empty %}
            <p>Henüz hiçbir materyal yüklenmemiş.</p>
        {% endfor %}
    </div>
</body>
</html>


Kaynak Kod: https://github.com/nuritiras/dosya_yukleme.git

Son Adımlar

Kodları yerleştirdikten sonra terminalde şu komutları çalıştırmayı unutma:

  1. pip install Pillow (Eğer henüz yüklü değilse, resim işlemleri için şart)

  2. python manage.py makemigrations

  3. python manage.py migrate

  4. python manage.py runserver

Bu proje, form yapısından model kaydına, resim göstermekten dosya indirmeye kadar tüm süreci eksiksiz ve temiz bir şekilde özetliyor.

Yorumlar

Bu blogdaki popüler yayınlar

Pardus Üzerine Django Kurulumu

Python ile Web Geliştirme: Django App Oluşturma