---
title: Flutter中的Parse 1对多关系实现指南
slug: docs-ch/flutter/parse-sdk/data-objects/flutter-one-to-many-relationship
description: 学习在Flutter中使用Parse创建和查询一对多数据对象关系。
image: https://www.back4app.com/images/logo-social.png
createdAt: 2024-03-15T15:14:39.658Z
---

# Flutter中的一对多关系


介绍
--


使用Parse，您可以存储数据对象并在它们之间建立关系。要建模这种行为，任何 <font color="#2166ae">`ParseObject`</font> 都可以作为其他 <font color="#2166ae">`ParseObject`</font> 的值。内部，Parse框架将只在一个地方存储被引用的对象，以保持一致性。这可以在构建和运行复杂查询时为您提供额外的能力。主要有三种关系类型：

-  <font color="#2166ae">`一对一`</font>, 在两个对象之间建立直接关系，仅限于这两个对象；
-  <font color="#2166ae">`一对多`</font>, 一个对象可以与多个其他对象相关；
-  <font color="#2166ae">`多对多`</font>, 可以在多个对象之间创建许多复杂的关系。

在本指南中，我们将详细说明如何使用一个实际应用示例来实现 <font color="#2166ae">`一对多`</font> 关系。 在 Parse 中有两种方法可以创建 <font color="#2166ae">`一对多`</font> 关系：

- 第一种是使用 <font color="#2166ae">`指针`</font> 在 <font color="#2166ae">`子类`</font>中，这是创建和查询时间最快的方法。
- 第二种是使用 <font color="#2166ae">`数组`</font> 的 <font color="#2166ae">`指针`</font> 在父类中，这可能会导致查询时间变慢，具体取决于它们的大小。由于这个性能问题，我们将仅使用指针示例。

您将实现一个 Flutter 图书注册应用，并将使用 Parse <font color="#2166ae">`指针`</font> 创建和查询相关对象。

