跳转到内容

Django框架入门和ORM操作

1. Django框架介绍

Django是一个高级Python Web框架,遵循"电池包含"的设计理念,提供了完整的Web开发解决方案。它被广泛应用于构建大型、复杂的Web应用。

1.1 Django的特点

  • 全功能:内置ORM、表单处理、认证系统等
  • 安全性:内置防止SQL注入、XSS、CSRF等攻击的功能
  • 可扩展性:支持插件和第三方库
  • MTV架构:Model-Template-View架构模式
  • 管理后台:自动生成的管理界面
  • 国际化:支持多语言和时区

1.2 Django与其他框架的比较

框架特点适用场景
Django全功能、电池包含大型应用、企业级项目
Flask轻量级、灵活小型应用、API开发
FastAPI类型提示、自动文档API开发、高性能需求
Pyramid灵活、可扩展中型应用、自定义需求

2. Django环境搭建

2.1 安装Python

Django需要Python 3.8或更高版本。首先确保你的系统已经安装了Python。

2.2 安装Django

使用pip安装Django:

bash
# 创建虚拟环境
python3 -m venv venv

# 激活虚拟环境
# Windows
venv\Scripts\activate
# Linux/macOS
source venv/bin/activate

# 安装Django
pip install Django

# 验证安装
python -m django --version

3. 创建Django项目

3.1 初始化项目

使用Django的命令行工具创建项目:

bash
# 创建项目
django-admin startproject mysite

# 进入项目目录
cd mysite

# 创建应用
python manage.py startapp blog

3.2 项目结构

创建完成后,项目结构如下:

mysite/
├── mysite/
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── blog/
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations/
│   ├── models.py
│   ├── tests.py
│   └── views.py
└── manage.py

3.3 配置项目

编辑mysite/settings.py文件,配置项目:

python
# 注册应用
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',  # 注册blog应用
]

# 数据库配置
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

# 时区配置
TIME_ZONE = 'Asia/Shanghai'

# 语言配置
LANGUAGE_CODE = 'zh-Hans'

# 静态文件配置
STATIC_URL = 'static/'

4. Django ORM基础

4.1 ORM概念

ORM(Object-Relational Mapping)是一种将数据库表结构映射到对象的技术,它允许开发者使用面向对象的方式操作数据库。

4.2 定义模型

blog/models.py中定义数据库模型:

python
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User

class Category(models.Model):
    name = models.CharField(max_length=100)
    created_at = models.DateTimeField(auto_now_add=True)
    
    def __str__(self):
        return self.name

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    published_at = models.DateTimeField(blank=True, null=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, blank=True, null=True)
    tags = models.ManyToManyField('Tag', blank=True)
    
    def publish(self):
        self.published_at = timezone.now()
        self.save()
    
    def __str__(self):
        return self.title

class Tag(models.Model):
    name = models.CharField(max_length=50, unique=True)
    
    def __str__(self):
        return self.name

4.3 模型字段类型

Django提供了多种字段类型:

字段类型描述示例
CharField字符串name = models.CharField(max_length=100)
TextField长文本content = models.TextField()
IntegerField整数age = models.IntegerField()
FloatField浮点数price = models.FloatField()
DecimalField十进制数amount = models.DecimalField(max_digits=10, decimal_places=2)
BooleanField布尔值active = models.BooleanField(default=False)
DateField日期birth_date = models.DateField()
DateTimeField日期时间created_at = models.DateTimeField(auto_now_add=True)
ForeignKey外键关系author = models.ForeignKey(User, on_delete=models.CASCADE)
ManyToManyField多对多关系tags = models.ManyToManyField('Tag')
OneToOneField一对一关系profile = models.OneToOneField('Profile', on_delete=models.CASCADE)

4.4 数据库迁移

执行数据库迁移命令:

bash
# 生成迁移文件
python manage.py makemigrations

# 执行迁移
python manage.py migrate

