Django 5.1环境搭建与MVT架构解析

简单来说,学一个新框架最怕的就是环境搞崩,或者版本对不上。咱们直接上最新的,就用 Django 5.1,这版本是 2024年8月7日** 刚发布的,里面对于类型提示(Type Hints)的支持又增强了不少,写代码的时候 IDE 提示更友好。

先聊聊环境。别直接在系统 Python 里装,那是新手最容易踩的坑。咱们用虚拟环境隔离一下。打开你的终端(Windows 下可能是 PowerShell 或者 CMD),咱们这么干:

# 创建项目文件夹 mkdir django_blog_tutorial cd django_blog_tutorial # 创建虚拟环境,python 3.10+ 都行,Django 5.1 对 Python 版本有要求 python -m venv venv # 激活虚拟环境 # Windows: venv\Scripts\activate # Mac/Linux: source venv/bin/activate # 安装 Django 5.1 pip install django==5.1

装好了?咱们来创建项目。Django 自带一个命令行工具 django-admin,用它来生成骨架代码最方便。

django-admin startproject config .

注意后面那个点 .,意思是把项目文件放在当前目录,而不是再套一层文件夹。这时候你的目录结构应该是这样的:

django_blog_tutorial/ ├── config/ # 项目配置目录 │ ├── __init__.py │ ├── settings.py # 核心配置文件,数据库、中间件都在这 │ ├── urls.py # 路由入口 │ └── wsgi.py # 部署用的,现在先不管 ├── manage.py # 项目管理脚本,各种命令都靠它 └── venv/

跑起来试试?执行 python manage.py runserver,看到那个火箭 emoji 和 "Starting development server" 就说明成了。

接下来咱们得搞懂 Django 的 MVT 架构。很多面试会问这和 MVC 有啥区别,换个角度看,就是换了个马甲。

🔧 实战技巧:刚开始别把 View 写得太臃肿。很多新手喜欢把一堆逻辑全塞进 views.py 的一个函数里,后期维护想死的心都有。尽量保持 View 只做“接收请求 -> 调用逻辑 -> 返回响应”这三件事。

咱们再创建一个应用(App),Django 推荐把功能拆分成不同的 App。

python manage.py startapp blog

创建完别忘了去 config/settings.py 里的 INSTALLED_APPS 加上 'blog',不然 Django 不知道你多了这么个应用。

# config/settings.py INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'blog', # 加上这一行,注册你的应用 ]

这就算把地基打好了。MVT 听起来玄乎,其实就是把“数据”、“逻辑”、“页面”分家,各管各的,这样代码才清爽。

模型(Model)与ORM:定义数据结构与迁移

搞定了环境,咱们来聊聊 Django 最核心的杀手锏——ORM (Object-Relational Mapping)。其实,就是你不用写恶心的 SQL 语句(比如 CREATE TABLE...),直接写 Python 类,Django 自动帮你翻译成数据库能懂的语言。

咱们在 blog 这个应用里搞个简单的博客系统。打开 blog/models.py,这就是定义数据结构的地方。

# blog/models.py from django.db import models from django.contrib.auth.models import User class Category(models.Model): name = models.CharField(max_length=100, verbose_name="分类名称") def __str__(self): return self.name class Post(models.Model): title = models.CharField(max_length=200, verbose_name="标题") content = models.TextField(verbose_name="正文内容") created_at = models.DateTimeField(auto_now_add=True, verbose_name="创建时间") updated_at = models.DateTimeField(auto_now=True, verbose_name="更新时间") author = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name="作者") category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True, verbose_name="分类") is_published = models.BooleanField(default=False, verbose_name="是否发布") class Meta: ordering = ['-created_at'] # 默认按创建时间倒序排列 verbose_name = "文章" verbose_name_plural = "文章列表" def __str__(self): return self.title

这里有几个点得值得留意的是,

写好了模型,数据库还不知道这事儿呢。咱们得做迁移 (Migration)。这是 Django 的版本控制机制,记录你对模型的每一次改动。

执行这两条命令:

python manage.py makemigrations python manage.py migrate

makemigrations 是生成迁移文件(相当于生成 SQL 脚本),migrate 是真正执行到数据库里。

⚡ 效率提示:千万别手贱去手动改数据库表结构,然后又去改 models.py。这样两边对不上,迁移文件会报错报得你怀疑人生。一定要遵循“改 Model -> makemigrations -> migrate”这个流程。

有时候你可能会遇到想执行原生 SQL 的情况,虽然 Django ORM 很强大,但复杂查询确实可能力不从心。这时候你可以这么做:

from django.db import connection def my_custom_sql(): with connection.cursor() as cursor: cursor.execute("SELECT * FROM blog_post WHERE title LIKE '%%Django%%'") row = cursor.fetchall() return row

不过换个角度看,90% 的场景 ORM 都能搞定,而且 ORM 自带防 SQL 注入的安全机制,比自己拼字符串安全多了。Django 5.1 在异步 ORM 这块也在持续发力,虽然现在咱们写同步代码居多,但这也是未来的趋势,写代码的时候心里得有个数。

Admin后台基础:自动注册与列表页优化

说真的,Django 之所以这么多年一直火,很大一部分原因就是它自带了一个开箱即用的 Admin 后台。你想想,你刚定义完模型,几行代码就能拥有一个功能完善的数据管理界面,这在其他框架里简直不敢想。

咱们先看看最基础的注册。打开 blog/admin.py

# blog/admin.py from django.contrib import admin from .models import Post, Category # 最简单粗暴的注册方式 admin.site.register(Post) admin.site.register(Category)

运行 python manage.py runserver,访问 http://127.0.0.1:8000/admin/。你会发现进不去,因为你还没创建超级用户。

python manage.py createsuperuser

按提示输入用户名、邮箱、密码(输入密码的时候终端是不显示的,别以为卡住了)。登录进去,你就能看到 PostCategory 了。点进去能增删改查,是不是很爽?

但是,默认的界面太简陋了。比如 Post 列表页,只显示一个标题,我想看作者、发布状态、创建时间怎么办?这时候就得定制 ModelAdmin 了。

咱们把 admin.py 改得高级一点:

# blog/admin.py from django.contrib import admin from .models import Post, Category @admin.register(Category) class CategoryAdmin(admin.ModelAdmin): list_display = ('id', 'name') search_fields = ('name',) @admin.register(Post) class PostAdmin(admin.ModelAdmin): # 1. 列表页显示哪些字段 list_display = ('id', 'title', 'author', 'category', 'is_published', 'created_at') # 2. 右侧增加过滤器 list_filter = ('is_published', 'category', 'author') # 3. 顶部增加搜索框,可以按标题和正文搜 search_fields = ('title', 'content') # 4. 编辑页的字段排序和分组 fieldsets = ( ('基础信息', { 'fields': ('title', 'author', 'category') }), ('内容发布', { 'fields': ('content', 'is_published') }), ) # 5. 默认排序 ordering = ('-created_at',) # 6. 自动填充作者(这个很实用,避免每次选自己) def save_model(self, request, obj, form, change): if not change: # 如果是新建,而不是修改 obj.author = request.user super().save_model(request, obj, form, change)

改完保存,刷新页面。你会发现界面瞬间清晰了。这就是 Admin 定制的魅力。

💡 经验总结:如果你发现列表页数据很多,加载很慢,记得用 list_select_related。比如咱们这里 Post 关联了 authorcategory,如果不加优化,Django 可能会执行 N+1 次查询(这是常见面试考点!)。加上这个属性,Django 会用 select_related 一次性把关联数据取出来,性能提升巨大。

class PostAdmin(admin.ModelAdmin): # ... 其他配置 ... list_select_related = ('author', 'category') # 优化关联查询性能

另外,Django 5.1 的 Admin 也在不断微调,虽然大变样没有,但底层的稳定性和对现代前端标准的支持一直在跟进。对于做企业内部工具(ERP/CRM)或者 CMS 系统来说,把 Admin 稍微定制一下,能省下 80% 的 CRUD 开发时间。别小看这个后台,很多创业公司早期的产品就是靠它撑起来的。

进阶实战:自定义Admin Action与Inline嵌套

简单来说,到了这一步,你的Django项目已经跑起来了,Admin后台也能看到数据了。但是,默认的Admin只能一条条地改数据,这在真实业务里简直是效率杀手。比如你是做电商后台的,运营突然说:“帮我把这100个SKU的状态批量改成‘已下架’”,你总不能让他一个个点进去改吧?这时候,自定义Admin Action就派上用场了。

另外,我们在设计数据模型时,经常有主表和子表的关系,比如一个Order(订单)对应多个OrderItem(订单项)。在Admin里,如果看个订单还要去另一个页面找订单项,那体验太差了。这时候就得用到Inline嵌套

实战:自定义批量操作(Admin Action)

自定义Action其实很简单,就是在Admin类里定义一个方法,然后把它注册到actions列表里。

假设我们有一批文章需要批量发布。在 Django 5.1 的环境下,代码是这样的:

# admin.py from django.contrib import admin from django.db.models import QuerySet from .models import Article @admin.register(Article) class ArticleAdmin(admin.ModelAdmin): list_display = ('title', 'status', 'created_at') actions = ['make_published'] # 注册这个action # 定义具体的Action函数 def make_published(self, request, queryset: QuerySet): # 关键点:这里用 update 而不是 save,效率更高,直接走SQL updated_count = queryset.update(status='published') # 给个提示,告诉用户操作成功 self.message_user(request, f"{updated_count} 篇文章已成功发布。") # 给这个Action起个好听的中文名 make_published.short_description = "批量发布选中的文章"

这段代码的逻辑很清晰。我们在ArticleAdmin里加了一个make_published方法,它接收requestquerysetqueryset就是你在Admin列表页勾选的那些数据对象。这里有个小技巧,尽量用 queryset.update(),别用循环 obj.save(),前者是一次SQL请求,后者是N次,性能差距在批量操作时非常明显。

实战:Inline嵌套编辑

再来看Inline。假设我们有Author(作者)和Book(书)两个模型,一本书只有一个作者,但一个作者有多本书。

# models.py from django.db import models class Author(models.Model): name = models.CharField(max_length=100) def __str__(self): return self.name class Book(models.Model): author = models.ForeignKey(Author, on_delete=models.CASCADE, related_name='books') title = models.CharField(max_length=200) price = models.DecimalField(max_digits=6, decimal_places=2) def __str__(self): return self.title

现在,我们希望在编辑作者信息的时候,直接就能添加或修改他写的书。这时候就要用到StackedInline或者TabularInline

# admin.py from django.contrib import admin from .models import Author, Book # 定义Inline类 class BookInline(admin.TabularInline): # 也可以用 StackedInline,那个是纵向堆叠,Tabular是表格形式,更省空间 model = Book extra = 1 # 默认显示几个空的添加框 fields = ['title', 'price'] # 指定在Inline里显示哪些字段 @admin.register(Author) class AuthorAdmin(admin.ModelAdmin): list_display = ('name',) inlines = [BookInline] # 把Inline加进来

配置好之后,你再去Admin后台编辑作者,会发现下面多了一个区域,可以直接在这个页面上给这个作者加书,或者修改他名下的书。这就是Inline的威力,它把一对多的关系在UI上做了一层“扁平化”处理,非常符合人类的操作直觉。

🔧 实战技巧:如果你的Inline数据量可能很大(比如一个订单有几百个商品),千万别直接用默认的Inline,因为Admin会一次性加载所有数据,页面会卡死。这时候应该考虑使用django-admin-autocomplete-filter或者自己写一个自定义的Admin页面,或者限制max_num的数量。

安全与权限:细粒度控制与CSRF防护

很多新手觉得Django自带的安全机制就是“银弹”,配置好就万事大吉了。其实不是的,安全这东西,稍微一偷懒就会出大问题。Django 5.1 延续了框架一贯的安全基因,内置了防SQL注入、XSS、CSRF等机制,但这就好比你买了辆坦克,你不能因为装甲厚就不锁门。

细粒度权限控制

Django的Admin自带了一套权限系统,默认是addchangedeleteview。但其实,这套东西太粗了。比如你的后台有“财务”和“编辑”两个角色,财务只能看金额,不能改文章;编辑只能改文章,不能看财务数据。这时候默认的Group权限就不够用了。

我们需要自定义权限(Custom Permissions)。

models.py里定义权限:

# models.py class Article(models.Model): title = models.CharField(max_length=200) content = models.TextField() is_approved = models.BooleanField(default=False) class Meta: permissions = [ # codename 尽量不要有空格,用下划线 ("can_approve_article", "Can Approve Article"), ("can_view_financial_data", "Can View Financial Data"), ]

然后,在Admin里重写get_querysethas_change_permission等方法,来实现逻辑上的拦截。

# admin.py from django.contrib import admin from django.contrib.auth.models import Permission from .models import Article @admin.register(Article) class ArticleAdmin(admin.ModelAdmin): list_display = ('title', 'is_approved') def get_queryset(self, request): qs = super().get_queryset(request) # 如果不是超级管理员,且拥有查看财务数据的权限,才能看到特定数据 if request.user.is_superuser: return qs if request.user.has_perm('blog.can_view_financial_data'): return qs.filter(is_approved=True) return qs.none() # 没权限就返回空 def has_change_permission(self, request, obj=None): # 只有拥有审批权限的人才能修改 if request.user.has_perm('blog.can_approve_article'): return True return False

这样,我们就把权限控制到了“谁能看”和“谁能改”的级别。

CSRF防护与中间件

