前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >如何使用Django构建现代Web应用程序来管理客户信息并在Ubuntu 18.04上进行反应

如何使用Django构建现代Web应用程序来管理客户信息并在Ubuntu 18.04上进行反应

原创
作者头像
吴凌云
修改2018-11-15 15:32:48
13.8K0
修改2018-11-15 15:32:48
举报

介绍

人们使用不同类型的设备连接到互联网并浏览网页。因此,需要从各种位置访问应用程序。对于传统网站,具有响应式UI通常就足够了,但更复杂的应用程序通常需要使用其他技术和体系结构。其中包括具有单独的REST后端和前端应用程序,可以实现为客户端Web应用程序,Progressive Web Apps(PWA)或本机移动应用程序。

构建更复杂的应用程序时可以使用的一些工具包括:

  • React,一个JavaScript框架,允许开发人员为他们的REST API后端构建Web和本地前端。
  • Django,一个免费的开源Python Web框架,遵循模型视图控制器(MVC)软件架构模式。
  • Django REST框架,一个功能强大且灵活的工具包,用于在Django中构建REST API。

在本教程中,您将使用React,Django和Django REST Framework构建一个带有单独REST API后端和前端的现代Web应用程序。通过将React与Django一起使用,您将能够从JavaScript和前端开发的最新进展中受益。您将使用React作为UI库,而不是构建使用内置模板引擎的Django应用程序,利用其虚拟文档对象模型(DOM),声明性方法和快速呈现数据更改的组件。

您将构建的Web应用程序在数据库中存储有关客户的记录,您可以将其用作CRM应用程序的起点。完成后,您将能够使用使用Bootstrap 4设置样式的React接口创建,读取,更新和删除记录。

先决条件

要完成本教程,您需要:

  • 使用Ubuntu 18.04的开发机器。没有服务器的同学可以在这里购买,不过我个人更推荐您使用免费的腾讯云开发者实验室进行试验,学会安装后再购买服务器
  • 将Python 3,pipvenv安装在您的机器上。
  • npm您的计算机上安装了Node.js 6+和5.2或更高版本。您可以按照如何在安装PPA时在Ubuntu 18.04上安装Node.js中的说明安装它们。

第1步 - 创建Python虚拟环境并安装依赖项

在这一步中,我们将创建一个虚拟环境并为我们的应用程序安装所需的依赖项,包括Django,Django REST框架和django-cors-headers

我们的应用程序将为Django和React使用两个不同的开发服务器。它们将在不同的端口上运行,并将作为两个独立的域运行。因此,我们需要启用跨源资源共享(CORS),以便将来自React的HTTP请求发送到Django,而不会被浏览器阻止。

导航到您的主目录并使用venvPython 3模块创建虚拟环境:

代码语言:javascript
复制
cd ~
python3 -m venv ./env

使用source激活创建的虚拟环境:

代码语言:javascript
复制
source env/bin/activate

接下来,使用pip安装项目的依赖项。这些将包括:

  • Django:项目的Web框架。
  • Django REST框架:使用Django构建REST API的第三方应用程序。
  • django-cors-headers:启用CORS的程序包。

安装Django框架:

代码语言:javascript
复制
pip install django djangorestframework django-cors-headers

安装项目依赖项后,您可以创建Django项目和React前端。

第2步 - 创建Django项目

在这一步中,我们将使用以下命令和实用程序生成Django项目:

  • django-admin startproject project-namedjango-admin是一个命令行实用程序,用于完成Django的任务。该startproject命令将创建一个新的Django项目。
  • python manage.py startapp myappmanage.py是一个实用程序脚本,自动添加到每个Django项目中,执行许多管理任务:创建新应用程序,迁移数据库以及在本地提供Django项目。它的startapp命令在Django项目中创建一个Django应用程序。在Django中,术语应用程序描述了一个Python包,它提供了项目中的一些功能集。

首先,使用django-admin startproject创建Django项目。我们将把我们的项目命名为djangoreactproject

代码语言:javascript
复制
django-admin startproject djangoreactproject 

在继续之前,让我们使用该tree命令查看Django项目的目录结构。

提示: tree命令对于从命令行查看文件和目录结构是有效的。您可以使用以下命令安装它:

代码语言:javascript
复制
sudo apt-get install tree

要使用它,请在您想要的目录中放入cd,并且键入tree或使用tree /home/sammy/sammys-project来提供起点的路径。

导航到项目根目录中的djangoreactproject文件夹并运行tree命令:

代码语言:javascript
复制
cd ~/djangoreactproject
tree

您将看到以下输出:

代码语言:javascript
复制
├── djangoreactproject
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.py

~/djangoreactproject文件夹是项目的根目录。在此文件夹中,有几个对您的工作很重要的文件:

  • manage.py:执行许多管理任务的实用程序脚本。
  • settings.py:Django项目的主要配置文件,您可以在其中修改项目的设置。这些设置包括变量,例如INSTALLED_APPS,指定项目的已启用应用程序的字符串列表。Django文档提供了有关可用设置的更多信息。
  • urls.py:此文件包含URL模式和相关视图的列表。每个模式都映射URL和应该为该URL调用的函数之间的连接。

我们使用该项目的第一步是配置我们在上一步中安装的软件包,包括Django REST框架和Django CORS软件包,方法是将它们添加到settings.py。用nano或其他你喜欢的编辑器打开文件:

代码语言:javascript
复制
nano ~/djangoreactproject/djangoreactproject/settings.py

导航到该INSTALLED_APPS设置并将rest_frameworkcorsheaders应用程序添加到列表底部:

代码语言:javascript
复制
...
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'corsheaders'
]

接下来,将先前安装的CORS包中的中间件corsheaders.middleware.CorsMiddleware添加到MIDDLEWARE设置中。此设置是中间件列表,这是一个Python类,包含每次Web应用程序处理请求或响应时处理的代码:

代码语言:javascript
复制
...
​
MIDDLEWARE = [
...
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'corsheaders.middleware.CorsMiddleware'
]

