欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

django2.0实现投票应用程序2

程序员文章站 2024-02-02 12:50:52
...

编写你的第一个 Django 应用,第 2 部分

现在接着教程的第1部分继续往后面讲。我们将建立数据库,创建您的第一个模型(models),并主要关注 Django 提供的自动生成的管理页面(admin)。

1.数据库配置

现在,打开 mysite/settings.py 。这是个包含了 Django 项目设置的 Python 模块。

通常,这个配置文件使用 SQLite 作为默认数据库。如果你不熟悉数据库,或者只是想尝试下 Django,这是最简单的选择。Python 内置 SQLite,所以你无需安装额外东西来使用它。当你开始一个真正的项目时,你可能更倾向使用一个更具扩展性的数据库,例如 PostgreSQL,避免中途切换数据库这个令人头疼的问题。

对于初学者来说,最好还是先使用默认的数据库。

在默认情况下,我们的 mysite/settings.py文件中的有关数据库的配置是这样的:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

如果你是使用的是mysql,所以,我们需要把这个地方修改一下,来使用你的数据库连接设置:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',   
        'NAME': 'db_polls', #可变
        'USER':'root',      #可变
        'PASSWORD':'root',  #可变
        'HOST':'localhost', 
        'PORT':'3306',  
    }
}

其中,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') 将会把数据库文件储存在项目的根目录。创建项目的时候,django已经帮你弄好了。

  • USER:连接数据库的用户名,也就是你之前安装mysql的时候填写的信息

  • PASSWORD: 连接数据库所使用的密码

  • HOST: 数据库所在的主机地址,默认是本地,所以是localhost

  • PORT: 数据库所占有的端口,也就是端口号

PS.注意大小写

编辑 mysite/settings.py 文件前,先设置 TIME_ZONE为设置成我们中国的时区’Asia/Shanghai’

TIME_ZONE = 'Asia/Shanghai'

此外,关注一下文件头部的 INSTALLED_APPS 设置项。这里包括了会在你项目中启用的所有 Django 应用。应用能在多个项目中使用,你也可以打包并且发布应用,让别人使用它们。

通常, INSTALLED_APPS默认包括了以下 Django 的自带应用:

  • django.contrib.admin – 管理员站点, 你很快就会使用它。
  • django.contrib.auth – 认证授权系统。
  • django.contrib.contenttypes – 内容类型框架。
  • django.contrib.sessions– 会话框架。
  • django.contrib.messages – 消息框架。
  • django.contrib.staticfiles – 管理静态文件的框架。

这些应用被默认启用是为了给常规项目提供方便,上面也有自带的后台管理,理解一下上面django自带的应用就好,当有需要改动的话,才去弄。

默认开启的某些应用需要至少一个数据表,所以,在使用他们之前需要在数据库中创建一些表。请执行以下命令:

python manage.py migrate

运行上面这条命令的结果:
django2.0实现投票应用程序2
这个 migrate命令检查 INSTALLED_APPS设置,为其中的每个应用创建需要的数据表,至于具体会创建什么,这取决于你的 mysite/settings.py 设置文件和每个应用的数据库迁移文件。

PS.如果你不需要某些默认的应用的话,你可以在运行 migrate前毫无顾虑地从 INSTALLED_APPS 里注释或者删除掉它们。 因为migrate只会把 INSTALLED_APPS 声明的应用进行数据库迁移。

Django 到底创建了哪些表 ,不同的数据库通过不同的命令查看

  • \dt (PostgreSQL),

  • SHOWTABLES; (MySQL),

  • .schema (SQLite)

  • SELECT TABLE_NAME FROM USER_TABLES; (Oracle)

2.创建模型

在 Django 里写一个数据库驱动的 Web 应用的第一步是定义模型 - 也就是数据库结构设计和附加的其它元数据。

简单来说,就是设计一下数据库,数据库里面存在哪些表。

PS.只需要定义数据模型,Django 的迁移代码是由你的模型文件自动生成的

