#5 关联关系

英文原版: https://guides.emberjs.com/v2.14.0/models/relationships/

Ember Data内建了若干关系类型帮你定义当前的模型与其他模型之间的联系。

一对一

要声明两个模型之间是一对一关系,使用 DS.belongsTo:

app/models/user.js

import DS from 'ember-data';

export default DS.Model.extend({
  profile: DS.belongsTo('profile')
});
app/models/profile.js

import DS from 'ember-data';

export default DS.Model.extend({
  user: DS.belongsTo('user')
});

一对多

要声明两个模型之间是一对多关系,结合使用DS.belongsTo和DS.hasMany:

app/models/blog-post.js

import DS from 'ember-data';

export default DS.Model.extend({
  comments: DS.hasMany('comment')
});
app/models/comment.js

import DS from 'ember-data';

export default DS.Model.extend({
  blogPost: DS.belongsTo('blog-post')
});

多对多

要声明两个模型之间为 多对多关系,使用DS.hasMany:

app/models/blog-post.js

import DS from 'ember-data';

export default DS.Model.extend({
  tags: DS.hasMany('tag')
});
app/models/tag.js

import DS from 'ember-data';

export default DS.Model.extend({
  blogPosts: DS.hasMany('blog-post')
});

显式反转

Ember Data将尽力发现那些映射到彼此的关系。 在上面的一对多代码中,例如,Ember Data可以知道,更改注释关系应该更新博客关系,因为blogPost是该模型的唯一关系。

但是,有时你可能会有相同类型的多个belongsTo / hasManys。 您可以使用DS.belongsTo或DS.hasMany的反向选项指定相关模型上的哪个属性为反向。 不带反向的关系可以通过设置{inverse:null}来表示。

app/models/comment.js

import DS from 'ember-data';

export default DS.Model.extend({
  onePost: DS.belongsTo('blog-post', { inverse: null }),
  twoPost: DS.belongsTo('blog-post'),
  redPost: DS.belongsTo('blog-post'),
  bluePost: DS.belongsTo('blog-post')
});
app/models/blog-post.js

import DS from 'ember-data';

export default DS.Model.extend({
  comments: DS.hasMany('comment', {
    inverse: 'redPost'
  })
});

自反关系

当你要定义一个自反关系时(模型与它本身建立关系),你必须显示的定义反向关系。如果不存在自反关系你需要设置{inverse:null}。

下例是一个一对多的自反关系:

app/models/folder.js

import DS from 'ember-data';

export default DS.Model.extend({
  children: DS.hasMany('folder', { inverse: 'parent' }),
  parent: DS.belongsTo('folder', { inverse: 'children' })
});

下面的例子是一个一对一的自反关系:

app/models/user.js

import DS from 'ember-data';

export default DS.Model.extend({
  name: DS.attr('string'),
  bestFriend: DS.belongsTo('user', { inverse: 'bestFriend' }),
});

你也可以定义一个不带反向联系的自反关系:

app/models/folder.js

import DS from 'ember-data';

export default DS.Model.extend({
  parent: DS.belongsTo('folder', { inverse: null })
});

多态性

多态是一个非常重要的概念,它使得开发者可以把公共的功能抽取到一个基类中。考虑下面的例子:一个用户有多种支付方式。他有个PayPal账户,和一堆信用卡。

注意,为了使多态起作用,Ember Data期望通过一个保留属性polymorphic声明”type”是多态的。难以理解?看下面例子:

首先,看一下模型的定义:

app/models/user.js

import DS from 'ember-data';

export default DS.Model.extend({
  paymentMethods: DS.hasMany('payment-method', { polymorphic: true })
});
app/models/payment-method.js

import DS from 'ember-data';

export default DS.Model.extend({
  user: DS.belongsTo('user', { inverse: 'paymentMethods' }),
});
app/models/payment-method-cc.js

import PaymentMethod from './payment-method';
import Ember from 'ember';

export default PaymentMethod.extend({
  obfuscatedIdentifier: Ember.computed('last4', function () {
    return `**** **** **** ${this.get('last4')}`;
  })
});
import PaymentMethod from './payment-method'
import DS from 'ember-data';
import Ember from 'ember';

export default PaymentMethod.extend({
  linkedEmail: DS.attr(),

  obfuscatedIdentifier: Ember.computed('linkedEmail', function () {
    let last5 = this.get('linkedEmail').split('').reverse().slice(0, 5).reverse().join('');

    return `••••${last5}`;
  })
});