接下来,您可以启用CORS。该CORS_ORIGIN_ALLOW_ALL设置指定是否要允许所有域的CORS,并且CORS_ORIGIN_WHITELIST是包含允许的URL的Python元组。在我们的例子中,因为React开发服务器将在http://localhost:3000上运行,我们将为我们的settings.py文件添加新的CORS_ORIGIN_ALLOW_ALL = FalseCORS_ORIGIN_WHITELIST('localhost:3000',)设置。在文件中的任何位置添加这些设置:

代码语言:javascript
复制
...
CORS_ORIGIN_ALLOW_ALL = False
​
CORS_ORIGIN_WHITELIST = (
       'localhost:3000',
)
...

您可以在 django-cors-headers文档中找到更多配置选项。

完成后保存文件并退出编辑器。

~/djangoreactproject目录中,继续创建一个名为customers的新Django应用程序:

代码语言:javascript
复制
python manage.py startapp customers

这将包含管理客户的模型视图。模型定义应用程序数据的字段和行为,而视图使我们的应用程序能够正确处理Web请求并返回所需的响应。

接下来,将此应用程序添加到项目settings.py文件中已安装应用程序的列表中,以便Django将其识别为项目的一部分。再次打开settings.py

代码语言:javascript
复制
nano ~/djangoreactproject/djangoreactproject/settings.py

添加customers应用程序:

代码语言:javascript
复制
...
INSTALLED_APPS = [
    ...
    'rest_framework',
    'corsheaders',
    'customers'
]
...

接下来,迁移数据库并启动本地开发服务器。迁移是Django将您对模型所做的更改传播到数据库模式的方法。例如,这些更改可能包括添加字段或删除模型等内容。

迁移数据库:

代码语言:javascript
复制
python manage.py migrate

启动本地开发服务器:

代码语言:javascript
复制
python manage.py runserver

您将看到类似于以下内容的输出:

代码语言:javascript
复制
Performing system checks...
​
System check identified no issues (0 silenced).
October 22, 2018 - 15:14:50
Django version 2.1.2, using settings 'djangoreactproject.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.

您的Web应用程序将从http://127.0.0.1:8000中运行。如果您在Web浏览器中导航到此地址,您应该看到以下页面:

此时,让应用程序继续运行并打开一个新终端以继续开发项目。

第3步 - 创建React前端

在本节中,我们将使用React创建项目的前端应用程序。

React有一个官方实用程序,允许您快速生成React项目,而无需直接配置Webpack。Webpack是一个模块捆绑器,用于捆绑Web资产,如JavaScript代码,CSS和图像。通常,在使用Webpack之前,您需要设置各种配置选项,但是由于该create-react-app实用程序,您不必直接处理Webpack,直到您决定需要更多控制。要运行create-react-app您可以使用npx,这是一个执行npm包二进制文件的工具。

在第二个终端中,确保您在项目目录中:

代码语言:javascript
复制
cd ~/djangoreactproject

使用 create-react-app和的npx创建一个名为frontend的React项目:

代码语言:javascript
复制
npx create-react-app frontend

接下来,在React应用程序中导航并启动开发服务器:

代码语言:javascript
复制
cd ~/djangoreactproject/frontend
npm start

您的应用程序将运行于http://localhost:3000/

让React开发服务器保持运行并打开另一个终端窗口继续。

要在此时查看整个项目的目录结构,请导航到根文件夹并再次运行tree

代码语言:javascript
复制
cd ~/djangoreactproject
tree

你会看到这样的结构:

代码语言:javascript
复制
├── customers
│   ├── admin.py
│   ├── apps.py
│   ├── __init__.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── djangoreactproject
│   ├── __init__.py
│   ├── __pycache__
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── frontend
│   ├── package.json
│   ├── public
│   │   ├── favicon.ico
│   │   ├── index.html
│   │   └── manifest.json
│   ├── README.md
│   ├── src
│   │   ├── App.css
│   │   ├── App.js
│   │   ├── App.test.js
│   │   ├── index.css
│   │   ├── index.js
│   │   ├── logo.svg
│   │   └── registerServiceWorker.js
│   └── yarn.lock
└── manage.py

我们的应用程序将使用Bootstrap 4来设置React接口的样式,因此我们将其包含在管理CSS设置的frontend/src/App.css文件中。打开文件:

代码语言:javascript
复制
nano ~/djangoreactproject/frontend/src/App.css

将以下导入添加到文件的开头。您可以删除文件的现有内容,但这不是必需的:

代码语言:javascript
复制
@import  'https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css';

@import是一个CSS指令,用于从其他样式表导入样式规则。

现在我们已经创建了后端和前端应用程序,让我们创建Customer模型和一些演示数据。

第4步 - 创建客户模型和初始数据

在创建Django应用程序和React前端之后,我们的下一步将是创建Customer模型,该模型表示将保存有关客户的信息的数据库表。您不需要任何SQL,因为Django 对象关系映射器(ORM)将通过将Python类和变量映射到SQL表和列来处理数据库操作。通过这种方式,Django ORM通过Python接口抽象出与数据库的SQL交互。

再次激活您的虚拟环境:

代码语言:javascript
复制
cd ~
source env/bin/activate

移动到customers目录,然后打开models.py,这是一个包含应用程序模型的Python文件:

代码语言:javascript
复制
cd ~/djangoreactproject/customers/
nano models.py

该文件将包含以下内容:

代码语言:javascript
复制
from django.db import models
# Create your models here.

由于该from django.db import modelsimport语句,Customer模型的API已经导入到文件中。您现在将添加Customer类,该类型将扩展models.Model。Django中的每个模型都是一个扩展django.db.models.Model的Python类。

Customer模型将具有以下数据库字段:

  • first_name - 客户的第一个名字。
  • last_name - 客户的姓氏。
  • email - 客户的电子邮件地址。
  • phone - 客户的电话号码。
  • address - 客户的地址。
  • description - 客户的描述。
  • createdAt - 添加客户的日期。

我们还将添加该__str__()函数,该函数定义了模型的显示方式。在我们的例子中,它将以客户的名字命名。

将以下代码添加到文件中:

