Python Crash Course读书笔记 - 第18章:GETTING STARTED WITH DJANGO

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/了。
在管理站点添加ChessRock 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步:

  1. 定义URL
  2. 写view,每一个URL对应一个view
  3. 写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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值