共计 8256 个字符,预计需要花费 21 分钟才能阅读完成。
安装
pip install django
安装完,在 Python 安装目录 Libsite-packages 下会多一个文件夹 django(框架源码),在 Scripts 文件夹下会多一个文件 django-admin.exe(用来创建 django 项目)。
创建项目
打开终端,进入某个目录(项目路径),执行命令创建项目:django-admin startproject mysite
。
运行后将在目录下生成一个 mysite 目录,也就是 Django 项目的根目录。
创建应用
在 Django 中,每一个应用(app)都是一个 Python 包。
cd mysite
python manage.py startapp app
运行后会自动生成 app
文件夹:
app
| admin.py # Django 默认提供 admin 后台管理
| apps.py # app 启动类
| models.py # 对数据库操作
| tests.py # 单元测试
| views.py # 视图函数
| __init__.py
|
---migrations # 数据框变更记录
__init__.py
注册应用
打开mysite/setting.py
,在 INSTALLED_APPS 当中注册 app 模块:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app',
]
设置
时区和语言
修改项目的 settings.py 文件如下:
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_TZ = False
admin 后台
设置 admin 后台应用显示为中文,需要修改应用目录下的 apps.py 文件:
class LoginConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'login'
verbose_name = '用户管理' # 新增
设置 admin 后台首页的网页标题,需要修改应用目录下的的 admin.py 文件:
admin.site.site_title = '后台管理' # 修改标题
admin.site.site_header = '后台管理' # 修改 header
本地配置
首先创建自己的本地配置文件:local_settings.py
。在 settings.py 中导入:
try:
from .local_settings import *
except ImportError:
pass
切记:给别人代码时,不要给local_settings.py
。
快速上手
URL 和视图配置
打开mysite/urls.py
,修改为:
from django.contrib import admin
from django.urls import path
from app import views # 导入 views
urlpatterns = [path('admin/', admin.site.urls),
# 域名 /index/ -> index 函数,即绑定 URL 与视图函数
path('index/', views.index),
]
编写视图函数
视图,即具体的业务代码,打开app/views.py
,修改为:
from django.shortcuts import render, HttpResponse
def index(requests):
return HttpResponse('欢迎使用')
启动项目
命令行启动
python manage.py runserver
Django 的服务器默认运行在 8000 端口,如果想指定端口,命令如下:
python manage.py runserver 8080
templates 模板
在 app 文件夹下新建 templates
文件夹,然后在 templates 文件夹下新建文件user_list
。
在文件 mysite/urls.py
中添加 URL:
path('user/list', views.user_list, {'name': '李小龙'}),
在文件 app/views.py
中添加函数:
def user_list(request):
# 去 app 目录下的 templates 目录寻找 user_list
return render(request, 'user_list')
静态文件
在 app 文件夹下新建 static
文件夹,然后在 static 目录下创建 css 目录,js 目录,img 目录,plugin 目录,分别放 css 文件,js 文件,图片和插件。
在模板中使用需要加入 {% load static %}
代码,以下实例从静态目录中引入图片,修改 user_list
为:
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title> 用户列表 </title>
</head>
<body>
<h1>{{name}}</h1>
<img src="{% static 'img/logo.webp' %}" alt="logo">
</body>
</html>
模板语法
本质上是在 html 中写一些占位符,由数据对这些占位符进行替换和处理。
变量相关
在文件 mysite/urls.py
中添加 URL:
path('tpl/', views.tpl),
在文件 app/views.py
中添加函数:
def tpl(request):
name = '李小龙'
roles = ['管理员', 'CEO', '保安']
user_info = {'name': " 李小龙 ", 'sex': '女', 'role': '保安'}
return render(request, 'tpl', {'n1': name, 'n2': roles, 'n3': user_info})
render 使用了一个字典作为参数,字典中元素的键值 n1 对应了模板中的变量 {{n1}}。
在 app/templates
目录下新建 tpl
文件,代码如下:
<h1>{{n1}}</h1>
<h1>{{n2}}</h1>
<h1>{{n2.0}}</h1>
<h1>{{n2.1}}</h1>
<h1>{{n3.name}}</h1>
<h1>{{n3.sex}}</h1>
访问 http://127.0.0.1:8000/tpl/
,查看页面输出结果。
逻辑相关
在 tpl
文件添加如下代码:
<hr/>
<div>
{% for item in n2 %}
<span>{{item}}</span>
{% endfor %}
</div>
<hr/>
<ul>
{% for k,v in n3.items %}
<li>{{k}} = {{v}}</li>
{% endfor %}
</ul>
访问 http://127.0.0.1:8000/tpl/
,查看页面输出结果。
模板继承
父模板
{% block 名称 %}
预留给子模板的区域,可以设置默认内容
{% endblock 名称 %}
子模板
{% extends " 父模板路径 " %}
子模板使用标签 extends 继承父模板。
接下来在 templates 目录中添加 base 文件,代码如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title> 编程笔记 </title>
</head>
<body>
<h1>Hello World!</h1>
<p> 编程笔记 Django 测试。</p>
{% block mainbody %}
<p>original</p>
{% endblock %}
</body>
</html>
index 继承 base,index 修改后的代码如下:
{%extends "base" %}
{% block mainbody %}
<p> 继承了 base 文件。</p>
{% endblock %}
访问 http://127.0.0.1:8000/index 查看页面输出结果。可以看到,这里相同名字的 block 标签用于替换 base 的相应 block。
请求和响应
在文件 mysite/urls.py
中添加 URL:
path('something/', views.something),
在文件 app/views.py
中添加函数:
from django.shortcuts import redirect
def something(request):
# 获取请求方式 GET/POST
print(request.method)
# 在 url 传递值 /something/?n1= 李小龙
print(request.GET)
# 在请求体中提交数据
print(request.POST)
# 重定向
return redirect('https://www.baidu.com/')
访问 http://127.0.0.1:8000/something/
,查看页面输出结果。
Django 模型
MySQL 是 Web 应用中最常用的数据库,这里以 MySQL 作为实例进行介绍。如果没安装 mysql 驱动,执行以下命令安装:
pip3 install pymysql
这里作个说明,pymysql 和 mysqlclient 是目前 Python 连接 MySQL 的主流方式。
区别是:mysqlclient 速度比 pymysql 快;pymysql 更加简单易使用。
如果是大型项目,当然是用 mysqlclient,该驱动性能比较优异;中小项目用 pymysql 就可以了,毕竟是使用 Python 写的,契合度更高。
数据库配置
在项目的 settings.py 文件中找到 DATABASES 配置项,将其信息修改为:
DATABASES = {
'default':
{
'ENGINE': 'django.db.backends.mysql', # 数据库引擎
'NAME': 'django', # 数据库名称
'HOST': '127.0.0.1', # 数据库地址,本机 ip 地址
'PORT': 3306, # 端口
'USER': 'root', # 数据库用户名
'PASSWORD': '', # 数据库密码
}
}
接下来,告诉 Django 使用 pymysql 模块连接 mysql 数据库:
## 在与 settings.py 同级目录下的 __init__.py 中引入模块和进行配置
import pymysql
pymysql.install_as_MySQLdb()
操作表
创建表
在 models.py 文件中:
class UserInfo(models.Model):
name = models.CharField(max_length=32)
# 设置默认值
age = models.IntegerField(default=0)
# 允许为空
data = models.DateField(null=True, blank=True)
执行命令:
## 创建应用的迁移数据文件
python manage.py makemigrations app
## 生成数据库表
python manage.py migrate
修改表
在文件 urls.py
中添加 URL:
path('orm/', views.orm),
在文件 views.py
中添加函数:
def orm(request):
# 新增
UserInfo.objects.create(name='李小龙', age=18, date='2022-04-04')
# 删除
# UserInfo.objects.filter(id=1).delete()
# UserInfo.objects.all().delete()
# 查询数据,data_list 是 QuerySet 类型
# data_list = UserInfo.objects.all()
# for obj in data_list:
# print(obj.name, obj.age, obj.date)
# 获取第一条数据
# row_obj = UserInfo.objects.filter(id=5).first()
# print(row_obj.id, row_obj.name)
# 更新数据
# UserInfo.objects.all().update(age=20)
# UserInfo.objects.filter(id=5).update(age=20)
return HttpResponse('成功')
访问 http://127.0.0.1:8000/orm/
,查看页面输出结果。
外键删除
一个模型如果使用了外键,那么在对方那个模型被删掉后,该进行什么样的操作,可通过 on_delete 来指定。指定的类型如下:
- CASCADE:级联操作。如果外键对应的那条数据被删除了,那么这条数据也会被删除
- PROTECT:受保护。即只要这条数据引用了外键的那条数据,那么就不能删除外键的那条数据。如果我们强行删除,Django 就会报错
- SET_NULL:设置为空。如果外键的那条数据被删除了,那么在本条数据上就将这个字段设置为空。如果设置这个选项,前提是要指定这个字段可以为空
- SET_DEFAULT:设置默认值。如果外键的那条数据被删除了,那么本条数据上就将这个字段设置为默认值。如果设置这个选项,前提是要指定这个字段一个默认值。
- SET():如果外键的那条数据被删除了,那么将会获取 SET 函数中的值来作为这个外键的值。SET 函数可以接收一个可以调用的对象(比如函数或者方法),如果是可以调用的对象,那么会将这个对象调用后的结果作为值返回回去。可以不用指定默认值
- DO_NOTHING:不采取任何行为。一切全看数据库级别的约束
数据库接口
QuerySet 是 Django 提供的强大的数据库接口(API)。从数据库中查询得到的结果一般是一个集合,这个集合叫做 QuerySet。
QuerySet 惰性
所谓惰性,就是不会主动去执行。创建 QuerySet 的行为(语句)不会马上执行 sql,而是返回一个 QuerySet(查询结果集对象),只有在调用 QuerySet 的时候才会涉及到数据库操作。
QuerySet 缓存
下例中例 1 比例 2 要好,因为在你打印文章标题后,Django 不仅执行了查询,还把查询到的 article_list 放在了缓存里,因此这个 article_list 是可以复用的。例 2 就不行了。
## Example 1: Good
article_list = Article.objects.filter(title_contains="django")
for article in article_list:
print(article.title)
## Example 2: Bad
for article in Article.objects.filter(title_contains="django"):
print(article.title)
在 loop 前加个 if 判断,防止 article_list
是个空数据集:
article_list = Article.objects.filter(title_contains="django")
if article_list:
for article in article_list:
print(article.title)
else:
print("No records")
如果只是希望了解查询的结果是否存在,而不需要使用整个数据集,这时 if 触发整个 queryset 的缓存变成了一件坏事情。这时可以用 exists()方法。与 if 判断不同,exists 只会检查查询结果是否存在,返回 True 或 False,而不会缓存 article_list。
article_list = Article.objects.filter(title_contains="django")
if article_list.exists():
print("Records found.")
else:
print("No records")
统计查询结果数量
len()与 count()均能统计查询结果的数量。一般来说 count 更快,因为它是从数据库层面直接获取查询结果的数量,而不是返回整个数据集,而 len 会导致 queryset 的执行,需要将整个 queryset 载入内存后才能统计其长度。但事情也没有绝对,如果数据集 queryset 已经在缓存里了,使用 len 更快,因为它不需要跟数据库再次打交道。
下面三个例子中,只有例 2 最差,尽量不要用:
## Example 1: Good
count = Article.objects.filter(title_contains="django").count()
## Example 2:Bad
count = Article.objects.filter(title_contains="django").len()
## Example 3: Good
article_list = Article.objects.filter(title_contains="django")
if article_list:
print("{} records found.".format(article_list.len()))
按需查询数据
当查询到的 queryset 非常大时,会大量占用内存(缓存)。我们可以使用 values 和 value_list 方法按需提取数据。
article_list = Article.objects.filter(title_contains="django").values('title')
if article_list:
print(article.title)
article_list = Article.objects.filter(title_contains="django").values_list('id', 'title')
if article_list:
print(article.title)
还可以使用 defer 和 only 这两个查询方法来实现按需查询数据。
defer 方法
defer 方法的用途是查询数据库时跳过指定的字段,防止不要的字段载入内存,从而节省空间:
## 查询时跳过 id 和 title 字段
Article.objects.defer('id', 'title')
该方法可以和 filter, exclude 方法联用,其顺序无关紧要,如下所示:
Article.objects.defer(‘id’).filter(title_contains=”django”).defer(‘title’)
only 方法
only 方法与 defer 方法作用类似,只不过 only 方法是指定需要载入的字段。比如下面查询将只会载入 id 和 title。
Article.objects.only('id', 'title')
使用 only 方法要注意它的顺序,它的执行以最后一个为准。下例将只会载入 pub_date:
Article.objects.only('id', 'title').only('pub_date')
only 方法与 defer 方法可以联用,但要注意先后顺序。下例只会载入 title:
Article.objects.only('id', 'title').defer('id')
更新数据库
更新部分字段
更新数据库部分字段更好的方式是用 update,而不是 save 方法。
## Example 1: Good
Article.objects.filter(id=10).update(title='Django')
## Example 2: Bad
article = Article.objects.get(id=10)
Article.title = "Django"
article.save()
批量创建或更新数据
Django 提供的 bulk_create 和 bulk_update 方法可以一次添加或更新多条数据,效率要高很多,如下所示:
## 内存生成多个对象实例
articles = [Article(title="title1", body="body1"), Article(title="title2", body="body2"), Article(title="title3", body="body3")]
## 执行一次 SQL 插入数据
Article.objects.bulk_create(articles)
命令
## 创建超级管理员
python manage.py createsuperuser
## 无需启动 Django 操作数据库中某个表的数据
python manage.py shell