# 查看迁移状态
python manage.py showmigrations

5. ORM查询操作

5.1 基本查询

python
# 获取所有对象
posts = Post.objects.all()

# 获取单个对象(不存在会抛出异常)
post = Post.objects.get(id=1)

# 获取单个对象(不存在返回None)
post = Post.objects.filter(id=1).first()

# 过滤查询
published_posts = Post.objects.filter(published_at__isnull=False)
recent_posts = Post.objects.filter(created_at__gte=timezone.now() - timezone.timedelta(days=7))

# 排序
posts = Post.objects.order_by('-created_at')  # 降序
posts = Post.objects.order_by('title')        # 升序

# 限制数量
recent_posts = Post.objects.order_by('-created_at')[:5]

# 排除查询
unpublished_posts = Post.objects.exclude(published_at__isnull=False)

# 搜索
posts = Post.objects.filter(title__contains='Django')
posts = Post.objects.filter(title__icontains='django')  # 不区分大小写

5.2 高级查询

python
# Q对象(复杂条件)
from django.db.models import Q
posts = Post.objects.filter(Q(title__contains='Django') | Q(content__contains='Django'))
posts = Post.objects.filter(Q(published_at__isnull=False) & Q(author__username='admin'))

# F对象(比较同一模型的不同字段)
from django.db.models import F
posts = Post.objects.filter(updated_at__gt=F('created_at'))

# 聚合查询
from django.db.models import Count, Sum, Avg, Max, Min
post_count = Post.objects.count()
avg_price = Product.objects.aggregate(Avg('price'))
max_price = Product.objects.aggregate(Max('price'))

# 分组查询
from django.db.models import Count
category_post_counts = Post.objects.values('category__name').annotate(post_count=Count('id'))

# 关联查询
# 正向查询
post = Post.objects.get(id=1)
author_name = post.author.username

# 反向查询
user = User.objects.get(username='admin')
user_posts = user.post_set.all()  # 默认使用模型名小写_set

# 预加载(避免N+1查询)
# 预加载外键
posts = Post.objects.select_related('author', 'category').all()

# 预加载多对多
posts = Post.objects.prefetch_related('tags').all()

# 组合使用
posts = Post.objects.select_related('author', 'category').prefetch_related('tags').all()

5.3 更新和删除

python
# 更新单个对象
post = Post.objects.get(id=1)
post.title = '新标题'
post.save()

# 批量更新
Post.objects.filter(published_at__isnull=True).update(published_at=timezone.now())

# 删除单个对象
post = Post.objects.get(id=1)
post.delete()

# 批量删除
Post.objects.filter(published_at__lt=timezone.now() - timezone.timedelta(days=365)).delete()

6. Django视图和URL

6.1 视图函数

blog/views.py中定义视图:

python
from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from .models import Post, Category, Tag
from .forms import PostForm

def post_list(request):
    posts = Post.objects.filter(published_at__isnull=False).order_by('-published_at')
    return render(request, 'blog/post_list.html', {'posts': posts})

def post_detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    return render(request, 'blog/post_detail.html', {'post': post})

@login_required
def post_new(request):
    if request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user
            post.save()
            return redirect('post_detail', pk=post.pk)
    else:
        form = PostForm()
    return render(request, 'blog/post_edit.html', {'form': form})

@login_required
def post_edit(request, pk):
    post = get_object_or_404(Post, pk=pk)
    if request.method == 'POST':
        form = PostForm(request.POST, instance=post)
        if form.is_valid():
            post = form.save()
            return redirect('post_detail', pk=post.pk)
    else:
        form = PostForm(instance=post)
    return render(request, 'blog/post_edit.html', {'form': form})

@login_required
def post_draft_list(request):
    posts = Post.objects.filter(published_at__isnull=True).order_by('-created_at')
    return render(request, 'blog/post_draft_list.html', {'posts': posts})

