下载工作台
Django Web 开发

静态资源、媒体上传与模板进阶

试读上半部分 · 解锁后可读全文

第 8 章 · 静态资源、媒体上传与模板进阶

本章目标:正确配置 STATIC_URL / STATICFILES_DIRS / STATIC_ROOTMEDIA;在模型中使用 ImageField 实现商品封面;模板中加载 {% static %};掌握 collectstatic 生产部署流程;运用模板继承include 构建 shop-demo 后台页面骨架。

学时建议:4 小时(含 1.5 小时图片上传跟练)

前置:本模块 ch01 模板基础;ch02 表单与 request.FILESch05 Product 模型;ch07 API(本章侧重服务端渲染与静态资源)。


8.1 静态文件 vs 媒体文件

类型来源示例部署
静态 Static开发者提交到仓库CSS、JS、Logo、faviconcollectstatic 收集到 STATIC_ROOT
媒体 Media用户上传商品封面、用户头像MEDIA_ROOT,由 Web 服务器或对象存储提供
开发环境 runserver
  /static/  → django.contrib.staticfiles
  /media/   → settings 中 DEBUG 时映射 MEDIA_ROOT

生产环境 Nginx
  /static/  → alias /var/www/shop-demo/static/
  /media/   → alias /var/www/shop-demo/media/  (或 OSS CDN)
本章基于虚构电商 shop-demo,域名示例 https://api.example.com 仅用于说明 CDN 地址,不涉及真实业务配置。

8.2 settings.py 完整配置

shopdemo/settings.py

from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent

# --- Static ---
STATIC_URL = "/static/"
STATICFILES_DIRS = [
    BASE_DIR / "assets",          # 源码中的静态目录
]
STATIC_ROOT = BASE_DIR / "staticfiles"  # collectstatic 输出目录

# --- Media ---
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"

# 开发期确保 staticfiles 应用已启用
INSTALLED_APPS = [
    # ...
    "django.contrib.staticfiles",
]
变量含义
STATIC_URL浏览器访问前缀
STATICFILES_DIRS开发时额外静态目录(可多个)
STATIC_ROOT生产收集目标(勿把源码直接放这里)
MEDIA_ROOT上传文件磁盘路径

目录结构建议

shop-demo/
├── assets/
│   ├── css/
│   │   └── admin-base.css
│   ├── js/
│   │   └── product-preview.js
│   └── img/
│       └── logo.png
├── media/              # .gitignore,不上传用户文件
│   └── products/
├── staticfiles/        # collectstatic 产出,.gitignore
└── templates/
    ├── base.html
    └── catalog/
        └── product_form.html

8.3 开发环境 URL 映射

shopdemo/urls.py

from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path("admin/", admin.site.urls),
    path("api/", include("catalog.urls")),
    path("", include("catalog.web_urls")),  # 服务端页面路由
]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
    # DEBUG 时 staticfiles 自动处理 STATIC_URL,一般无需再 manual static()

注意DEBUG=False 时 Django 不再托管媒体文件,必须由 Nginx 或云存储处理。


8.4 ImageField 与 Pillow

pip install Pillow

catalog/models.py

from django.db import models


def product_cover_path(instance, filename):
    ext = filename.rsplit(".", 1)[-1].lower()
    return f"products/{instance.slug}.{ext}"


class Product(models.Model):
    category = models.ForeignKey(
        "catalog.Category",
        on_delete=models.PROTECT,
        related_name="products",
    )
    name = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    stock = models.PositiveIntegerField(default=0)
    cover = models.ImageField(upload_to=product_cover_path, blank=True, null=True)
    description = models.TextField(blank=True)
    is_published = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name
category 沿用 ch05 的分类外键(PROTECT 防误删有商品的分类);本章在此基础上新增 cover/description/updated_at 字段。

迁移:

python manage.py makemigrations catalog
python manage.py migrate

表单处理(ch02 ModelForm 延伸):

# catalog/forms.py
from django import forms
from .models import Product


class ProductForm(forms.ModelForm):
    class Meta:
        model = Product
        fields = ["name", "slug", "price", "stock", "cover", "description", "is_published"]
        widgets = {
            "description": forms.Textarea(attrs={"rows": 4}),
        }

以下内容需解锁后阅读

试读已结束。解锁本章 ¥5.00,或开通年度会员畅读全部教程。
年度会员 ¥199.00/年; 小紫 AI 工作台有效会员 ¥99.00/年

正文仅在服务端鉴权后下发,未付费无法获取下半部分内容。