【译】Django官方教程——​编写你的第一个Django app,部分1

老子叫甜甜 提交于 2020-04-10 11:46:29

编写你的第一个Django app,部分1

【中括号中的绿色字是我自己添加的辅助内容,非官方文档内容,仅供参考】

【英文原文地址:https://docs.djangoproject.com/en/1.6/intro/tutorial01/

【转载本文时请注明出处:http://my.oschina.net/mayepythoner/blog/297550

我们通过一个例子开始学习。

通过这个教程,我们将带领你创建一个基本的投票应用。

它包括两个部分:

  • 一个公开的站点,让人们查看投票和进行投票。

  • 一个管理站点,让你添加、更改、删除投票。

我们假设你已经安装的Django。你可以通过运行下面的命令查看Django是否安装和它的版本:

$ python -c "import django; print(django.get_version())"
1.6.5

如果Django已经安装,你可以看到你安装的版本【如我的结果,我安装的是1.6.5】。如果没有安装,你会得到错误信息:“No module named django”。

这个教程基于Django1.6 和 Python2.X 写成。如果你的Django版本不匹配,你可以利用页面右侧的版本切换器切换到你所用的版本的教程,或者升级你的Django版本到最新。如果你正在用 Python3.X,要意识到你的代码可能需要与教程中的有一定差异,并且你要知道如何使用Python3.X才能继续使用这个教程。

查看  How to install Django 去了解怎样删除老版本的Django和安装一个更新的版本。

在哪里能获得帮助

如果你在学习教程中遇到困难,请发送消息到   django-users 或者加入 #django on irc.freenode.net ,和那些可能帮助到你的其他Django用户交谈。

创建一个项目

如果这是你第一次用Django,你必须认真注意最初的几步。也就是说,你将需要自动产生一些代码去建立一个Django项目,包括数据库配置、Django定制选项和应用定制配置。

在命令行下,cd到你喜欢储存代码的目录,然后运行下面的命令【如果成功这行代码没有任何输出,但你的文件夹下面会多了个mysite的目录】

django-admin.py startproject mysite

这条命令在你所在的文件夹下创建了一个 mysite 目录。如果它没有起作用,参看    Problems running django-admin.py

注意

你将需要避免使用 built-in Python 或者 Django 的组件命名。特别是,这意味着你应该避免用django这样的名字(这与Django自身产生冲突) 或者 test (这与 built-in Python package 产生冲突)。

代码应该存放在哪里?

如果你的背景是平凡老旧的PHP(没有使用现代的框架),你可能习惯于把代码放在Web server的文档根目录下(例如 /var/www)。而使用Django,你无需这么做。把所有的Python代码都放到你的Web server文档根目录下不是一个好的想法,因为这增大了人们通过Web看到你的代码的可能性。这不利于安全性。

把你的代码放到文档根目录之外的某个目录,例如 /home/mycode。

我们看一下 startproject 为我们产生了什么:

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

和你所看见的不匹配?

最近默认的项目布局已经改变。如果你看见一个“单调”的布局(没有里层的mysite目录),你可能正在使用和本教程不匹配的版本。你应该寻找老版的教程或者新的Django版本。

这些文件是:

  • 外层的 mysite/ 根目录只是你项目的一个容器。它的名字不与Django对应;你可以把它改成任何你喜欢的名字。

  • manage.py:一个让你可以通过多种方式与Django项目交互的命令行工具。你可以在  django-admin.py and manage.py 中读到关于manage.py的全部细节。

  • 内层的 mysite/ 目录是你项目真实的 Python package。它的名字就是你需要import到各处的 Python package 的名字。(例如:mysite.urls)。

  • mysite/__init__.py:这是一个空文件,他告诉 Python 这个目录应该被当做一个 Python package。(如果你是个Python的初学者,通过 Python的官方文档了解更多packages的信息。)

  • mysite/settings.py:这个 Django 项目的配置文件。Django settings 将会告诉你关于settings如何工作的全部内容。

  • mysite/urls.py:这个Django项目的 url 声明;是你的 Django-powered 站点的一个“内容列表”。你可以了解更多关于 URLs的内容在  URL dispatcher 。

  • mysite/wsgi.py:一个为 WSGI 兼容的 Web server 服务你的项目的入口点。更多细节请参见 How to deploy with WSGI 