@login_required
def post_publish(request, pk):
    post = get_object_or_404(Post, pk=pk)
    post.publish()
    return redirect('post_detail', pk=pk)

@login_required
def post_remove(request, pk):
    post = get_object_or_404(Post, pk=pk)
    post.delete()
    return redirect('post_list')

6.2 URL配置

blog/urls.py中配置URL:

python
from django.urls import path
from . import views

urlpatterns = [
    path('', views.post_list, name='post_list'),
    path('post/<int:pk>/', views.post_detail, name='post_detail'),
    path('post/new/', views.post_new, name='post_new'),
    path('post/<int:pk>/edit/', views.post_edit, name='post_edit'),
    path('drafts/', views.post_draft_list, name='post_draft_list'),
    path('post/<int:pk>/publish/', views.post_publish, name='post_publish'),
    path('post/<int:pk>/remove/', views.post_remove, name='post_remove'),
]

mysite/urls.py中包含应用URL:

python
from django.contrib import admin
from django.urls import path, include

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

7. Django表单

7.1 模型表单

blog/forms.py中定义表单:

python
from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'content', 'category', 'tags']
        widgets = {
            'content': forms.Textarea(attrs={'rows': 10}),
        }

7.2 自定义表单

python
from django import forms

class ContactForm(forms.Form):
    name = forms.CharField(max_length=100, label='姓名')
    email = forms.EmailField(label='邮箱')
    message = forms.CharField(widget=forms.Textarea, label='留言')
    
    def clean_name(self):
        name = self.cleaned_data.get('name')
        if len(name) < 2:
            raise forms.ValidationError('姓名至少需要2个字符')
        return name

7.3 表单验证

python
# 视图中处理表单
if request.method == 'POST':
    form = ContactForm(request.POST)
    if form.is_valid():
        # 处理表单数据
        name = form.cleaned_data['name']
        email = form.cleaned_data['email']
        message = form.cleaned_data['message']
        # 发送邮件或保存数据
        return redirect('success')
else:
    form = ContactForm()

8. Django管理后台

8.1 注册模型

blog/admin.py中注册模型:

python
from django.contrib import admin
from .models import Post, Category, Tag

admin.site.register(Category)
admin.site.register(Tag)

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'author', 'category', 'published_at', 'created_at')
    list_filter = ('category', 'author', 'published_at')
    search_fields = ('title', 'content')
    prepopulated_fields = {'slug': ('title',)}
    raw_id_fields = ('author',)
    date_hierarchy = 'published_at'
    ordering = ('-published_at', '-created_at')

8.2 创建超级用户

bash
python manage.py createsuperuser

8.3 访问管理后台

启动开发服务器:

bash
python manage.py runserver

访问 http://localhost:8000/admin/ 登录管理后台。

9. Django中间件

9.1 中间件概念

中间件是Django处理请求和响应的钩子,它可以在请求到达视图之前或响应返回客户端之前执行一些操作。

9.2 内置中间件

Django默认包含以下中间件:

python
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

9.3 自定义中间件

创建自定义中间件:

python
# blog/middleware.py
class CustomMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
    
    def __call__(self, request):
        # 请求处理前
        print('请求到达')
        
        response = self.get_response(request)
        
        # 响应处理后
        print('响应返回')
        
        return response

在settings.py中注册中间件:

python
MIDDLEWARE = [
    # 其他中间件
    'blog.middleware.CustomMiddleware',
]

10. Django静态文件和媒体文件

10.1 静态文件配置

settings.py中配置静态文件:

python
# 静态文件URL
STATIC_URL = 'static/'

# 静态文件目录
STATICFILES_DIRS = [
    BASE_DIR / 'static',
]

# 生产环境静态文件根目录
STATIC_ROOT = BASE_DIR / 'staticfiles'

10.2 媒体文件配置

settings.py中配置媒体文件:

python
# 媒体文件URL
MEDIA_URL = 'media/'

# 媒体文件根目录
MEDIA_ROOT = BASE_DIR / 'media'