代码语言:javascript
复制
from django.db import models
​
class Customer(models.Model):
    first_name = models.CharField("First name", max_length=255)
    last_name = models.CharField("Last name", max_length=255)
    email = models.EmailField()
    phone = models.CharField(max_length=20)
    address =  models.TextField(blank=True, null=True)
    description = models.TextField(blank=True, null=True)
    createdAt = models.DateTimeField("Created At", auto_now_add=True)
​
    def __str__(self):
        return self.first_name

接下来,迁移数据库以创建数据库表。该makemigrations命令将创建将添加模型更改的迁移文件,并将迁移文件中的更改的migrate应用于数据库。

导航回项目的根文件夹:

代码语言:javascript
复制
cd ~/djangoreactproject

运行以下命令以创建迁移文件:

代码语言:javascript
复制
python manage.py makemigrations

您将获得如下所示的输出:

代码语言:javascript
复制
customers/migrations/0001_initial.py
    - Create model Customer

将这些更改应用于数据库:

代码语言:javascript
复制
python manage.py migrate

您将看到指示成功迁移的输出:

代码语言:javascript
复制
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, customers, sessions
Running migrations:
  Applying customers.0001_initial... OK

接下来,您将使用数据迁移文件来创建初始客户数据。一个数据迁移文件是指添加或在数据库中的改变的数据的迁移。为customers应用程序创建一个空数据迁移文件:

代码语言:javascript
复制
python manage.py makemigrations --empty --name customers customers

您将看到以下有关迁移文件名称的确认:

代码语言:javascript
复制
Migrations for 'customers':
  customers/migrations/0002_customers.py

请注意,迁移文件的名称是0002_customers.py

接下来,在customers应用程序的迁移文件夹中导航:

代码语言:javascript
复制
cd ~/djangoreactproject/customers/migrations

打开创建的迁移文件:

代码语言:javascript
复制
nano 0002_customers.py

这是文件的初始内容:

代码语言:javascript
复制
from django.db import migrations
​
class Migration(migrations.Migration):
    dependencies = [
        ('customers', '0001_initial'),
    ]
    operations = [
    ]        

import语句migrationsdjango.db包含用于处理数据库的类的内置包中导入API(用于创建迁移的Django API)。

Migration类是一种Python类,介绍了迁移数据库时执行的操作。这个类扩展了migrations.Migration并有两个列表:

  • dependencies:包含依赖迁移。
  • operations:包含应用迁移时将执行的操作。

接下来,添加一个方法来创建演示客户数据。在定义Migration类之前添加以下方法:

代码语言:javascript
复制
...
def create_data(apps, schema_editor):
    Customer = apps.get_model('customers', 'Customer')
    Customer(first_name="Customer 001", last_name="Customer 001", email="customer001@email.com", phone="00000000", address="Customer 000 Address", description= "Customer 001 description").save()
​
...

在这种方法中,我们抓住Customer应用程序的customers类并创建一个插入数据库的演示客户。

为了获得Customer能够创建新客户的类,我们使用apps对象的get_model()方法。该apps对象表示已安装应用程序及其数据库模型的注册表

当我们使用RunPython()方法运行create_data()时,该apps对象将从该RunPython()方法传递。将方法migrations.RunPython()添加到空operations列表:

代码语言:javascript
复制
...
    operations = [
        migrations.RunPython(create_data),
    ]  

RunPython()是Migrations API的一部分,允许您在迁移中运行自定义Python代码。我们的operations列表指定在应用迁移时将执行此方法。

这是完整的文件:

代码语言:javascript
复制
from django.db import migrations
​
def create_data(apps, schema_editor):
    Customer = apps.get_model('customers', 'Customer')
    Customer(first_name="Customer 001", last_name="Customer 001", email="customer001@email.com", phone="00000000", address="Customer 000 Address", description= "Customer 001 description").save()
​
class Migration(migrations.Migration):
    dependencies = [
        ('customers', '0001_initial'),
    ]
    operations = [
        migrations.RunPython(create_data),
    ]        

有关数据迁移的更多信息,请参阅Django中有关数据迁移的文档

要迁移数据库,首先导航回项目的根文件夹:

代码语言:javascript
复制
cd ~/djangoreactproject

迁移数据库以创建演示数据:

代码语言:javascript
复制
python manage.py migrate

您将看到确认迁移的输出:

代码语言:javascript
复制
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, customers, sessions
Running migrations:
  Applying customers.0002_customers... OK

通过创建Customer模型和演示数据,我们可以继续构建REST API。

第5步 - 创建REST API

在这一步中,我们将使用Django REST Framework创建REST API。我们将创建几个不同的API视图。API视图是处理API请求或调用的函数,而API端点是表示REST系统的接触点的唯一URL。例如,当用户向API端点发送GET请求时,Django会调用相应的函数或API视图来处理请求并返回任何可能的结果。

我们还将使用序列化器。在API消耗方面,在Django的REST框架中的一个串行器允许将复杂的模型实例和查询集转换成JSON格式。序列化程序类也可以在另一个方向上工作,提供将数据解析和反序列化为Django模型和QuerySets的机制。

我们的API端点包括:

  • api/customers:此端点用于创建客户并返回分页的客户组。
  • api/customers/<pk>:此端点用于按主键或ID获取,更新和删除单个客户。

我们还将在项目的urls.py文件中为相应的端点(即api/customersapi/customers/<pk>)创建URL 。

让我们从为Customer模型创建序列化程序类开始。

添加Serializer类

为我们的Customer模型创建序列化程序类是将客户实例和QuerySet转换为JSON和从JSON转换的必要条件。要创建序列化程序类,首先在customers应用程序中创建一个serializers.py文件:

代码语言:javascript
复制
cd ~/djangoreactproject/customers/
nano serializers.py

添加以下代码以导入序列化程序API和Customer模型:

代码语言:javascript
复制
from rest_framework import serializers
from .models import Customer

接下来,创建一个扩展serializers.ModelSerializer的序列化器类,并指定将被序列化的字段:

代码语言:javascript
复制
...
class CustomerSerializer(serializers.ModelSerializer):
​
    class Meta:
        model = Customer 
        fields = ('pk','first_name', 'last_name', 'email', 'phone','address','description')

Meta类指定型号和字段进行序列化:pkfirst_namelast_nameemailphoneaddressdescription

这是文件的完整内容:

代码语言:javascript
复制
from rest_framework import serializers
from .models import Customer
​
class CustomerSerializer(serializers.ModelSerializer):
​
    class Meta:
        model = Customer 
        fields = ('pk','first_name', 'last_name', 'email', 'phone','address','description')

现在我们已经创建了序列化器类,我们可以添加API视图。

添加API视图

在本节中,我们将为我们的应用程序创建API视图,当用户访问对应于视图函数的端点时,Django将调用这些视图。

开放~/djangoreactproject/customers/views.py

代码语言:javascript
复制
nano ~/djangoreactproject/customers/views.py

删除那里的内容并添加以下导入:

代码语言:javascript
复制
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status
​
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from .models import Customer 
from .serializers import *

我们正在导入我们创建的序列化器,以及Customer模型和Django和Django REST Framework API。

接下来,添加用于处理POST和GET HTTP请求的视图:

代码语言:javascript
复制
...
​
@api_view(['GET', 'POST'])
def customers_list(request):
    """
 List  customers, or create a new customer.
 """
    if request.method == 'GET':
        data = []
        nextPage = 1
        previousPage = 1
        customers = Customer.objects.all()
        page = request.GET.get('page', 1)
        paginator = Paginator(customers, 10)
        try:
            data = paginator.page(page)
        except PageNotAnInteger:
            data = paginator.page(1)
        except EmptyPage:
            data = paginator.page(paginator.num_pages)
​
        serializer = CustomerSerializer(data,context={'request': request} ,many=True)
        if data.has_next():
            nextPage = data.next_page_number()
        if data.has_previous():
            previousPage = data.previous_page_number()
​
        return Response({'data': serializer.data , 'count': paginator.count, 'numpages' : paginator.num_pages, 'nextlink': '/api/customers/?page=' + str(nextPage), 'prevlink': '/api/customers/?page=' + str(previousPage)})
​
    elif request.method == 'POST':
        serializer = CustomerSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

首先,我们使用@api_view(['GET', 'POST'])decorator 创建一个可以接受GET和POST请求的API视图。 decorator 是一个函数,它的另一功能和动态延伸它。

在方法体中,我们使用request.method变量来检查当前的HTTP方法,并根据请求类型执行相应的逻辑:

  • 如果是GET请求,则该方法使用Django Paginator对数据进行分页,并返回序列化后的第一页数据,可用客户的数量,可用页面的数量以及前一页和下一页的链接。Paginator是一个内置的Django类,它将数据列表分页到页面中,并提供访问每个页面的项目的方法。
  • 如果是POST请求,则该方法序列化接收的客户数据,然后调用save()序列化程序对象的方法。然后它返回一个Response对象,一个HttpResponse实例,带有201状态代码。您创建的每个视图都负责撤消HttpResponse对象。该save()方法将序列化数据保存在数据库中。

现在添加API视图,该视图将负责处理通过pk(主键)获取,更新和删除客户的GET,PUT和DELETE请求:

代码语言:javascript
复制
...
@api_view(['GET', 'PUT', 'DELETE'])
def customers_detail(request, pk):
 """
 Retrieve, update or delete a customer by id/pk.
 """
    try:
        customer = Customer.objects.get(pk=pk)
    except Customer.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)
​
    if request.method == 'GET':
        serializer = CustomerSerializer(customer,context={'request': request})
        return Response(serializer.data)
​
    elif request.method == 'PUT':
        serializer = CustomerSerializer(customer, data=request.data,context={'request': request})
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
​
    elif request.method == 'DELETE':
        customer.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

该方法@api_view(['GET', 'PUT', 'DELETE'])用于表示它是一个可以接受GET,PUT和DELETE请求的API视图。

request.method字段中的检查验证请求方法,并根据其值调用正确的逻辑:

  • 如果是GET请求,则客户数据将被序列化并使用Response对象发送。
  • 如果是PUT请求,则该方法为新客户数据创建序列化程序。接下来,它调用save()创建的序列化程序对象的方法。最后,它发送一个带有更新客户的Response对象。
  • 如果是DELETE请求,则该方法调用delete()customer对象的方法将其删除,然后返回一个没有数据的Response对象。

完成的文件如下所示:

代码语言:javascript
复制
from rest_framework.response import Response
from rest_framework.decorators import api_view
from rest_framework import status
​
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
from .models import Customer 
from .serializers import *
​
​
@api_view(['GET', 'POST'])
def customers_list(request):
    """
 List  customers, or create a new customer.
 """
    if request.method == 'GET':
        data = []
        nextPage = 1
        previousPage = 1
        customers = Customer.objects.all()
        page = request.GET.get('page', 1)
        paginator = Paginator(customers, 5)
        try:
            data = paginator.page(page)
        except PageNotAnInteger:
            data = paginator.page(1)
        except EmptyPage:
            data = paginator.page(paginator.num_pages)
​
        serializer = CustomerSerializer(data,context={'request': request} ,many=True)
        if data.has_next():
            nextPage = data.next_page_number()
        if data.has_previous():
            previousPage = data.previous_page_number()
​
        return Response({'data': serializer.data , 'count': paginator.count, 'numpages' : paginator.num_pages, 'nextlink': '/api/customers/?page=' + str(nextPage), 'prevlink': '/api/customers/?page=' + str(previousPage)})
​
    elif request.method == 'POST':
        serializer = CustomerSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
​
@api_view(['GET', 'PUT', 'DELETE'])
def customers_detail(request, pk):
    """
 Retrieve, update or delete a customer by id/pk.
 """
    try:
        customer = Customer.objects.get(pk=pk)
    except Customer.DoesNotExist:
        return Response(status=status.HTTP_404_NOT_FOUND)
​
    if request.method == 'GET':
        serializer = CustomerSerializer(customer,context={'request': request})
        return Response(serializer.data)