用于开发的服务器

让我们验证它可行。进入外层的mysite目录。执行命令 python manage.py runserver(如果你还没有这样做的话)。你将看到下面的输出信息:

Validating models...

0 errors found
August 02, 2014 - 15:50:53
Django version 1.6, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

你已经开启了Django的开发服务器,这是用纯Python编写的一个轻量级 Web server。我们把他包括在Django里以便你可以更快速的开发东西,而不需要去处理服务器产品的配置(例如Apache),直到你已经准备好用于生产环境。

是时候该告诉你这个注意事项了: 不能将这个服务器用到任何生产环境当中。他的目的只是用于开发。(我们做的是一个Web框架,而不是一个Web 服务器。)

现在这个程序正在运行, 用你的浏览器访问 http://127.0.0.1:8000/ 你将看到 “Welcome to Django”页面, 令人愉快的浅蓝色,它工作了!【下图为我个人截图,为了读者能更直观感受到Django的魅力】

改变端口

在默认情况下,runserver 命令在内置IP的8000端口开始开发服务器。

如果你想改变服务器端口,把他当做参数传给命令行。例如,下面的命令将在8080端口开启服务器:

$ python manage.py runserver 8080

如果你想改变服务器IP,把他当做参数传给命令行,所以如果你想监听所有公共IP(如果你想在其他电脑上查看它是否工作,这个选项很有用。)利用下面的命令:

$ python manage.py runserver 0.0.0.0:8000

关于开服文档的全部说明,请参看 runserver

runserver的自动重载

当每次请求时,开发服务器会自动重载你的python代码。你不需要重启服务器去让改变了的代码生效。然而,像添加文件或编译转换文件这些动作不会触发重启,所以在这种情况下你不得不重启服务器。

数据库设置

现在,编辑 mysite/settings.py. 这是一个常规的 Python 模块,用模块级别的变量代表 Django 的配置。

在默认情况下,配置利用 SQLite。如果你是一个数据库方面的新手,或者你只是因为兴趣而尝试Django。这是最简单的选择。SQLite 被包含在 Python 里 。所以你不需要安装任何其他东西去支持你的数据库。

如果你希望使用其他的数据库,安装合适的 database bindings ,并且在 DATABASES 的‘default’条目中改变下面的键值,用来匹配你的数据库连接设置:

  • ENGINE  - 可以是  'django.db.backends.sqlite3', 'django.db.backends.postgresql_psycopg2', 'django.db.backends.mysql', 或者 'django.db.backends.oracle',其他引擎串可以参看   also available 。

  • NAME - 你数据库的名字。如果你使用的是SQLite,数据库将只是在你电脑上的一个文件;在这种情况下,NAME应该是完整的绝对路径,包括这个文件的文件名。默认情况下,路径 os.path.join(BASE_DIR, 'db.sqlite3') 将会储存这个文件在你的项目文件夹中。

如果你没有使用 SQLite 作为你的数据库。额外的设置例如  USERPASSWORDHOST 必须被添加。 更多细节,可以参见  DATABASES 的说明文档。

注意

如果你使用的是 PostgreSQL 或是 MySQL,确保在这一时刻你已经创建了一个数据库。利用你的数据库交互工具,输入 “CREATE DATABASE database_name;”完成这个操作。

如果你使用的是 SQLite ,你在这之前不需要创建人和东西 —— 当你需要时,数据库文件将会被自动创建。

编写 mysite/settings.py,通过  TIME_ZONE  设置时间区域。

同时,注意在你文件顶端的 INSTALLED_APPS  设置。他用来控制在这个Django实例中被激活的全部 Django 应用的名字。 Apps 可以被用到多个项目中,你可以把它们打包并发布到其他项目当中。

默认情况下, INSTALLED_APPS 包括以下 apps,他们全部来自 Django:

为了普通情况的便捷性,这些应用将被包括在默认选项中。

这些应用中的一些至少要用到一张数据库表,所以我们需要在使用它们之前创建数据库表。这做到这一点,利用下面的命令:

$ python manage.py syncdb

syncdb 命令会查看  INSTALLED_APPS 的配置情况,然后在你在 mysite/settings.py 中配置的数据库中创建所有所需要的数据库表。你可以看到每个数据表创建时的信息,并且你会被询问是否要为你你身份认证系统设置一个超级用户权限,继续并操作它。

如果你有兴趣,在你数据库的客户端命令行运行命令展示Django为你创建的数据表,  输入  \dt (PostgreSQL), SHOW TABLES; (MySQL), or .schema(SQLite)

最低配置

正如我们上面所说,默认的应用将包括标准的配置,但是不是所有人都需要它们。如果你不需要它们中的一些或者全部,在使用 sybcdb 之前,注释掉或删掉  INSTALLED_APPS  中的那些行。sybcdb 命令只会创建  INSTALLED_APPS  中的apps所需要的数据表。

创建 models

现在,在你的环境中,一个“项目”已经被创建,你可以开始着手你的工作了。

你在 Django 里写的每一个应用都组成了遵循某些管理的一个 Python package。Django 拥有可以自动产生 app 目录结构的工具,所以你可以专注于写代码而不是创建目录。

Projects vs. apps

项目和 app 之间有什么区别?一个 app 是一个可以完成某些任务的 Web应用 —— 例如,一个网络博客系统,一个公共记录的数据库或者是一个简单的投票应用。一个项目是配置与特殊网络站点应用的集合。一个项目可以包括很多个 app。 一个 app 可以被应用到多个项目当中。 

你的 apps 可以存在在你 Python 路径中的任何位置。 在本教程中,我们将通过 manage,.py 创建我们的投票 app,以便于他可以被当做一个顶层模块导入,而不是以 mysite 的下级模块。

为了创建的你 app,确保你在 manage.py 的同目录下,运行下面命令:

$ python manage.py startapp polls

这将产生一个 polls 目录,像下面这样的布局:

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

这个目录将是 polls 应用的家目录。

在 Django 中编写一个数据库 Web app 的第一步就是定义你的 models - 其本质上就是你的数据库布局再加上元数据。

原理思想

一个 model 就是你的数据的一个单独且确定的数据元。它包括你存储的数据的基本阈值及特征。 Django 遵循  DRY Principle 【Don't Repeat Yourself】。其目的就是定义你的数据 model 在一个位置,然后自动的驱动它。

在你简单的投票 app 中, 我们将建立两个 models: Poll 和 Choice。 Poll 拥有一个问题和一个发布日期。 Choice拥有两个字段:选择的内容和投票个数。每个 Choice 对应一个 Poll。

这些概念将被一个简单的 Python 类表示。编辑 polls/models.py 文件让他变成下面的样子:

from django.db import models

class Poll(models.Model):
    question = models.CharField(max_length=200)
    pub_date = models.DateTimeField('date published')

class Choice(models.Model):
    poll = models.ForeignKey(Poll)
    choice_text = models.CharField(max_length=200)
    votes = models.IntegerField(default=0)

这个代码是直白的。每个model被表示成一个类, 它们都是 django.db.models.Model  的子类。每个model 有一系列类变量,每个类变量代表一个数据库字段。

每个字段被表示成一个 Field 类的实例 —— 例如,用作字符字段的 ChatField 和用作 datetimes 的 DateTimeField。他们告诉 Django 每个字段用哪种类型。

每个 Field 实例的名字(例如, question 和 pub_date)就是字段的名字,利用机器友好的格式。你将在 Python 代码中应用到它们的值,而数据库将把它们用作列名。

你可以使用 Field 的可选择的第一个潜在参数去设计一个人类可读的名字。他会同时应用到 Django 的两个部分,同时还可以作为文档。如果这个字段不被提供,Django 会使用一个机器可读的名字。在我们的例子里,我们仅为 Poll.pub_date定义了一个人类可读的名字,对于这个 model 里的其他的字段,字段的机器可读名字已经满足于人类可读的名字。

