Django完整入门指南-第3部分--1

Django完整入门指南-第3部分-1

Django完整入门指南-第3部分

 苹果电脑  视窗  的Linux

系列3/7

 

介绍

在本教程中,我们将深入探讨两个基本概念:URL和表单。 在此过程中,我们将探索许多其他概念,例如创建可重复使用的模板和安装第三方库。 我们还将编写大量的单元测试。

如果您从第一部分开始就遵循本教程系列,对项目进行编码并逐步按照本教程进行操作,则可能需要在开始之前更新models.py :

board/models.py

class Topic(models.Model):
    # other fields...
    # Add `auto_now_add=True` to the `last_updated` field
    last_updated = models.DateTimeField(auto_now_add=True)

class Post(models.Model):
    # other fields...
    # Add `null=True` to the `updated_by` field
    updated_by = models.ForeignKey(User, null=True, related_name='+')
  

现在,在激活virtualenv的情况下运行命令:

 python manage.py makemigrations 
python manage.py migrate 

如果您在updated_by字段中已经有null=True ,在last_updated字段中已经有auto_now_add=True ,则可以放心地忽略上面的说明。

如果您愿意将我的源代码用作起点,可以在GitHub上获取它。

项目的当前状态可以在发行标签v0.2-lw下找到。 下面的链接将带您到正确的地方:

https://github.com/sibtc/django-beginners-guide/tree/v0.2-lw

开发将在这里进行。


URLs

继续开发我们的应用程序,现在我们必须实现一个新页面,以列出属于给定Board的所有主题。 回顾一下,您可以在下面看到我们在上一教程中绘制的线框:

线框主题

图1:Boards项目线框,列出了Django Board中的所有主题。

我们将从编辑myproject文件夹中的urls.py开始:

myproject / urls.py

from django.conf.urls import url
from django.contrib import admin

from boards import views

urlpatterns = [
    url(r'^$', views.home, name='home'),
    url(r'^boards/(?P<pk>\d+)/$', views.board_topics, name='board_topics'),
    url(r'^admin/', admin.site.urls),
]
  

这次让我们花点时间分析urlpatternsurl 。

URL调度程序和URLconf (URL配置)是Django应用程序的基本部分。 一开始,它看起来很混乱;我记得刚开始使用Django时遇到困难。

实际上,目前Django开发人员正在研究简化路由语法提案 。 但是目前,按照1.11版本,这就是我们所拥有的。 因此,让我们尝试了解其工作原理。

一个项目可以在应用程序之间分布许多urls.py。 但是Django需要使用url.py作为起点。 这个特殊的urls.py称为root URLconf 。 它在settings.py文件中定义。

myproject / settings.py

 ROOT_URLCONF = 'myproject.urls' 

它已经配置好了,因此您无需在此处进行任何更改。

Django收到请求后,便开始在项目的URLconf中搜索匹配项。 它从urlpatterns变量的第一个条目开始,并针对每个url条目测试请求的URL。

如果Django找到匹配项,它将把请求传递给view函数 ,该函数url的第二个参数。urlpatterns的顺序urlpatterns重要,因为Django一旦找到匹配项就会停止搜索。 现在,如果Django在URLconf中找不到匹配项,它将引发404异常,这是Page Not Found的错误代码。

这是url函数的剖析:

 def url ( regex , view , kwargs = None , name = None ): # ... 
  • regex :用于匹配字符串中的URL模式的正则表达式。 请注意,这些正则表达式不会搜索GETPOST参数。 在对http://127.0.0.1:8000/boards/?page=2的请求中,仅/ boards /将被处理。
  • view :一种视图功能,用于处理用户对匹配URL的请求。 它还接受django.conf.urls.include函数的返回,该函数用于引用外部urls.py文件。 例如,您可以使用它来定义一组特定于应用程序的URL,并使用前缀将其包含在根URLconf中。 稍后我们将进一步探讨这个概念。
  • kwargs :传递给目标视图的任意关键字参数。 通常用于对可重用视图进行一些简单的自定义。我们不经常使用它。
  • name :给定URL的唯一标识符。 这是一个非常重要的功能。 永远记住为您的URL命名。 这样,您只需更改正则表达式即可更改整个项目中的特定URL。 因此,切勿在视图或模板中对URL进行硬编码,并始终使用其名称来引用URL,这一点很重要。

