Django | Part3-Advanced Concepts笔记

Part3

教程:原文链接

两个重点概念:URLs 和 Forms

Tips:写Python一定要主义代码缩进,有些缩进问题是肉眼看不出来的。注意IndentationError,TabError

URLs

  1. 路由的几个参数辨析
  2. 路由的顺序问题,可能会导致无法检索
  3. 路由的正则表达式命名规范
  • 比如:
url(r'^boards/(?P<pk>\d+)/$', views.board_topics, name='board_topics')

那么相应的控制器中:

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

/d代表的是匹配整型,?P<pk>代表参数名使用规范的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...

都可以,但是这不规范,不建议使用。

  • 此外,文章谈及了命名规范中pk和id的区别,可见django框架设计中的一些细节。
  1. 关于页面之间的跳转,有些同学可能会被测试单元中的代码搞晕。其实并不复杂,如下:
<a href="{% url 'board_topics' board.pk %}">{{ board.name }}</a>

其中url应该是指明了urls.py中的entry,然后board_topics就是通过name值来寻找对应url,board.pk作为这条url的参数传入。

我们可以使用topics.html中的代码来印证上面的猜测:

<li class="breadcrumb-item"><a href="{% url 'home' %}">Boards</a></li>

其中name为home所对应的url不需要额外参数。

TESTs

  1. setUp方法的使用。需要创建一个实体类来完成一系列的测试操作,因为测试环境使用的不是当前数据库而是一个全新的测试数据库。当完成所有测试时,这个测试数据库会被销毁。

  2. 注意每次修改业务代码后,都要进行测试。

TEMPLETEs

  1. 使用模板继承的时候遇到一个玄学问题。我明明已经将{% extends 'base.html' %}放在html文件的顶部。

但是还是会抛出这个异常。如下:

django.template.exceptions.TemplateSyntaxError: <ExtendsNode: extends 'base.html'> must be the first tag in the template.

解决方法(#°Д°):


FORMs

Tips:

细节

防御跨站请求伪造:{% csrf_token %}

异常解决
  • 首先来分析一下这个官方错误,虽然原文后续已经写了解决方法,但现实场景是大多数时候我们自己的代码出现异常时,往往没有这么直接针对性的解答教程,只能靠自己分析解决。


    官方错误

第一个ERROR,最关键的是django.utils.datastructures.MultiValueDictKeyError: "'subject'",大致是在一个多值的字典里,一个名为subject的键出了问题。

回溯代码,发现可能出问题的应该在这里:

subject = request.POST['subject']
message = request.POST['message']

此时通过subject获取的值可能为空值或者是一些危险的注入或攻击。

第二个问题参考上述。

根据对上述两个问题的理解,可以快速找到问题所在,然后通过改造表单来修复。

  • 第二个是我自己遇到的一个问题。在模仿了教程的测试代码后,运行出现这个错误。
ERROR: test_new_topic_valid_post_data (boards.tests.NewTopicTests)
----------------------------------------------------------------------
Traceback (most recent call last):
...
sqlite3.IntegrityError: NOT NULL constraint failed: boards_topic.starter_id

The above exception was the direct cause of the following exception:
...
django.db.utils.IntegrityError: NOT NULL constraint failed: boards_topic.starter_id

----------------------------------------------------------------------
Ran 10 tests in 0.152s

FAILED (errors=1)

可以理解为:

数据库字段的完整性错误:非空约束:boards_topic.starter_id
上述异常直接导致了下面的异常
django.db.utils.IntegrityError: NOT NULL constraint failed: boards_topic.starter_id

检查代码:

def new_topic(request,pk):
    board =get_object_or_404(Board,pk=pk)
    user=User.objects.first() #get currently logged in user

    if request.method=='POST':
        form =NewTopicForm(request.POST)
        if form.is_valid():
            topic=form.save(commit=False)
            topic.board=board
            topic.starter=user
            topic.save()
            post=Post.objects.create(
                message=form.cleaned_data.get('message'),
                topic=topic,
                created_by=user
                )
            return redirect('board_topics',pk=board.pk) 
    else:
        form=NewTopicForm()

    return render(request, 'new_topic.html', {'board': board})

很疑惑,我已经将user设置为获取当前用户,然后将topic的starter设置为user,应该是没有问题的,但现实就是出错了,这个就是最纠结的时刻,头发基本都是因为这些掉的。

后来发现可能是测试代码中的问题。

def test_new_topic_valid_post_data(self):
        url = reverse('new_topic', kwargs={'pk': 1})
        
        data = {
            'subject': 'Test title',
            'message': 'Lorem ipsum dolor sit amet'
        }
        response = self.client.post(url, data)
        self.assertTrue(Topic.objects.exists())
        self.assertTrue(Post.objects.exists())

在测试的代码中提交的data中没有starter这个字段,而报错是starter.id具有非空约束,不能传空参进入。所以我觉得问题可能出在这里。然后我稍稍改造了测试方法,创建一个User对象并作为参数传入。

def test_new_topic_valid_post_data(self):
        url = reverse('new_topic', kwargs={'pk': 1})
        user1=User.objects.create(password='$R3n69v8eV3laNxBmKWCnVWfqOGYcNDOjAjHj2+EwUpc=')
        
        data = {
            'subject': 'Test title',
            'message': 'Lorem ipsum dolor sit amet',
            'starter': user1

        }
        response = self.client.post(url, data)
        self.assertTrue(Topic.objects.exists())
        self.assertTrue(Post.objects.exists())

此时再测试,显示通过:


扫一扫,关注公众号
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。