Django框架基础:MTV模式、Admin、ORM与模板
Django框架基础:MTV模式、Admin、ORM与模板
Django是Python最强大的全功能Web框架,遵循"自带电池"的设计理念。它提供了完善的ORM、Admin后台、模板系统等组件,让你能快速构建高质量的Web应用。本文将带你掌握Django的核心基础。
安装Django
pip install django
# 创建项目
django-admin startproject myproject
cd myproject
# 创建应用
python manage.py startapp myapp
# 启动开发服务器
python manage.py runserver
MTV架构模式
Django采用MTV(Model-Template-View)架构:
- Model:数据模型,对应数据库表
- Template:模板,HTML页面
- View:视图,处理业务逻辑
# 项目结构
"""
myproject/
├── manage.py # 项目管理脚本
├── myproject/
│ ├── __init__.py
│ ├── settings.py # 项目配置
│ ├── urls.py # URL配置
│ ├── wsgi.py # WSGI入口
│ └── asgi.py # ASGI入口
└── myapp/
├── __init__.py
├── admin.py # Admin配置
├── apps.py # 应用配置
├── models.py # 数据模型
├── views.py # 视图
├── tests.py # 测试
├── urls.py # 应用URL
└── templates/ # 模板目录
"""
Model定义
# myapp/models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
name = models.CharField(max_length=100, unique=True)
slug = models.SlugField(unique=True)
description = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = "categories"
ordering = ["name"]
def __str__(self):
return self.name
def get_absolute_url(self):
return f"/categories/{self.slug}/"
class Article(models.Model):
STATUS_CHOICES = [
("draft", "草稿"),
("published", "已发布"),
]
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="articles")
category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
tags = models.ManyToManyField("Tag", blank=True)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default="draft")
views_count = models.PositiveIntegerField(default=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ["-created_at"]
indexes = [
models.Index(fields=["-created_at"]),
models.Index(fields=["status"]),
]
def __str__(self):
return self.title
def increment_views(self):
self.views_count += 1
self.save(update_fields=["views_count"])
class Tag(models.Model):
name = models.CharField(max_length=50, unique=True)
def __str__(self):
return self.name
ORM操作
from myapp.models import Article, Category, Tag
from django.db.models import Q, Count, Avg, Sum, F
from django.utils import timezone
# 创建对象
category = Category.objects.create(name="Python", slug="python")
article = Article.objects.create(
title="Django入门",
slug="django-intro",
content="内容...",
author=user,
category=category,
status="published"
)
# 查询对象
articles = Article.objects.all() # 所有文章
published = Article.objects.filter(status="published") # 过滤
article = Article.objects.get(id=1) # 获取单个
article = Article.objects.filter(slug="django-intro").first() # 安全获取
# 链式查询
results = Article.objects.filter(
status="published",
category__name="Python"
).order_by("-created_at")[:10]
# Q对象(复杂查询)
articles = Article.objects.filter(
Q(title__icontains="django") | Q(content__icontains="python")
)
# 聚合查询
stats = Article.objects.filter(status="published").aggregate(
total=Count("id"),
avg_views=Avg("views_count"),
total_views=Sum("views_count")
)
# 分组查询
from django.db.models import Count
category_stats = Category.objects.annotate(
article_count=Count("article")
).order_by("-article_count")
# 更新对象
Article.objects.filter(status="draft").update(status="published")
# 使用F对象(原子更新)
Article.objects.filter(id=1).update(views_count=F("views_count") + 1)
# 删除对象
Article.objects.filter(status="draft").delete()
# 关联查询
# 正向查询
article = Article.objects.get(id=1)
print(article.category.name)
# 反向查询
category = Category.objects.get(id=1)
articles = category.article_set.all() # 或使用related_name
Admin后台
# myapp/admin.py
from django.contrib import admin
from .models import Article, Category, Tag
@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
list_display = ["name", "slug", "created_at"]
prepopulated_fields = {"slug": ("name",)}
search_fields = ["name"]
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
list_display = ["title", "author", "category", "status", "created_at"]
list_filter = ["status", "category", "created_at"]
search_fields = ["title", "content"]
prepopulated_fields = {"slug": ("title",)}
raw_id_fields = ["author"]
date_hierarchy = "created_at"
list_editable = ["status"]
list_per_page = 20
def get_queryset(self, request):
qs = super().get_queryset(request)
if request.user.is_superuser:
return qs
return qs.filter(author=request.user)
def save_model(self, request, obj, form, change):
if not change: # 新建时
obj.author = request.user
super().save_model(request, obj, form, change)
@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
list_display = ["name"]
# 创建超级用户
# python manage.py createsuperuser
URL配置
# myproject/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("myapp.urls")),
]
# myapp/urls.py
from django.urls import path
from . import views
app_name = "myapp" # 命名空间
urlpatterns = [
path("", views.index, name="index"),
path("articles/", views.article_list, name="article-list"),
path("articles/<slug:slug>/", views.article_detail, name="article-detail"),
path("articles/create/", views.article_create, name="article-create"),
path("articles/<int:pk>/edit/", views.article_edit, name="article-edit"),
path("articles/<int:pk>/delete/", views.article_delete, name="article-delete"),
path("categories/<slug:slug>/", views.category_detail, name="category-detail"),
path("api/", include("myapp.api_urls")),
]
View处理
# myapp/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.http import JsonResponse, HttpResponseForbidden
from django.contrib.auth.decorators import login_required
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from .models import Article, Category
from .forms import ArticleForm
# 函数视图
def index(request):
latest_articles = Article.objects.filter(status="published")[:5]
categories = Category.objects.all()
return render(request, "myapp/index.html", {
"latest_articles": latest_articles,
"categories": categories
})
def article_list(request):
articles = Article.objects.filter(status="published")
return render(request, "myapp/article_list.html", {"articles": articles})
def article_detail(request, slug):
article = get_object_or_404(Article, slug=slug, status="published")
article.increment_views()
return render(request, "myapp/article_detail.html", {"article": article})
@login_required
def article_create(request):
if request.method == "POST":
form = ArticleForm(request.POST)
if form.is_valid():
article = form.save(commit=False)
article.author = request.user
article.save()
return redirect("myapp:article-detail", slug=article.slug)
else:
form = ArticleForm()
return render(request, "myapp/article_form.html", {"form": form})
# 类视图
class ArticleListView(ListView):
model = Article
template_name = "myapp/article_list.html"
context_object_name = "articles"
paginate_by = 10
def get_queryset(self):
return Article.objects.filter(status="published")
class ArticleDetailView(DetailView):
model = Article
template_name = "myapp/article_detail.html"
def get_object(self):
obj = super().get_object()
obj.increment_views()
return obj
class ArticleCreateView(LoginRequiredMixin, CreateView):
model = Article
form_class = ArticleForm
template_name = "myapp/article_form.html"
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class ArticleUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Article
form_class = ArticleForm
template_name = "myapp/article_form.html"
def test_func(self):
article = self.get_object()
return self.request.user == article.author
class ArticleDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Article
template_name = "myapp/article_confirm_delete.html"
success_url = "/articles/"
def test_func(self):
article = self.get_object()
return self.request.user == article.author
模板系统
<!-- templates/base.html -->
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}我的博客{% endblock %}</title>
<link rel="stylesheet" href="{% static 'css/style.css' %}">
</head>
<body>
<nav>
<a href="{% url 'myapp:index' %}">首页</a>
<a href="{% url 'myapp:article-list' %}">文章</a>
{% if user.is_authenticated %}
<a href="{% url 'myapp:article-create' %}">写文章</a>
<a href="{% url 'admin:index' %}">后台</a>
{% endif %}
</nav>
<main>
{% block content %}{% endblock %}
</main>
<footer>© 2024 我的博客</footer>
</body>
</html>
<!-- templates/myapp/article_list.html -->
{% extends "base.html" %}
{% block title %}文章列表{% endblock %}
{% block content %}
<h1>文章列表</h1>
{% for article in articles %}
<article>
<h2><a href="{% url 'myapp:article-detail' slug=article.slug %}">{{ article.title }}</a></h2>
<p class="meta">
{{ article.author.username }} | {{ article.created_at|date:"Y-m-d" }} |
{{ article.category.name }} | 阅读 {{ article.views_count }}
</p>
<p>{{ article.content|truncatewords:30 }}</p>
</article>
{% empty %}
<p>暂无文章</p>
{% endfor %}
<!-- 分页 -->
{% if is_paginated %}
<div class="pagination">
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}">上一页</a>
{% endif %}
<span>第 {{ page_obj.number }} / {{ page_obj.paginator.num_pages }} 页</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">下一页</a>
{% endif %}
</div>
{% endif %}
{% endblock %}
<!-- 模板标签和过滤器 -->
{% load static %}
{% load myapp_tags %}
{{ article.title|upper }}
{{ article.content|truncatewords:50|linebreaks }}
{{ article.created_at|date:"Y年m月d日" }}
{{ article.views_count|default:"0" }}
{% if user.is_superuser %}
<a href="{% url 'admin:myapp_article_change' article.id %}">编辑</a>
{% endif %}
表单
# myapp/forms.py
from django import forms
from .models import Article
class ArticleForm(forms.ModelForm):
class Meta:
model = Article
fields = ["title", "slug", "content", "category", "tags", "status"]
widgets = {
"title": forms.TextInput(attrs={"class": "form-control"}),
"content": forms.Textarea(attrs={"class": "form-control", "rows": 10}),
"slug": forms.TextInput(attrs={"class": "form-control"}),
}
def clean_title(self):
title = self.cleaned_data.get("title")
if len(title) < 5:
raise forms.ValidationError("标题至少需要5个字符")
return title
实战示例:博客应用
# 完整的博客应用配置
# settings.py关键配置
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"myapp",
]
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": BASE_DIR / "db.sqlite3",
}
}
LANGUAGE_CODE = "zh-hans"
TIME_ZONE = "Asia/Shanghai"
STATIC_URL = "/static/"
STATICFILES_DIRS = [BASE_DIR / "static"]
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR / "media"
LOGIN_URL = "/auth/login/"
总结
Django是一个功能完善的Web框架,适合构建大型复杂的Web应用。掌握MTV架构、Model定义、ORM操作、Admin配置、视图和模板系统后,你就能构建专业的Web应用。建议进一步学习Django REST Framework来构建API,以及Django的认证、权限和中间件系统。