匹配的网址格式

基本URLs

基本URL的创建非常简单。 这只是匹配字符串的问题。 例如,假设我们要创建一个“关于”页面,可以这样定义:

from django.conf.urls import url
from boards import views

urlpatterns = [
    url(r'^$', views.home, name='home'),
    url(r'^about/$', views.about, name='about'),
]
  

我们还可以创建更深的URL结构:

 from django.conf.urls import url from boards import views urlpatterns = [ url ( r'^$' , views . home , name = 'home' ), url ( r'^about/$' , views . about , name = 'about' ), url ( r'^about/company/$' , views . about_company , name = 'about_company' ), url ( r'^about/author/$' , views . about_author , name = 'about_author' ), url ( r'^about/author/vitor/$' , views . about_vitor , name = 'about_vitor' ), url ( r'^about/author/erica/$' , views . about_erica , name = 'about_erica' ), url ( r'^privacy/$' , views . privacy_policy , name = 'privacy_policy' ), ] 

这些是一些简单的URL路由示例。 对于上述所有示例,view函数将遵循以下结构:

from django.conf.urls import url
from boards import views

urlpatterns = [
    url(r'^$', views.home, name='home'),
    url(r'^about/$', views.about, name='about'),
    url(r'^about/company/$', views.about_company, name='about_company'),
    url(r'^about/author/$', views.about_author, name='about_author'),
    url(r'^about/author/vitor/$', views.about_vitor, name='about_vitor'),
    url(r'^about/author/erica/$', views.about_erica, name='about_erica'),
    url(r'^privacy/$', views.privacy_policy, name='privacy_policy'),
]
  

进阶URLs

通过利用正则表达式来匹配某些类型的数据并创建动态URL,可以实现URL路由的更高级用法。

例如,要创建一个个人资料页面,就像github.com/vitorfs或twitter.com/vitorfs等许多服务一样,其中“ vitorfs”是我的用户名,我们可以执行以下操作:

from django.conf.urls import url
from boards import views

urlpatterns = [
    url(r'^$', views.home, name='home'),
    url(r'^(?P<username>[\w.@+-]+)/$', views.user_profile, name='user_profile'),
]
  

这将匹配Django用户模型的所有有效用户名。

现在观察到上面的示例是一个非常宽松的 URL。 这意味着它将匹配许多URL模式,因为它是在URL的根目录中定义的,没有像/ profile / <username> /这样的前缀。 在这种情况下,如果我们想定义一个名为/ about /的URL,我们将在用户名URL模式之前进行定义:

from django.conf.urls import url
from boards import views

urlpatterns = [
    url(r'^$', views.home, name='home'),
    url(r'^about/$', views.about, name='about'),
    url(r'^(?P<username>[\w.@+-]+)/$', views.user_profile, name='user_profile'),
]
  

如果“ about”页面是用户名URL模式之后定义的,则Django将永远找不到它,因为单词“ about”将与用户名regex匹配,并且将处理视图user_profile而不是about视图功能。

有一些副作用。 例如,从现在开始,我们将必须将“ about”视为禁止的用户名,因为如果用户选择“ about”作为其用户名,则此人将永远不会看到其个人资料页面。

URL路由顺序很重要

 旁注:如果要为用户配置文件设计很酷的URL,避免URL冲突的最简单解决方案是添加一个前缀,例如/ u / vitorfs / ,或者像Medium一样添加/ @ vitorfs / ,其中“ @”是前缀。

如果根本不需要前缀,请考虑使用这样的禁止名称列表: github.com/shouldbee/reserved-usernames 。 另一个例子是我在学习Django时开发的应用程序; 我当时创建了清单: github.com/vitorfs/parsifal/ 。

这些冲突很常见。 以GitHub为例; 他们有此URL可以列出您当前正在查看的所有存储库: github.com/watching 。 某人在GitHub上注册了一个名为“ watching”的用户名,因此该人看不到他的个人资料页面。 我们可以通过尝试以下URL来查看具有该用户名的用户: github.com/watching/repositories ,它应该列出用户的存储库,例如我的github.com/vitorfs/repositories 。