在这个简单的投票应用中,需要创建两个模型:

  • Question 模型包括问题描述和发布时间。

  • Choice 模型有两个字段,选项描述和当前得票数。每个选项属于一个问题。 涉及到外键的问题

编辑 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默认帮你导入了,所以你自己不用再导入了。

每个模型被表示为 django.db.models.Model类的子类。所以我们都要把models.Model作为参数传进去。

每个模型有一些类变量,它们都表示模型里的一个数据库字段,通过例子来说明:

question_text = models.CharField(max_length = 200)
  • question_text就是类变量,同时也代表一个数据库的字段(字段名)
  • 每个字段都是 Field类的实例 ,字符字段被表示为 CharField,日期时间字段被表示为 DateTimeField
  • 同时也表明每个字符要处理的数据类型

PS.你可以使用可选的选项来为 Field 定义一个人类可读的名字。这个功能在很多 Django 内部组成部分中都被使用了,而且作为文档的一部分。如果某个字段没有提供此名称,Django 将会使用对机器友好的名称,也就是变量名。

简单来说,你没有给字段一个名字的时候,django默认使用那个类变量的名字。

定义某些 Field类实例需要参数。

CharField 需要一个 max_length 参数。

Field也能够接收多个可选参数votes 的 `default也就是默认值,设为0。

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

3.**模型

Django 通过这些信息,可以:

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

首先我们得把polls应用安装到我们的项目里,就是说告诉我们的项目polls app被安装了。

官网上面添加'polls.apps.PollsConfig',应该是跟polls一样的,都是代表那个应用。

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

现在django项目会包含polls应用,接着运行下面的命令:

python 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 会检测你对模型文件(models.py)的修改(在这种情况下,你已经取得了新的),并且把修改的部分储存为一次 迁移。,那个一次迁移保存在polls\migrations\0001_initial.py这个文件里面。

PS.你不需要每次都阅读迁移文件,但是它们被设计成人类可读的形式,这是为了便于你手动修改它们。

小提示:

migrate命令:Django 有一个自动执行数据库迁移并同步管理你的数据库结构

sqlmigrate 命令接收一个迁移的名称,然后返回对应的 SQL(查看迁移命令会执行哪些命令):

你会看到类似下面的这样的输出,我把它修改好看点,实际上不是这个格式的:

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", "question_id", "votes") SELECT "id", "choice_text", NULL, "votes" FROM "polls_choice__old";
DROP TABLE "polls_choice__old";
CREATE INDEX "polls_choice_question_id_c5b4b260" ON "polls_choice" ("question_id");
COMMIT;

PS.小提示

  • 输出的内容和你使用的数据库有关,上面的输出示例使用的是 默认的数据库。

  • 数据库的表名是由应用名(polls)和模型名的小写形式( questionchoice)通过下划线连接而来。

  • 主键(IDs)会被自动创建。(当然,你也可以自定义。)

  • 默认的,Django 会在外键字段名后追加字符串 "_id" 。(同样,这也可以自定义。)

  • 外键关系由 FOREIGN KEY 生成。你不用关心 DEFERRABLE 部分,它只是告诉SQLite,请在事务全都执行完之后再创建外键关系

  • 生成的 SQL 语句是为你所用的数据库定制的,所以那些和数据库有关的字段类型,比如 auto_increment (MySQL)、 serial (PostgreSQL)和 integer primary key autoincrement (SQLite),Django 会帮你自动处理。那些和引号相关的事情 - 例如,是使用单引号还是双引号 - 也一样会被自动处理。

  • 这个 sqlmigrate命令并没有真正在你的数据库中的执行迁移 - 它只是把命令输出到屏幕上,让你看看 Django 认为需要执行哪些 SQL 语句。

  • 可以自定义数据库表名,主键(IDS)

一个命令帮助你检查项目中的问题,并且在检查过程中不会对数据库进行任何操作。

python manage.py check

现在运行migrate命令,在数据库里创建新定义的模型的数据表(执行迁移):

python manage.py migrate

这个 migrate命令选中所有还没有执行过的迁移(Django 通过在数据库中创建一个特殊的表 django_migrations 来跟踪执行过哪些迁移)并应用在数据库上 - 也就是将你对模型的更改同步到数据库结构上。

迁移是非常强大的功能,它能让你在开发过程中持续的改变数据库结构而不需要重新删除和创建表 - 它专注于使数据库平滑升级而不会丢失数据。

敲黑板,重点来了(改变模型,三步走):

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

数据库迁移被分解成生成和应用两个命令目的:

  • 为了让你能够在代码控制系统上提交迁移数据并使其能在多个应用里使用
  • 开发更加简单,也给别的开发者和生产环境中的使用带来方便

4. 初试API

交互式 Python 命令行,尝试一下 Django 为你创建的各种 API。 通过以下命令打开 Python 命令行:

python manage.py shell 

我们使用这个命令而不是简单的使用 “Python” 是因为 manage.py 会设置 DJANGO_SETTINGS_MODULE 环境变量,这个变量会让 Django 根据 mysite/settings.py 文件来设置 Python 包的导入路径。

当你成功进入命令行后,来试试 database API吧:

(django2.0) F:\WorkSpace\django\mysite>python manage.py shell
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:01:18) [MSC v.1900 32 bit (Intel)] 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   #导入django自带的模块
>>> q = Question(question_text="What's new?",pub_date=timezone.now())   #创建新的问题
>>> q.save()    #保存到数据库
>>> q.id    #查看问题的id
1
>>> q.question_text #查看问题的内容
"What's new?"
>>> q.pub_date  #查看问题的时间
datetime.datetime(2018, 6, 10, 6, 7, 53, 902262, tzinfo=<UTC>)
>>> q.question_text = "What's up?"  #修改问题的内容
>>> q.save()
>>> Question.objects.all()  #展现所有存在数据库中的问题
<QuerySet [<Question: Question object (1)>]>

进入Python 命令行的时候,上面这些命令就相当于是跟数据库交流和沟通,相信你会觉得好烦,我同样也是这样觉得,但是我还是按照步骤操作啦!!

<Question: Question object (1)> 对于我们了解这个对象的细节没什么帮助。让我们通过编辑 Question模型的代码(位于 polls/models.py 中)来修复这个问题。给 QuestionChoice 增加 __str__()方法。 同时也会在admin后台展现给让人类看的有意义些。

from django.db import models

class Question(models.Model):
    question_text = models.CharField(max_length = 200)
    pub_date = models.DateTimeField('date published')
    def __str__(self):
        return self.question_text

class Choice(models.Model):
    question = models.ForeignKey(Question,on_delete = models.CASCADE)
    choice_text = models.CharField(max_length = 200)
    votes = models.IntegerField(default = 0)
    def __str__(self):
        return self.choice_text

已经打过的代码就不用重复,只需要在原有的基础上补充完整就好。

PS.这些都是常规的 Python方法。让我们添加一个自定义was_published_recently函数(关于是否是最近才发送的问题),这只是为了演示:

from django.db import models
import datetime #导入Python 的标准 datetime模块
from django.utils import timezone   #导入 Django 中和时区相关的django.utils.timezone`工具模块。