​
    elif request.method == 'PUT':
        serializer = CustomerSerializer(customer, data=request.data,context={'request': request})
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
​
    elif request.method == 'DELETE':
        customer.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

我们现在可以继续创建我们的端点。

添加API端点

我们现在将创建API端点:api/customers/,用于查询和创建客户,以及api/customers/<pk>,通过pk获取,更新或删除单个客户的API端点。

开放~/djangoreactproject/djangoreactproject/urls.py

代码语言:javascript
复制
nano ~/djangoreactproject/djangoreactproject/urls.py

留下什么,但将导入添加到customers文件顶部的视图:

代码语言:javascript
复制
from django.contrib import admin
from django.urls import path
from customers import views
from django.conf.urls import url

接下来,将 api/customers/api/customers/<pk>的URL 添加到包含应用程序URL 的urlpatterns列表中:

代码语言:javascript
复制
...
​
urlpatterns = [
    path('admin/', admin.site.urls),
    url(r'^api/customers/$', views.customers_list),
    url(r'^api/customers/(?P<pk>[0-9]+)$', views.customers_detail),
]

创建我们的REST端点后,让我们看看如何使用它们。

第6步 - 使用Axios使用REST API

在此步骤中,我们将安装Axios,即我们将用于进行API调用的HTTP客户端。我们还将创建一个类来使用我们创建的API端点。

首先,停用您的虚拟环境:

代码语言:javascript
复制
deactivate

接下来,导航到您的frontend文件夹:

代码语言:javascript
复制
cd ~/djangoreactproject/frontend

npm处安装axios

代码语言:javascript
复制
npm install axios --save

--save选项将axios依赖项添加到应用程序的package.json文件中。

接下来,创建一个名为CustomersService.js的JavaScript文件,其中包含调用REST API的代码。我们将在src文件夹中进行此操作,我们项目的应用程序代码将存在于该文件夹中:

代码语言:javascript
复制
cd src
nano CustomersService.js

添加以下代码,其中包含连接到Django REST API的方法:

代码语言:javascript
复制
import axios from 'axios';
const API_URL = 'http://localhost:8000';
​
export default class CustomersService{
​
    constructor(){}
​
​
    getCustomers() {
        const url = `${API_URL}/api/customers/`;
        return axios.get(url).then(response => response.data);
    }  
    getCustomersByURL(link){
        const url = `${API_URL}${link}`;
        return axios.get(url).then(response => response.data);
    }
    getCustomer(pk) {
        const url = `${API_URL}/api/customers/${pk}`;
        return axios.get(url).then(response => response.data);
    }
    deleteCustomer(customer){
        const url = `${API_URL}/api/customers/${customer.pk}`;
        return axios.delete(url);
    }
    createCustomer(customer){
        const url = `${API_URL}/api/customers/`;
        return axios.post(url,customer);
    }
    updateCustomer(customer){
        const url = `${API_URL}/api/customers/${customer.pk}`;
        return axios.put(url,customer);
    }
}

CustomersService类将调用下面的Axios方法:

  • getCustomers():获取客户的第一页。
  • getCustomersByURL():通过URL获取客户。这样就可以通过传递链接来获取下一页客户/api/customers/?page=2
  • getCustomer():通过主键获取客户。
  • createCustomer():创建一个客户。
  • updateCustomer():更新客户。
  • deleteCustomer():删除客户。

我们现在可以通过创建CustomersList组件在我们的React UI界面中显示API中的数据。

第7步 - 在React应用程序中显示API中的数据

在这一步中,我们将创建CustomersListReact 组件。React组件代表UI的一部分; 它还允许您将UI拆分为独立的,可重用的部分。

首先在frontend/src中创建CustomersList.js

代码语言:javascript
复制
nano ~/djangoreactproject/frontend/src/CustomersList.js

首先导入ReactComponent去创建一个React组件:

代码语言:javascript
复制
import  React, { Component } from  'react';

接下来,导入并实例化您在上一步中创建的CustomersService模块,该模块提供与REST API后端接口的方法:

代码语言:javascript
复制
...
import  CustomersService  from  './CustomersService';
​
const  customersService  =  new  CustomersService();

接下来,创建一个可以扩展Component来调用REST API 的CustomersList组件。React组件应该扩展或子类化Component

添加以下代码以创建扩展react.Component的React组件:

代码语言:javascript
复制
...
class  CustomersList  extends  Component {
​
    constructor(props) {
        super(props);
        this.state  = {
            customers: [],
            nextPageURL:  ''
        };
        this.nextPage  =  this.nextPage.bind(this);
        this.handleDelete  =  this.handleDelete.bind(this);
    }
}
export  default  CustomersList;

构造函数中,我们正在初始化state对象。这使用空customers 数组来保存组件的状态变量。此阵列将保留客户和可以保存从后端API检索的下一页的URL的nextPageURL。我们还为此this结合nextPage()handleDelete()方法,以使他们将会从HTML代码访问。

接下来,在结束大括号之前,在CustomersList类中添加componentDidMount()方法和调用getCustomers()

componentDidMount()方法是组件的生命周期方法,在创建组件并将其插入DOM时调用该方法。getCustomers()调用Customers Service对象以获取Django后端的第一页数据和下一页的链接:

代码语言:javascript
复制
...
componentDidMount() {
    var  self  =  this;
    customersService.getCustomers().then(function (result) {
        self.setState({ customers:  result.data, nextPageURL:  result.nextlink})
    });
}

现在添加处理删除客户的handleDelete()方法,如下componentDidMount()所示:

代码语言:javascript
复制
...
handleDelete(e,pk){
    var  self  =  this;
    customersService.deleteCustomer({pk :  pk}).then(()=>{
        var  newArr  =  self.state.customers.filter(function(obj) {
            return  obj.pk  !==  pk;
        });
        self.setState({customers:  newArr})
    });
}

handleDelete()方法调用该deleteCustomer()方法通过使用其pk(主键)删除客户。如果操作成功,则会针对已删除的客户筛选出customers阵列。