这种URL路由的整个想法是创建动态页面,其中部分URL将用作特定资源的标识符,该标识符将用于构成页面。 例如,该标识符可以是整数ID或字符串。

最初,我们将使用Board ID来为Topics创建一个动态页面。 让我们再次阅读我在URL部分开头给出的示例:

 url ( r'^boards/(?P<pk> \ d+)/$' , views . board_topics , name = 'board_topics' ) 

正则表达式\d+将匹配任意大小的整数。 该整数将用于从数据库中检索Board 。 现在观察到我们将正则表达式写为(?P<pk>\d+) ,这是在告诉Django将值捕获到名为pk的关键字参数中。

这是我们为其编写视图函数的方式:

 def board_topics ( request , pk ): # do something... 

因为我们使用了(?P<pk>\d+)正则表达式,所以board_topics的关键字参数必须命名为pk 。

如果我们想使用任何名称,可以这样做:

 url ( r'^boards/( \ d+)/$' , views . board_topics , name = 'board_topics' ) 

然后可以这样定义视图函数:

 def board_topics ( request , board_id ): # do something... 

或像这样:

 def board_topics ( request , id ): # do something... 

名字没关系。 但是,使用命名参数是一个好习惯,因为当我们开始编写更大的URL来捕获多个ID和变量时,它将更易于阅读。

 旁注: PK还是ID?

PK代表主键 。 这是访问模型主键的快捷方式。 所有Django模型都具有此属性。

在大多数情况下,使用pk属性与id相同。 这是因为,如果我们不为模型定义主键,则Django将自动创建一个名为idAutoField ,它将作为其主键。

例如,如果您为模型定义了其他主键,那么假设字段email是您的主键。 要访问它,您可以使用obj.emailobj.pk

使用URL API

现在该写一些代码了。 让我们实现我在URL部分开头提到的主题列表页面(参见图1 )。

首先,编辑urls.py,添加我们的新网址路由:

myproject / urls.py

from django.conf.urls import url
from django.contrib import admin

from boards import views

urlpatterns = [
    url(r'^$', views.home, name='home'),
    url(r'^boards/(?P<pk>\d+)/$', views.board_topics, name='board_topics'),
    url(r'^admin/', admin.site.urls),
]
  

现在让我们创建视图功能board_topics :

boards/views.py

from django.shortcuts import render
from .models import Board

def home(request):
    # code suppressed for brevity

def board_topics(request, pk):
    board = Board.objects.get(pk=pk)
    return render(request, 'topics.html', {'board': board})
  

模板文件夹中,创建一个名为topic.html的新模板:

template / topics.html

{% load static %}<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>{{ board.name }}</title>
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
  </head>
  <body>
    <div class="container">
      <ol class="breadcrumb my-4">
        <li class="breadcrumb-item">Boards</li>
        <li class="breadcrumb-item active">{{ board.name }}</li>
      </ol>
    </div>
  </body>
</html>
  

 注意:目前,我们仅创建新的HTML模板。 不用担心,在下一节中,我将向您展示如何创建可重用的模板。

现在,在网络浏览器中检查URL http://127.0.0.1:8000/boards/1/ 。 结果应为以下页面:

主题页面

是时候编写一些测试了! 编辑tests.py文件,并在文件底部添加以下测试:

boards / tests.py

from django.core.urlresolvers import reverse
from django.urls import resolve
from django.test import TestCase
from .views import home, board_topics
from .models import Board

class HomeTests(TestCase):
    # ...

class BoardTopicsTests(TestCase):
    def setUp(self):
        Board.objects.create(name='Django', description='Django board.')

    def test_board_topics_view_success_status_code(self):
        url = reverse('board_topics', kwargs={'pk': 1})
        response = self.client.get(url)
        self.assertEquals(response.status_code, 200)

    def test_board_topics_view_not_found_status_code(self):
        url = reverse('board_topics', kwargs={'pk': 99})
        response = self.client.get(url)
        self.assertEquals(response.status_code, 404)

    def test_board_topics_url_resolves_board_topics_view(self):
        view = resolve('/boards/1/')
        self.assertEquals(view.func, board_topics)
  

