整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

Django框架实战教程:搭建自动化测试平台

Django框架实战教程:搭建自动化测试平台

面是一个使用Django框架搭建自动化测试平台的基本教程。我们将创建一个简单的自动化测试平台,它能够接收用户的测试用例提交、执行这些测试用例,并返回测试结果。

第一步:安装Django

确保你已经安装了Python。然后,在命令行中安装Django:

Bash

深色版本

1pip install django

第二步:创建项目

创建一个新的Django项目:

Bash

深色版本

1django-admin startproject test_platform
2cd test_platform

第三步:创建应用

创建一个名为tests的应用:

Bash

深色版本

1python manage.py startapp tests

第四步:配置URLs

编辑test_platform/urls.py文件,将新的应用添加到URL配置中:

Python

深色版本

1from django.contrib import admin
2from django.urls import path, include
3
4urlpatterns=[
5    path('admin/', admin.site.urls),
6    path('', include('tests.urls')),
7]

在tests应用中创建urls.py文件:

Python

深色版本

1from django.urls import path
2from . import views
3
4urlpatterns=[
5    path('', views.index, name='index'),
6    path('submit_test/', views.submit_test, name='submit_test'),
7    path('results/<int:test_id>/', views.results, name='results'),
8]

第五步:编写视图

在tests/views.py文件中编写视图函数:

Python

深色版本

1from django.shortcuts import render, redirect, get_object_or_404
2from .forms import TestForm
3from .models import Test
4from django.http import HttpResponse
5from django.core.files.storage import FileSystemStorage
6
7def index(request):
8    return render(request, 'tests/index.html')
9
10def submit_test(request):
11    if request.method=='POST':
12        form=TestForm(request.POST, request.FILES)
13        if form.is_valid():
14            test=form.save()
15            # 在这里可以调用脚本来执行测试
16            return redirect('results', test_id=test.id)
17    else:
18        form=TestForm()
19    return render(request, 'tests/submit_test.html', {'form': form})
20
21def results(request, test_id):
22    test=get_object_or_404(Test, pk=test_id)
23    # 从数据库或其他地方获取测试结果
24    return render(request, 'tests/results.html', {'test': test})

第六步:创建模型

在tests/models.py文件中定义模型:

Python

深色版本

1from django.db import models
2
3class Test(models.Model):
4    name=models.CharField(max_length=200)
5    description=models.TextField()
6    file=models.FileField(upload_to='uploads/')
7    created_at=models.DateTimeField(auto_now_add=True)
8
9    def __str__(self):
10        return self.name

第七步:创建表单

在tests/forms.py文件中定义表单:

Python

深色版本

1from django import forms
2from .models import Test
3
4class TestForm(forms.ModelForm):
5    class Meta:
6        model=Test
7        fields=['name', 'description', 'file']

第八步:创建模板

在tests/templates/tests目录下创建模板文件:

  • index.html
  • submit_test.html
  • results.html

例如,submit_test.html可以这样创建:

Html

深色版本

1{% extends 'base.html' %}
2
3{% block content %}
4<h1>Submit a New Test</h1>
5<form method="post" enctype="multipart/form-data">
6    {% csrf_token %}
7    {{ form.as_p }}
8    <button type="submit">Submit Test</button>
9</form>
10{% endblock %}

第九步:数据库迁移

运行以下命令创建数据库表:

Bash

深色版本

1python manage.py makemigrations
2python manage.py migrate

第十步:创建静态文件和媒体文件

在项目目录下创建static和media文件夹。然后在settings.py中添加以下设置:

Python

深色版本

1STATIC_URL='/static/'
2MEDIA_URL='/media/'
3MEDIA_ROOT=os.path.join(BASE_DIR, 'media')

第十一步:运行服务器

启动开发服务器:

Bash

深色版本

1python manage.py runserver

第十二步:测试自动化脚本

在视图函数submit_test中,你可以添加代码来执行自动化测试脚本。这通常涉及读取用户上传的文件,解析测试用例,并使用如unittest或pytest这样的测试框架来运行测试。

例如,你可以在submit_test函数中添加如下代码来执行测试脚本:

Python

深色版本

1import subprocess
2
3def execute_test_script(file_path):
4    # 假设测试脚本是一个可执行的Python脚本
5    try:
6        output=subprocess.check_output(['python', file_path], stderr=subprocess.STDOUT)
7        # 将输出保存到数据库或返回给用户
8        return output.decode('utf-8')
9    except subprocess.CalledProcessError as e:
10        # 处理错误情况
11        return e.output.decode('utf-8')