接下来,添加一个nextPage()方法来获取下一页的数据并更新下一页链接:

代码语言:javascript
复制
...
nextPage(){
    var  self  =  this;
    customersService.getCustomersByURL(this.state.nextPageURL).then((result) => {
        self.setState({ customers:  result.data, nextPageURL:  result.nextlink})
    });
}

nextPage()方法调用一个getCustomersByURL()方法,该方法从状态对象this.state.nextPageURL获取下一页URL ,并使用返回的数据更新customers数组。

最后,添加组件render()方法,该方法从组件状态呈现客户表:

代码语言:javascript
复制
...
render() {
​
    return (
    <div  className="customers--list">
        <table  className="table">
            <thead  key="thead">
            <tr>
                <th>#</th>
                <th>First Name</th>
                <th>Last Name</th>
                <th>Phone</th>
                <th>Email</th>
                <th>Address</th>
                <th>Description</th>
                <th>Actions</th>
            </tr>
            </thead>
            <tbody>
                {this.state.customers.map( c  =>
                <tr  key={c.pk}>
                    <td>{c.pk}  </td>
                    <td>{c.first_name}</td>
                    <td>{c.last_name}</td>
                    <td>{c.phone}</td>
                    <td>{c.email}</td>
                    <td>{c.address}</td>
                    <td>{c.description}</td>
                    <td>
                    <button  onClick={(e)=>  this.handleDelete(e,c.pk) }> Delete</button>
                    <a  href={"/customer/" + c.pk}> Update</a>
                    </td>
                </tr>)}
            </tbody>
        </table>
        <button  className="btn btn-primary"  onClick=  {  this.nextPage  }>Next</button>
    </div>
    );
}

这是文件的完整内容:

代码语言:javascript
复制
import  React, { Component } from  'react';
import  CustomersService  from  './CustomersService';
​
const  customersService  =  new  CustomersService();
​
class  CustomersList  extends  Component {
​
constructor(props) {
    super(props);
    this.state  = {
        customers: [],
        nextPageURL:  ''
    };
    this.nextPage  =  this.nextPage.bind(this);
    this.handleDelete  =  this.handleDelete.bind(this);
}
​
componentDidMount() {
    var  self  =  this;
    customersService.getCustomers().then(function (result) {
        console.log(result);
        self.setState({ customers:  result.data, nextPageURL:  result.nextlink})
    });
}
handleDelete(e,pk){
    var  self  =  this;
    customersService.deleteCustomer({pk :  pk}).then(()=>{
        var  newArr  =  self.state.customers.filter(function(obj) {
            return  obj.pk  !==  pk;
        });
​
        self.setState({customers:  newArr})
    });
}
​
nextPage(){
    var  self  =  this;
    console.log(this.state.nextPageURL);        
    customersService.getCustomersByURL(this.state.nextPageURL).then((result) => {
        self.setState({ customers:  result.data, nextPageURL:  result.nextlink})
    });
}
render() {
​
    return (
        <div  className="customers--list">
            <table  className="table">
            <thead  key="thead">
            <tr>
                <th>#</th>
                <th>First Name</th>
                <th>Last Name</th>
                <th>Phone</th>
                <th>Email</th>
                <th>Address</th>
                <th>Description</th>
                <th>Actions</th>
            </tr>
            </thead>
            <tbody>
            {this.state.customers.map( c  =>
                <tr  key={c.pk}>
                <td>{c.pk}  </td>
                <td>{c.first_name}</td>
                <td>{c.last_name}</td>
                <td>{c.phone}</td>
                <td>{c.email}</td>
                <td>{c.address}</td>
                <td>{c.description}</td>
                <td>
                <button  onClick={(e)=>  this.handleDelete(e,c.pk) }> Delete</button>
                <a  href={"/customer/" + c.pk}> Update</a>
                </td>
            </tr>)}
            </tbody>
            </table>
            <button  className="btn btn-primary"  onClick=  {  this.nextPage  }>Next</button>
        </div>
        );
  }
}
export  default  CustomersList;

现在我们已经创建了用于显示客户列表的CustomersList组件,我们可以添加处理客户创建和更新的组件。

第8步 - 添加客户创建和更新React组件

在此步骤中,我们将创建CustomerCreateUpdate组件,该组件将处理创建和更新客户。它将通过提供一个表单来实现此目的,用户可以使用该表单输入有关新客户的数据或更新现有条目。

frontend/src中,创建一个CustomerCreateUpdate.js文件:

代码语言:javascript
复制
nano ~/djangoreactproject/frontend/src/CustomerCreateUpdate.js

添加以下代码以创建React组件,导入ReactComponent

代码语言:javascript
复制
import  React, { Component } from  'react';

我们还可以导入和实例化我们在上一步中创建的CustomersService类,该类提供与REST API后端交互的方法:

代码语言:javascript
复制
...
import  CustomersService  from  './CustomersService';
​
const  customersService  =  new  CustomersService();

接下来,创建一个扩展Component为创建和更新客户的CustomerCreateUpdate组件:

代码语言:javascript
复制
...
class  CustomerCreateUpdate  extends  Component {
​
    constructor(props) {
        super(props);
    }
​
}
export default CustomerCreateUpdate;

在类定义中,添加组件的render()方法,该方法呈现一个HTML表单,其中包含有关客户的信息:

代码语言:javascript
复制
...
render() {
        return (
          <form onSubmit={this.handleSubmit}>
          <div className="form-group">
            <label>
              First Name:</label>
              <input className="form-control" type="text" ref='firstName' />
​
            <label>
              Last Name:</label>
              <input className="form-control" type="text" ref='lastName'/>
​
            <label>
              Phone:</label>
              <input className="form-control" type="text" ref='phone' />
​
            <label>
              Email:</label>
              <input className="form-control" type="text" ref='email' />
​
            <label>
              Address:</label>
              <input className="form-control" type="text" ref='address' />
​
            <label>
              Description:</label>
              <textarea className="form-control" ref='description' ></textarea>
​
​
            <input className="btn btn-primary" type="submit" value="Submit" />
            </div>
          </form>
        );
  }