这里要注意几件事。 这次我们使用了setUp方法。 在设置方法中,我们创建了一个可在测试中使用的Board实例。 我们必须这样做,因为Django测试套件不会针对当前数据库运行测试。 为了运行测试,Django会动态创建一个新数据库,应用所有模型迁移,运行测试,完成后销毁测试数据库。

因此,在setUp方法中,我们准备了用于运行测试的环境,以便模拟场景。

  • test_board_topics_view_success_status_code方法:正在测试Django是否为现有Board返回状态码200(成功)。
  • test_board_topics_view_not_found_status_code方法:正在测试Django是否针对数据库中不存在的Board返回状态码404(找不到页面)。
  • test_board_topics_url_resolves_board_topics_view方法:正在测试Django是否使用正确的视图函数来呈现主题。

现在是时候运行测试了:

 python manage.py test 

并输出:

 Creating test database for alias 'default'... System check identified no issues (0 silenced). .E... ====================================================================== ERROR: test_board_topics_view_not_found_status_code (boards.tests.BoardTopicsTests) ---------------------------------------------------------------------- Traceback (most recent call last): # ... boards.models.DoesNotExist: Board matching query does not exist. ---------------------------------------------------------------------- Ran 5 tests in 0.093s FAILED (errors=1) Destroying test database for alias 'default'... 

测试test_board_topics_view_not_found_status_code失败。 我们可以在Traceback中看到它返回了一个异常“ boards.models.DoesNotExist:董事会匹配查询不存在。”

主题错误500页

DEBUG=False生产中,访问者将看到500 Internal Server Error页面。 但这不是我们想要的行为。

我们要显示404页面未找到 。 因此,让我们重构一下观点:

板/views.py

 from django.shortcuts import render from django.http import Http404 from .models import Board def home ( request ): # code suppressed for brevity def board_topics ( request , pk ): try : board = Board . objects . get ( pk = pk ) except Board . DoesNotExist : raise Http404 return render ( request , 'topics.html' , { 'board' : board }) 

让我们再次测试:

 python manage.py test 
 Creating test database for alias 'default'... System check identified no issues (0 silenced). ..... ---------------------------------------------------------------------- Ran 5 tests in 0.042s OK Destroying test database for alias 'default'... 

好极了! 现在它正在按预期工作。

主题错误404页面

这是Django在DEBUG=False显示的默认页面。 稍后,我们可以自定义404页面以显示其他内容。

现在,这是一个非常常见的用例。 实际上,Django具有尝试获取对象或返回不存在该对象的404的快捷方式。

因此,让我们再次重构board_topics视图:

 from django.shortcuts import render , get_object_or_404 from .models import Board def home ( request ): # code suppressed for brevity def board_topics ( request , pk ): board = get_object_or_404 ( Board , pk = pk ) return render ( request , 'topics.html' , { 'board' : board }) 

更改代码? 测试一下。

 python manage.py test 
 Creating test database for alias 'default'... System check identified no issues (0 silenced). ..... ---------------------------------------------------------------------- Ran 5 tests in 0.052s OK Destroying test database for alias 'default'... 

没有破坏任何东西。 我们可以继续发展。

现在的下一步是在屏幕中创建导航链接。 主页上应有一个链接,可将访​​问者带到给定董事会的主题页面。 同样,主题页面应具有指向首页的链接。

线框链接

我们可以从为HomeTests类编写一些测试开始:

boards / tests.py

 class HomeTests ( TestCase ): def setUp ( self ): self . board = Board . objects . create ( name = 'Django' , description = 'Django board.' ) url = reverse ( 'home' ) self . response = self . client . get ( url ) def test_home_view_status_code ( self ): self . assertEquals ( self . response . status_code , 200 ) def test_home_url_resolves_home_view ( self ): view = resolve ( '/' ) self . assertEquals ( view . func , home ) def test_home_view_contains_link_to_topics_page ( self ): board_topics_url = reverse ( 'board_topics' , kwargs = { 'pk' : self . board . pk }) self . assertContains ( self . response , 'href="{0}"' . format ( board_topics_url )) 