第十三步:部署

一旦完成开发和测试,你可以考虑将应用部署到生产环境。可以选择使用如Heroku、Docker、AWS等服务进行部署。


以上是从零开始使用Django框架搭建自动化测试平台的基本步骤。你可以根据实际需求进一步完善功能,例如增加用户认证、测试报告生成、邮件通知等功能。希望这份指南对你有所帮助!

一篇:Python之Django系列-创建第一个应用-5

我们接着上一篇开始,这一篇我们主要会讲到投票视图以及最终投票结果视图,我们先修改上一篇有说到的polls/templates/polls/detail.html页面,让用户跳转到这个界面时可以选择任意一项进行投票,修改后最终代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>问题详情页</title>
</head>
<body>
<h1>{{ question.question_text }}</h1>

{% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}

<form action="{% url 'polls:vote' question.id %}" method="post">
{% csrf_token %}
{% for choice in question.choice_set.all %}
    <input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">
    <label for="choice{{ forloop.counter }}">{{ choice.choice_text }}</label><br>
{% endfor %}
<input type="submit" value="投票">
</form>
</body>
</html>

代码讲解

  • {% if error_message %}代表该属性是否有值,有值的话就显示出来
  • {% url 'polls:vote' question.id %} 这一块中的url'polls:vote'的作用是把接口路径用变量表示,而polls:vote是代表的polls/urls.py中的path('<int:question_id>/vote/', views.vote, name='vote')这一段代码,polls:vote中的polls需要在polls/urls.py中命名一个全局变量app_name='polls',而vote则引用的path方法中的name='vote'

polls/urls.py

  • {% csrf_token %}防止跨站点请求伪造
  • forloop.counter指的是循环次数

这个页面修改成功后,目前数据库中的django_question表和django_choice表的数据截图如下:


django_question(问题表)


django_choice(投票记录表)

我们启动服务,打开浏览器http://127.0.0.1:8000/polls/1/,可以看到简单的投票页面


投票详情页面

当polls/templates/polls/detail.html页面完善后,打开页面看到上图,但是我们点投票的时候并没有做视图相关的业务处理,我们看看detail.html中submit提交的接口为{% url 'polls:vote' question.id %}这个翻译过来假如question.id=1那么这个地址就是/polls/1/vote,但是我们vote视图处理目前还是如下代码

def vote(request, question_id):
    return HttpResponse("进行投票 %s." % question_id)

并没有实现投票的业务逻辑,现在我们稍微来改造下这个方法如下

from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from .models import Question, Choice

# ...为了让文章篇幅更短,此处省略其他方法
def vote(request, question_id):
    question=get_object_or_404(Question, pk=question_id)
    try:
        selected_choice=question.choice_set.get(pk=request.POST['choice'])
    except(KeyError, Choice.DoesNotExist):
        return render(request, 'polls/detail.html', {'question': question, 'error_message': '你没有选择一个选项'})
    else:
        selected_choice.votes +=1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

代码讲解

  • question=get_object_or_404(Question, pk=question_id) 这个代码的意思是根据question_id查询具体问题对象,如打开http://127.0.0.1:8000/polls/2/查询不到数据则向页面抛出404,如图404界面

404

  • selected_choice=question.choice_set.get(pk=request.POST['choice']) 我们拆开来讲,先看request.POST['choice']这个是在detail.html界面单选展示的单选按钮的name属性的值,当向接口提交时,这个值则为单选按钮的value值,我们回顾下前面details.html写的这段代码"<input type="radio" name="choice" id="choice{{ forloop.counter }}" value="{{ choice.id }}">",value值则为choice.id。而question.choice_set则是查询与question_id关联的choice集合,假如question_id=1,那么数据则为如下图中的三条

  • selected_choice.save() 而这个方法以及上面的question.choice_set.get都是在做数据模型迁移的时候django会帮你自动生成,可以参考文章Python之Django系列-创建第一个应用-4
  • HttpResponseRedirect(reverse('polls:results', args=(question.id,))),还是拆开来讲reverse方法最终会解析成'/polls/1/results/'类似这样,这样避免URL硬编码,HttpResponseRedirect意思是返回结果重定向到这个界面

说到这里,我们就还需要编辑重定向后的视图以及界面,重定向后的视图编码为