我们接口会如下设置关联关系:

{
    "data": {
        "id": "8675309",
        "type": "user",
        "attributes": {
            "name": "Anfanie Farmeo"
        },
        "relationships": {
            "payment-methods": {
                "data": [{
                    "id": "1",
                    "type": "PaymentMethodPaypal" }, {
                    "id": "2",
                    "type": "PaymentMethodCc" }, {
                    "id": "3",
                    "type": "PaymentMethodApplePay" }]
            }
        }
    },
    "included": [{
        "id": "1",
        "type": "PaymentMethodPaypal",
        "attributes": {
            "linked-email": "ryan@gosling.io"
        }
    }, {
        "id": "2",
        "type": "PaymentMethodCc",
        "attributes": {
            "last4": "1335"
        }
    }, {
        "id": "3",
        "type": "PaymentMethodApplePay",
        "attributes": {
            "last4": "5513"
        }
    }]
}

只读的嵌套数据

一些模型有可能带有一些深度嵌套的只读的属性。比较容易想到的解决方法是为每一层嵌套的对象使用hasMany和belongsTo来创建带有关联关系的模型对象。然而,由于只读数据不需要被更新和保存,所以这往往会导致创建的大量代码,而实际上的收益是非常小的。一个比较好的实现方式是用一个未定义转换器的属性来定义关联关系。这使得通过计算属性和模板来访问只读数据时避免了定义外部模型的开销。

创建记录

假设现在我们有个blog-post和一个comment模型,它们彼此间存在联系:

app/models/blog-post.js

import DS from 'ember-data';

export default DS.Model.extend({
  comments: DS.hasMany('comment')
});
app/models/comment.js

import DS from 'ember-data';

export default DS.Model.extend({
  blogPost: DS.belongsTo('blog-post')
});

当用户在博客上发表了一条评论,我们需要建立这两条记录之间的联系。我们可以通过belongsTo关系在为新创建的comment找到一个宿主blogPost:

let blogPost = this.get('store').peekRecord('blog-post', 1);
let comment = this.get('store').createRecord('comment', {
  blogPost: blogPost
});
comment.save();

这将会创建一个新的comment记录并且保存到服务器。同时Ember Data也需要借助blogPost对于comments的关系来更新blogPost的相关数据。

在此,我们通过hasMany关系在blogPost上增加一条寄生的评论:

let blogPost = this.get('store').peekRecord('blog-post', 1);
let comment = this.get('store').createRecord('comment', {
});
blogPost.get('comments').pushObject(comment);
comment.save().then(function () {
  blogPost.save();
});

在上述情况下,这个新创建的comment从属关系将会被定向到blogPost上。

虽然createRecord比较简单,但是需要注意的是你不能为promise指派关联关系。

例子,如果你希望为blogPost设置一个author属性,在user对象被存储进store之前,这是不能得到正确的结果的:

this.get('store').createRecord('blog-post', {
  title: 'Rails is Omakase',
  body: 'Lorem ipsum',
  author: this.get('store').findRecord('user', 1)
});

然而,你可以在promise返回之后再来设置这个关系:

let blogPost = this.get('store').createRecord('blog-post', {
  title: 'Rails is Omakase',
  body: 'Lorem ipsum'
});

this.get('store').findRecord('user', 1).then(function(user) {
  blogPost.set('author', user);
});

检索相关的记录

当你从服务器请求的模型数据包含多个关联关系时,你或许会想要检索到与关联关系对应的记录。比如,当你检索一篇博客的时候,你也需要检索这篇博客下面对应的评论。 JSON API specification规定服务器通过接收一个带有键值include的请求参数来返回包含相关记录的响应。参数的值应该是一串被逗号分割的被关联的模型的名称列表。

如果你用的是支持JSON API的适配器,比如Ember默认的JSONAPIAdapter,你可以通过findRecord(), findAll(), query() 和queryRecord()方法轻松的发起请求。

findRecord() 和findAll()都可以带一个options参数,通过这个参数你可以指定include参数。例子,给定一个带有hasMany关系的post模型,该关系指向一个comment模型,当检索指定的post时同时可以要求服务器返回相关的comments:

app/routes/post.js