对于每个表单输入元素,该方法添加一个ref属性以访问和设置表单元素的值。

接下来,在render()方法上方,定义一个handleSubmit(event)方法,以便在用户单击提交按钮时具有正确的功能:

代码语言:javascript
复制
...
handleSubmit(event) {
    const { match: { params } } =  this.props;
    if(params  &&  params.pk){
        this.handleUpdate(params.pk);
    }
    else
    {
        this.handleCreate();
    }
    event.preventDefault();
}
​
...

handleSubmit(event)方法处理表单提交,并根据路由调用handleUpdate(pk)方法以使用传递更新客户pk,或调用handleCreate()创建新客户的方法。我们将很快定义这些方法。

回到组件构造函数,绑定新添加的handleSubmit()方法至this,以便您可以在表单中访问它:

代码语言:javascript
复制
...
class CustomerCreateUpdate extends Component {
​
constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
}
...

接下来,定义从表单数据创建客户的handleCreate()方法。在handleSubmit(event)方法上方,添加以下代码:

代码语言:javascript
复制
...
handleCreate(){
    customersService.createCustomer(
        {
        "first_name":  this.refs.firstName.value,
        "last_name":  this.refs.lastName.value,
        "email":  this.refs.email.value,
        "phone":  this.refs.phone.value,
        "address":  this.refs.address.value,
        "description":  this.refs.description.value
        }).then((result)=>{
                alert("Customer created!");
        }).catch(()=>{
                alert('There was an error! Please re-check your form.');
        });
}
​
...

handleCreate()方法将用于根据输入的数据创建客户。它调用相应的CustomersService.createCustomer()方法,该方法对后端进行实际API调用以创建客户。

接下来,在handleCreate()方法下面,定义实现更新的handleUpdate(pk)方法:

代码语言:javascript
复制
...
handleUpdate(pk){
customersService.updateCustomer(
    {
    "pk":  pk,
    "first_name":  this.refs.firstName.value,
    "last_name":  this.refs.lastName.value,
    "email":  this.refs.email.value,
    "phone":  this.refs.phone.value,
    "address":  this.refs.address.value,
    "description":  this.refs.description.value
    }
    ).then((result)=>{
​
        alert("Customer updated!");
    }).catch(()=>{
        alert('There was an error! Please re-check your form.');
    });
}

updateCustomer()方法将通过pk,使用客户信息表中的新信息,从而来更新客户。它调用customersService.updateCustomer()方法。

接下来,添加一个componentDidMount()方法。如果用户访问customer/:pk路线,我们希望使用URL中的主键为表单填写与客户相关的信息。为此,我们可以在组件在生命周期的componentDidMount()事件中挂载之后添加该getCustomer(pk)方法。在组件构造函数下面添加以下代码以添加此方法:

代码语言:javascript
复制
...
componentDidMount(){
    const { match: { params } } =  this.props;
    if(params  &&  params.pk)
    {
        customersService.getCustomer(params.pk).then((c)=>{
            this.refs.firstName.value  =  c.first_name;
            this.refs.lastName.value  =  c.last_name;
            this.refs.email.value  =  c.email;
            this.refs.phone.value  =  c.phone;
            this.refs.address.value  =  c.address;
            this.refs.description.value  =  c.description;
        })
    }
}

这是文件的完整内容:

代码语言:javascript
复制
import React, { Component } from 'react';
import CustomersService from './CustomersService';
​
const customersService = new CustomersService();
​
class CustomerCreateUpdate extends Component {
    constructor(props) {
        super(props);
​
        this.handleSubmit = this.handleSubmit.bind(this);
      }
​
      componentDidMount(){
        const { match: { params } } = this.props;
        if(params && params.pk)
        {
          customersService.getCustomer(params.pk).then((c)=>{
            this.refs.firstName.value = c.first_name;
            this.refs.lastName.value = c.last_name;
            this.refs.email.value = c.email;
            this.refs.phone.value = c.phone;
            this.refs.address.value = c.address;
            this.refs.description.value = c.description;
          })
        }
      }
​
      handleCreate(){
        customersService.createCustomer(
          {
            "first_name": this.refs.firstName.value,
            "last_name": this.refs.lastName.value,
            "email": this.refs.email.value,
            "phone": this.refs.phone.value,
            "address": this.refs.address.value,
            "description": this.refs.description.value
        }          
        ).then((result)=>{
          alert("Customer created!");
        }).catch(()=>{
          alert('There was an error! Please re-check your form.');
        });
      }
      handleUpdate(pk){
        customersService.updateCustomer(
          {
            "pk": pk,
            "first_name": this.refs.firstName.value,
            "last_name": this.refs.lastName.value,
            "email": this.refs.email.value,
            "phone": this.refs.phone.value,
            "address": this.refs.address.value,
            "description": this.refs.description.value
        }          
        ).then((result)=>{
          console.log(result);
          alert("Customer updated!");
        }).catch(()=>{
          alert('There was an error! Please re-check your form.');
        });
      }
      handleSubmit(event) {
        const { match: { params } } = this.props;
​
        if(params && params.pk){
          this.handleUpdate(params.pk);
        }
        else
        {
          this.handleCreate();
        }
​
        event.preventDefault();
      }
​
      render() {
        return (
          <form onSubmit={this.handleSubmit}>
          <div className="form-group">
            <label>
              First Name:</label>
              <input className="form-control" type="text" ref='firstName' />
​
            <label>
              Last Name:</label>
              <input className="form-control" type="text" ref='lastName'/>
​
            <label>
              Phone:</label>
              <input className="form-control" type="text" ref='phone' />
​
            <label>
              Email:</label>
              <input className="form-control" type="text" ref='email' />
​
            <label>
              Address:</label>
              <input className="form-control" type="text" ref='address' />
​
            <label>
              Description:</label>
              <textarea className="form-control" ref='description' ></textarea>
​
​
            <input className="btn btn-primary" type="submit" value="Submit" />
            </div>
          </form>
        );
      }  
}
​
export default CustomerCreateUpdate;