说到安全,不得不提CSRF(跨站请求伪造)。Django默认在settings.pyMIDDLEWARE里开启了django.middleware.csrf.CsrfViewMiddleware

这个中间件的作用就是给所有的POST表单自动注入一个csrf_token。如果你在写前端表单时忘了加{% csrf_token %},提交表单时就会遇到那个著名的403 Forbidden错误。

<!-- 模板中的表单 --> <form method="post"> {% csrf_token %} {{ form.as_p }} <button type="submit">提交</button> </form>

实际案例提醒:很多新手在写API接口或者前后端分离项目时,会发现POST请求一直403。这时候千万别图省事直接把CSRF中间件注释掉!那是在裸奔。正确的做法是,如果是纯API,应该使用django-rest-framework并在前端处理Token;如果是非表单提交,确保你在请求头里带了X-CSRFToken

另外,Django 5.1 在安全机制上也在持续跟进现代Web标准。比如,你可以配合django-csp(内容安全策略)这个第三方库来防止XSS攻击,或者利用框架内置的点击劫持保护(X-Frame-Options)。

⚡ 效率提示:当你在Admin里定制了一些自定义的视图(比如一个自定义的按钮触发一个函数),如果这个视图是接收POST请求的,记得要么加上@csrf_protect装饰器,要么确保你的前端请求带上了Token。对于Admin后台这种内部系统,建议开启双因子认证(2FA),这比单纯依赖Django的Session安全多了。

常见问题(FAQ)与Django Admin面试考点

作为一个写了5年Django的工程师,我见过太多新手在Admin和ORM上反复经验之谈。这里我整理了一些在2024年社区里讨论热度依然很高的问题,以及面试时经常被问到的点。

FAQ:那些让人头大的报错

Q1: 为什么我在Admin里改了数据,但是数据库没变,或者页面不显示?

这通常是因为你改了Model,但没有做makemigrationsmigrate。简单来说,Django的ORM是靠那张django_migrations表来追踪变更的。你改了Python代码,数据库不知道,所以不同步。

还有一种情况是你用了queryset.annotate()或者select_related(),导致列表页显示的数据是“计算值”而不是数据库里的原始值,这时候去改,可能改不到点子上。

Q2: Admin后台加载太慢了,怎么优化?

这是典型的N+1查询问题。当你在list_display里放了一个外键字段,比如author.name,Admin默认会去查N次作者表。

解决方案:重写get_queryset方法,使用select_related(针对一对一、外键)或prefetch_related(针对多对多、反向外键)。

# admin.py @admin.register(Article) class ArticleAdmin(admin.ModelAdmin): list_display = ('title', 'author_name') # 这里直接显示作者名字 def author_name(self, obj): return obj.author.name author_name.short_description = '作者' def get_queryset(self, request): # 核心要点:这里用 select_related 把 author 表 join 进来,避免循环查询 return super().get_queryset(request).select_related('author')

Q3: 如何在Admin里集成富文本编辑器?

现在最流行的是django-ckeditor或者django-summernote。以django-ckeditor为例,安装后直接把Model里的TextField替换成RichTextField即可,Admin会自动识别并渲染。

面试考点精讲

面试的时候,面试官通常不会问你“怎么用”,而是问你“为什么”。

考点1:Django的MVT和MVC有什么区别?

这是必考题。简单来说,MVC是标准,MVT是Django的特化。

所以,Django是MTV架构,本质上还是MVC,只是把V和C的角色换了一下名字。

考点2:Django Admin是如何实现自动注册和展示的?

这涉及到Django的自动发现机制(Autodiscovery)。当你运行admin.autodiscover()(在Django 5.1中,这通常在AdminConfig里自动处理)时,Django会遍历所有已安装App下的admin.py文件。

admin.py里,我们通过@admin.register(Model)或者admin.site.register(Model, ModelAdmin)把模型和Admin类绑定。Django内部维护了一个_registry字典,存放这些映射关系。当请求到来时,Admin的URLConf会根据这个注册表动态生成对应的视图和界面。

考点3:解释中间件(Middleware)的执行流程。

这是一个高频点。Django的中间件像是一个洋葱,请求进来是一层层进去,响应出去是一层层出来。

在2024-2026年的趋势里,随着异步支持深化,面试官可能还会问你asyncMiddleware怎么写,这时候你要提到__call__方法里对asyncio.iscoroutinefunction的判断。

📖 学习建议:准备面试时,不要死记硬背。去翻翻Django 5.1的源码,特别是django.contrib.admin.sitesdjango.core.handlers.base这两个文件,看看AdminSite是怎么注册URL的,看看BaseHandler是怎么处理中间件的。源码看明白了,这些面试题就是送分题。