export default Ember.Route.extend({
  model(params) {
   return this.store.findRecord('post', params.post_id, {include: 'comments'});
  }
});

随后,对应于此post的comments就可以通过model.comments被使用了。

嵌套的关联关系可以通过点分形式来获取。所以,获取与post相关的comments以及这些comments的作者可以通过以下形式:

app/routes/post.js

export default Ember.Route.extend({
  model(params) {
   return this.store.findRecord('post', params.post_id, {include: 'comments,comments.author'});
  }
});

query()和queryRecord()可以带一个查询参数对象,此参数对象直接被序列化进URL中:

app/routes/adele.js

export default Ember.Route.extend({
  model() {
    // GET to /artists?filter[name]=Adele&include=albums
    this.store.query('artist', {
      filter: {name: 'Adele'},
      include: 'albums'
    }).then(function(artists) {
      return artists.get('firstObject');
    });
  }
});

更新记录

有时候我们需要在已有的记录上设置关联关系。我们可以如此简单的设置belongsTo关系:

let blogPost = this.get('store').peekRecord('blog-post', 1);
let comment = this.get('store').peekRecord('comment', 1);
comment.set('blogPost', blogPost);
comment.save();

另外,我们可以通过将一条记录推入post来更新hasMany关系的相关数据:

let blogPost = this.get('store').peekRecord('blog-post', 1);
let comment = this.get('store').peekRecord('comment', 1);
blogPost.get('comments').pushObject(comment);
blogPost.save();

删除关联关系

要删除belongsTo关联关系,只需要设置null值,而且会自动从hasMany关系中剔除该comment:

let comment = this.get('store').peekRecord('comment', 1);
comment.set('blogPost', null);
comment.save();

当然,删除hasMany关联关系可以如下操作,也会从belongsTo关系中剔除该blogPost:

let blogPost = this.get('store').peekRecord('blog-post', 1);
let comment = this.get('store').peekRecord('comment', 1);
blogPost.get('comments').removeObject(comment);
blogPost.save();

作为promise的关联关系

当处理关联关系的时候,请记住它们会返回promise对象。

例子,如果我们要在blogPost上异步的处理comments,我们不得不等到知道promise被返回:

let blogPost = this.get('store').peekRecord('blog-post', 1);

blogPost.get('comments').then((comments) => {
  // now we can work with the comments
});

对于belongsTo也是一样的:

let comment = this.get('store').peekRecord('comment', 1);

comment.get('blogPost').then((blogPost) => {
  // the blogPost is available here
});

Handlerbars模板将会自动的从promise中更新带回来的数据。我们可以这样显示blogPost中的那些comments:

<ul>
  {{#each blogPost.comments as |comment|}}
    <li>{{comment.id}}</li>
  {{/each}}
</ul>

Ember Data将会从服务器查询响应的记录,并且在收到数据后重新渲染模板。

本节完

———– PS—————
自反关系的部分翻译的不准确

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python 关联关系图是一种用于描述对象之间联系和依赖关系的图形表示方式。它常用于软件设计和系统分析,以帮助我们理解系统的结构和组件之间的互动关系。 在 Python ,我们可以使用一些工具和库来绘制关联关系图。其,一个常用的工具是 Graphviz,它提供了一种简洁的描述图形结构的语言 DOT,并通过将 DOT 文件转换为图像来实现绘图的功能。此外,还有一些 Python 库,如 NetworkX 和 Matplotlib,也可以用于绘制关联关系图。 关联关系的节点表示对象,而边表示对象之间的联系或依赖关系。在关联关系,我们可以清晰地看到对象之间的引用关系、调用关系或其他关联关系。这有助于我们理解系统的组成部分以及它们之间的相互作用。 绘制关联关系图的过程通常包括以下几个步骤: 1. 确定需要绘制关联关系图的对象和它们之间的关系。 2. 使用相应的工具或库创建一个空白的关联关系图。 3. 根据对象之间的关系,添加节点和边到关联关系。 4. 进行布局设置,以使关联关系图更加清晰易读。 5. 根据需要,对关联关系图进行美化或进一步编辑。 6. 将关联关系图导出为图像或其他格式,以便进一步使用或分享。 总之,Python 关联关系图是一种有助于我们理解对象之间联系和依赖关系的图形表示方式。通过绘制关联关系图,我们可以更好地理解和分析系统的结构,从而提升软件设计和系统分析的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值