Django-begin

早有听说django的大名,加上我对这个hexo的自定义博客的页面有一点小小的不满意,所以打算好好学一下,没准还能换个样子

Django 框架学习

什么是Django?什么是框架?

Django 是由 Python 开发的一个免费的开源网站框架,可以用于快速搭建高性能,优雅的网站。就我目前短浅的眼光看,框架就是一个【将大部分的底层功能函封装成函数使得大部分实现能够直接通过调用函数实现】。

动手实现第一个网站


配置Django

这里参考了django官方网站上面的相关指导:
1.在一个网站目录下面键入:

1
$django-admin startproject mysite

此时就会创建一个网站文件夹目录,这个目录下面会形成一个网站所需的基本目录。文件夹的目录大致为:
mysite/
manage.py
mysite/
init.py
settings.py
urls.py
wsgi.py
它们分别的作用是:

  • 外部文件夹mysite:存放了当前网站的文件夹
  • manage.py:命令行工具,方便我们使用命令行交互。相关文档为: django-admin and manage.py
  • 内部文件夹mysite:真正存放了网站的目录。我们可以通过import mysite.url这种形式将其导入
  • mysite/init.py:一个告知python这个目录是一个package目录的空文件。
  • mysite/settings.py:django项目的设置文件。
  • mysite/urls.py:一个声明项目url的文件,也就是我们项目的目录文件。
  • mysite/wsgi.py:一个指向WSGI服务器的空指针

我们运行服务器的方式是在外mysite文件夹内直接运行:

1
python manage.py runserver

然后会出现下列结果:

1
2
3
4
5
6
7
8
System check identified no issues (0 silenced).
You have 13 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.
February 17, 2017 - 16:17:49
Django version 1.10.5, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

此时就在本地启动了我们的Django服务(端口号为8000)。同时注意,不要将这个服务器运行在任何的生产环境下。
runserver可以通过在后方增加数字的形式使得其运行在不同的端口上。

接下来教程将会教会我们如何制作一个投票网站app。
首先我们创建一个相关的文件目录:

1
python manage.py startapp polls

这个命令会创建一个新的文件夹目录,里面会将必要的文件夹进行组织。

1
2
3
4
5
6
7
8
9
polls/
__init__.py
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py

创建第一个页面

首先打开views.py,键入以下代码:

1
2
3
4
5
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world. You're at the polls index.")

可以看出此时通过HttpResponse,将会回复一个带有简单文字的界面。
为了能够调用这个views,我们需要将其映射到一个url上(这就是为什么我们需要urlconf)
在pulls目录下创建一个urls.py的文件,并且在里面键入:

1
2
3
4
5
6
7
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name='index'),
]

此时正则意思就是【匹配一个空行】

然后将根目录下的URLconf指向polls.urls。在mysite/urls.py中,添加django.conf.urls.include库,然后在urlpatterns列表中插入include()。

1
2
3
4
5
6
7
from django.conf.urls import include, url
from django.contrib import admin
urlpatterns = [
url(r'^polls/', include('polls.urls')),
url(r'^admin/', admin.site.urls),
]

这里的匹配的意思是【匹配只用polls/或者admin/开头的url】。注意,这里写的所有都是基于在主目录下的匹配,也就是说,127.0.0.1/这个目录其实已经被默认加在了前面。此时的匹配成功的内容会调用后方参数位置上的urls解析。使用了include的意思就是,在127.0.0.1/polls/目录下的文件解析。

注意,当我们使用其他的url模式的时候,一定要使用**include**
此时我们需要访问的是:
http://localhost:8000/polls/
因为此时在根目录下匹配的是polls,那么此时匹配的内容就是我们response中所布置的网页。

这里介绍一下url函数

url(regex, view, kwargs, )

参数:

  • regex: 这个参数是一个正则表达式,表示一个url的模式。Djagon会从第一个正则表达式开始比较,直到比较到符合的那个。
    注意这个url是不会区分get和post请求的,就比如说https://www.example.com/myapp/,RURLConf回去寻找myapp/的表达式,而https://www.example.com/myapp/?page=3,依然是寻找myapp/的表达式。
    这些表达式在URLconf被load的时候才会加载。
  • view: 如果在某个URLconf中完成了匹配,那么这里就会调用这个特殊的view 函数,并且将HttpRequest对象作为第一个参数传入,并且还包含了其他的从正则表达式中获取的参数。如果只是简单的匹配的话,那么参数是作为可选参数传入;如果命名了补货结果,那么参数值会作为键值出阿奴人。
  • kwargs: 在target view中的可选传入参数。
  • name: 给url的命名。

给Django加上数据库