注意,现在我们还为HomeTest添加了setUp方法。 这是因为现在我们将需要一个Board实例,并且还将url响应移动到setUp ,因此我们可以在新测试中重用相同的响应。

此处的新测试是test_home_view_contains_link_to_topics_page 。 在这里,我们使用assertContains方法测试响应主体是否包含给定的文本。 我们在测试中使用的文本是标签的href部分。 因此,基本上,我们正在测试响应主体是否具有文本href="/boards/1/" 。

让我们运行测试:

 python manage.py test 
 Creating test database for alias 'default'... System check identified no issues (0 silenced). ....F. ====================================================================== FAIL: test_home_view_contains_link_to_topics_page (boards.tests.HomeTests) ---------------------------------------------------------------------- # ... AssertionError: False is not true : Couldn't find 'href="/boards/1/"' in response ---------------------------------------------------------------------- Ran 6 tests in 0.034s FAILED (failures=1) Destroying test database for alias 'default'... 

现在,我们可以编写使此测试通过的代码。

编辑home.html模板:

templates / home.html

 <!-- code suppressed for brevity --> <tbody> {% for board in boards %} <tr> <td> <a href= " {% url 'board_topics' board.pk %} " > {{ board.name }} </a> <small class= "text-muted d-block" > {{ board.description }} </small> </td> <td class= "align-middle" > 0 </td> <td class= "align-middle" > 0 </td> <td></td> </tr> {% endfor %} </tbody> <!-- code suppressed for brevity --> 

所以基本上我们改变了这一行:

 {{ board.name }} 

至:

 <a href= " {% url 'board_topics' board.pk %} " > {{ board.name }} </a> 

一律使用{ % url % } { % url % } { % url % }模板标记来组成应用程序URL。 第一个参数是URL的名称 (在URLconf中定义,即urls.py ),然后可以根据需要传递任意数量的参数。

如果它是一个简单的URL(例如首页),则将只是{ % url 'home' % } { % url 'home' % } { % url 'home' % } 。

保存文件并再次运行测试:

 python manage.py test 
 Creating test database for alias 'default'... System check identified no issues (0 silenced). ...... ---------------------------------------------------------------------- Ran 6 tests in 0.037s OK Destroying test database for alias 'default'... 

好! 现在我们可以检查它在网络浏览器中的外观:

链接板

现在链接回来了。 我们可以先编写测试:

boards / tests.py

 class BoardTopicsTests ( TestCase ): # code suppressed for brevity... def test_board_topics_view_contains_link_back_to_homepage ( self ): board_topics_url = reverse ( 'board_topics' , kwargs = { 'pk' : 1 }) response = self . client . get ( board_topics_url ) homepage_url = reverse ( 'home' ) self . assertContains ( response , 'href="{0}"' . format ( homepage_url )) 

运行测试:

 python manage.py test 
 Creating test database for alias 'default'... System check identified no issues (0 silenced). .F..... ====================================================================== FAIL: test_board_topics_view_contains_link_back_to_homepage (boards.tests.BoardTopicsTests) ---------------------------------------------------------------------- Traceback (most recent call last): # ... AssertionError: False is not true : Couldn't find 'href="/"' in response ---------------------------------------------------------------------- Ran 7 tests in 0.054s FAILED (failures=1) Destroying test database for alias 'default'... 

更新董事会主题模板:

template / topics.html

 {% load static %}<!DOCTYPE html> <html> <head> <!-- code suppressed for brevity --> </head> <body> <div class= "container" > <ol class= "breadcrumb my-4" > <li class= "breadcrumb-item" ><a href= " {% url 'home' %} " > Boards </a></li> <li class= "breadcrumb-item active" > {{ board.name }} </li> </ol> </div> </body> </html> 

运行测试:

 python manage.py test 
 Creating test database for alias 'default'... System check identified no issues (0 silenced). ....... ---------------------------------------------------------------------- Ran 7 tests in 0.061s OK Destroying test database for alias 'default'... 

带有链接的董事会主题

如前所述,URL路由是Web应用程序的基本部分。 有了这些知识,我们应该能够进行开发。 接下来,要完成关于URL的部分,您将找到有用的URL模式的摘要。

有用的网址格式列表