一些  Field 类需要参数。例如,CharField 需要你给他一个 max_hength。它不仅用于数据库模式,而且还用于验证,我们马上就会看到。

一个  Field 也可以拥有多个可选的参数;在这个例子中,我们把 votes 的默认值设为 0 。

最后,注意一个关系被定义,利用 ForeignKey  。他告诉 Django 每个 Choice 与一个单独的 Poll关联。Django 支持所有通用的数据库关系: 多对一、多对多、一对一。

激活 models

这一小段代码给予了 Django 很多信息。 通过它, Django可以做到:

  • 为 app 创建一个数据库模式(CREATE TABLE 语句)。

  • 为 Poll 和 Choice 对象创建了一个 Python 数据库调用 API 入口。

但是首先我们要告诉我们的项目 polls app 已经被安装。

原理思想

Django apps 是可插拔式的:你可以在多个项目中使用同一个 app,并且你可以发布 apps, 因为它们不需要绑定在一个 Django的安装实例上。

再次编辑 mysite/settings.py 文件, 改变 INSTALLED_APP设置,让它包括字符串‘polls’,如下所述:

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'polls',
)

现在 Django 知道你包含了 polls app。让我们运行下面命令:

$ python manage.py sql polls

你可以看到一些类似于下面的结果(poll app 的 CREATE TABLE SQL 语句):

BEGIN;
CREATE TABLE "polls_poll" (
    "id" integer NOT NULL PRIMARY KEY,
    "question" varchar(200) NOT NULL,
    "pub_date" datetime NOT NULL
);
CREATE TABLE "polls_choice" (
    "id" integer NOT NULL PRIMARY KEY,
    "poll_id" integer NOT NULL REFERENCES "polls_poll" ("id"),
    "choice_text" varchar(200) NOT NULL,
    "votes" integer NOT NULL
);
COMMIT;

【下图为我的结果,和教程完全一致。】

注意下面几点:

  • 这个精准的输出依赖于你所使用的数据库而变化。上面的例子基于 SQLite 所产生。

  • 表明会自动的根据两部分拼装而成:app 的名字与小写的 model 的名字 —— poll 和 choice。(你可以重写这里。)

  • 主键(IDs)会被自动添加(你也可以重写这里。)

  • 根据惯例,Django 增加 "_id" 作为外检的名字。(是的,你也可以重写这里。)

  • 外键关系被  REFERENCES  语句定义的很明确。

  • 它剪裁了你对数据库的使用,很多数据库特性的字段类型, 例如,  auto_increment (MySQL), serial (PostgreSQL), or integer primary key (SQLite) 被自动控制。同样的原理试用于字段名称对于引号的使用 —— 例如,困惑于用双引号还是单引号。【这里 Django 都为你自动解决。】

  • sql 命令并非真的在你的数据库中运行了sql语句。它只是把语句打印在屏幕上以便让你看见 Djaogo 所获得的 sql 语句是什么样子的。如果你想执行它,可以把它复制并粘贴到你的数据库命令交互窗口。然而,正如我们不久前看到的,Django 为我们提供货了提交SQL到数据库的更简单的方法。

如果你感兴趣,也试试下面的命令:

  • python manage.py validate - 检查你的 models 结构的任何错误。

  • python manage.py sqlcustom polls - 输出任何为这个应用定义的惯例SQL语句。(例如表的修改和限制)

  • python manage.py sqlclear polls - 通过你在数据库中已经存在的表(如果有的话),输出这个 app 必要的 DROP TABLE 语句。

  • python manage.py sqlindexes polls  - 为你的 app 输出 CREAT INDEX 语句。

  • python manage.py sqlall polls - 所有SQL 语句的集合, 包括: sql, sqlcustom, and sqlindexes 命令 。

查看这些命令的输出有助于你了解背后真正发生了什么。

现在,运行再次 syncdb 去在你的数据库中创建 model tables:

$ python manage.py syncdb