更改前:

#问题结果页
def results(request, question_id):
    return HttpResponse("查看问题的结果 %s." % question_id)

更改后:

from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from .models import Question, Choice

# ...为了让文章篇幅更短,此处省略其他方法
def results(request, question_id):
    question=get_object_or_404(Question, pk=question_id)
    return render(request, 'polls/results.html', {'question': question})

我们还需要新增polls/templates/polls/results.html界面,如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>投票结果</title>
</head>
<body>
<h1>{{ question.question_text }}</h1>

<ul>
{% for choice in question.choice_set.all %}
    <li>{{ choice.choice_text }} -- {{ choice.votes }} vote{{ choice.votes|pluralize }}</li>
{% endfor %}
</ul>

<a href="{% url 'polls:detail' question.id %}">再次投票?</a>
</body>
</html>

编辑完成后,现在我们打开地址:http://127.0.0.1:8000/polls/1/ 选择任意一个选项,点击"投票"按钮,就会展示如下结果:


点击再次投票,再跳转到投票详情页面,反复执行,将会看到投票数的变化

通用视图

在使用通用视图之前,我们先了解下通用视图的定义,通用意思是重复用,避免重复造轮子,通用视图旨在将常见的模式抽象化,可以使你在编写应用时甚至不需要编写Python代码。

既然说是通用视图,那么我们先来改造视图,先直接贴出最终代码:

from django.shortcuts import render, get_object_or_404
from django.http import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from django.views import generic
from .models import Question, Choice


# Create your views here.

class IndexView(generic.ListView):
    template_name='polls/index.html'
    context_object_name='latest_question_list'

    def get_queryset(self):
        return Question.objects.order_by("-pub_date")[:5]


class DetailView(generic.DetailView):
    model=Question
    template_name='polls/detail.html'


class ResultsView(generic.DetailView):
    model=Question
    template_name='polls/results.html'


def vote(request, question_id):
    question=get_object_or_404(Question, pk=question_id)
    try:
        selected_choice=question.choice_set.get(pk=request.POST['choice'])
    except(KeyError, Choice.DoesNotExist):
        return render(request, 'polls/detail.html', {'question': question, 'error_message': '你没有选择一个选项'})
    else:
        selected_choice.votes +=1
        selected_choice.save()
        return HttpResponseRedirect(reverse('polls:results', args=(question.id,)))

我们会看到index(),detail(),results()方法已经都被删除,而新增的有类IndexView,DetailView,ResultsView,下面我们来讲讲这些变化的地方

IndexView讲解

新增类IndexView继承generic.ListView,其中属性template_name,context_object_name都进行重写,默认情况下ListView会生成模板<app_name>/<model_name>_list.html,app_name则是我们URLconf(polls/urls.py文件)中的变量,mode_name则是Question,那么默认的模板名字则是"polls/question_list.html",而我们重写template_name值,则会直接拿重写后的值也就是"polls/index.html",而context_object_name我们需要重写则是因为我们html模板需要用到该属性,以免报错

DetailView讲解

DetailView和ResultsView我就挑DetailView描述下,两个实现原理其实都差不多,这个需要结合改造后的URLconf(polls/url.py)来讲,URLconf改造如下:

改造前:

from django.urls import path
from . import views

app_name='polls'
urlpatterns=[
    path('', views.index, name='index'),#问题列表页
    path('<int:question_id>/', views.detail, name='detail'),#问题详情页
    path('<int:question_id>/results/', views.results, name='results'),#问题结果页
    path('<int:question_id>/vote/', views.vote, name='vote'),#投票处理器
]

改造后:

from django.urls import path
from . import views

app_name='polls'
urlpatterns=[
    path('', views.IndexView.as_view(), name='index'),#问题列表页
    path('<int:pk>/', views.DetailView.as_view(), name='detail'),#问题详情页
    path('<int:pk>/results/', views.ResultsView.as_view(), name='results'),#问题结果页
    path('<int:question_id>/vote/', views.vote, name='vote'),#投票处理器
]

其中列表页,详情页,结果页都已经更改,question_id更改为pk,而这个pk则代表数据库表的主键ID,当我们访问详情页(DetailView)时,传入主键ID,模板就能渲染相关数据,而DetailView的父类generic.DetailView默认的模板地址生成为<app_name>/<model_name>_detail.html,那么就可能变成"polls/question_detail.html",但是该模板页面我们并没有开发,所以我们重写了model以及template_name属性,最终会生成为"polls/detail.html"