技巧部分是正则表达式 。 因此,我准备了最常用的URL模式列表。 需要特定的URL时,您始终可以参考此列表。

主键自动字段
正则表达式(?P<pk>\d+)
url(r'^questions/(?P<pk>\d+)/$', views.question, name='question')
有效网址/questions/934/
捕获{'pk': '934'}
子弹场
正则表达式(?P<slug>[-\w]+)
url(r'^posts/(?P<slug>[-\w]+)/$', views.post, name='post')
有效网址/posts/hello-world/
捕获{'slug': 'hello-world'}
带主键的子弹场
正则表达式(?P<slug>[-\w]+)-(?P<pk>\d+)
url(r'^blog/(?P<slug>[-\w]+)-(?P<pk>\d+)/$', views.blog_post, name='blog_post')
有效网址/blog/hello-world-159/
捕获{'slug': 'hello-world', 'pk': '159'}
Django用户名
正则表达式(?P<username>[\w.@+-]+)
url(r'^profile/(?P<username>[\w.@+-]+)/$', views.user_profile, name='user_profile')
有效网址/profile/vitorfs/
捕获{'username': 'vitorfs'}
正则表达式(?P<year>[0-9]{4})
url(r'^articles/(?P<year>[0-9]{4})/$', views.year_archive, name='year')
有效网址/articles/2016/
捕获{'year': '2016'}
年/月
正则表达式(?P<year>[0-9]{4})/(?P<month>[0-9]{2})
url(r'^articles/(?P<year>[0-9]{4})/(?P<month>[0-9]{2})/$', views.month_archive, name='month')
有效网址/articles/2016/01/
捕获{'year': '2016', 'month': '01'}

您可以在这篇文章中找到有关这些模式的更多详细信息: 有用的URL模式列表 。


可重用模板

到目前为止,我们一直在复制和粘贴HTML,重复执行HTML文档的多个部分,从长远来看,这并不是很可持续。 这也是一个坏习惯。

在本部分中,我们将重构HTML模板,创建一个母版页,并仅为每个模板添加唯一的部分。

模板文件夹中创建一个名为base.html的新文件:

templates / base.html

{% load static %}<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>{% block title %}Django Boards{% endblock %}</title>
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
  </head>
  <body>
    <div class="container">
      <ol class="breadcrumb my-4">
        {% block breadcrumb %}
        {% endblock %}
      </ol>
      {% block content %}
      {% endblock %}
    </div>
  </body>
</html>
  

这将是我们的主页。 我们创建的每个模板都将扩展此特殊模板。 现在观察,我们介绍了{ % block % } 标签。 它用于在模板中保留一个空间,“子”模板(扩展了母版页)可以在该空间中插入代码和HTML。

对于{ % block title % }我们还设置了默认值“ Django Boards”。如果我们没有为{ % block title % }在子模板中。

现在,让我们重构两个模板: home.htmltopic.html 。

templates / home.html

{% extends 'base.html' %}

{% block breadcrumb %}
  <li class="breadcrumb-item active">Boards</li>
{% endblock %}

{% block content %}
  <table class="table">
    <thead class="thead-inverse">
      <tr>
        <th>Board</th>
        <th>Posts</th>
        <th>Topics</th>
        <th>Last Post</th>
      </tr>
    </thead>
    <tbody>
      {% for board in boards %}
        <tr>
          <td>
            <a href="{% url 'board_topics' board.pk %}">{{ board.name }}</a>
            <small class="text-muted d-block">{{ board.description }}</small>
          </td>
          <td class="align-middle">0</td>
          <td class="align-middle">0</td>
          <td></td>
        </tr>
      {% endfor %}
    </tbody>
  </table>
{% endblock %}
  

home.html模板的第一行是{ % extends 'base.html' % } { % extends 'base.html' % } { % extends 'base.html' % } 。 这个标记告诉Django将base.html模板用作母版页。 之后,我们使用这些来放置页面的唯一内容。

template / topics.html

{% extends 'base.html' %}

{% block title %}
  {{ board.name }} - {{ block.super }}
{% endblock %}

{% block breadcrumb %}
  <li class="breadcrumb-item"><a href="{% url 'home' %}">Boards</a></li>
  <li class="breadcrumb-item active">{{ board.name }}</li>
{% endblock %}