urls.py中添加媒体文件URL:

python
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    # 其他URL
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

11. Django认证系统

11.1 内置认证视图

urls.py中包含认证URL:

python
from django.contrib.auth import views as auth_views

urlpatterns = [
    # 登录
    path('login/', auth_views.LoginView.as_view(template_name='registration/login.html'), name='login'),
    # 登出
    path('logout/', auth_views.LogoutView.as_view(), name='logout'),
    # 密码重置
    path('password_reset/', auth_views.PasswordResetView.as_view(), name='password_reset'),
    path('password_reset/done/', auth_views.PasswordResetDoneView.as_view(), name='password_reset_done'),
    path('reset/<uidb64>/<token>/', auth_views.PasswordResetConfirmView.as_view(), name='password_reset_confirm'),
    path('reset/done/', auth_views.PasswordResetCompleteView.as_view(), name='password_reset_complete'),
]

11.2 用户注册

创建用户注册视图:

python
from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import login

def register(request):
    if request.method == 'POST':
        form = UserCreationForm(request.POST)
        if form.is_valid():
            user = form.save()
            login(request, user)
            return redirect('post_list')
    else:
        form = UserCreationForm()
    return render(request, 'registration/register.html', {'form': form})

11.3 权限控制

python
from django.contrib.auth.decorators import login_required, permission_required

# 要求登录
@login_required
def profile(request):
    return render(request, 'profile.html')

# 要求特定权限
@permission_required('blog.change_post')
def post_edit(request, pk):
    # 视图代码
    pass

# 检查用户是否有权限
if request.user.has_perm('blog.change_post'):
    # 用户有修改文章的权限
    pass

if request.user.has_perms(['blog.add_post', 'blog.change_post']):
    # 用户有添加和修改文章的权限
    pass

12. Django REST Framework

12.1 安装DRF

bash
pip install djangorestframework

12.2 配置DRF

settings.py中添加DRF:

python
INSTALLED_APPS = [
    # 其他应用
    'rest_framework',
]

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ],
}

12.3 创建API视图

python
from rest_framework import viewsets
from rest_framework import permissions
from .models import Post, Category
from .serializers import PostSerializer, CategorySerializer

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    permission_classes = [permissions.IsAuthenticated]

class CategoryViewSet(viewsets.ModelViewSet):
    queryset = Category.objects.all()
    serializer_class = CategorySerializer
    permission_classes = [permissions.IsAuthenticated]

12.4 序列化器

blog/serializers.py中定义序列化器:

python
from rest_framework import serializers
from .models import Post, Category, Tag

class TagSerializer(serializers.ModelSerializer):
    class Meta:
        model = Tag
        fields = ['id', 'name']

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = ['id', 'name']

class PostSerializer(serializers.ModelSerializer):
    category = CategorySerializer(read_only=True)
    tags = TagSerializer(many=True, read_only=True)
    
    class Meta:
        model = Post
        fields = ['id', 'title', 'content', 'author', 'category', 'tags', 'published_at', 'created_at']

12.5 配置API路由

blog/urls.py中配置API路由:

python
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views
from .api_views import PostViewSet, CategoryViewSet

router = DefaultRouter()
router.register(r'posts', PostViewSet)
router.register(r'categories', CategoryViewSet)

urlpatterns = [
    # 其他URL
    path('api/', include(router.urls)),
]

13. 部署Django应用

13.1 使用Gunicorn和Nginx

  1. 安装Gunicorn
bash
pip install gunicorn
  1. 创建Gunicorn配置文件
python
# gunicorn.conf.py
bind = '0.0.0.0:8000'
workers = 4
worker_class = 'gevent'
timeout = 30
  1. 启动Gunicorn
bash
gunicorn mysite.wsgi:application -c gunicorn.conf.py
  1. 配置Nginx
