这里记录一下 Django 的基础入门教程,是根据官网提供的文档整理记录。
安装 Django 很简单,一条命令就可以了:
pip install Django
。
新建一个项目
django-admin startproject mysite
这将会在终端当前的目录中新建一个 mysite 的文件夹。在实际应用中,最好不要在类似 /var/www 目录下新建这个项目文件夹,而是在 /home/mycode 中比较稳妥。
mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
asgi.py
wsgi.py
- 最外围的 mysite/ 目录是当前这个项目的容器,其名称对 Django 并不敏感,可以修改为自己需要的值。
- manage.py 是一个命令行工具,可以用来和 Django 交互,执行相应的操作。文档见 django-admin and manage.py 。
- 内部的 mysite 文件夹是项目真正的 Python 包,它的名称也是 Python 包名称,在 import 的时候使用,例如 mysite.urls。
- mysite/__init__.py 是一个空文件,告诉 Python 这个目录是一个 Python 包。
- mysite/settings.py 是 Django 项目的配置文件,文档见 Django settings
- mysite/urls.py 是这个 Django 项目的 URL 声明,一个由 Django 驱动的站点的“目录”。文档见 URL dispatcher
- mysite/asgi.py 是 ASGI 兼容的 Web 服务器的入口点。详见 How to deploy with ASGI
- mysite/wsgi.py 是 WSGI 兼容的 Web 服务器的入口点。详见 How to deploy with WSGI
使用开发服务器
python manage.py runserver
# 或者以下命令以特定 ip 和端口启动
django-admin runserver 1.2.3.4:8000
访问 http://127.0.0.1:8000/ 即可看到自己的 Django 项目网站。注意 runserver 打开的这个开发服务器不要在生产环境中使用。runserver 文档见 runserver 。
在开发过程中,runserver 打开的开发服务器会自动重新加载修改过的 Python 代码,因此无需在修改代码后重启服务器。特别地,添加新文件之后需要手动重启生效。
新建 Polls 应用
应用可以放在任意的 Python path 可以访问到的位置。本教程中放在与 manage.py 同级,使这个 app 可以作为自己的顶级模块导入,而不是作为 mysite 的子模块导入。
python manage.py startapp polls
polls/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py
编写第一个视图
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world. You're at the polls index.")
为了调用展示这个视图,需要将它映射到一个 URL,因此我们需要一个 URLconf,在 polls 文件夹下新建一个 urls.py 文件,新的 polls 目录结构如下:
polls/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
urls.py
views.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('polls/', include('polls.urls')),
path('admin/', admin.site.urls),
]
需要注意的是,除了 admin.site.urls,其他任何时候都应当使用 include() 来引入包含其他的 URL 匹配。
经过以上配置,已经向 URLconf 中配置了 index 视图,访问 http://localhost:8000/polls/ 即可看到浏览器返回的 Hello, world. You’re at the polls index. 字符。
URLs 配置中的 path() 函数接受四个参数,其中 route 和 view 是必需参数,kwargs 和 name 是可选参数。
- route 是一个包含 URL 模式匹配的字符串。在处理请求时,Django从 urlpatterns 中的第一个匹配模式开始,然后在列表中向下移动,将请求的 URL 与每个匹配模式进行比较,直到找到匹配的模式。匹配模式不会搜索域名以及 GET 和 POST 的参数,例如 https://www.example.com/myapp/ 和 https://www.example.com/myapp/?page=3 都将匹配到 myapp/。
- view 是当 Django 发现匹配的模式时,调用的指定的视图函数。调用时以 HttpRequest 对象作为第一个参数,以路由中“捕获”的任何值作为关键字参数。
- kwargs 实现在字典中将任意关键字参数传递给目标视图。
- name 命名 URL,可以实现在 Django 的其他地方明确地引用这个 URL,尤其是在模板中。这个强大的特性允许全局修改 URL 时只需要在一个地方修改即可。
设置数据库
打开 mysite/settings.py,这是包含了 Django 项目设置的 Python 模块。通常,这个配置文件使用 SQLite 作为默认数据库。如果需要更改想要使用的数据库引擎,首先需要安装合适的 database bindings ,然后在配置文件中 DATABASES ‘default’ 进行相应更改:
- ENGINE – 可选值有 ‘django.db.backends.sqlite3’,’django.db.backends.postgresql’,’django.db.backends.mysql’,或 ‘django.db.backends.oracle’ 等等 其他后端 。
- NAME - 数据库的名称。如果使用的是 SQLite,数据库将是你电脑上的一个文件,在这种情况下, NAME 应该是此文件的绝对路径,包括文件名。默认值 os.path.join(BASE_DIR, ‘db.sqlite3’) 将会把数据库文件储存在项目的根目录。
- 如果不使用 SQLite,则必须添加一些额外设置,比如 USER 、 PASSWORD 、 HOST 等等。想了解更多数据库设置方面的内容,请看文档: DATABASES 。
同时,在 mysite/settings.py 配置文件中,可以修改设置 TIME_ZONE 为自己的时区。
此外,在 mysite/settings.py 配置文件头部的 INSTALLED_APPS 设置项包括了在项目中启用的所有 Django 应用。应用能在多个项目中使用,也可以打包并且发布应用,让别人使用它们。
通常,INSTALLED_APPS 默认包括了以下 Django 的自带应用:
- django.contrib.admin – 管理员站点。
- django.contrib.auth – 认证授权系统。
- django.contrib.contenttypes – 内容类型框架。
- django.contrib.sessions – 会话框架。
- django.contrib.messages – 消息框架。
- django.contrib.staticfiles – 管理静态文件的框架。
这些应用默认启用,可以为常规项目提供方便。
这些默认应用有的使用了至少一张数据表,因此通过以下命令新建表:
python manage.py migrate
migrate 命令检查 INSTALLED_APPS 设置,根据 mysite/settings.py 中的数据库设置和每个应用的数据库迁移文件,为其中的应用创建需要的数据表。命令所执行的每个迁移操作都会在终端中显示出来。
需要注意的是,INSTALLED_APPS 设置里的应用并不是每个人都需要,因此可以根据自己项目需求,在运行 migrate 前从 INSTALLED_APPS 里注释或者删除掉它们。
创建模型
在 Django 里写一个数据库驱动的 Web 应用的第一步是定义模型 - 也就是数据库结构设计和附加的其它元数据。
这些概念通过一个 Python 类来描述。向 polls/models.py 文件添加以下代码:
from django.db import models
class Question(models.Model):
question_text = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class Choice(models.Model):
question = models.ForeignKey(Question, on_delete=models.CASCADE)
choice_text = models.CharField(max_length=200)
votes = models.IntegerField(default=0)
每个模型被表示为 django.db.models.Model 类的子类。每个模型有许多类变量,它们都表示模型里的一个数据库字段。
每个字段都是 Field 类的实例 - 比如,字符字段被表示为 CharField,日期时间字段被表示为 DateTimeField。这将告诉 Django 每个字段要处理的数据类型。
每个 Field 类实例变量的名字(例如 question_text 或 pub_date )也是字段名,将会在 Python 代码里使用它们,而数据库会将它们作为列名。
激活模型
上面的一小段用于创建模型的代码给了 Django 很多信息,通过这些信息,Django 可以:
INSTALLED_APPS = [
'polls.apps.PollsConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
此时,polls 应用已经安装到我们的项目里了,接着运行以下命令:
python manage.py makemigrations polls
通过运行 makemigrations 命令,告诉 Django 你对模型文件进行了修改或新建,希望 Django 把修改的部分储存为一次迁移。
python manage.py sqlmigrate polls 0001
- 输出的内容和使用的数据库引擎有关。
- 数据库的表名是由应用名 polls 和模型名 question 和 choice 的小写形式连接而来。
- 主键 id 会被自动创建。
- 默认的,Django 会在外键字段名后追加字符串 “_id”。
- 外键关系由 FOREIGN KEY 生成。
- 生成的 SQL 语句是为所用的数据库定制的,所以那些和数据库有关的字段类型 Django 会自动处理。
- 这个 sqlmigrate 命令并没有真正在数据库中执行迁移,只是把命令输出到屏幕上,让你检查 Django 认为需要执行哪些 SQL 语句。
现在,再次运行 migrate 命令,在数据库里创建新定义的模型的数据表:
python manage.py migrate
迁移是非常强大的功能,它能让你在开发过程中持续的改变数据库结构而不需要重新删除和创建表 - 它专注于使数据库平滑升级而不会丢失数据。
- 编辑 models.py 文件,改变模型。
- 运行 python manage.py makemigrations 为模型的改变生成迁移文件。
- 运行 python manage.py migrate 来应用数据库迁移。
使用 API
在 Python 命令行中可以使用 Django 创建的各种 API。通过以下命令打开 Python 命令行:
python manage.py shell
# Import the model classes we just wrote.
>>> from polls.models import Choice, Question
# No questions are in the system yet.
>>> Question.objects.all()
<QuerySet []>
# Create a new Question.
# Support for time zones is enabled in the default settings file, so
# Django expects a datetime with tzinfo for pub_date. Use timezone.now()
# instead of datetime.datetime.now() and it will do the right thing.
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date=timezone.now())
# Save the object into the database. You have to call save() explicitly.
>>> q.save()
# Now it has an ID.
# Access model field values via Python attributes.
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)
# Change values by changing the attributes, then calling save().
>>> q.question_text = "What's up?"
>>> q.save()
# objects.all() displays all the questions in the database.
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>
然而,
<QuerySet [<Question: Question object (1)>]>
并不是一个友好的可以了解对象细节的结果。编辑 polls/models.py 中的模型的代码,给 Question 和 Choice 增加
__str__()
方法:
from django.db import models
class Question(models.Model):
# ...
def __str__(self):
return self.question_text
class Choice(models.Model):
# ...
def __str__(self):
return self.choice_text
为模型
__str__()
方法可以为命令行里使用带来方便,同时在 Django 自动生成的 admin 里也使用这个方法来表示对象。
继续为 Question 模型添加一个自定义方法:
import datetime
from django.db import models
from django.utils import timezone
class Question(models.Model):
# ...
def was_published_recently(self):
return self.pub_date >= timezone.now() - datetime.timedelta(days=1)
保存文件后,通过
python manage.py shell
命令再次打开 Python 交互式命令行:
>>> from polls.models import Choice, Question
# Make sure our __str__() addition worked.
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>
# Django provides a rich database lookup API that's entirely driven by
# keyword arguments.
>>> Question.objects.filter(id=1)
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith='What')
<QuerySet [<Question: What's up?>]>
# Get the question that was published this year.
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)
<Question: What's up?>
# Request an ID that doesn't exist, this will raise an exception.
>>> Question.objects.get(id=2)
Traceback (most recent call last):
DoesNotExist: Question matching query does not exist.
# Lookup by a primary key is the most common case, so Django provides a
# shortcut for primary-key exact lookups.
# The following is identical to Question.objects.get(id=1).
>>> Question.objects.get(pk=1)
<Question: What's up?>
# Make sure our custom method worked.
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
# Give the Question a couple of Choices. The create call constructs a new
# Choice object, does the INSERT statement, adds the choice to the set
# of available choices and returns the new Choice object. Django creates
# a set to hold the "other side" of a ForeignKey relation
# (e.g. a question's choice) which can be accessed via the API.
>>> q = Question.objects.get(pk=1)
# Display any choices from the related object set -- none so far.
>>> q.choice_set.all()
<QuerySet []>
# Create three choices.
>>> q.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> q.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = q.choice_set.create(choice_text='Just hacking again', votes=0)
# Choice objects have API access to their related Question objects.
>>> c.question
<Question: What's up?>
# And vice versa: Question objects get access to Choice objects.
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()
# The API automatically follows relationships as far as you need.
# Use double underscores to separate relationships.
# This works as many levels deep as you want; there's no limit.
# Find all Choices for any question whose pub_date is in this year
# (reusing the 'current_year' variable we created above).