Django是一个web框架。可用来构建交互式网站。
设置项目
首先需要写项目说明书(spec
)。
然后需要创建虚拟环境(virtual environment
)。
虚拟环境是一个隔离的环境,可以单独virtual environment
)。
虚拟环境是一个隔离的环境,可以单独为应用安装package,而非整个系统可见
首先创建虚拟环境,并在其中安装Django模块:
# 为显示美观,需设置提示符
$ export PS1='$ '
$ mkdir learning_log
$ cd learning_log
# 注意使用python3而非python, venv是虚拟环境模块,ll_env是虚拟环境的名字
$ python3 -m venv ll_env
# 激活虚拟环境,停用虚拟环境输入deactivate
$ source ll_env/bin/activate
(ll_env) $
# 升级pip,本步骤可选
(ll_env) $ pip install --upgrade pip
# 在虚拟环境下安装django,此package只在此环境下可见。也就是说,每个虚拟环境下都需要安装一次。
(ll_env) $ pip install django
然后创建Django项目learning_log:
# 创建项目,learning_log为项目名
(ll_env) $ django-admin startproject learning_log .
# 查看当前目录,和操作系统下的显示是一致的
(ll_env) $ ls
learning_log ll_env manage.py
# 查看learning_log目录
(ll_env) $ ls learning_log
asgi.py __init__.py settings.py urls.py wsgi.py
# 查看ll_env目录,似乎操作系统下的命令也可以在虚拟环境中执行
(ll_env) $ ls ll_env
bin include lib lib64 pip-selfcheck.json pyvenv.cfg
(ll_env) $ du -sk ll_env
49544 ll_env
以上的几个文件说明以下,详细说明见这里:
- manage.py,此项目的主程序,接受参数并传递给Django执行
- learning_log/settings.py, 项目配置文件
- learning_log/urls.py,针对浏览器请求,构建什么页面
- learning_log/wsgi.py,web server gateway interface配置文件
- learning_log/asgi.py,Asynchronous Server Gateway Interface配置文件
接下来创建数据库,因Django需要用其存放数据:
# migrate命令会确保数据库与项目的当前状态匹配,第一次执行时会创建数据库
# 数据库使用的是SQLite,数据库名为db.sqlite3,生成在当前目录下
# 另外,在虚拟环境下,直接用python而非python3,因其会自动匹配python版本
(ll_env) $ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying sessions.0001_initial... OK
如果你在运行以上migrate
命令时,报错说SQLite版本过低,则需要升级SQLite,参照此文。
大略过程如下,请用root执行:
# cd ~
# wget https://www.sqlite.org/2019/sqlite-autoconf-3270200.tar.gz
# tar -zxvf sqlite-autoconf-3270200.tar.gz
# cd sqlite-autoconf-3270200
# ./configure --prefix=/usr/local
# make && make install
# mv /usr/bin/sqlite3 /usr/bin/sqlite3_old
# ln -s /usr/local/bin/sqlite3 /usr/bin/sqlite3
# OS中查看版本
# sqlite3 --version
3.27.2 2019-02-25 16:06:06 bd49a8271d650fa89e446b42e513b595a717b9212c91dd384aab871fc1d0f6d7
# 下面的环境变量设置非常重要,请写在用户启动文件中,如.bash_profile
# export LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:/usr/local/lib
# Python中查看版本,这里显示对了才是真起作用。如果不设置LD_LIBRARY_PATH则仍显示老版本
# python3
Python 3.6.8 (default, Aug 7 2019, 08:02:28)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39.0.1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import sqlite3
>>> sqlite3.sqlite_version
'3.27.2'
查看项目:
# 以前台方式运行server,默认监听端口是8000,在runserver后面可以直接指定其它端口号
(ll_env) $ python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
January 18, 2020 - 08:00:19
Django version 3.0.2, using settings 'learning_log.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C. <- 就停在这了
这时在其它终端可以测试server是否正常:
$ netstat -an|grep 8000
tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN
$ curl http://127.0.0.1:8000/
如果用浏览器看,就是下面这个样子:
启动应用
Django项目由多个应用组成,我们先启动一个应用。以下命令需另启一终端执行:
$ source ll_env/bin/activate
# 应用名称为learning_logs,比项目名称learning_log多一个s
(ll_env) $ python manage.py startapp learning_logs
(ll_env) $ ls
db.sqlite3 learning_log learning_logs ll_env manage.py
(ll_env) $ ls learning_logs
admin.py apps.py __init__.py migrations models.py __pycache__ tests.py views.py
接下来定义Model,也就是业务模型,代码层面就是Class。
用户可创建多个topic,每个topic可有多个entry。entry为文本类型,同时需要记录时间。因此,在应用目录下编辑文件models.py
如下:
from django.db import models
# Create your models here.
class Topic(models.Model):
"""A topic the user is learning about."""
text = models.CharField(max_length=200)
date_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
"""Return a string representation of the model."""
return self.text
class Entry(models.Model):
"""Something specific learned about a topic."""
topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = 'entries'
def __str__(self):
"""Return a string representation of the model."""
return f"{self.text[:50]}..."
然后需要激活模型,在项目的settings.py文件中先添加应用,放在最前,余下的是系统应用:
INSTALLED_APPS = [
'learning_logs',
...
'django.contrib.messages',
'django.contrib.staticfiles',
]
因为发生了修改,因此就需要应用这些修改,步骤如下。。
(ll_env) $ python manage.py makemigrations learning_logs
Migrations for 'learning_logs':
learning_logs/migrations/0001_initial.py
- Create model Topic
(ll_env) $ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, learning_logs, sessions
Running migrations:
Applying learning_logs.0001_initial... OK
以后每次发生修改都需要这么做,例如第二次应用变更时的输出如下:
(ll_env) $ python manage.py makemigrations learning_logs
Migrations for 'learning_logs':
learning_logs/migrations/0002_entry.py
- Create model Entry
(ll_env) $ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, learning_logs, sessions
Running migrations:
Applying learning_logs.0002_entry... OK
一些操作可通过Django管理站点(Admin Site)来做,例如添加Topic,添加Entry。不过首先需要创建管理用户:
(ll_env) $ python manage.py createsuperuser
Username (leave blank to use 'vagrant'): ll_admin
Email address:
Password: Welcome1
Password (again): Welcome1
This password is too common.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.
创建完用户后,然后将model加入到应用的admin.py文件中:
from django.contrib import admin
# Register your models here.
from .models import Topic, Entry
admin.site.register(Topic)
admin.site.register(Entry)
然后就可以访问管理站点http://localhost:8000/admin/
了。
在管理站点添加Chess
和Rock Climbing
两个Topic。为Chess
创建1个entry,Rock Climbing
创建2个entry:
在本节最后,我们介绍Django Shell,是一个交互式终端,主要用于调试和故障排除。
## 进入Shell
(ll_env) $ python manage.py shell
Python 3.6.8 (default, Aug 7 2019, 08:02:28)
[GCC 4.8.5 20150623 (Red Hat 4.8.5-39.0.1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from learning_logs.models import Topic
>>> Topic.objects.all()
<QuerySet [<Topic: Chess>, <Topic: Rock Climbing>]>
>>> topics = Topic.objects.all()
>>> for topic in topics:
... print(topic.id, topic)
...
1 Chess
2 Rock Climbing
>>> t = Topic.objects.get(id=1)
>>> t.text
'Chess'
>>> t.date_added
datetime.datetime(2020, 1, 19, 4, 5, 27, 998766, tzinfo=<UTC>)
>>> t.entry_set.all()
<QuerySet [<Entry: The opening is the first part of the game, roughly...>, <Entry: In the opening phase of the game, it’s important t...>]>
# 退出Shell
>>> <Ctrl+D>
now exiting InteractiveConsole...
构建主页
Django中制作页面包括3步:
- 定义URL
- 写view,每一个URL对应一个view
- 写template,view使用template做渲染,template决定了页面的模样。
首先在项目目录下定义URL,编辑urls.py文件如下:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls), # 这个是已有的管理主页
path('', include('learning_logs.urls')), # 这个是新增的主页
]
在以上文件中,path('admin/', admin.site.urls)
是已有的行,表示路径admin/
下的页面由模块admin.site.urls
定义。
然后我们进入应用目录learning_log,编辑urls.py文件如下:
"""Defines URL patterns for learning_logs."""
from django.urls import path
from . import views
app_name = 'learning_logs'
urlpatterns = [
# Home page
# 第一个参数表示URL的模式,''表示主页,也就是http://localhost:8000/;
# 第二个参数表示当URL匹配时,调用的函数,这里就是views.py中的index()函数。
# 第三个参数是URL的快捷名称,方便引用。
path('', views.index, name='index'),
]
至此,URL定义完毕,开始写view。
在应用目录下编辑文件views.py如下,可以看到请求被转向了learning_logs/index.html
:
from django.shortcuts import render
# Create your views here.
def index(request):
"""The home page for Learning Log."""
return render(request, 'learning_logs/index.html')
接下来需要写template,在应用目录下创建templates
目录,然后再创建目录learning_logs
。
在最里一层目录编辑文件index.html
如下:
<p>Learning Log</p>
<p>Learning Log helps you keep track of your learning, for any topic you're
learning about.</p>
好了,此时如访问页面http://localhost:8000/
,显示如下:
如果遇到ModuleNotFoundError: No module named 'learning_logs.urls'
的错误,<Ctrl+C>
退出服务,然后重新启动服务即可:
python manage.py runserver
理解这个页面的运转流程非常重要,虽然看似复杂,但将URL,view和template分开的架构不就是MVC架构吗,架构人员,编程人员,Web设计人员可以专注于自己的领域。
构建其它页面
制作两个页面,分别显示所有的topic和特定topic下的所有view。
如果一些元素在每个页面中都需要,就可以定义基础模板,然后其它模板全部继承此基础模板。
在index.html
同一目录下编辑文件base.html
如下:
<p>
<a href="{% url 'learning_logs:index' %}">Learning Log</a>
</p>
{% block content %}{% endblock content %}
包含在% %
之间的称为template tag,可以理解为占位符。最后一行{% block content %}{% endblock content %}
是需要各子目标实现的部分。
然后重写index.html
以继承基础模板:
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Learning Log helps you keep track of your learning, for any topic you're
learning about.</p>
{% endblock content %}
接下来定义topic页面。在应用目录修改urls.py
如下:
"""Defines URL patterns for learning_logs."""
from django.urls import path
from . import views
app_name = 'learning_logs'
urlpatterns = [
# Home page
path('', views.index, name='index'),
path('topics/', views.topics, name='topics'),
]
然后为topic页面写view。在应用目录下修改views.py
如下:
from django.shortcuts import render
from .models import Topic
# Create your views here.
def index(request):
"""The home page for Learning Log."""
return render(request, 'learning_logs/index.html')
def topics(request):
"""Show all topics."""
topics = Topic.objects.order_by('date_added')
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
在以上文件中,context是需要传递给template的数据。在本例中就是字典。
然后为topic定义template。在index.html
同一目录下编辑文件topics.html
如下:
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Topics</p>
<ul>
{% for topic in topics %}
<li>{{ topic }}</li>
{% empty %}
<li>No topics have been added yet.</li>
{% endfor %}
</ul>
{% endblock content %}
然后修改基础模板添加指向topics.html的链接。修改base.html
如下:
<p>
<a href="{% url 'learning_logs:index' %}">Learning Log</a>
<a href="{% url 'learning_logs:topics' %}">Topics</a>
</p>
{% block content %}{% endblock content %}
此时测试页面http://localhost:8000/topics/
,显示如下:
接下来需要为每一个topic设计页面,我们准备用topic ID来区分,例如http://localhost:8000/topics/1/
。
因此我们修改urls.py
如下:
"""Defines URL patterns for learning_logs."""
from django.urls import path
from . import views
app_name = 'learning_logs'
urlpatterns = [
# Home page
path('', views.index, name='index'),
path('topics/', views.topics, name='topics'),
path('topics/<int:topic_id>/', views.topic, name='topic'),
]
然后为topic(注意是topic不是topics)定义view。修改view文件views.py
如下:
from django.shortcuts import render
from .models import Topic
# Create your views here.
def index(request):
"""The home page for Learning Log."""
return render(request, 'learning_logs/index.html')
def topics(request):
"""Show all topics."""
topics = Topic.objects.order_by('date_added')
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
def topic(request, topic_id):
"""Show a single topic and all its entries."""
topic = Topic.objects.get(id=topic_id)
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_logs/topic.html', context)
然后为topic定义view。创建topic.html
文件如下:
{% extends 'learning_logs/base.html' %}
{% block content %}
<p>Topic: {{ topic }}</p>
<p>Entries:</p>
<ul>
{% for entry in entries %}
<li>
<p>{{ entry.date_added|date:'M d, Y H:i' }}</p>
<p>{{ entry.text|linebreaks }}</p>
</li>
{% empty %}
<li>There are no entries for this topic yet.</li>
{% endfor %}
</ul>
{% endblock content %}
最后,在topics.html
页面中添加指向各topic的链接。修改topics.html
如下:
{% extends "learning_logs/base.html" %}
{% block content %}
<p>Topics</p>
<ul>
{% for topic in topics %}
<a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a>
{% empty %}
<li>No topics have been added yet.</li>
{% endfor %}
</ul>
{% endblock content %}
测试效果如下:
最后,我们回顾一下目录结构:
$ ls -1
db.sqlite3 # SQLite数据库
learning_log
learning_logs # 应用目录
ll_env # 虚拟环境
manage.py # Django主程序
# 查看应用目录
$ tree learning_logs
learning_logs
├── admin.py
├── apps.py
├── __init__.py
├── migrations
│ ├── 0001_initial.py
│ ├── 0002_entry.py
│ ├── __init__.py
│ └── __pycache__
│ ├── 0001_initial.cpython-36.pyc
│ ├── 0002_entry.cpython-36.pyc
│ └── __init__.cpython-36.pyc
├── models.py
├── __pycache__
│ ├── admin.cpython-36.pyc
│ ├── __init__.cpython-36.pyc
│ ├── models.cpython-36.pyc
│ ├── urls.cpython-36.pyc
│ └── views.cpython-36.pyc
├── templates
│ └── learning_logs
│ ├── base.html
│ ├── index.html
│ ├── topic.html
│ └── topics.html
├── tests.py
├── urls.py
└── views.py
5 directories, 22 files