~/icsd.ir — bash
SYSTEM_ONLINE

📁 فصل ۸: فایل‌های Static و Media

یاد بگیر چطور CSS، JS، تصاویر و فایل‌های آپلودی رو در جنگو مدیریت کنی.

یاد بگیر چطور CSS، JS، تصاویر و فایل‌های آپلودی رو در جنگو مدیریت کنی.

🎯 تفاوت Static و Media

📦 Static

فایل‌های ثابت توسعه‌دهنده

  • CSS
  • JavaScript
  • تصاویر سایت (لوگو، آیکون)
  • فونت‌ها

قبل از دیپلوی، توسعه‌دهنده اضافه می‌کنه

🖼️ Media

فایل‌های آپلودی کاربر

  • تصویر پروفایل
  • تصاویر محصولات
  • فایل‌های PDF
  • فایل‌های صوتی

کاربر در زمان استفاده آپلود می‌کنه

📦 فایل‌های Static

تنظیمات

myproject/settings.py
import os

# URL برای دسترسی به فایل‌ها
STATIC_URL = '/static/'

# پوشه‌های اضافی برای فایل‌های static (در حالت توسعه)
STATICFILES_DIRS = [
    BASE_DIR / 'static',
]

# پوشه نهایی برای جمع‌آوری در production
STATIC_ROOT = BASE_DIR / 'staticfiles'

ساختار پوشه

📂 ساختار پروژه
myproject/
├── manage.py
├── static/                    # ← static کلی پروژه
│   ├── css/
│   │   └── main.css
│   ├── js/
│   │   └── app.js
│   └── images/
│       └── logo.png
├── products/
│   ├── static/               # ← static اپ خاص
│   │   └── products/
│   │       └── style.css
│   └── ...
└── ...

استفاده در قالب

قالب
{% load static %}

<link rel="stylesheet" href="{% static 'css/main.css' %}">
<script src="{% static 'js/app.js' %}"></script>
<img src="{% static 'images/logo.png' %}" alt="لوگو">