现在让我们重新打开http://127.0.0.1:8000/polls/1/看看通用视图是否生效吧

下一篇:Python之Django系列-创建第一个应用-7

构建功能完备且用户体验良好的Web应用时,表单验证是一个不可或缺的部分。本文将深入探讨如何使用 Flask 框架集成的 flask-wtf 库实现网页表单验证,并提供详细的代码示例以助您快速掌握这一实用技能。

Flask-WTF简介

flask-wtf 是 Flask 用于处理 Web 表单的一个扩展库,它基于 wtforms 库,提供了便捷的方式来创建和验证 HTML 表单。通过 Flask-wtf,您可以轻松定义表单字段、编写验证规则并处理提交的数据。

安装与配置

首先确保已安装 flask-wtf:

pip install Flask-WTF

在 Flask 应用中引入并初始化 flask-wtf:

from flask import Flask
from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length, EqualTo

app=Flask(__name__)
app.config['SECRET_KEY']='your-secret-key'  # 设置一个安全密钥用于表单验证

创建表单类与验证规则

下面是一个用户注册表单的示例,包含用户名、密码以及确认密码字段,其中包含了数据必填性、长度限制以及密码匹配等验证规则:

class RegistrationForm(FlaskForm):
    username=StringField('用户名', validators=[DataRequired(), Length(min=2, max=20)])
    password=PasswordField('密码', validators=[DataRequired()])
    confirm_password=PasswordField('确认密码', 
                                    validators=[DataRequired(), EqualTo('password', message='两次密码不一致')])
    submit=SubmitField('Sign Up')

视图函数中的表单处理

在视图函数中,我们可以实例化表单对象,渲染表单至模板,并处理表单提交后的验证结果:

@app.route('/register', methods=['GET', 'POST'])
def register():
    form=RegistrationForm()

    if form.validate_on_submit():  # 验证表单数据
        # 这里是处理有效表单数据的逻辑,例如保存到数据库
        flash('用户注册成功!', 'success')
        return redirect(url_for('index'))

    return render_template('register.html', title='Register', form=form)

模板中的表单渲染

在 Jinja2 模板文件(如 templates/register.html)中,我们可以渲染表单:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <title>{{ title }}</title>
</head>
<body>
    <h1>用户注册</h1>
    <form method="POST" action="{{ url_for('register') }}">
        {{ form.csrf_token }}  <!-- 添加CSRF令牌 -->
        <div>
            {{ form.username.label }}<br>
            {{ form.username(size=20) }}
        </div>
        <div>
            {{ form.password.label }}<br>
            {{ form.password() }}
        </div>
        <div>
            {{ form.confirm_password.label }}<br>
            {{ form.confirm_password() }}
        </div>
        <div>
            {{ form.submit() }}
        </div>
    </form>
</body>
</html>

通过以上步骤,我们成功地在 Flask 应用中使用了 flask-wtf 实现了一个具有验证功能的用户注册表单。

处理表单验证失败

当表单验证失败时(form.validate_on_submit()返回False),flask-wtf会将验证错误信息存储在表单对象中。我们可以在模板中遍历这些错误并显示给用户:

<!-- 在templates/register.html中添加错误信息显示 -->
{% for field, errors in form.errors.items() %}
    {% for error in errors %}
        <p style="color: red;">{{ form[field].label }}: {{ error }}</p>
    {% endfor %}
{% endfor %}

自定义错误消息

在某些情况下,您可能需要为特定字段或验证器提供自定义错误消息。这可以通过在定义表单类时直接指定验证器的message参数来实现:

from wtforms import ValidationError

class RegistrationForm(FlaskForm):
    # ...
    
    def validate_username(self, username_field):
        if User.query.filter_by(username=username_field.data).first():
            raise ValidationError('Username already exists. Please choose another one.')

总结

通过flask-wtf,我们可以高效且规范地管理Web应用中的表单验证和数据处理流程。结合wtforms强大的验证器库,您可以根据需求创建各种复杂度的表单,并确保用户的输入满足预设的业务规则。

实践是检验真理的唯一标准,鼓励各位读者在实际项目开发中尝试运用flask-wtf进行表单验证,不断优化和完善代码逻辑,从而提升Python Web开发的能力和效率。

关注我,手把手带你快速入门Python Web编程!