class Question(models.Model):
    question_text = models.CharField(max_length = 200)
    pub_date = models.DateTimeField('date published')
    def __str__(self):
        return self.question_text
    def was_published_recently(self):
        return self.pub_date >= timezone.now() - datetime.timedelta(days=1)

class Choice(models.Model):
    question = models.ForeignKey(Question,on_delete = models.CASCADE)
    choice_text = models.CharField(max_length = 200)
    votes = models.IntegerField(default = 0)
    def __str__(self):
        return self.choice_text

保存文件然后通过 python manage.py shell 命令再次打开 Python 交互式命令行:

PS.其实不一定要在Python交互式命令行操作,可以在后台直接操作添加,不通过代码操作。

(django2.0) F:\WorkSpace\django\mysite>python manage.py shell
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:01:18) [MSC v.1900 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from polls.models import Choice,Question    #导入模型类
>>>  Question.objects.all()     #获取所有的问题,因为那里多了一个空格,所以出错了
  File "<console>", line 1
    Question.objects.all()
    ^
IndentationError: unexpected indent
>>> Question.objects.all()
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(id=1)   #通过id来查找\筛选
<QuerySet [<Question: What's up?>]>
>>> Question.objects.filter(question_text__startswith= 'What')  #__startswith这个是双下划线的,相当于是匹配到一些字符就通过
<QuerySet [<Question: What's up?>]>
>>> from django.utils import timezone
>>> current_year = timezone.now().year
>>> Question.objects.get(pub_date__year=current_year)   #通过pub_date__year筛选
<Question: What's up?>
>>> Question.objects.get(id=2)  #通过id来获取,但是不存在,所以报错了
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "C:\Users\ASUS-PC\Envs\django2.0\lib\site-packages\django\db\models\manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "C:\Users\ASUS-PC\Envs\django2.0\lib\site-packages\django\db\models\query.py", line 403, in get
    self.model._meta.object_name
polls.models.DoesNotExist: Question matching query does not exist.
>>> Question.objects.get(pk=1)  #通过pk获取,pk跟id是一样的
<Question: What's up?>
>>> q = Question.objects.get(pk=1)
>>> q.was_published_recently()
True
>>> q
<Question: What's up?>
>>> q.choice_set.all()  #显示来自相关对象集的任何选项-到目前为止没有
<QuerySet []>
>>> 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)
>>> q.question  #打错了,q对象下面没有question这个属性
Traceback (most recent call last):
  File "<console>", line 1, in <module>
AttributeError: 'Question' object has no attribute 'question'
>>> c.question
<Question: What's up?>
>>> q.choice_set.all()
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> q.choice_set.count()    #统计选项的个数
3
>>> Choice.objects.filter(question__pub_date__year = current_year)  #过滤/筛选
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
>>> c = q.choice_set.filter(choice_text__startswith = 'Just hacking')
>>> c
<QuerySet [<Choice: Just hacking again>]>
>>> c.delete()  #删除c这个选项
(1, {'polls.Choice': 1})

PS.我在做上面的操作时,出现下面的错误,因为我把双下划线弄成单下划线,所以就报错了。

>>> Choice.objects.filter(question_pub_date__year = current_year)
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "C:\Users\ASUS-PC\Envs\django2.0\lib\site-packages\django\db\models\manager.py", line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "C:\Users\ASUS-PC\Envs\django2.0\lib\site-packages\django\db\models\query.py", line 836, in filter
    return self._filter_or_exclude(False, *args, **kwargs)
  File "C:\Users\ASUS-PC\Envs\django2.0\lib\site-packages\django\db\models\query.py", line 854, in _filter_or_exclude
    clone.query.add_q(Q(*args, **kwargs))
  File "C:\Users\ASUS-PC\Envs\django2.0\lib\site-packages\django\db\models\sql\query.py", line 1253, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
  File "C:\Users\ASUS-PC\Envs\django2.0\lib\site-packages\django\db\models\sql\query.py", line 1277, in _add_q
    split_subq=split_subq,
  File "C:\Users\ASUS-PC\Envs\django2.0\lib\site-packages\django\db\models\sql\query.py", line 1153, in build_filter
    lookups, parts, reffed_expression = self.solve_lookup_type(arg)
  File "C:\Users\ASUS-PC\Envs\django2.0\lib\site-packages\django\db\models\sql\query.py", line 1015, in solve_lookup_type
    _, field, _, lookup_parts = self.names_to_path(lookup_splitted, self.get_meta())
  File "C:\Users\ASUS-PC\Envs\django2.0\lib\site-packages\django\db\models\sql\query.py", line 1379, in names_to_path
    "Choices are: %s" % (name, ", ".join(available)))
django.core.exceptions.FieldError: Cannot resolve keyword 'question_pub_date' into field. Choices are: choice_text, id, question, question_id, votes

5.介绍 Django 管理页面

Django 全自动地根据模型创建后台界面。 Django 通过为站点管理人员创建统一的内容编辑界面解决了这个问题。管理界面不是为了网站的访问者,而是为管理者准备的所以对于管理者来说是,十分方便的。

创建一个管理员账号

首先,我们得创建一个能登录管理页面的用户。请运行下面的命令:

python manage.py createsuperuser

键入你想要使用的用户名(比如是admin),然后按下回车键:

Username (leave blank to use 'asus-pc'): ojl

然后提示你输入想要使用的邮件地址,这个可以直接回车不输入

最后一步是输入密码。你会被要求输入两次密码,第二次的目的是为了确认第一次输入的确实是你想要的密码。

密码的要求如下:

  • 密码不能太短,它必须至少包含8个字符。
  • 密码不能太普通
  • 密码不能是纯数字
Password:
Password (again):
Superuser created successfully.

PS.密码默认是不显示,所以不要说为什么没有看到密码?

启动开发服务器

启动开发服务器,用以下命令启动它:

python manage.py runserver

在浏览器中输入http://127.0.0.1:8000/admin/,即可访问到管理员登录界面:

django2.0实现投票应用程序2

进入管理站点页面

现在,试着使用你在上一步中创建的超级用户(管理员)来登录。然后你将会看到 Django 管理页面的索引页:

你会看到django默认帮你弄好的东西。

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

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

from django.contrib import admin

from .models import Question

admin.site.register(Question)

现在我们向管理页面注册了问题 Question 类。Django 知道它应该被显示在索引页里:

django2.0实现投票应用程序2

点击 “Questions” 。现在看到是问题 “Questions” 对象的列表 “change list” 。这个界面会显示所有数据库里的问题 Question 对象,你可以选择一个来修改。这里现在有我们在上一部分中创建的 “What’s up?” 问题。

django2.0实现投票应用程序2
点击 “What’s up?” 来编辑这个问题(Question)对象:

django2.0实现投票应用程序2

PS.

  • 这个表单是从(models.py里面的)问题 Question 模型中自动生成的
  • 不同的字段类型(日期时间字段 DateTimeField 、字符字段 CharField会生成对应的 HTML 输入控件。每个类型的字段都知道它们该如何在管理页面里显示自己。
  • 每个日期时间字段 DateTimeField都有 JavaScript 写的快捷按钮。日期有转到今天(Today)的快捷按钮和一个弹出式日历界面。时间有设为现在(Now)的快捷按钮和一个列出常用时间的方便的弹出式列表。
  • 后台管理界面是按照你模型类字段排列的顺序设置的

页面的底部提供了几个选项:

  • 保存(Save) - 保存改变,然后返回对象列表。
  • 保存并继续编辑(Save and continue editing) - 保存改变,然后重新载入当前对象的修改界面。
  • 保存并新增(Save and add another) - 保存改变,然后添加一个新的空对象并载入修改界面。
  • 删除(Delete) - 显示一个确认删除页面。

没有正确的设置 TIME_ZONE的话,发布日期(Date Published)会跟你电脑上的时间,就需要去设置:

TIME_ZONE = 'Asia/Shanghai'

点击上面那个图片右上角的 “历史(History)”按钮。你会看到一个列出了所有通过 Django 管理页面对当前对象进行的改变的页面,其中列出了时间戳和进行修改操作的用户名

总结:

  • 建立数据库.涉及到模型类的修改和添加,数据库API的使用
  • 利用Django 提供的自动生成的管理页面(admin)

参考资料(Django的官网):
编写你的第一个 Django 应用,第 2部分