{# با متغیر #}
<img src="{% static image_path %}" alt="...">

collectstatic – برای دیپلوی

قبل از دیپلوی، همه فایل‌های static رو در یک پوشه جمع می‌کنیم:

📟 ترمینال
python manage.py collectstatic
📚

چرا collectstatic؟

در production، سرور وب (Nginx) نباید با جنگو درگیر بشه برای فایل‌های ثابت. بهتره همه فایل‌های static رو یک‌جا جمع کنیم تا Nginx مستقیم سرو کنه.

🖼️ فایل‌های Media

تنظیمات

myproject/settings.py
# URL برای دسترسی
MEDIA_URL = '/media/'

# مسیر فیزیکی فایل‌ها
MEDIA_ROOT = BASE_DIR / 'media'

تنظیم URL برای حالت توسعه

myproject/urls.py
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path

urlpatterns = [
    path('admin/', admin.site.urls),
    # ...
]

# فقط در حالت DEBUG=True
if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

📸 کار با ImageField

نصب Pillow

برای کار با تصاویر، نیاز به کتابخانه Pillow داریم:

📟 ترمینال
pip install Pillow

تعریف فیلد در مدل

products/models.py
class Product(models.Model):
    name = models.CharField(max_length=200)
    
    # تصویر اصلی
    image = models.ImageField(
        upload_to='products/',
        blank=True,
        null=True,
        verbose_name='تصویر محصول'
    )
    
    # با تاریخ - مرتب‌سازی خودکار
    image_dated = models.ImageField(
        upload_to='products/%Y/%m/%d/',
        blank=True
    )
    
    # فایل
    catalog = models.FileField(
        upload_to='catalogs/',
        blank=True
    )

استفاده در قالب

قالب
{% if product.image %}
    <img src="{{ product.image.url }}" alt="{{ product.name }}">
</p>

<p>مسیر: {{ product.image.url }}</p>
<p>اندازه: {{ product.image.size|filesizeformat }}</p>
<p>عرض: {{ product.image.width }}</p>
<p>ارتفاع: {{ product.image.height }}</p>
{% else %}
    <img src="{% static 'images/no-image.png' %}" alt="بدون تصویر">
{% endif %}

تابع upload_to پویا

اگه می‌خوای مسیر آپلود رو خودت تعیین کنی:

products/models.py
def product_image_path(instance, filename):
    # filename اسم اصلی فایله
    # instance آبجکت محصوله
    return f'products/{instance.category.slug}/{instance.id}/{filename}'

class Product(models.Model):
    name = models.CharField(max_length=200)
    image = models.ImageField(upload_to=product_image_path)

🖼️ ساخت تامبنیل

برای کاهش حجم تصاویر، می‌تونیم تامبنیل بسازیم:

روش ۱: با Pillow در مدل

products/models.py
from PIL import Image
from django.db import models

class Product(models.Model):
    image = models.ImageField(upload_to='products/')
    
    def save(self, *args, **kwargs):
        super().save(*args, **kwargs)  # اول ذخیره
        
        if self.image:
            img = Image.open(self.image.path)
            
            # تغییر اندازه اگه بزرگ‌تر از ۸۰۰x۸۰۰ بود
            if img.height > 800 or img.width > 800:
                output_size = (800, 800)
                img.thumbnail(output_size)
                img.save(self.image.path, quality=85, optimize=True)

روش ۲: استفاده از کتابخانه easy-thumbnails

📟 ترمینال
pip install easy-thumbnails
settings.py
INSTALLED_APPS = [
    # ...
    'easy_thumbnails',
]

THUMBNAIL_ALIASES = {
    '': {
        'small': {'size': (100, 100), 'crop': True},
        'medium': {'size': (300, 300), 'crop': True},
        'large': {'size': (800, 600)},
    },
}
قالب
{% load thumbnail %}

{# ساخت تامبنیل با اندازه دلخواه #}
<img src="{% thumbnail product.image 200x200 crop %}" alt="...">

{# با alias تعریف‌شده در settings #}
<img src="{% thumbnail product.image 'small' %}" alt="...">

🎯 مثال‌های کاربردی

آسان

مثال ۱: لوگو و فاوآیکون سایت

قالب base.html
{% load static %}
<!DOCTYPE html>
<html>
<head>
    <link rel="icon" href="{% static 'images/favicon.ico' %}">
    <link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body>
    <header>
        <img src="{% static 'images/logo.png' %}" alt="ICSD" height="50">
    </header>
    
    <script src="{% static 'js/main.js' %}"></script>
</body>
</html>

متوسط

مثال ۲: گالری تصاویر محصول

products/models.py
class Product(models.Model):
    name = models.CharField(max_length=200)
    main_image = models.ImageField(upload_to='products/')

class ProductImage(models.Model):
    product = models.ForeignKey(
        Product,
        related_name='images',
        on_delete=models.CASCADE
    )
    image = models.ImageField(upload_to='products/gallery/')
    alt_text = models.CharField(max_length=200, blank=True)
    order = models.IntegerField(default=0)
    
    class Meta:
        ordering = ['order']
products/detail.html
<div class="main-image">
    <img src="{{ product.main_image.url }}" id="mainImage">
</div>

<div class="thumbnails">
    {% for img in product.images.all %}
        <img 
            src="{{ img.image.url }}" 
            alt="{{ img.alt_text }}"
            onclick="document.getElementById('mainImage').src=this.src"
            class="thumb">
    {% endfor %}
</div>

پیشرفته

مثال ۳: واترمارک خودکار + تامبنیل

products/models.py
from PIL import Image
from io import BytesIO
from django.core.files.uploadedfile import InMemoryUploadedFile
from django.db import models
import sys

class Product(models.Model):
    name = models.CharField(max_length=200)
    image = models.ImageField(upload_to='products/')
    thumbnail = models.ImageField(upload_to='products/thumbs/', blank=True)
    
    def save(self, *args, **kwargs):
        # پردازش تصویر اصلی
        if self.image:
            img = Image.open(self.image)
            
            # واترمارک
            self._add_watermark(img)
            
            # ذخیره با کیفیت
            output = BytesIO()
            img.save(output, format='JPEG', quality=90, optimize=True)
            output.seek(0)
            
            self.image = InMemoryUploadedFile(
                output, 'ImageField',
                f'{self.image.name.split(".")[0]}.jpg',
                'image/jpeg', sys.getsizeof(output), None
            )
        
        super().save(*args, **kwargs)
        
        # ساخت تامبنیل بعد از ذخیره
        if self.image and not self.thumbnail:
            self._create_thumbnail()
    
    def _add_watermark(self, img):
        from PIL import ImageDraw, ImageFont
        draw = ImageDraw.Draw(img)
        text = 'ICSD.IR'
        # اضافه کردن متن گوشه پایین
        draw.text(
            (img.width - 150, img.height - 30),
            text,
            fill=(255, 255, 255, 128)
        )
    
    def _create_thumbnail(self):
        img = Image.open(self.image.path)
        img.thumbnail((300, 300))
        
        thumb_path = self.image.path.replace('products/', 'products/thumbs/')
        img.save(thumb_path)
        
        self.thumbnail.name = self.image.name.replace('products/', 'products/thumbs/')
        super().save(update_fields=['thumbnail'])

📝 تمرین‌ها

آسان

تمرین ۱

یه قالب پایه بساز که فایل CSS و JS سایتت رو از پوشه static بارگذاری کنه.

متوسط

تمرین ۲

یه مدل UserProfile بساز که فیلد avatar داشته باشه. در قالب نمایش بده. اگه عکس نداشت، عکس پیش‌فرض از static نشون بده.

پیشرفته

تمرین ۳

یه سیستم آپلود تصویر بساز که قبل از ذخیره: ۱) اندازه رو به حداکثر ۱۰۰۰ پیکسل برسونه ۲) واترمارک اضافه کنه ۳) تامبنیل ۲۰۰x۲۰۰ بسازه.

نمایش سایت

رنگ سایت
حالت نمایش
اندازهٔ متن
خوانایی

این تنظیمات فقط روی مرورگر شما ذخیره می‌شود.