Python系列21-Web应用程序-设置样式

一.设置样式

当前,项目“学习笔记”功能已齐备,但未设置样式,也只是在本地计算机上运行。在本博客中,我们将以简单而专业的方式设置这个项目的样式,再将其部署到一台服务器上,让世界上的任何人都能够建立账户。

为设置样式,我们将使用Bootstrap库,这是一组工具,用于为Web应用程序设置样式,使其在任何现代设备上都看起来很专业,无论是大型的平板显示器还是智能手机。为此,我们将使用应用程序django-bootstrap3,这也让你能够练习使用其他Django开发人员开发的应用程序。

我们将把项目“学习笔记”部署到Heroku,这个网站让你能够将项目推送到其服务器,让任何有网络连接的人都可使用它。我们还将使用版本控制系统Git来跟踪对这个项目所做的修改。

完成项目“学习笔记”后,你将能够开发简单的Web应用程序,让它们看起来很漂亮,再将它们部署到服务器。你还能够利用更高级的学习资源来提高技能。

1.1 设置项目“学习笔记”的样式

我们一直专注于项目“学习笔记”的功能,而没有考虑样式设置的问题,这是有意为之的。这是一种不错的开发方法,因为能正确运行的应用程序才是有用的。当然,应用程序能够正确运行后,外观就显得很重要了,因为漂亮的应用程序才能吸引用户使用它。

在本博客中,我将简要地介绍应用程序django-bootstrap3,并演示如何将其继承到项目中,为部署项目做好准备。

1.1.1 应用程序django-bootstrap3

我们将使用django-bootstrap3来将Bootstrap继承到项目中。这个应用程序下载必要的Bootstrap文件,将它们放到项目的合适位置,让你能够在项目的模板中使用样式设置指令。

为安装django-bootstrap3,在活动的虚拟环境中执行如下命令:

(ll_env) E:\python\my_learning_log>pip install django-bootstrap3
Collecting django-bootstrap3
  Downloading https://files.pythonhosted.org/packages/ed/84/892d3cc98fc597b7a7dec7de5afa434a22c4e0acfb25dd71ea0f4eb67ec5/django_bootstrap3-14.2.0-py3-none-any.w
hl
Collecting importlib-metadata<2.0.0,>=1.5.0; python_version < "3.8" (from django-bootstrap3)
  Cache entry deserialization failed, entry ignored
  Downloading https://files.pythonhosted.org/packages/8e/58/cdea07eb51fc2b906db0968a94700866fc46249bdc75cac23f9d13168929/importlib_metadata-1.7.0-py2.py3-none-a
ny.whl
Requirement already satisfied: django<4.0,>=2.2 in e:\python\my_learning_log\ll_env\lib\site-packages (from django-bootstrap3)
Collecting zipp>=0.5 (from importlib-metadata<2.0.0,>=1.5.0; python_version < "3.8"->django-bootstrap3)
  Cache entry deserialization failed, entry ignored
  Cache entry deserialization failed, entry ignored
  Downloading https://files.pythonhosted.org/packages/0f/8c/715c54e9e34c0c4820f616a913a7de3337d0cd79074dd1bed4dd840f16ae/zipp-3.4.1-py3-none-any.whl
Requirement already satisfied: sqlparse>=0.2.2 in e:\python\my_learning_log\ll_env\lib\site-packages (from django<4.0,>=2.2->django-bootstrap3)
Requirement already satisfied: asgiref<4,>=3.2.10 in e:\python\my_learning_log\ll_env\lib\site-packages (from django<4.0,>=2.2->django-bootstrap3)
Requirement already satisfied: pytz in e:\python\my_learning_log\ll_env\lib\site-packages (from django<4.0,>=2.2->django-bootstrap3)
Installing collected packages: zipp, importlib-metadata, django-bootstrap3
Successfully installed django-bootstrap3-14.2.0 importlib-metadata-1.7.0 zipp-3.4.1

(ll_env) E:\python\my_learning_log>

接下来,需要在settings.py的INSTALLED_APPS 中添加如下代码,在项目中包含应用程序django-boostrap3:
settings.py

--snip--
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # 第三方应用程序
    'bootstrap3',
    
    # 我的应用程序
    'learning_logs',
    'users',
]
--snip--

新建一个用于指定其他开发人员开发的应用程序的片段,将其命名为“第三方应用程序”,并在其中添加'bootstrap3' 。大多数应用程序都需要包含在INSTALLED_APPS 中,为确定这一点,请阅读要使用的应用程序的设置说明。

