[学习笔记]Django开发

有一段时间没用过django了,此次项目中有需要,紧跟官方django的最新版本,所以学习整理一下笔记

一张图看懂Django:

团队成员制作

一、第一个项目

1.创建应用

cd desktop         // 切换到桌面
mkdir testProject        // 新建一个文件夹testProject
cd testProject        // 打开testProject文件夹
django-admin startproject mysite    // 在当前目录下生成一个mysite 的 django应用

在对应的 testProject 目录下,创建第一个项目 mysite

项目目录:

mysite/
manage.py
mysite/
    __init__.py
    settings.py
    urls.py
    wsgi.py

2.运行服务

python3 manage.py runserver 0:8000

会生成如下代码:

Performing system checks...

System check identified no issues (0 silenced).

You have 15 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
October 10, 2018 - 17:30:51
Django version 2.1.2, using settings 'mysite.settings'
Starting development server at http://0:8000/
Quit the server with CTRL-BREAK.

如果是在外网服务器上,那么其他的用户通过互联网就可以访问此WEB项目


二、第一个应用

项目和应用有啥区别?应用是一个专门做某件事的网络应用程序——比如博客系统,或者公共记录的数据库,或者简单的投票程序。项目则是一个网站使用的配置和应用的集合。项目可以包含很多个应用。应用可以被很多个项目使用。

依然在 testProject 目录下创建一个投票调查应用

python3 manage.py startapp polls

应用目录:

polls/
    __init__.py
    admin.py
    apps.py
    migrations/
        __init__.py
    models.py
    tests.py
    views.py

三、第一个视图

Django 中的视图的概念是「一类具有相同功能和模板的网页的集合

在目录 polls/views.py 文件中添加如下代码

from django.http import HttpResponse


def index(request):
    return HttpResponse("Hello, world. You're at the polls index.")

这是 Django 中最简单的视图。如果想看见效果,我们需要将一个 URL 映射到它——这就是我们需要 URLconf 的原因。

polls 目录下新建一个 urls.py 文件

在该文件下,写入如下代码:

from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

下一步是要在根 URLconf 文件中指定我们创建的 polls.urls 模块。在 mysite/urls.py 文件的 urlpatterns 列表里插入一个 include(), 如下:

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

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

函数 include() 允许引用其它 URLconfs。每当 Django 遇到 :func:~django.urls.include 时,它会截断与此项匹配的 URL 的部分,并将剩余的字符串发送到 URLconf 以供进一步处理。

我们设计 include() 的理念是使其可以即插即用。因为投票应用有它自己的 URLconf( polls/urls.py ),他们能够被放在 "/polls/""/fun_polls/""/content/polls/",或者其他任何路径下,这个应用都能够正常工作。

  • 当包括其它 URL 模式时你应该总是使用 include() , admin.site.urls 是唯一例外。

函数 path() 具有四个参数,两个必须参数:routeview,两个可选参数:kwargsname


四、配置

打开 mysite/settings.py

设置文件中 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,则必须添加一些额外设置,比如 USERPASSWORDHOST 等等

INSTALLED_APPS = [
    '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 设置文件和每个应用的数据库迁移文件(我们稍后会介绍这个)。这个命令所执行的每个迁移操作都会在终端中显示出来。如果你感兴趣的话,运行你数据库的命令行工具,并输入 \dt (PostgreSQL), SHOW TABLES; (MySQL), .schema (SQLite)或者 SELECT TABLE_NAME FROM USER_TABLES; (Oracle) 来看看 Django 到底创建了哪些表。

五、创建模型

在这个简单的投票应用中,需要创建两个模型:问题 Question 和选项 ChoiceQuestion 模型包括问题描述和发布时间。Choice 模型有两个字段,选项描述和当前得票数。一个选项属于一个问题。

这些概念可以通过一个简单的 Python 类来描述。按照下面的例子来编辑 polls/models.py 文件:

from django.db import models

# Create your models here.


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_textpub_date )也是字段名,所以最好使用对机器友好的格式。你将会在 Python 代码里使用它们,而数据库会将它们作为列名。

定义某些 Field 类实例需要参数。例如 CharField 需要一个 max_length 参数。

Field 也能够接收多个可选参数;在上面的例子中:将 votesdefault 也就是默认值,设为0

使用 ForeignKey 定义了一个关系。这将告诉 Django,每个 Choice 对象都关联到一个 Question 对象。Django 支持所有常用的数据库关系:多对一多对多一对一

激活模型

上面的一小段用于创建模型的代码给了 Django 很多信息,通过这些信息,Django 可以:

  • 为这个应用创建数据库 schema(生成 CREATE TABLE 语句)。
  • 创建可以与 QuestionChoice 对象进行交互的 Python 数据库 API

首先得把 polls 应用安装到项目里。

需要在配置类 INSTALLED_APPS 中添加设置。因为 PollsConfig 类写在文件 polls/apps.py 中,所以它的点式路径是 ‘polls.apps.PollsConfig‘。在文件 mysite/settings.pyINSTALLED_APPS 子项添加点式路径后,它看起来像这样:

INSTALLED_APPS = [
    'polls.apps.PollsConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

接着输入如下命令:

python3 manage.py makemigrations polls

输出:

Migrations for 'polls':
  polls\migrations\0001_initial.py
    - Create model Choice
    - Create model Question
    - Add field question to choice

通过运行 makemigrations 命令,Django 会检测你对模型文件的修改(在这种情况下,你已经取得了新的),并且把修改的部分储存为一次 迁移。

迁移是 Django 对于模型定义(也就是你的数据库结构)的变化的储存形式 - 没那么玄乎,它们其实也只是一些你磁盘上的文件。如果你想的话,你可以阅读一下你模型的迁移数据,它被储存在 polls/migrations/0001_initial.py 里。别担心,你不需要每次都阅读迁移文件,但是它们被设计成人类可读的形式,这是为了便于你手动修改它们。

Django 有一个自动执行数据库迁移并同步管理你的数据库结构的命令 - 这个命令是 migrate,我们马上就会接触它 - 但是首先,让我们看看迁移命令会执行哪些 SQL 语句。sqlmigrate 命令接收一个迁移的名称,然后返回对应的 SQL

python3 manage.py sqlmigrate polls 0001

输出:

BEGIN;
--
-- Create model Choice
--
CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL);
--
-- Create model Question
--
CREATE TABLE "polls_question" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "question_text" varchar(200) NOT NULL, "pub_date" datetime NOT NULL);
--
-- Add field question to choice
--
ALTER TABLE "polls_choice" RENAME TO "polls_choice__old";
CREATE TABLE "polls_choice" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "choice_text" varchar(200) NOT NULL, "votes" integer NOT NULL, "question_id" integer NOT NULL REFERENCES "polls_question" ("id") DEFERRABLE INITIALLY DEFERRED);
INSERT INTO "polls_choice" ("id", "choice_text", "votes", "question_id") SELECT "id", "choice_text", "votes", NULL FROM "polls_choice__old";
DROP TABLE "polls_choice__old";
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");
COMMIT;

这个 sqlmigrate 命令并没有真正在你的数据库中的执行迁移

再次运行 migrate 命令,在数据库里创建新定义的模型的数据表:

C:\Users\stdy\Desktop\testdjango\mysite>python3 manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, polls, sessions
Running migrations:
  Applying polls.0001_initial... OK

这个 migrate 命令选中所有还没有执行过的迁移(Django 通过在数据库中创建一个特殊的表 django_migrations 来跟踪执行过哪些迁移)并应用在数据库上 - 也就是将你对模型的更改同步到数据库结构上。迁移是非常强大的功能,它能让你在开发过程中持续的改变数据库结构而不需要重新删除和创建表 - 它专注于使数据库平滑升级而不会丢失数据。

改变模型需要这三步:

  • 编辑 models.py 文件,改变模型。
  • 运行 python manage.py makemigrations 为模型的改变生成迁移文件。
  • 运行 python manage.py migrate 来应用数据库迁移。

六、初试API

打开命令行:

python3 manage.py shell

运行过程:

C:\Users\stdy\Desktop\testdjango\mysite>python3 manage.py shell
Python 3.6.5 (v3.6.5:f59c0932b4, Mar 28 2018, 17:00:18) [MSC v.1900 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from polls.models import Choice,Question
>>> Question.objects.all()
<QuerySet []>
>>> from django.utils import timezone
>>> q = Question(question_text = 'What is new?',pub_date = timezone.now())
>>> q
<Question: Question object (None)>
>>> q.save()
>>> q.id
1
>>> q.question_text
'What is new?'
>>> q.pub_date
datetime.datetime(2018, 10, 10, 12, 4, 33, 172278, tzinfo=<UTC>)
>>> q.question_text
'What is new?'
>>> q.question_text = 'What is up?'
>>> q.question_text
'What is up?'
>>> Question.objects.all()
<QuerySet [<Question: Question object (1)>]>
>>>
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 里也使用这个方法来表示对象。


七、管理界面

1.创建管理账号

python3 manage.py createsuperuser

输出:

C:\Users\stdy\Desktop\testdjango\mysite>python3 manage.py createsuperuser
Username (leave blank to use 'dyboy'): dyboy
Email address: dyboy2017@qq.com
Password:***********
Password (again):***********
This password is too common.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

2.进入管理站点

地址:http://127.0.0.1:8000/admin/

将会看到几种可编辑的内容:组和用户。它们是由 django.contrib.auth 提供的,这是 Django 开发的认证框架。

3.向管理页面中加入投票应用

但是我们的投票应用在哪呢?它没在索引页面里显示。

只需要做一件事:我们得告诉管理页面,问题 Question 对象需要被管理。打开 polls/admin.py 文件,把它编辑成下面这样:

向管理页面注册了问题 Question

from django.contrib import admin

# Register your models here.

from .models import Question

admin.site.register(Question)

manager


八、概览

视图的概念是「一类具有相同功能和模板的网页的集合」

在我们的投票应用中,我们需要下列几个视图:

  • 问题索引页——展示最近的几个投票问题。
  • 问题详情页——展示某个投票的问题和不带结果的选项列表。
  • 问题结果页——展示某个投票的结果。
  • 投票处理器——用于响应用户为某个问题的特定选项投票的操作。

网页和其他内容都是从视图派生而来。每一个视图表现为一个简单的 Python 函数(或者说方法,如果是在基于类的视图里的话)。Django 将会根据用户请求的 URL 来选择使用哪个视图(更准确的说,是根据 URL 中域名之后的部分)。

在你上网的过程中,很可能看见过像这样美丽的 URL: “ME2/Sites/dirmod.asp?sid=&type=gen&mod=Core+Pages&gid=A6CD4967199A42D9B65B1B“ 。别担心,Django 里的 URL 规则 要比这优雅的多!

一个 URL 模式定义了某种 URL 的基本格式——举个例子:/newsarchive/<year>/<month>/

为了将 URL 和视图关联起来,Django 使用了 ‘URLconfs‘ 来配置。URLconfURL 模式映射到视图。

现在让向 polls/views.py 里添加更多视图:

def detail(request, question_id):
    return HttpResponse("You're looking at question %s." % question_id)

def results(request, question_id):
    response = "You're looking at the results of question %s."
    return HttpResponse(response % question_id)

def vote(request, question_id):
    return HttpResponse("You're voting on question %s." % question_id)

把这些新视图添加进 polls.urls 模块里

urlpatterns = [
    # ex: /polls/
    path('', views.index, name='index'),
    # ex: /polls/5/
    path('<int:question_id>/', views.detail, name='detail'),
    # ex: /polls/5/results/
    path('<int:question_id>/results/', views.results, name='results'),
    # ex: /polls/5/vote/
    path('<int:question_id>/vote/', views.vote, name='vote'),
]

写一个真正有用的视图

index() 函数里插入了一些新内容,位于 polls/views.py,让它能展示数据库里以发布日期排序的最近 5 个投票问题,以空格分割:

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    output = ', '.join([q.question_text for q in latest_question_list])
    return HttpResponse(output)

模版

新建一个模版文件 polls/templates/polls/index.html

polls/templates/polls/index.html:

{% if latest_question_list %}
    <ul>
    {% for question in latest_question_list %}
        <li><a href="/polls/{{ question.id }}/">{{ question.question_text }}</a></li>
    {% endfor %}
    </ul>
{% else %}
    <p>No polls are available.</p>
{% endif %}

polls/views.py

from django.http import HttpResponse
from django.template import loader

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    template = loader.get_template('polls/index.html')
    context = {
        'latest_question_list': latest_question_list,
    }
    return HttpResponse(template.render(context, request))

一个快捷函数: render()

「载入模板,填充上下文,再返回由它生成的 HttpResponse 对象」是一个非常常用的操作流程。于是 Django 提供了一个快捷函数,我们用它来重写 index() 视图:

from django.shortcuts import render

from .models import Question


def index(request):
    latest_question_list = Question.objects.order_by('-pub_date')[:5]
    context = {'latest_question_list': latest_question_list}
    return render(request, 'polls/index.html', context)

注意到,我们不再需要导入 loader 和 HttpResponse 。不过如果你还有其他函数(比如说 detail, results, 和 vote )需要用到它的话,就需要保持 HttpResponse 的导入。


九、抛出 404 错误

polls/views.py:

from django.http import Http404
from django.shortcuts import render

from .models import Question
# ...
def detail(request, question_id):
    try:
        question = Question.objects.get(pk=question_id)
    except Question.DoesNotExist:
        raise Http404("Question does not exist")
    return render(request, 'polls/detail.html', {'question': question})

polls/templates/polls/detail.html

{{ question }}

一个快捷函数: get_object_or_404()

尝试用 get() 函数获取一个对象,如果不存在就抛出 Http404 错误也是一个普遍的流程。Django 也提供了一个快捷函数,下面是修改后的详情 detail() 视图代码:

#polls/views.py
from django.shortcuts import get_object_or_404, render

from .models import Question
# ...
def detail(request, question_id):
    question = get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/detail.html', {'question': question})

模版系统

# polls/templates/polls/detail.html
<h1>{{ question.question_text }}</h1>
<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }}</li>
{% endfor %}
</ul>

模板系统统一使用点符号来访问变量的属性。在示例 {{ question.question_text }} 中,首先 Django 尝试对 question 对象使用字典查找(也就是使用 obj.get(str) 操作),如果失败了就尝试属性查找(也就是 obj.str 操作),结果是成功了。如果这一操作也失败的话,将会尝试列表查找(也就是 obj[int] 操作)。

在 {% for %} 循环中发生的函数调用:question.choice_set.all 被解释为 Python 代码 question.choice_set.all() ,将会返回一个可迭代的 Choice 对象,这一对象可以在 {% for %} 标签内部使用。

reference: https://docs.djangoproject.com/zh-hans/2.0/topics/templates/

未完待续…

发表评论 / Comment

用心评论~

金玉良言 / Appraise
地狱边缘LV 1
2019-10-19 01:52
真好
LV 1
2019-05-01 21:56
博主辛苦了
跨境卖家导航LV 2
2018-10-12 11:03
新手学习了~~