mysite/settings.py为Django的基本配置文件。这个文件中可以修改我们的默认数据库。系统默认的是python自带的sqlite3,如果需要修改的话,需要找到DATABASES变量,修改下列属性:

  • ‘defualt’改成’django.db.backends.sqlite3’, ‘django.db.backends.postgresql’, ‘django.db.backends.mysql’, or ‘django.db.backends.oracle’等,其他的后台也是支持的
  • ‘name’后的文件要改成对应数据库文件的绝对路径。

在这个文件中,可以设置时区
同时,配置文件中,还有一个叫做INSTALLED_APPS的文件。这个文件是Django使用中会用到的所有对象。这其中的appsu和i在多工程的场合下使用。默认状态下包含一下文件:

  • django.contrib.admin – 系统后台
  • django.contrib.auth – 官方系统
  • django.contrib.contenttypes – 内容类型的框架
  • django.contrib.sessions – 会话框架
  • django.contrib.messages – 信息类框架
  • django.contrib.staticfiles – 静态文件框架

这些些apps有的用到了至少一个数据库。所以在使用之前我们需要创建数据库。

1
python manage.py migrate # 迁移数据库

migrate会从INSTALLED_APPS查找所有的apps并且安装所有必要的数据库。并且将迁移数据库与应用程序。当然,不需要的内容可以直接从这里删除。

我们简单的投票app中将会创建两个简单的模型:Question和Choice。Question中包含了一个问题和公示数据。Choice含含有两个域:选择的内容和机票。每一个Choice都将和Quesiton关联。

我们修改polls/models.py文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
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则是一个时间的子域。
变量的名字就是域的名字。同时我们还能够给我们的域起一个可以理解的名字。比如在Question.pub_date中的命名一样。
有一些域需要传入默认值,就比如CharField,规定改域的大小。
域可以设置默认值,votes中就设置了初始值0。
同时注意到模型中进行了关联,ForeignKey表示Django中的Choice会关联到一个Question上。Django支持所有类型的数据库关系:多对一,一对一,一对多。

激活模型

通过上面代码,Django可以完成:

  • weiapp创建合适的数据库模型
  • 创建python的数据库API,从而操作Question和Choice数据库
    但是我们首先需要将polls app安装。
    因为Django的原则是pluggable(可安装的),也就是说每个apps可以在不同的项目中都是可以运行,所以要通过安装让其可以运行在当前的环境中。
    安装这个过程就是我们之前看到的INSTALLED_APPS设置,由于我们的配置文件默认在polls.apps.PollsConfig,所以我们要在mysite/settings.py中增加这个类:

    INSTALLED_APPS = [
    • ‘polls.apps.PollsConfig’,

      接下来我们用另一个命令:
      1
      python manage.py makemigrations polls

会出现如下的提示:

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

上述指令的意思相当于告诉Django我们将要改变我们当前的模型,并且这个改变将会被作为迁移存储下来。
迁移记录了Djange如何修改我们模型。记录在了polls/migrations/0001_initial.py中。使用指令

1
python manage.py sqlmigrate polls 0001

将会展示第0001次修改中发生了什么:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
BEGIN;
--
-- Create model Choice
--
CREATE TABLE "polls_choice" (
"id" serial NOT NULL PRIMARY KEY,
"choice_text" varchar(200) NOT NULL,
"votes" integer NOT NULL
);
--
-- Create model Question
--
CREATE TABLE "polls_question" (
"id" serial NOT NULL PRIMARY KEY,
"question_text" varchar(200) NOT NULL,
"pub_date" timestamp with time zone NOT NULL
);
--
-- Add field question to choice
--
ALTER TABLE "polls_choice" ADD COLUMN "question_id" integer NOT NULL;
ALTER TABLE "polls_choice" ALTER COLUMN "question_id" DROP DEFAULT;
CREATE INDEX "polls_choice_7aa0f6ee" ON "polls_choice" ("question_id");
ALTER TABLE "polls_choice"
ADD CONSTRAINT "polls_choice_question_id_246c99a640fbbd72_fk_polls_question_id"
FOREIGN KEY ("question_id")
REFERENCES "polls_question" ("id")
DEFERRABLE INITIALLY DEFERRED;
COMMIT;

注意以下:

  • 上述的输出内容取决于数据库类型。
  • 表单的名字会由polls和我们model的小写状态组成(比如polls_choice)
  • 主关键字(IDS)会自动添加(可重载)
  • Django会在外键后加上_id。(可重载)
  • Django会适应我们的数据库,所以数据库的具体域类型,比如sqlite的INTEGER PRIMARY KEY AUTOINCREMENT(自增整数主键)将会为我们自动处理。
  • 外键关系被 foreign key约束,所以不用担心延迟。
  • sqlmigrate命令并不会自动运行迁移,这只是将这个过程输入到中断,所以我们能看到Django需要怎么样的SQL。我们可以确认Djang将会取做什么或者数据库管理员要进行sql脚本如何处理

    使用指令

    1
    python manage.py check

可以检查是否有问题而不会真正的迁移数据库。
一切准备就绪之后,就可以使用下列指令:

1
python manage.py migrate

从而将数据模型实现。
这个指令会将之前没有实现的模型完全实现(django使用了django_migrations跟踪数据库)并且同步数据库的改变。

迁移是非常有用的,它可以让你不需要删除数据库或者表就可以改变模型。可以动态的升级数据库而不丢失数据。
三步修改数据模型:

  • 在models.py中修改数据模型
  • 执行python manage.py makemigrations为所有的改变创建一个迁移
  • 执行python manage.py migrate实现上述所有的更改。
    通过分离迁移实现的过程,使得我们能够提交我们的改变(版本号),从而识自己和他人便于维护。

使用API

首先我们尝试使用python shell和当前的apps交互:

1
python manage.py shell

这里我们使用python shell与其进行交互,注意这里的属性修改如果想要存储在database中的话,需要调用save函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
>>> from polls.models import Question, Choice
>>> Question.objects.all()
<QuerySet []>
>>> from django.utils import timezone
>>> q = Question(question_text="What's new?", pub_date = timezone.now())
>>> timezone.now()
datetime.datetime(2017, 2, 22, 8, 43, 6, 815834, tzinfo=<UTC>)
>>> q.save()
>>> q.id
1
>>> q.question_text
"What's new?"
>>> q.pub_date
datetime.datetime(2017, 2, 22, 8, 42, 53, 211365, tzinfo=<UTC>)
>>> q.question_text = "What's ip"
>>> q.question_text
"What's ip"
>>> q.question_text = "What's up"
>>> q.question_text
"What's up"
>>> q.save()
>>> Question.objects.all()
<QuerySet [<Question: Question object>]>

然后,注意到这个Question.objects.all类的名字不太好听,我们可以在Question类中给其取一个好听的名字:

1
2
3
4
5
6
7
# use this decorator to compate python2
from django.utils.encoding import python_2_unicode_compatible
...
class Choice(models.Model):
@python_2_unicode_compatible
def __str__(self):
return self.choice_text

通过这个方式,可以实现在输出的时候就会将设置的text作为名字。同时对象的表示会在整个Django的生成中使用。
然后我们添加新的models:

1
2
3
4
5
6
7
8
9
10
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)

