Django
快速开始
- 安装Django
# 全局安装django
pip install django
# 使用 pipenv 创建虚拟环境并安装 Django
mkdir startdjango && cd startdjango # 创建虚拟环境
pipenv install django # 在虚拟环境中安装django
pipenv shell # 激活虚拟环境
- 创建项目
django-admin startproject [项目名称] # 新建一个django项目
django-admin startproject [项目目录] . # 在当前目录创建django项目
- 创建APP
python manage.py startapp [APP名称]
- 运行项目
python manage.py runserver
项目文件架构
django_project → 项目根目录
├── manage.py → 项目的管理,启动项目、创建app、数据管理(常使用)
└── django_project → 项目配置目录(与项目同名)
├── __init__.py → 标识为Python包
├── settings.py → 全局配置(常修改)
├── urls.py → 主路由配置(常修改)
├── asgi.py → ASGI服务器配置, 异步接收网络请求
└── wsgi.py → WSGI服务器配置, 同步接收网络请求
APP
在 Django 中,APP(应用) 是一个可重用的功能模块,用于实现特定的业务逻辑。它是 Django 项目的基本组成单元,遵循 "一个APP只做一件事" 的设计原则。
- 独立性: 每个APP是一个独立的Python包,包含完整的功能闭环(模型、视图、模板等)
- 低耦合:APP之间应通过公共接口通信,尽量减少直接依赖
- 可复用:每个APP可以单独摘出来跨项目复用
- 配置分离:敏感配置应放在项目settings中,而非APP内
- 项目
- 业务APP: 用户管理、订单系统等(需自行开发)
- 通用APP: 评论系统、API接口等(可第三方安装)
- 内置APP: admin(后台)、auth(认证)、sessions 等
创建APP
在项目根目录下执行bashpython manage.py startapp blog
会在此项目文件夹下创建新app如下:
markdownblog # APP名称(Python包) ├── migrations/ # 数据库变更记录(自动生成) │ ├── __init__.py │ ├── templates/ # 专属模板目录(html文件) │ └── blog/ # 推荐加APP名前缀避免命名冲突 │ ├── base.html │ ├── post_list.html │ └── post_detail.html │ ├── static/ # 专属静态文件(图片、css、js等文件) │ └── blog/ │ ├── js/ │ ├── plugins/ # 推荐加APP名前缀避免命名冲突 │ ├── css/ │ │ └── style.css │ └── images/ │ └── logo.png │ ├── __init__.py ├── admin.py # django默认提供了admin后台管理 ├── apps.py # APP配置类(自动生成) ├── models.py # 数据模型定义(重要,数据库操作相关) ├── tests.py # 单元测试 ├── urls.py # 子路由配置(需手动创建) └── views.py # 视图逻辑(重要,函数)
注册APP
将新创建的app添加到项目同名目录下的settings.py
文件中的INSTALLED_APPS
列表中,格式为'app_name.apps.App_nameConfig'
。pythonINSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'blog.apps.BlogConfig', # 注册新创建的app ]
URL与视图(View)
URL介绍
URL是Uniform Resource Locator
的简写,即统一资源定位符
一个URL由以下几个部分组成:
- Scheme:访问协议,一般是http或者https或ftp等
- Domain Name:域名或ip地址,比如
www.baidu.com
- Port:端口号,
http
协议是80端口,https
是443端口 - Path to the file:用来指定资源的路径
- Parameters:查询字符串,比如:
www.baidu.com/s?wd=python
, 后面的wd=python
就是查询字符串 - Anchor:锚点,用于前端做页面定位
注意:URL中所有字符都基于ASCII字符集,浏览器会对中文字符先进行编码再传输
路由模块化
django可以在app
内部写自己的urls.py
, 而在项目的urls.py
中包含这个app
的urls.py
。
# mysite/mysite/urls.py
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls')), # 将blog/的请求交给blog/urls.py处理
]
# mysite/blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('articles/', views.article_list), # 显示所有文章列表
path('article/<int:id>/', views.article_detail),
]
# mysite/blog/views.py
from django.shortcuts import render, HttpResponse
def article_list(request):
# 示例1:返回HTTP页面
return render(request, "article_list.html")
def article_detail(request, id)
# 示例2:返回文本内容
return HttpResponse(f"文章ID:{id}")
path 和 re_path 方法
Django提供了两种设计URL的方法:path
和 re_path
, 它们均支持视图函数或类传递参数。path
和 re_path
支持传递的数据类型只有 str
, int
, slug
, uuid
四种。一般来说 re_path
更强大,但写起来更复杂一些:
- path方法python
path(route, view, name=None, kwargs = None) # 参数说明: # route:url的匹配规则。这个参数中可以指定url中需要传递的参数。 # 传递参数是通过<>来指定的,并且可以指定以下几种常用类型: # str:默认类型,非空字符串,字符串内容不能包含斜杠'/' # int: 一般整形 # slug:由横杠'-'或下划线'_'连接英文字符或数字组成的字符串,如:abc-122_dasd # uuid: 匹配uuid字符串 # path:可用来传递文件路径等,如:'C:/Document/demo.txt' # view: 可以是视图函数、类视图或者是django.urls.include()函数的返回值 # name: url别名,当项目庞大,url较多时,需要对每个url起别名
python# 使用<变量类型:变量名> or <变量名>传递, 例如 <int:id> 或 <username> urlpatterns = [ path('blog/articles/<int:id>/', views.article_detail), ]
- re_path方法python示例
# `re_path`方法是采用正则表达式regex匹配 # 采用命名组(?P<变量名>表达式)的方式传递参数 # 使用re_path时不一定总是以$结尾 urlpatterns = [ re_path(r'^blog/articles/(?P<id>\d+)/$', views.article_detail), ]
python# 示例一,path方法 from django.urls import path from . import views urlpatterns = [ path('articles/2003/', views.special_case_2003), path('articles/<int:year>/', views.year_archive), path('articles/<int:year>/<int:month>/', views.month_archive), path('articles/<int:year>/<int:month>/<slug:slug>/', views.article_detail), ] # 示例二:re_path方法,结果与上例等同 from django.urls import path, re_path from . import views urlpatterns = [ path('articles/2003/', views.special_case_2003), re_path(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive), re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive), re_path(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/(?P<slug>[\w-]+)/$', views.article_detail), ]
URL的命名与命名空间
url的命名
为url命名可以根据
path.name
反向读取路径python# mysite/blog/urls.py from django.urls import path, re_path from . import views app_name = 'blog' # 命名空间 urlpatterns = [ path('articles/', views.article_list, name = 'article_list'), path('articles/<int:id>/', views.article_detail, name = 'article_detail'), ]
命名空间
- 在项目的主
urls.py
中定义命名空间
python# 项目根 urls.py from django.urls import include, path # 通过 include() 指定 namespace urlpatterns = [ path('blog/', include('blog.urls', namespace='blog')), # 命名空间 blog path('news/', include('news.urls', namespace='news')), # 命名空间 news ]
- 在应用的
urls.py
中定义app_name
python# blog/urls.py from django.urls import path from . import views app_name = 'blog' # 应用命名空间(必须与主 urls.py 的 namespace 一致) urlpatterns = [ path('detail/<int:id>/', views.article_detail, name='detail'), # 实际 URL: /blog/articles/1/ ]
- 在项目的主
URL的反向解析
当知道path.name
,可以通过reverse()
反向解析出对应path路径
在视图函数中:
pythonfrom django.urls import reverse def my_view(request): url_1 = reverse('article_list') # 生成 /blog/articles/ url_2 = reverse('article_detail', kwargs={'id': 1}) # 生成 /blog/articles/1/
如果有应用命名空间,那么反向解析时应加上命名空间:
pythonfrom django.urls import reverse def my_view(request): url_1 = reverse('blog:article_list') # 生成 /blog/articles/ url_2 = reverse('blog:article_detail', kwargs={'id': 1}) # 生成 /blog/articles/1/
在模板中:
html<a href="{% url 'article_list' %}">查看文章列表</a> <a href="{% url 'article_detail' id=1 %}">查看文章1</a>
支持位置参数或关键字参数:
pythonreverse('article-detail', args=[1]) # 位置参数 reverse('article-detail', kwargs={'pk': 1}) # 关键字参数
注意:reverse反转url时不区分GET请求和POST请求,因此不能在反转的时候query-srting参数。如果想要添加query-srting参数,只能手动添加:
login_url = reverse('article_by_year') + '?year=2023'
> /articles?year=2023
URL传参方式
- 路径传参
这是最常用的方式,直接在URL路径中包含参数如:http://127.0.0.1:8000/articles/2008/3/
# urls.py
from django.urls import path
from . import views
urlpatterns = [
# 需定义参数的类型,如果类型不匹配就跳转404页面
path('articles/<int:year>/<int:month>/', views.month_archive),
]
# views.py
def month_archive(request, year, month):
return HttpResponse(f"您查找的文章日期为:{year}年{month}月")
- Query-String
如:https://127.0.0.1:8000/articles?year=2008&month=12
python# urls.py from django.urls import path from . import views urlpatterns = [ # 使用query-string传参时路径没有'/' path('articles', views.year_archive, name='article_by_year'), ]
python# views.py def article_list(request): year = request.GET.get('year') month = request.GET.get('month') return HttpResponse(f"您查找的文章日期为:{year}年{month}月")
模板(Template)
Django自带的模板引擎是DTL(Django Tamplate Language),当然Django也支持Jinja2等其他模板引擎。
官方文档参考:Django模板
模板的渲染
render_to_string
: 找到模板,将模板编译后渲染成Python字符串格式,然后通过HttpResponse类包装成一个HttpResponse对象返回。
from django.template.loader import render_to_string
from django.http import HttpResponse
def article_detail(request, book_id):
html = render_to_string('detail.html')
return HttpResponse(html)
- 使用**
render
**一步到位完成渲染成字符串 + 包装成HttpResponse对象:
# 使用render.context向模板传递参数,context是字典类型
from django.shortcuts import render
def article_list(request):
return render(request, 'list.html', context={
'username': '张三',
'parent': ['father', 'mother'],
'age': 10
})
变量
变量被包围在双层
{
中,就像这样:html<p>My first name is {{ first_name }}. My last name is {{ last_name }}.</p>
字典查找、属性查找和列表索引查找都使用点表示法实现:
html<p>{{ my_dict.key }}</p> <p>{{ my_object.attribute }}</p> <p>{{ my_list.0 }}</p>
变量可以渲染对象:
python# views.py from django.shortcuts import render class Person: def __init__(self, name): self.name = name def info(request): return render(request, 'info.html', context={'person': Person('张三')})
html# info.html <p>实例化一个Person对象:{{person.name}} </p>
模板常用标签
标签被包围在 {% 和 %} 中,就像这样:
{% csrf_token %}
if
标签:if-elif-else-endif
.if标签
中可以使用==
,!=
,<
,<=
,>
,>=
,in
,not in
,is
,is not
等判断运算符html{% if age >= 18 %} <p>成年</p> {% else %} <p>未成年</p> {% endif %}
for ... in ...
标签:类似于Python
中的for ... in ...
. 可以遍历列表、元组、字符串、字典等一切可遍历对象html{% for person in persons %} <p>{{ person.name }}</p> {% endfor %}
如果需要反向遍历,需要加上
reversed
html{% for person in persons reversed %} <p>{{ forloop.counter0 }} {{ person.name }}</p> {% endfor %}
在
for
循环中,DTL还提供了一些变量可供使用:forloop.counter
:当前循环的下标,从1
开始forloop.counter0
:当前循环的下标,从0
开始forloop.revcounter
:当前循环的反向下标,从n->1forloop.revcounter0
:反向下标,从n->0forloop.first
:是否第一次遍历forloop.last
:是否最后一次遍历
for ... in ... empty ...
:与for ... in ...
的区别在于当遍历的对象如果为空时,执行empty中的内容html{% for person in persons %} <p>{{ person.name }}</p> {% empty %} <p>暂时没有任何人</p> {% endfor %}
with
标签:在模板中定义变量。有时候对一个变量访问比较繁琐,可以先将这个变量缓存到新建的一个变量上进行使用
context = {"persons": ["张三", "李四"]}
{% with Li=persons.1 %}
<p>{{ Li }}</p>
{% endwith %}
url
标签:它的主要作用是根据urls.py中的名称(name)动态生成URL,而不是硬编码URL路径,这使得项目更易于维护
<a herf="{% url 'blog:article_list' %}">文章列表</a>
如果url反转的时候需要传递参数,可以传位置参数和关键字参数,但是两种参数不能同时使用, 多个参数间使用空格分隔:
# url.py
path('article_detail/<int:id>/', views.article_detial, name='article_detail')
# 位置参数
<a herf="{% url 'blog:article_detail' 1 %}">图书详情页面</a>
# 关键字参数
<a herf="{% url 'blog:article_detail' id=1 %}">图书详情页面</a>
# 需手动添加query-string
<a herf="{% url 'blog:article_detail' id=1 %}?page=1">图书详情页面</a>
模板常用过滤器
过滤器就是模板的函数,有时需要对一些数据进行处理后才能使用,因此需要模板过滤器,常用的有这些
add
:将右边的参数累加到左边的值。这个过滤器会尝试将值和参数转换成整型再相加。如果转换成整型失败,那么会将值和参数进行拼接。如果是值是字符串,则会拼接字符串;如果值是列表,则会拼接成一个新列表html{{ value|add:"2" }}
cut
:移除值中所有指定的字符串。类似于pyhton的replace(args, " ")html{{ value|cut:" " }}
date
:将一个日期按照指定的格式,格式化成字符串pythoncontext = { "birthday": datetime.now() }
html<p>{{birthday|date:"Y/m/d"}}</p>
default
:如果值是[], "", {}, None等在if判断中为False的值, 则使用default过滤器提供的默认值:html<p>{{value|default:"nothing"}}</p>
floatformat
:使用四舍五入的方式格式化一个浮点类型。如果这个过滤器没有传递参数则默认保留一位小数,如果小数点后面全是0则只会保留整数。参数值代表要保留几位小数html# 假设value = 3.1415 {{value|floatformat}} -> 3.1 {{value|floatformat:2}} -> 3.14
join
:类似Python中的join,将列表\元组\字符串用指定的字符进行拼接html{{value|join:"-"}}
length
: 获取一个列表\元组\字符串\字典的长度html{{value|length}}
safe
:标记一个字符串是安全的,即关掉字符串的自动转义
当值是html语句时,可以自动转义成网页效果html# 假设value = <h1>标题 1</h1> {{value|safe}}
slice
:切片过滤器,类似Python中的切片操作。可以对列表\元组\字符串进行切片操作html{{value|slice:":3"}} # 取前3个元素 {{value|slice:"2:4"}} # 取第2到第4个元素
striptags
:移除字符串中的html标签html{{value|striptags}}
模板结构
include
标签{% include %}
标签用于在当前模板中包含另一个模板的内容,实现模板的模块化和代码复用。
应用场景:- 将页面的公共部分(如页眉、页脚、导航栏)提取为单独文件
- 包含重复使用的UI组件
- 动态加载不同部分的模版
示例如下:
<!-- parent.html -->
{% with secret_key="12345" public_info="Welcome" %}
<!-- 包含子模板,不使用 only -->
{% include "child.html" with message="Hello" %}
{% endwith %}
<!-- child.html -->
<p>消息: {{ message }}</p> {# 能访问显式传递的 message #}
<p>公共信息: {{ public_info }}</p> {# 能访问父模板的 public_info #}
<p>密钥: {{ secret_key }}</p> {# 意外暴露了 secret_key #}
输出结果:
消息: Hello
公共信息: Welcome
密钥: 12345 <!-- 这可能是安全隐患! -->
使用only
控制子模版只能访问传给他的变量,不能访问上下文变量:
<!-- parent.html -->
{% with secret_key="12345" public_info="Welcome" %}
<!-- 使用 only 限制访问 -->
{% include "child.html" with message="Hello" only %}
{% endwith %}
<!-- child.html -->
<p>消息: {{ message }}</p> {# 能访问显式传递的 message #}
<p>公共信息: {{ public_info }}</p> {# 无法访问,变量不存在 #}
<p>密钥: {{ secret_key }}</p> {# 无法访问,变量不存在 #}
输出结果:
消息: Hello
公共信息:
密钥:
模版继承 模板继承是 Django 模板系统中最强大的部分,允许你构建一个基础"骨架"模板,然后子模板可以覆盖骨架中的特定部分,实现代码复用和统一布局。
应用场景:- 为整个网站创建统一布局
- 根据不同应用或页面类型创建不同的基础模版
- 实现多级继承(如全站基础模版->应用基础模版->具体页面模版)
- 基础模版(base.html)
html<!DOCTYPE html> <html> <head> <title>{% block title %}默认标题{% endblock %}</title> </head> <body> {% block content %}这是父模版的body{% endblock %} </body> </html>
- 子模版(index.html)
html{% extends "base.html" %} {% block title %}我的页面标题{% endblock %} {% block content %} {{ block.super }} {# 包含父模板中的内容: "这是父模版的body" #} <p>这是子模版的部分...</p> {% endblock %}
加载静态文件
1. 首先确保 django.contrib.staticfiles
已经添加到 settings.INSTALLED_APPS
中.
2. 确保在 settings.py
中设置了 STATIC_URL
, 比如:
STATIC_URL = "static/"
3. 在app01
文件夹下创建static
文件夹,在static
文件夹下创建app01
同名文件夹用于存放静态文件。这样做的目的是避免多个app
下具有同名静态文件的路径产生混淆。
4. 工程可能包含未与任何app绑定的静态资源。除了在 my_app
中使用static/
目录,还可以在setting.py
中定义一个目录列表(STATICFILES_DIRS
) ,Django 会从中寻找静态文件。
STATICFILES_DIRS = [
BASE_DIR / "static",
"/var/www/static/",
]
5. 在模版中使用load标签加载static标签,示例代码如下:
{% load static %}
<!-- CSS 文件 -->
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<!-- JavaScript 文件 -->
<script src="{% static 'js/main.js' %}"></script>
<!-- 图片 -->
<img src="{% static 'images/logo.png' %}" alt="Logo">
数据库的配置和使用
配置数据库
以MySQL为例,首先需要安装pymysql
库(需要先安装mysql数据库),可以使用以下命令安装:
pip install pymysql
然后在Django项目的settings.py
文件中配置数据库连接信息。以下是一个MySQL数据库的配置示例:
# project_demo/settings.py
DATABASES = {
'default': {
# 数据库引擎(是mysql还是oracle等)
'ENGINE': 'django.db.backends.mysql',
# 数据库名
'NAME': 'database_demo ',
# 连接数据库的用户名
'USER': 'root',
# 连接数据库的密码
'PASSWORD': '123456qwe',
# 数据库的主机地址
'HOST': '127.0.0.1',
# 数据库的端口号
'PORT': '3306',
}
}
原生SQL
操作数据库
使用原生 sql
语句操作数据库其实是使用 python db api
来操作的,比如mysql使用的 api
是 pymysql
(需要pip install pymysql
) 示例代码:
# connection对象会自动读取setting.py中数据库的配置信息
from django.db import connection
# 获取游标对象
cursor = connection.cursor()
# 执行SQL语句
cursor.execute("SELECT * FROM student")
# 获取所有的数据
rows = cursor.fetchall()
# 遍历查询到的数据
for row in rows:
print(row)
- 游标对象常用api
- 执行sql语句python
# 执行一次sql语句 cursor. execute(sql, params=None) # params可以是列表、元组或字典 # 示例 cursor.exexute("SELECT * FROM student WHERE id = %s", [1]) cursor.execute( "SELECT * FROM myapp_mymodel WHERE id = %(id)s AND status = %(status)s", {'id': 1, 'status': 'active'})
python# 多次执行同一sql语句,每次使用不同参数 cursor.executemany(sql, param_list) # param_list 为列表类型 # 示例 data = [(1, 'John'), (2, 'Jane'), (3, 'Mike')] cursor.executemany("INSERT INTO student (id, name) VALUES (%s, %s)", data)
- 获取查询结果的一行或多行python
# 获取查询结果的下一行 row = cursor.fetchone() # 获取查询结果的多行 rows = cursor.fetchmany(size=None) # size: 行数 # 获取查询结果的所有剩余行 all_rows = cursor.fetchall()
- 关闭游标python
# 注意:关闭游标后此游标不能再次使用 cursor.close()
- 执行sql语句
<后续Django-ORM...>