:::hint{type="info"}
关系如 <font color="#2166ae">`一对一`</font> 并不常见，我们不会在我们的指南中涵盖。 作为示例，用户类与另一个类之间的关系将包含敏感用户数据，出于 [安全原因](https://blog.back4app.com/parse-server-best-practices/) （*1.4. 不要让用户访问其他用户的敏感数据*)。
:::

::embed[]{url="https://www.youtube.com/embed/b6fdFD0hlJo"}

## 前提条件

:::hint{type="info"}
- [Flutter 版本 2.2.x 或更高版本](https://flutter.dev/docs/get-started/install)
- [Android Studio ](https://developer.android.com/studio)或 [已安装的 VS Code](https://code.visualstudio.com/)（带有 [插件](https://docs.flutter.dev/get-started/editor) Dart 和 Flutter）
- 在 Back4App 上 [创建的应用](https://www.back4app.com/docs/get-started/new-parse-app)：
  - **注意：&#x20;**&#x8BF7;遵循 [新 Parse 应用教程](https://www.back4app.com/docs/get-started/new-parse-app) 了解如何在 Back4App 上创建 Parse 应用。
- 一个连接到 Back4app 的 Flutter 应用。
  - **注意：&#x20;**&#x8BF7;遵循 [在 Flutter 项目中安装 Parse SDK](https://www.back4app.com/docs/flutter/parse-sdk/parse-flutter-sdk) 以创建一个连接到 Back4App 的 Flutter 项目。
- 一台运行 Android 或 iOS 的设备（或虚拟设备）。
:::

## 理解书籍应用


您将使用的主要对象类是 <font color="#2166ae">`书籍`</font> 类，存储每本书的注册条目。此外，还有其他三个对象类：

- <font color="#2166ae">`出版商`</font>: 书籍出版商名称，与 <font color="#2166ae">`书籍`</font> 的一对多关系；
- <font color="#2166ae">`类型`</font>: 书籍类型，与 <font color="#2166ae">`书籍`</font> 的一对多关系。请注意，在本示例中，我们将考虑一本书只能有一个类型；
- <font color="#2166ae">`作者`</font>: 书籍作者，与 <font color="#2166ae">`书籍`</font> 的多对多关系，因为一本书可以有多个作者，而一个作者也可以有多本书；

这些数据模型的可视化表示：

![](https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/A3OFhXHEJEAvPvi6DVVuq_image.png)

我们将假设每个对象类 (<font color="#2166ae">`发布者`</font>, <font color="#2166ae">`类型`</font>) 只有一个字符串类型的 <font color="#2166ae">`名称`</font> 属性，而 <font color="#2166ae">`书籍`</font> 具有 <font color="#2166ae">`标题`</font> 和 <font color="#2166ae">`年份`</font>, 除了任何额外的关系属性。在之前的指南中，我们已经看到如何保存和读取 <font color="#2166ae">`ParseObject`</font>，因此在本指南中，我们将不再介绍如何保存和读取 <font color="#2166ae">`类型`</font> 和 <font color="#2166ae">`发布者`</font> 对象。

您将在书籍应用程序中找到以下屏幕：

- **类型的注册和列表**
- **发布者的注册和列表**
- **书籍注册**
- **发布者和书籍列表**
- **书籍详情**

我们不会解释Flutter应用程序代码，因为本指南的主要重点是使用Flutter与Parse进行关系。

## 1 - 创建图书应用模板

让我们首先运行图书应用项目模板。从之前的指南中打开您的Flutter项目 **Flutter插件用于Parse服务器**。 [图书Flutter应用](https://github.com/templates-back4app/flutter_associations) 仓库也可以供您克隆并运行该项目。复制 [main.dart](https://github.com/templates-back4app/flutter_associations/blob/master/lib/main.dart) 文件并替换您之前指南中的当前代码。

**注意：**
当 <font color="#2166ae">`debug`</font> 参数在函数 <font color="#2166ae">`Parse().initialize`</font> 为 <font color="#2166ae">`true`</font>, 允许在控制台上显示Parse API调用。此配置可以帮助调试代码。建议在发布版本中禁用调试。

### 步骤 2 - 将模板连接到 Back4app 项目

通过导航到您的应用程序仪表板找到您的应用程序 ID 和客户端密钥凭据，地址为 [Back4App 仪表板->应用设置->安全性与密钥](https://www.back4app.com/docs/parse-dashboard/app-settings)。在 <font color="#2166ae">`main.dart`</font> 中更新您的代码，使用您项目的 ApplicationId 和 ClientKey 的值在 Back4app 中.

- **keyApplicationId = 应用程序 ID**
- **keyClientKey = 客户端密钥**

运行项目，应用程序将如图所示加载.

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/yMFpRGpZJLsxJjtLPBDdk_image.png" size="30" width="323" height="627" position="center" showCaption="false"}

点击 <font color="#2166ae">`添加类型`</font> 来注册并查看将用于书籍注册的 <font color="#2166ae">`类型`</font> 列表.

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/AXMne48nSmI_omEvIIFLT_image.png" size="30" width="323" height="627" position="center" showCaption="false"}

点击 <font color="#2166ae">`添加出版商`</font> 注册并查看将用于书籍注册的 <font color="#2166ae">`出版商`</font> 列表。

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/s566XXvwuWEOBBdq6LHC5_image.png" size="30" width="323" height="627" position="center" showCaption="false"}

点击 <font color="#2166ae">`添加书籍`</font> 注册新的 <font color="#2166ae">`书籍`</font>，使用与 <font color="#2166ae">`类型`</font> 和 <font color="#2166ae">`出版商`</font> 的关系。

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/jVUGzMZD8IIhrw57GNnRc_image.png" size="30" width="328" height="637" position="center" showCaption="false"}

点击 <font color="#2166ae">`列表出版商/书籍`</font> 查看 <font color="#2166ae">`出版商`</font> 和 <font color="#2166ae">`书籍`</font> 的列表。

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/EGNDJ3ZrgC9_P-vPrSZ_a_image.png" size="30" width="323" height="627" position="center" showCaption="false"}

## 3 - 保存书籍对象及其关系

此功能将在 Back4app 数据库中创建一个新的书籍及其关系。搜索文件中的函数 <font color="#2166ae">`doSaveBook`</font> <font color="#2166ae">`main.dart`</font>, 并在 <font color="#2166ae">`Future<void> doSaveBook()`</font> 函数中插入以下代码:

```dart
1       final book = ParseObject('Book')
2         ..set('title', controllerTitle.text.trim())
3         ..set('year', int.parse(controllerYear.text.trim()))
4         //the objectId will be converted to a Pointer on the save() method
5         ..set('genre', ParseObject('Genre')..objectId = genre.objectId)
6         //you can also convert to a Pointer object before the saving using the .toPointer() method
7         ..set(
8             'publisher',
9             (ParseObject('Publisher')..objectId = publisher.objectId)
10                .toPointer());
11
12      await book.save();
```

要构建此功能，请按照以下步骤操作：

- 1\. 创建一个新的 Parse <font color="#2166ae">`Book`</font> 类的实例，使用命令 <font color="#2166ae">`ParseObject('Book')`</font>.
- 2\. 使用 <font color="#2166ae">`set`</font> 函数来设置此对象的字段。
  - 2.1.<font color="#2166ae">`title`</font> 是一个文本属性，从文本控制器接收值。
  - 2.2.<font color="#2166ae">`genre`</font> 通过定义一个 <font color="#2166ae">`ParseObject`</font> 来接收值，使用所选的 <font color="#2166ae">`objectId`</font> 的 <font color="#2166ae">`Genre`</font>. (*Parse 将在保存时转换为指针*)
  - 2.3.<font color="#2166ae">`publisher`</font> 通过定义一个 <font color="#2166ae">`ParseObject`</font> 来接收值，使用所选的 <font color="#2166ae">`objectId`</font> 的 <font color="#2166ae">`Publisher`</font>. (*请注意，我们可以为 Parse 指定我们希望保存为一个&#x20;*<font color="#2166ae">`pointer`</font>*，使用&#x20;*<font color="#2166ae">`toPointer()`</font>*&#x20;方法*)
- 3\. 在 <font color="#2166ae">`save`</font> 函数中调用 <font color="#2166ae">`ParseObject`</font>, 这将有效地将对象注册到您的 Back4app Dashboard 数据库中。

运行应用程序并测试新功能。

- 点击 <font color="#2166ae">`添加书籍`</font> 按钮。
- 填写书籍信息。

该应用程序需要选择作者，但相关代码将在下一个指南中介绍。

- 点击 <font color="#2166ae">`保存书籍`</font> 按钮



::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/cssL-jdLG4ecfman8hIGp_image.png" size="30" width="323" height="627" position="center" showCaption="false"}

要确认新对象是否与关系一起保存在数据库中，您可以访问 <font color="#2166ae">`Back4app Dashboard`</font> 并访问 <font color="#2166ae">`Book`</font> 类。

![](https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/SEpUCYQHevWFwhR33NxCp_image.png)

如果您使用仪表板访问您的 Book 类，您可以单击对象指针值，您将被重定向到引用的对象。这看起来可能是一个无害的功能，但这使得调试和错误追踪比手动搜索快得多。

## 4 - 查询书籍列表及其相关对象

此功能将使用与出版商的关系在 Back4app 数据库中查询书籍。通过出版商，我们将获得书籍列表。

搜索文件中的函数 <font color="#2166ae">`getBookList`</font> ，在 <font color="#2166ae">`main.dart`</font> 文件中，然后在 <font color="#2166ae">`Future<List<ParseObject>> getBookList(String publisherId)`</font> 函数中替换以下代码:

```dart
1       QueryBuilder<ParseObject> queryBook =
2           QueryBuilder<ParseObject>(ParseObject('Book'))
3             ..whereEqualTo('publisher',
4                 (ParseObject('Publisher')..objectId = publisherId).toPointer())
5             ..orderByAscending('title');
6       final ParseResponse apiResponse = await queryBook.query();
7   
8       if (apiResponse.success && apiResponse.results != null) {
9         return apiResponse.results;
10      } else {
11        return [];
12      }
```

要构建此功能，请按照以下步骤操作：

1. 为 <font color="#2166ae">`ParseQuery`</font> 对象创建一个实例，针对 <font color="#2166ae">`Book`</font> 类。插入查询条件，以搜索 <font color="#2166ae">`Books`</font>，其中 <font color="#2166ae">`publisher`</font> 字段等于 <font color="#2166ae">`pointer`</font> 的 <font color="#2166ae">`Publisher ParseObject`</font>.
2. 我们按升序 <font color="#2166ae">`name`</font> 排序结果
3. 使用 <font color="#2166ae">`query()`</font> 方法进行查询的搜索。
4. 如果操作成功，将返回 <font color="#2166ae">`Book`</font> 中的对象。

运行应用程序并测试新的查询。首先，单击 <font color="#2166ae">`List Publisher/Book`</font> 按钮。

::Image[]{src="https://api.archbee.com/api/optimize/yD3zCY-NNBBIfd0uqcfR5/hUJS9Me_FZYq1DeuzH22Z_image.png" size="30" width="327" height="634" position="center" showCaption="false"}

## 完成了！

在这一点上，您已经学习了如何在 Parse 中创建和查询一对多关系，<font color="#2166ae">`Flutter`</font>。在下一个指南中，我们将向您展示如何建立多对多关系以及如何执行返回相关对象数据的查询。