nginx
server {
    listen 80;
    server_name example.com;
    
    location /static/ {
        alias /path/to/mysite/staticfiles/;
        expires 30d;
    }
    
    location /media/ {
        alias /path/to/mysite/media/;
        expires 30d;
    }
    
    location / {
        proxy_pass http://127.0.0.1:8000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

13.2 使用Docker

创建Dockerfile:

dockerfile
FROM python:3.9-slim

WORKDIR /app

COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

COPY . .

RUN python manage.py collectstatic --noinput

EXPOSE 8000

CMD ["gunicorn", "mysite.wsgi:application", "-b", "0.0.0.0:8000"]

创建docker-compose.yml:

yaml
version: '3.8'

services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DEBUG=False
      - SECRET_KEY=your-secret-key
    depends_on:
      - db
  
  db:
    image: postgres:13
    environment:
      - POSTGRES_DB=mysite
      - POSTGRES_USER=admin
      - POSTGRES_PASSWORD=password
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

14. 最佳实践

14.1 项目结构

  • 使用应用分离功能
  • 合理组织代码结构
  • 使用版本控制

14.2 安全性

  • 使用HTTPS
  • 防止SQL注入
  • 防止XSS攻击
  • 防止CSRF攻击
  • 安全处理用户上传
  • 使用强密码
  • 定期更新依赖

14.3 性能优化

  • 使用缓存
  • 优化数据库查询
  • 使用索引
  • 启用Gzip压缩
  • 优化静态文件
  • 使用CDN

14.4 测试

  • 编写单元测试
  • 编写集成测试
  • 使用测试覆盖率工具
  • 自动化测试

14.5 部署

  • 使用环境变量
  • 配置不同环境的设置
  • 备份数据
  • 监控应用
  • 日志管理

15. 实战项目:博客系统

15.1 项目结构

blog_project/
├── blog/
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── forms.py
│   ├── migrations/
│   ├── models.py
│   ├── serializers.py
│   ├── templates/
│   │   └── blog/
│   │       ├── base.html
│   │       ├── post_list.html
│   │       ├── post_detail.html
│   │       ├── post_edit.html
│   │       └── post_draft_list.html
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── blog_project/
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── static/
│   ├── css/
│   └── js/
├── media/
├── requirements.txt
└── manage.py

15.2 核心功能

  • 文章发布和管理
  • 分类和标签
  • 评论功能
  • 搜索功能
  • 响应式设计
  • API接口

15.3 技术栈

  • 后端:Django 4.0+, Python 3.9+
  • 数据库:PostgreSQL
  • 前端:HTML, CSS, JavaScript, Bootstrap
  • API:Django REST Framework
  • 部署:Gunicorn, Nginx, Docker

16. 总结

Django是一个功能强大、设计优雅的Python Web框架,它提供了完整的Web开发解决方案,包括ORM、表单处理、认证系统、管理后台等。通过本文的学习,你已经掌握了:

  • Django框架的基本概念和特点
  • Django环境搭建和项目创建
  • Django ORM的使用和数据库操作
  • Django视图和URL配置
  • Django表单和验证
  • Django管理后台
  • Django中间件
  • Django静态文件和媒体文件
  • Django认证系统
  • Django REST Framework
  • Django应用部署
  • 最佳实践和实战项目

Django的学习曲线相对平缓,易于上手,同时又足够强大,可以满足各种Web开发需求。希望本文对你的Django学习之旅有所帮助!

17. 练习与思考

  1. 实现一个完整的博客系统,包括文章、评论、分类、标签等功能
  2. 添加用户认证和授权功能
  3. 实现RESTful API接口
  4. 集成第三方登录(如GitHub、Google)
  5. 添加搜索功能和分页
  6. 实现文件上传和图片处理
  7. 优化数据库查询和页面加载速度
  8. 编写完整的测试用例

通过这些练习,你将进一步巩固Django的使用技能,为实际项目开发打下坚实的基础。

评论区

专业的Linux技术学习平台,从入门到精通的完整学习路径