{% block content %}
    <!-- just leaving it empty for now. we will add core here soon. -->
{% endblock %}

topic.html模板中,我们正在更改{ % block title % } 默认值。 请注意,我们可以通过调用{ { block.super } } 。 因此,这里我们使用的是网站标题,我们在base.html中将其定义为“ Django Boards”。因此,对于“ Python” board页面,标题将为“ Python-Django Boards”,即“ Random” board。标题为“ Random-Django Boards”。

现在让我们运行测试,看看我们没有破坏任何东西:

 python manage.py test 
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.......
----------------------------------------------------------------------
Ran 7 tests in 0.067s

OK
Destroying test database for alias 'default'...

好! 一切看起来都很好。

现在有了base.html模板,我们可以轻松添加带有菜单的顶部栏:

templates / base.html

{% load static %}<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>{% block title %}Django Boards{% endblock %}</title>
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
  </head>
  <body>

    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
      <div class="container">
        <a class="navbar-brand" href="{% url 'home' %}">Django Boards</a>
      </div>
    </nav>

    <div class="container">
      <ol class="breadcrumb my-4">
        {% block breadcrumb %}
        {% endblock %}
      </ol>
      {% block content %}
      {% endblock %}
    </div>
  </body>
</html>

Django Boards标头

Django Boards标头

我使用的HTML是Bootstrap 4导航栏组件的一部分 。

我想添加的一个不错的方法是更改​​页面“徽标”( .navbar-brand )中的字体。

前往,输入“ Django Boards”或您为项目指定的任何名称,然后单击“ 应用于所有字体” 。 浏览一下,找到您想要的。fonts.google.com 

Google字体

base.html模板中添加字体:

{% load static %}<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>{% block title %}Django Boards{% endblock %}</title>
    <link href="https://fonts.googleapis.com/css?family=Peralta" rel="stylesheet">
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/app.css' %}">
  </head>
  <body>
    <!-- code suppressed for brevity -->
  </body>
</html>
  

现在,在static / css文件夹中创建一个名为app.css的新CSS文件:

static/css/app.css

 .navbar-brand { font-family : 'Peralta' , cursive ; } 

Django Boards徽标


表格

表单用于处理用户输入。 在任何Web应用程序或网站中,这都是非常常见的任务。 标准的方法是通过HTML表单,其中用户输入一些数据,将其提交到服务器,然后服务器对它进行处理。

所有输入都是邪恶的

表单处理是一项相当复杂的任务,因为它涉及与应用程序的许多层进行交互。 还有很多问题要注意。 例如,所有提交给服务器的数据都采用字符串格式,因此在对其执行任何操作之前,我们必须将其转换为适当的数据类型(整数,浮点数,日期等)。 我们必须验证有关应用程序业务逻辑的数据。 我们还必须正确地清理,清理数据,以避免诸如SQL Injection和XSS攻击之类的安全问题。

好消息是,Django Forms API使整个过程变得更加容易,并自动完成了大部分工作。 而且,最终结果是比大多数程序员自己能够实现的代码安全得多。 因此,无论HTML表单多么简单,请始终使用表单API。

如何不执行表格

起初,我考虑过直接跳转到表单API。 但是我认为,花一些时间来了解表单处理的基本细节对我们来说是个好主意。 否则,它将最终看起来像魔术,这是一件坏事,因为当事情出错时,您将不知道在哪里寻找问题。

对某些编程概念有了更深入的了解,我们可以更好地控制这种情况。 处于控制状态很重要,因为它使我们可以更加自信地编写代码。 一旦我们确切知道发生了什么,就可以轻松实现可预测行为的代码。 由于您知道要查找的位置,因此调试和查找错误也容易得多。

无论如何,让我们从实现以下表单开始:

线框新主题

这是我们在上一教程中绘制的线框之一。 我现在意识到这可能是一个不好的例子,因为这种特殊形式涉及处理两个不同模型的数据: 主题 (主题)和帖子 (消息)。

到目前为止,我们还没有讨论过另一个重要方面,那就是用户身份验证。 我们只应向经过身份验证的用户显示此屏幕。 这样我们就可以知道是谁创建了主题帖子 。