然后我们重新打开shell进行操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
>>> from polls.models import Question, Choice
# 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()
True
# 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()
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 question whose pub_date is in this year
# (reusing the 'current_year' variable we created above).
>>> Choice.objects.filter(question__pub_date__year=current_year)
<QuerySet [<Choice: Not much>, <Choice: The sky>, <Choice: Just hacking again>]>
# Let's delete one of the choices. Use delete() for that.
>>> c = q.choice_set.filter(choice_text__startswith='Just hacking')
>>> c.delete()
  • 在使用filter过滤数据的时候,可以通过使用filter中对参数的赋值实现查找,比如:id=1,也可以通过加上startswith(对应字符串)或者year(对于时间)变量实现部分查找。
  • 如果企图查找不存在的键值的话,那么将会引发出错。
  • 如果只是以primary key作为查找目标的话,那么pk缩写可以代表这个意思。
  • 通过get获取Questoin对应的对象,可以对其关联的键值进行操作,通过调用外键_set.create即可创建外键对象。
  • 外键对象也可以通过:本键名称 的方法获得本键的对象。同样的,本键名称__之后可以按照本键的使用方法使用
  • 通过filter获得的对象可以调用delete方法将数据删除。

Django的管理员

Django会自动生成一个管理员页面,这个页面是由网站的的后台管理员使用。Django将页面拆分成了publisherpublic两部分,分别给管理者和用户使用。

创建后台管理员

使用指令:

1
python manage.py createsuperuser

可以创建后台。然后我们重新运行manage.py,访问/admin/后便可登陆后台。这个页面的元素实在django.contrib.auth中所提供的。

让管理员可以修改poll应用

注意到,poll 应用并未展示在这个页面上,因为我们还没有告知管理员我们有了Question对象。所以,我们需要打开polls/admin.py文件,并且进行一定的修改:

1
2
3
4
5
from django.contrib import admin
from .models import Question
admin.site.register(Question)

然后我们就会找到这个叫做Question的app,这里会展示我们数据库中所有存在的问题,我们可以在这个页面对其进行修改。
注意:

  • 这个表格是由Question模型自动生成的
  • 不同的模型字段会自动匹配到不同的html输入组件。每种组件都会在Django中合适的展示。
  • 每个DateTimeFiled(日期组件)都会得到一个js的快捷方式。日期将得到一个叫做【Today】的快捷方式(快速的定位当前日期)和一个弹窗,而时间将会得到一个叫做【now】的快捷方式(快速的定位当前时间)和一个快速选择时间的弹窗。,
(未完待续)