我们需要让django-bootstrap3包含jQuery,这是一个JavaScript库,让你能够使用Bootstrap模板提供的一些交互式元素。请在settings.py的末尾添加如下代码:

# 我的设置
LOGIN_URL = '/users/login/'

# django-bootstrap3的设置
BOOTSTRAP3 = {
    'include_jquery': True,
}

这些代码让你无需手工下载jQuery并将其放到正确的地方。

1.1.2 使用Bootstrap来设置项目“学习笔记”的样式

Bootstrap基本上就是一个大型的样式设置工具集,它还提供了大量的模板,你可将它们应用于项目以创建独特的总体风格。对Bootstrap初学者来说,这些模板比各个样式设置工具使用起来要容易得多。要查看Bootstrap提供的模板,可访问http://getbootstrap.com/ ,单击Getting Started,再向下滚动到Examples部分,并找到Navbars in action。我们将使用模板Static top navbar,它提供了简单的顶部导航条、页面标题和用于放置页面内容的容器。

知道要获得的效果后,接下来的内容理解起来将更容易

1.1.3 修改base.html

我们需要修改模板base.html,以使用前述Bootstrap模板。我们把新的base.html分成几个部分进行介绍。

1. 定义HTML头部
对base.html所做的第一项修改是,在这个文件中定义HTML头部,使得显示“学习笔记”的每个页面时,浏览器标题栏都显示这个网站的名称。我们还将添加一些在模板中使用Bootstrap所需的信息。删除base.html的全部代码,并输入下面的代码:
base.html

{% load bootstrap3 %}

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1"

      <title>Learning Log</title>

      {% bootstrap_css %}
      {% bootstrap_javascript %}
  </head>
</html>

我们加载了django-bootstrap3中的模板标签集。接下来,我们将这个文件声明为使用英语编写的HTML文档。HTML文件分为两个主要部分:头部(head)和主体 (body);HTML文件的头部不包含任何内容:它只是将正确显示页面所需的信息告诉浏览器。我们包含了一个title元素,在浏览器中打开网站“学习笔记”的页面时,浏览器的标题栏将显示该元素的内容。

我们使用了django-bootstrap3的一个自定义模板标签,它让Django包含所有的Bootstrap样式文件。接下来的标签启用你可能在页面中使用的所有交互式行为,如可折叠的导航栏。结束标签</head> 。

2. 定义导航栏
下面来定义页面顶部的导航栏:

{% load bootstrap3 %}

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1"

      <title>Learning Log</title>

      {% bootstrap_css %}
      {% bootstrap_javascript %}
  </head>

  <body>
    <!-- Static navbar -->
    <nav class="navbar navbar-default navbar-static-top">
        <div class="container">

            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed"
                   data-toggle="collapse" data-target="#navbar"
                   aria-expanded="false" aria-controls="navbar">
                </button>
                <a class="navbar-brand" href="{% url 'learning_logs:index' %}">
                    Learning Log</a>
            </div>

            <div id="navbar" class="navbar-collapse collapse">
               <ul class="nav navbar-nav">
                  <li><a href="{% url 'learning_logs:topics' %}">Topics</a></li>
               </ul>

               <ul class="nav navbar-nav navbar-right">
                  {% if user.is_authenticated %}
                     <li><a>Hello, {{ user.username }}.</a></li>
                     <li><a href="{% url 'users:logout' %}">log out</a></li>
                  {% else %}
                     <li><a href="{% url 'users:register' %}">register</a></li>
                     <li><a href="{% url 'users:login' %}">log in</a></li>
                  {% endif %}
               </ul>
            </div><!--/.nav-collapse -->

        </div>
    </nav>

  </body>

</html>

第一个元素为起始标签<body> 。HTML文件的主体包含用户将在页面上看到的内容。<nav> 元素,表示页面的导航链接部分。对于这个元素内的所有内容,都将根据选择器 (selector)navbar 、navbar-default 和navbar-static-top 定义的Bootstrap样式规则来设置样式。选择器决定了特定样式规则将应用于页面上的哪些元素。

这个模板定义了一个按钮,它将在浏览器窗口太窄、无法水平显示整个导航栏时显示出来。如果用户单击这个按钮,将出现一个下拉列表,其中包含所有的导航元素。

在用户缩小浏览器窗口或在屏幕较小的移动设备上显示网站时,collapse 会使导航栏折叠起来。我们在导航栏的最左边显示项目名,并将其设置为到主页的链接,因为它将出现在这个项目的每个页面中。我们定义了一组让用户能够在网站中导航的链接。导航栏其实就是一个以<ul> 打头的列表,其中每个链接都是一个列表项(<li> )。要添加更多的链接,可插入更多使用下述结构的行:

<li><a href="{% url 'learning_logs:title' %}">Title</a></li>

这行表示导航栏中的一个链接。这个链接是直接从base.html的前一个版本中复制而来的。

我们添加了第二个导航链接列表,这里使用的选择器为navbar-right 。选择器navbar-right 设置一组链接的样式,使其出现在导航栏右边——登录链接和注册链接通常出现在这里。在这里,我们要么显示问候语和注销链接,要么显示注册链接和登录链接。这部分余下的代码结束包含导航栏的元素。

3. 定义页面的主要部分
base.html的剩余部分包含页面的主要部分:

{% load bootstrap3 %}

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1"

      <title>Learning Log</title>

      {% bootstrap_css %}
      {% bootstrap_javascript %}
  </head>

  <body>
    <!-- Static navbar -->
    <nav class="navbar navbar-default navbar-static-top">
        <div class="container">

            <div class="navbar-header">
                <button type="button" class="navbar-toggle collapsed"
                   data-toggle="collapse" data-target="#navbar"
                   aria-expanded="false" aria-controls="navbar">
                </button>
                <a class="navbar-brand" href="{% url 'learning_logs:index' %}">
                    Learning Log</a>
            </div>

            <div id="navbar" class="navbar-collapse collapse">
               <ul class="nav navbar-nav">
                  <li><a href="{% url 'learning_logs:topics' %}">Topics</a></li>
               </ul>

               <ul class="nav navbar-nav navbar-right">
                  {% if user.is_authenticated %}
                     <li><a>Hello, {{ user.username }}.</a></li>
                     <li><a href="{% url 'users:logout' %}">log out</a></li>
                  {% else %}
                     <li><a href="{% url 'users:register' %}">register</a></li>
                     <li><a href="{% url 'users:login' %}">log in</a></li>
                  {% endif %}
               </ul>
            </div><!--/.nav-collapse -->

        </div>
    </nav>

    <div class="container">
        <div class="page-header">
            {% block header %}{% endblock header %}
        </div>
        {% block content %}{% endblock content %}
    </div><!-- /container -->


  </body>

</html>

一个<div> 起始标签,其class属性为container 。div是网页的一部分,可用于任何目的,并可通过边框、元素周围的空间(外边距)、内容和边框之间的间距(内边距)、背景色和其他样式规则来设置其样式。这个div是一个容器,其中包含两个元素:一个新增的名为header 的块content 。header 块的内容告诉用户页面包含哪些信息以及用户可在页面上执行哪些操作;其class属性值page-header 将一系列样式应用于这个块。content 块是一个独立的div,未使用class属性指定样式。

1.1.4 使用jumbotron设置主页的样式

下面来使用新定义的header 块及另一个名为jumbotron的Bootstrap元素修改主页。jumbotron元素是一个大框,相比于页面的其他部分显得鹤立鸡群,你想在其中包含什么东西都可以;它通常用于在主页中呈现项目的简要描述。我们还可以修改主页显示的消息。index.html的代码如下:

{% extends "learning_logs/base.html" %}
   {% block header %}
      <div class='jumbotron'>
         <h1>Track your learning.</h1>
      </div>
{% endblock header %}

{% block content %}
   <h2>
      <a href="{% url 'users:register' %}">Register an account</a> to make
       your own Learning Log, and list the topics you're learning about.
    </h2>
    <h2>
       Whenever you learn something new about a topic, make an entry
       summarizing what you've learned.
    </h2>
{% endblock content %}

我们告诉Django,我们要定义header 块包含的内容。在一个jumbotron 元素中,我们放置了一条简短的标语——Track your Learning,让首次访问者大致知道“学习笔记”是做什么用的。

我们通过添加一些文本,做了更详细的说明。我们邀请用户建立账户,并描述了用户可执行的两种主要操作:添加新主题以及在主题中创建条目。

image.png

1.1.5 设置登录页面的样式

我们改进了登录页面的整体外观,但还未改进登录表单,下面来让表单与页面的其他部分一致
login.html

{% extends "learning_logs/base.html" %}
   {% load bootstrap3 %}
   {% block header %}
<h2>Log in to your account.</h2>
{% endblock header %}
{% block content %}

 <form method="post" action="{% url 'users:login' %}" class="form">
    {% csrf_token %}
       {% bootstrap_form form %}
       {% buttons %}
       <button name="submit" class="btn btn-primary">log in</button>
     {% endbuttons %}
     <input type="hidden" name="next" value="{% url 'learning_logs:index' %}" />
</form>
{% endblock content %}