syncdb 命令运行来源于sqlall的SQL语句,它们对应于 INSTALLED_APPS 中的所有在数据库中还没有存在的apps。他创建所有的表、初始值和序列,对应于你上次运行syncdb之后所向项目里增加的任何 apps。 syncdb 可以随时被执行,他智慧创建还没有被创建的表。

【这里同样给各位看下我的输出吧,因为这是我这个项目第一次运行 syncdb,教程中的第一回我没有做,所以所有 app 的表都在这里创建了~】

Creating tables ...
Creating table django_admin_log
Creating table auth_permission
Creating table auth_group_permissions
Creating table auth_group
Creating table auth_user_groups
Creating table auth_user_user_permissions
Creating table auth_user
Creating table django_content_type
Creating table django_session
Creating table polls_poll
Creating table polls_choice

You just installed Django's auth system, which means you don't have any superusers defined.
Would you like to create one now? (yes/no): yes
Username (leave blank to use 'root'): root
Email address: mayuanchi1989@163.com
Password: 
Password (again): 
Superuser created successfully.
Installing custom SQL ...
Installing indexes ...
Installed 0 object(s) from 0 fixture(s)


阅读 django-admin.py documentation 以了解关于 m,anage.py 所能做的所有信息。

调戏API

【playing with the API。 只能这么翻译了。。。囧】

现在,我们进入 Python 的交互 shell,调戏一下 Django 提供给我们的免费API。进入 Python shell, 利用一下命令:

$ python manage.py shell

我们利用这个代替了简单的输入"python",因为 manage,py 设置了 DJANGO_SETTINGS_MODULE 环境变量,此环境变量给予Django Python路径到你的mysite/settings.py 文件。

绕过 manage.py

如果你不喜欢用manage.py, 没关系。 只需设置  DJANGO_SETTINGS_MODULE 环境变量到 mysite/settings 且运行 Python 在manage.py相同的文件夹下。(或者保证目录在Python path中,以便于 import mysite 可用)。

更多信息,参考  django-admin.py documentation

当你进入这个shell时,先导入 database API :

>>> from polls.models import Poll, Choice   # Import the model classes we just wrote.

# 系统中还没有 polls
>>> Poll.objects.all()
[]

# 创建一个 Poll.
# 时间区域已经被默认支持到配置文件中,
# 所以 Django 为 pub_data 期望一个时间区域信息的datetime。
# 我们用 timezone.now() 代替 datetime.datetime.now(),它会为我们做正确的事情。
>>> from django.utils import timezone
>>> p = Poll(question="What's new?", pub_date=timezone.now())

# 保存对象到数据库。你可以明确的调用save()。
>>> p.save()

# 现在它有了一个ID,注意它可能是"1L"而不是"1",这取决于你正在使用的数据库。
# 这不是什么大事,这值意味着你的数据库是喜欢返回一个整数作为 Python 的长整型对象。
>>> p.id
1

# 通过 Python 属性获取数据库的列。
>>> p.question
"What's new?"
>>> p.pub_date
datetime.datetime(2012, 2, 26, 13, 0, 0, 775217, tzinfo=<UTC>)

# 通过改变属性来改变数值,然后调用 save() 
>>> p.question = "What's up?"
>>> p.save()

# objects.all() 展示你数据库中的所有 polls
>>> Poll.objects.all()
[<Poll: Poll object>]

【对于上面命令我的输出】

>>> from polls.models import Poll, Choice
>>> Poll.objects.all()
[]
>>> from django.utils import timezone
>>> p = Poll(question="What's new?", pub_date=timezone.now())
>>> p.save()
>>> p.id
1
>>> p.question
"What's new?"
>>> p.pub_date
datetime.datetime(2014, 8, 3, 8, 26, 23, 407698, tzinfo=<UTC>)
>>> p.question = "What's up?"
>>> p.save()
>>> Poll.objects.all()
[<Poll: Poll object>]

等一下,<Poll: Poll object> 完全是一个没有帮助的对对象的表示。我们通过编辑 polls model 修正它(在 poll/models.py文件中),增加 __unicode__() 方法到 Poll 和 Choice。在 Python3中,只要将例子中的 __unicode__改为 __str__就可以了:

from django.db import models

class Poll(models.Model):
    # ...
    def __unicode__(self):  # Python 3: def __str__(self):
        return self.question

class Choice(models.Model):
    # ...
    def __unicode__(self):  # Python 3: def __str__(self):
        return self.choice_text

在你的 models 中增加  __unicode__() ( 在 Python3中是 __str__() )方法是很必要的,不仅是因为当你与命令行交互时更加清楚,也是因为对象的表达被用在 Django 自动生成的 admin 系统中。

 __unicode__ 还是  __str__ ?

在 Python3 中,事情变得简单,只需应用  __str__() 而可以忘记掉  __unicode__()。

如果你很熟悉 Python2, 你也许会习惯于增加  __str__()方法到你的类,而不是  __unicode__()方法。我们这里应用 __unicode__() 是因为 Django models 默认使用 Unicode。所有你数据库上的数据被返回时都会被转成 Unicode。

Django models 有一个默认的  __str__()方法,他会调用  __unicode__() 并且把结果转换成  UTF-8 字节流。这意味着 unicode(p) 将会返回 一个 Unicode 字符串,而str(p)会返回一个字符编码为UTF-8的正常字符串。

如果你认为这一切令人费解,只要记住增加  __unicode__()方法到你的 models。 非常幸运,事情总是会正常运转。

注意这些都是标准的Python方法。我们增加个惯例方法,仅仅是为了示范:

import datetime
from django.utils import timezone
# ...
class Poll(models.Model):
    # ...
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

注意所添加的 import datetime 和 from django.utils import timezone,分别引入了Python的标准 datatime model 和 Django 在  from django.utils 中的 timezone 相关工具。如果你不熟悉Python 中的时区控制,可以参考 time zone support docs

保存这些变更,开启一个新的 Python shell:、

>>> from polls.models import Poll, Choice

# Make sure our __unicode__() addition worked.
>>> Poll.objects.all()
[<Poll: What's up?>]

# Django provides a rich database lookup API that's entirely driven by
# keyword arguments.
>>> Poll.objects.filter(id=1)
[<Poll: What's up?>]
>>> Poll.objects.filter(question__startswith='What')
[<Poll: What's up?>]

# Get the poll that was published this year.
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Poll.objects.get(pub_date__year=current_year)
<Poll: What's up?>

# Request an ID that doesn't exist, this will raise an exception.
>>> Poll.objects.get(id=2)
Traceback (most recent call last):
    ...
DoesNotExist: Poll 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 Poll.objects.get(id=1).
>>> Poll.objects.get(pk=1)
<Poll: What's up?>

# Make sure our custom method worked.
>>> p = Poll.objects.get(pk=1)
>>> p.was_published_recently()
True

# Give the Poll 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 poll's choices) which can be accessed via the API.
>>> p = Poll.objects.get(pk=1)

# Display any choices from the related object set -- none so far.
>>> p.choice_set.all()
[]

# Create three choices.
>>> p.choice_set.create(choice_text='Not much', votes=0)
<Choice: Not much>
>>> p.choice_set.create(choice_text='The sky', votes=0)
<Choice: The sky>
>>> c = p.choice_set.create(choice_text='Just hacking again', votes=0)

# Choice objects have API access to their related Poll objects.
>>> c.poll
<Poll: What's up?>

# And vice versa: Poll objects get access to Choice objects.
>>> p.choice_set.all()
[<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]
>>> p.choice_set.count()
3

# 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 poll whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
>>> Choice.objects.filter(poll__pub_date__year=current_year)
[<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]

# Let's delete one of the choices. Use delete() for that.
>>> c = p.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()

更多关于 model 关系的信息,请参看  Accessing related objects 。关于怎样使用 double underscores 【双下划线?】去通过API完成字段查找,请参看 Field lookups 。 关于数据库 API 的全部信息,请参看 Database API reference

当你适应于API,可以开始阅读 part 2 of this tutorial 去获取 Django 的 admin 是如何自动工作的。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!