因此,现在让我们抽象一些细节,并集中于了解如何在数据库中保存用户输入。

首先,让我们创建一个名为new_topic的新URL路由:

myproject / urls.py

from django.conf.urls import url
from django.contrib import admin

from boards import views

urlpatterns = [
    url(r'^$', views.home, name='home'),
    url(r'^boards/(?P<pk>\d+)/$', views.board_topics, name='board_topics'),
    url(r'^boards/(?P<pk>\d+)/new/$', views.new_topic, name='new_topic'),
    url(r'^admin/', admin.site.urls),
]
  

我们构建URL的方式将帮助我们识别正确的Board 。

现在让我们创建new_topic视图函数:

boards/views.py

from django.shortcuts import render, get_object_or_404
from .models import Board

def new_topic(request, pk):
    board = get_object_or_404(Board, pk=pk)
    return render(request, 'new_topic.html', {'board': board})
  

目前为止, new_topic视图函数的外观与board_topics完全相同 。 这是有目的的,让我们一次迈出一步。

现在,我们只需要一个名为new_topic.html的模板即可查看一些代码的工作情况:

templates / new_topic.html

{% extends 'base.html' %}

{% block title %}Start a New Topic{% endblock %}

{% block breadcrumb %}
  <li class="breadcrumb-item"><a href="{% url 'home' %}">Boards</a></li>
  <li class="breadcrumb-item"><a href="{% url 'board_topics' board.pk %}">{{ board.name }}</a></li>
  <li class="breadcrumb-item active">New topic</li>
{% endblock %}

{% block content %}

{% endblock %}
  

现在,我们只需要保证导航即可。 观察到我们将URL包括回到board_topics视图。

打开URL http://127.0.0.1:8000/boards/1/new/ 。 现在的结果是以下页面:

开始一个新主题

我们仍然没有实现到达此新页面的方法,但是如果将URL更改为http://127.0.0.1:8000/boards/2/new/ ,它应该带我们到Python Board :

开始一个新主题 注意:

用于URL来标识正确的资源。

我们已经可以添加一些测试:

boards / tests.py

from django.core.urlresolvers import reverse
from django.urls import resolve
from django.test import TestCase
from .views import home, board_topics, new_topic
from .models import Board

class HomeTests(TestCase):
    # ...

class BoardTopicsTests(TestCase):
    # ...

class NewTopicTests(TestCase):
    def setUp(self):
        Board.objects.create(name='Django', description='Django board.')

    def test_new_topic_view_success_status_code(self):
        url = reverse('new_topic', kwargs={'pk': 1})
        response = self.client.get(url)
        self.assertEquals(response.status_code, 200)

    def test_new_topic_view_not_found_status_code(self):
        url = reverse('new_topic', kwargs={'pk': 99})
        response = self.client.get(url)
        self.assertEquals(response.status_code, 404)

    def test_new_topic_url_resolves_new_topic_view(self):
        view = resolve('/boards/1/new/')
        self.assertEquals(view.func, new_topic)

    def test_new_topic_view_contains_link_back_to_board_topics_view(self):
        new_topic_url = reverse('new_topic', kwargs={'pk': 1})
        board_topics_url = reverse('board_topics', kwargs={'pk': 1})
        response = self.client.get(new_topic_url)
        self.assertContains(response, 'href="{0}"'.format(board_topics_url))
  

我们的新类NewTopicTests的测试的快速摘要:

  • setUp:创建要在测试期间使用的Board实例
  • test_new_topic_view_success_status_code:检查对视图的请求是否成功
  • test_new_topic_view_not_found_status_code:检查在委员会不存在时视图是否引发404错误
  • test_new_topic_url_resolves_new_topic_view : check if the right view is being used
  • test_new_topic_view_contains_link_back_to_board_topics_view : ensure the navigation back to the list of topics

Run the tests:

 python manage.py test 
 Creating test database for alias 'default'... 
System check identified no issues (0 silenced).
 ...........
 ---------------------------------------------------------------------- 
Ran 11 tests in 0.076s OK Destroying test database for alias 'default'... 

Django完整入门指南-第3部分--2

https://blog.csdn.net/weixin_41131063/article/details/102846762

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值