随着CustomerCreateUpdate组件创建,我们可以更新主要的App成分添加链接到我们创建的不同组件。

第9步 - 更新主应用程序组件

在本节中,我们将更新App应用程序的组件,以创建指向我们在前面步骤中创建的组件的链接。

从该frontend文件夹中,运行以下命令以安装React Router,它允许您在各种React组件之间添加路由和导航:

代码语言:javascript
复制
cd ~/djangoreactproject/frontend
npm install --save react-router-dom

接下来,打开~/djangoreactproject/frontend/src/App.js

代码语言:javascript
复制
nano ~/djangoreactproject/frontend/src/App.js

删除那里的所有内容并添加以下代码以导入添加路由所需的类。这些包括创建了路由器组件的BrowserRouter,和创建了路由组件的Route

代码语言:javascript
复制
import  React, { Component } from  'react';
import { BrowserRouter } from  'react-router-dom'
import { Route, Link } from  'react-router-dom'
import  CustomersList  from  './CustomersList'
import  CustomerCreateUpdate  from  './CustomerCreateUpdate'
import  './App.css';

BrowserRouter使用HTML5历史记录API使UI与URL保持同步。

接下来,创建一个基本布局,该布局提供要由BrowserRouter组件包装的基本组件:

代码语言:javascript
复制
...
​
const  BaseLayout  = () => (
<div  className="container-fluid">
    <nav  className="navbar navbar-expand-lg navbar-light bg-light">
        <a  className="navbar-brand"  href="#">Django React Demo</a>
        <button  className="navbar-toggler"  type="button"  data-toggle="collapse"  data-target="#navbarNavAltMarkup"  aria-controls="navbarNavAltMarkup"  aria-expanded="false"  aria-label="Toggle navigation">
        <span  className="navbar-toggler-icon"></span>
    </button>
    <div  className="collapse navbar-collapse"  id="navbarNavAltMarkup">
        <div  className="navbar-nav">
            <a  className="nav-item nav-link"  href="/">CUSTOMERS</a>
            <a  className="nav-item nav-link"  href="/customer">CREATE CUSTOMER</a>
        </div>
    </div>
    </nav>
    <div  className="content">
        <Route  path="/"  exact  component={CustomersList}  />
        <Route  path="/customer/:pk"  component={CustomerCreateUpdate}  />
        <Route  path="/customer/"  exact  component={CustomerCreateUpdate}  />
    </div>
</div>
)

我们使用该Route组件来定义应用程序的路由; 一旦找到匹配,路由器应加载的组件。每个路由需要一个 path来指定要匹配的路径,一个component来指定要加载的组件。该exact属性告诉路由器匹配确切的路径。

最后,创建React应用程序的App组件,根或顶级组件:

代码语言:javascript
复制
...
​
class  App  extends  Component {
​
render() {
    return (
    <BrowserRouter>
        <BaseLayout/>
    </BrowserRouter>
    );
}
}
export  default  App;

我们已经使用BrowserRouter组件包装了BaseLayout组件,因为我们的应用程序是在浏览器中运行的。

完成的文件如下所示:

代码语言:javascript
复制
import React, { Component } from 'react';
import { BrowserRouter } from 'react-router-dom'
import { Route, Link } from 'react-router-dom'
​
import  CustomersList from './CustomersList'
import  CustomerCreateUpdate  from './CustomerCreateUpdate'
import './App.css';
​
const BaseLayout = () => (
  <div className="container-fluid">
<nav className="navbar navbar-expand-lg navbar-light bg-light">
  <a className="navbar-brand" href="#">Django React Demo</a>
  <button className="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Toggle navigation">
    <span className="navbar-toggler-icon"></span>
  </button>
  <div className="collapse navbar-collapse" id="navbarNavAltMarkup">
    <div className="navbar-nav">
      <a className="nav-item nav-link" href="/">CUSTOMERS</a>
      <a className="nav-item nav-link" href="/customer">CREATE CUSTOMER</a>
​
    </div>
  </div>
</nav>  
​
    <div className="content">
      <Route path="/" exact component={CustomersList} />
      <Route path="/customer/:pk"  component={CustomerCreateUpdate} />
      <Route path="/customer/" exact component={CustomerCreateUpdate} />
​
    </div>
​
  </div>
)
​
class App extends Component {
  render() {
    return (
      <BrowserRouter>
        <BaseLayout/>
      </BrowserRouter>
    );
  }
}
​
export default App;

在将路由添加到我们的应用程序后,我们现在可以测试该应用程序。导航到http://localhost:3000。您应该看到应用程序的第一页:

有了这个应用程序,您现在可以拥有CRM应用程序的基础。

结论

在本教程中,您使用Django和React创建了一个演示应用程序。您使用Django REST框架构建REST API,使用Axios来使用API,使用Bootstrap 4来构建CSS样式。您可以在此GitHub存储库中找到此项目的源代码。

更多Ubuntu教程请前往腾讯云+社区学习更多知识。


参考文献:《How To Build a Modern Web Application to Manage Customer Information with Django and React on Ubuntu 18.04》

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 介绍
  • 先决条件
  • 第1步 - 创建Python虚拟环境并安装依赖项
  • 第2步 - 创建Django项目
  • 第3步 - 创建React前端
  • 第4步 - 创建客户模型和初始数据
  • 第5步 - 创建REST API
    • 添加Serializer类
      • 添加API视图
        • 添加API端点
        • 第6步 - 使用Axios使用REST API
        • 第7步 - 在React应用程序中显示API中的数据
        • 第8步 - 添加客户创建和更新React组件
        • 第9步 - 更新主应用程序组件
        • 结论
        相关产品与服务
        消息队列 TDMQ
        消息队列 TDMQ (Tencent Distributed Message Queue)是腾讯基于 Apache Pulsar 自研的一个云原生消息中间件系列,其中包含兼容Pulsar、RabbitMQ、RocketMQ 等协议的消息队列子产品,得益于其底层计算与存储分离的架构,TDMQ 具备良好的弹性伸缩以及故障恢复能力。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档