我们在这个模板中加载了bootstrap3模板标签。我们定义了header 块,它描述了这个页面是做什么用的。注意,我们从这个模板中删除了{% if form.errors %} 代码块,因为django-bootstrap3会自动管理表单错误。

我们添加了属性class="form" ;然后使用模板标签{% bootstrap_form %} 来显示表单;这个标签替换了我们在第19章使用的标签{{ form.as_p }} 。模板标签{% booststrap_form %} 将Bootstrap样式规则应用于各个表单元素。bootstrap3起始模板标签{% buttons %} ,它将Bootstrap样式应用于按钮。

下图显示了现在渲染的登录表单。这个页面比以前整洁得多,其风格一致,用途明确。如果你尝试使用错误的用户名或密码登录,将发现消息的样式与整个网站也是一致的,毫无违和感。


image.png

1.1.6 设置new_topic 页面的样式

下面来让其他网页的风格也一致。首先来修改new_topic 页面

{% extends "learning_logs/base.html" %}
{% load bootstrap3 %}

{% block header %}
   <h2>Add a new topic:</h2>
{% endblock header %}

{% block content %}
    <form action="{% url 'learning_logs:new_topic' %}" method='post'
       class="form">

         {% csrf_token %}
         {% bootstrap_form form %}
         {% buttons %}
         <button name="submit" class="btn btn-primary">add topic</button>
       {% endbuttons %}
     </form>
{% endblock content %}

这里的大多数修改都类似于对login.html所做的修改:在加载bootstrap3,添加header 块并在其中包含合适的消息;接下来,我们在标签<form> 中添加属性class="form",使用模板标签{% bootstrap_form %} 代替{{ form.as_p }} ,并使用bootstrap3结构来定义提交按钮。如果你现在登录并导航到new_topic 页面,将发现其外观类似于登录页面。

1.1.7 设置topics页面的样式

下面来确保用于查看信息的页面的样式也是合适的,首先来设置topics页面的样式:
topics.html

{% extends "learning_logs/base.html" %}
{% block header %}
  <h1>Topics</h1>
{% endblock header %}

{% block content %}
  <ul>
     {% for topic in topics %}
       <li>
          <h3>
             <a href="{% url 'learning_logs:topic' topic.id %}">{{ topic }}</a>
          </h3>
       </li>
      {% empty %}
      <li>No topics have been added yet.</li>
      {% endfor %}
</ul>
   <h3><a href="{% url 'learning_logs:new_topic' %}">Add new topic</h3>

{% endblock content %}

我们不需要标签{% load bootstrap3 %} ,因为我们在这个文件中没有使用任何bootstrap3自定义标签。我们在header 块中添加了标题Topics。为设置每个主题的样式,我们将它们都设置为<h3> 元素,让它们在页面上显得大些;对于添加新主题的链接,也做了同样的处理。

1.1.8 设置topic页面中条目的样式

topic页面包含的内容比其他大部分页面都多,因此需要做的样式设置工作要多些。我们将使用Bootstrap面板 (panel)来突出每个条目。面板是一个带预定义样式的div,非常适合用于显示主题的条目:
topic.html

{% extends 'learning_logs/base.html' %}

{% block header %}
   <h2>{{ topic }}</h2>
{% endblock header %}

{% block content %}
   <p>
      <a href="{% url 'learning_logs:new_entry' topic.id %}">add new entry</a>
   </p>

    {% for entry in entries %}
       <div class="panel panel-default">
       <div class="panel-heading">
          <h3>
            {{ entry.date_added|date:'M d, Y H:i' }}
            <small>
                 <a href="{% url 'learning_logs:edit_entry' entry.id %}">
                     edit entry</a>
            </small>
          </h3>
       </div>
       <div class="panel-body">
           {{ entry.text|linebreaks }}
        </div>
       </div> <!-- panel -->
   {% empty %}
   There are no entries for this topic yet.
   {% endfor %}
{% endblock content %}

我们首先将主题放在了header 块中。然后,我们删除了这个模板中以前使用的无序列表结构。我们创建了一个面板式div元素(而不是将每个条目作为一个列表项),其中包含两个嵌套的div:一个面板标题(panel-heading )div和一个面板主体(panel-body )div。其中面板标题div包含条目的创建日期以及用于编辑条目的链接,它们都被设置为<h3> 元素,而对于编辑条目的链接,还使用了标签<small> ,使其比时间戳小些。

面板主体div,其中包含条目的实际文本。注意,只修改了影响页面外观的元素,对在页面中包含信息的Django代码未做任何修改。

image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339

推荐阅读更多精彩内容