Redux - ORM 软件
见 使用 Redux- ORM 创建简单应用程序的指南 (包括来源). 其README尚未更新为0.9,但 代码有它的分支。 。 。 .
0.9.x版本为API带来了很大的突破变化. 请看 移徙指南 如果你是从早期的版本迁移过来的.
找0.8个医生? 读取 重播中的旧读取器. 对于 API 的参考文献,克隆 repo, npm install, (中文(简体) ), make build 打开 index.html 在您的浏览器中。 抱歉给您带来不便.
API可以不稳定到1.0. 在 1. 0. 0 之前的小版本会起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起起 将在本报告中予以说明 更改日志。 。 。 .
安装
npm install redux-orm --save
或带有脚本标签
<script src="https://tommikaikkonen.github.io/redux-orm/dist/redux-orm.min.js"></script>
多元填充
Redux-ORM使用一些ES2015+特性,例如: Set。如果在 ES2015+ 之前的环境中使用 Redux- ORM ,您应该装入像 ES2015+ 那样的多填充 babel-polyfill 在使用Redux-ORM之前.
扩展
redux-orm-proptypes: React PropTypes 验证和 Redux-ORM 模型的默认Props 混用
使用量
宣布您的模式
您可以使用 ES6 类语法来声明您的模型, 扩展为 Model。您需要在模型上声明您的全部非关系字段,并且推荐所有数据字段,因为库在即时模型时不需要重新定义获取器和设置器。 Redux-ORM除了外国密钥外,还支持一对一和多对多的关系(oneToOne, (中文(简体) ), many 财务报告和审定财务报表 fk (分别为进口)。 非相干属性可以和普通JavaScript对象一样被访问.
// models.js
import {fk, many, attr, Model} from 'redux-orm';
class Book extends Model {
toString() {
return `Book: ${this.name}`;
}
// Declare any static or instance methods you need.
}
Book.modelName = 'Book';
// Declare your related fields.
Book.fields = {
id: attr(), // non-relational field for any value; optional but highly recommended
name: attr(),
authors: many('Author', 'books'),
publisher: fk('Publisher', 'books'),
};
export default Book;
注册模式并生成一个空数据库状态
在模型上定义字段,规定了该模型数据库中的表格结构。 为了生成整个数据库结构的描述,我们需要一个中心位置来注册我们想要使用的所有模型.
ORM类的一个实例是登记模型,处理从所有模型中生成一个完整的计划,并将这些信息传送到数据库。 您通常想要有一个文件, 可以在应用程序上导入一个 ORM 实例, 像这样 :
// orm.js
import { ORM } from 'redux-orm';
import { Book, Author, Publisher } from './models';
const orm = new ORM();
orm.register(Book, Author, Publisher);
export default orm;
您也可以定义 财务报告和审定财务报表 将模型注册到同一文件中的 ORM 实例,并全部导出.
现在我们已经注册了模型, 我们可以生成一个空的数据库状态。 目前,这是一个平地,嵌入式的JavaScript对象,其结构类似于关系数据库.
// index.js
import orm from './orm';
const emptyDBState = orm.getEmptyState();
将更新应用到数据库
当我们有数据库状态时,我们可以开始一个关于此的ORM会话来应用更新. ORM实例提供了一个 session 用于接受数据库状态作为唯一参数的方法,并返回会话实例.
const session = orm.session(emptyDBState);
特定会话分类的已注册模型作为会话对象的属性可供使用.
const Book = session.Book;
模型提供了查询和更新数据库状态的接口.
Book.withId(1).update({ name: 'Clean Code' });
Book.all().filter(book => book.name === 'Clean Code').delete();
Book.idExists(1)
// false
初始数据库状态没有变异. 可在 state 属性.
const updatedDBState = session.state;
Redux 整合
要将 Redux-ORM 与 Redux 整合到最基本的级别,您可以定义一个从 Redux 原子上所持有的数据库状态中即时生成一个会话的还原器,然后,当您应用了所有更新时,您就可以从会话中返回下一个状态.
import orm from './orm';
function ormReducer(dbState, action) {
const sess = orm.session(dbState);
// Session-specific Models are available
// as properties on the Session instance.
const { Book } = sess;
switch (action.type) {
case 'CREATE_BOOK':
Book.create(action.payload);
break;
case 'UPDATE_BOOK':
Book.withId(action.payload.id).update(action.payload);
break;
case 'REMOVE_BOOK':
Book.withId(action.payload.id).delete();
break;
case 'ADD_AUTHOR_TO_BOOK':
Book.withId(action.payload.bookId).authors.add(action.payload.author);
break;
case 'REMOVE_AUTHOR_FROM_BOOK':
Book.withId(action.payload.bookId).authors.remove(action.payload.authorId);
break;
case 'ASSIGN_PUBLISHER':
Book.withId(action.payload.bookId).publisher = action.payload.publisherId;
break;
}
// the state property of Session always points to the current database.
// Updates don't mutate the original state, so this reference is not
// equal to `dbState` that was an argument to this reducer.
return sess.state;
}
之前Redux-ORM主张通过附加静态来减少模型特有的还原器 reducer 函数。 如果您要在模型类中定义更新逻辑, 您可以指定一个 reducer 在您的模型上采用静态方法,将动作作为第一个参数,会话特定模型作为第二个参数,整个会话作为第三个参数.
class Book extends Model {
static reducer(action, Book, session) {
switch (action.type) {
case 'CREATE_BOOK':
Book.create(action.payload);
break;
case 'UPDATE_BOOK':
Book.withId(action.payload.id).update(action.payload);
break;
case 'REMOVE_BOOK':
const book = Book.withId(action.payload);
book.delete();
break;
case 'ADD_AUTHOR_TO_BOOK':
Book.withId(action.payload.bookId).authors.add(action.payload.author);
break;
case 'REMOVE_AUTHOR_FROM_BOOK':
Book.withId(action.payload.bookId).authors.remove(action.payload.authorId);
break;
case 'ASSIGN_PUBLISHER':
Book.withId(action.payload.bookId).publisher = action.payload.publisherId;
break;
}
// Return value is ignored.
return undefined;
}
toString() {
return `Book: ${this.name}`;
}
}
要为Redux获得一个调用这些的简化器 reducer 方法:
import { createReducer } from 'redux-orm';
import orm from './orm';
const reducer = createReducer(orm);
createReducer 很简单,所以我就贴上源头.
function createReducer(orm, updater = defaultUpdater) {
return (state, action) => {
const session = orm.session(state || orm.getEmptyState());
updater(session, action);
return session.state;
};
}
function defaultUpdater(session, action) {
session.sessionBoundModels.forEach(modelClass => {
if (typeof modelClass.reducer === 'function') {
modelClass.reducer(action, modelClass, session);
}
});
}
如你所见,它只是即兴地播放了一个新会话,循环了会话中的所有模型,并称为 reducer 方法,如果它存在。 然后它返回所有应用更新的新数据库状态.
用反应
使用被纪念的选择器对状态进行查询. Redux-ORM使用智能记忆:以下选择器访问 Author 财务报告和审定财务报表 AuthorBooks 分支AuthorBooks 是一个从模型字段声明生成的从多到多的分支,只有在这些分支发生变化时,选择者才会重新计算。 进入的分行在第一次运行时解决.
// selectors.js
import { createSelector } from 'redux-orm';
import orm from './orm';
const dbStateSelector = state => state.db;
const authorSelector = createSelector(
orm,
// The first input selector should always select the db-state.
// Behind the scenes, `createSelector` begins a Redux-ORM session
// with the value returned by `dbStateSelector` and passes
// that Session instance as an argument instead.
dbStateSelector,
session => {
return session.Author.all().toModelArray().map(author => {
// Returns a reference to the raw object in the store,
// so it doesn't include any reverse or m2m fields.
const obj = author.ref;
// Object.keys(obj) === ['id', 'name']
return Object.assign({}, obj, {
books: author.books.toRefArray().map(book => book.name),
});
});
}
);
// Will result in something like this when run:
// [
// {
// id: 0,
// name: 'Tommi Kaikkonen',
// books: ['Introduction to Redux-ORM', 'Developing Redux applications'],
// },
// {
// id: 1,
// name: 'John Doe',
// books: ['John Doe: an Autobiography']
// }
// ]
选择器创建 createSelector 可用作任何额外的输入 reselect 选择要使用的选项。 也有很多东西可以使用 redux-thunk: 使整个州与 getState(),将ORM分支传递给选择器,并获取结果。 一个很好的用法案例是将数据序列化为第三方API呼叫的自定义格式.
因为选取者被回忆,所以可以在React中使用纯渲染来获得性能收益.
// components.js
import React from 'react';
import { authorSelector } from './selectors';
import { connect } from 'react-redux';
class App extends React.PureComponent {
render() {
const authors = this.props.authors.map(author => {
return (
<li key={author.id}>
{author.name} has written {author.books.join(', ')}
</li>
);
});
return (
<ul>
{authors}
</ul>
);
}
}
function mapStateToProps(state) {
return {
authors: authorSelector(state),
};
}
export default connect(mapStateToProps)(App);
理解Redux-ORM
手术室?
对 Redux-ORM处理相关数据,结构类似于关系数据库. 此情况下的数据库是一个简单的JavaScript对象数据库.
为什么?
对于简单的应用程序来说,手写还原器是好的,但是当对象类型数量增加,需要保持它们之间的关系时,事物会变得毛发. ImmustableJS在降低你的还原器的复杂度方面有很长的路要走,但Redux-ORM是专用于关系数据的.
可变性
我们从一个位于Redux原子的初始数据库状态开始一个会话,更新某一本书的名称.
第一,新会议:
import { orm } from './models';
const dbState = getState().db; // getState() returns the redux state.
const sess = orm.session(dbState);
会话保持数据库状态的引用. 我们没有 更新数据库状态,因此它仍然等于原始状态 状态.
sess.state === dbState
// true
让我们应用更新.
const book = sess.Book.withId(1)
book.name // 'Refactoring'
book.name = 'Clean Code'
book.name // 'Clean Code'
sess.state === dbState
// false.
应用了更新,由于会话没有变换出原始状态,它创建了一个新的状态并互换了 sess.state 来指向新的一个.
让我们通过ORM再次更新数据库状态.
// Save this reference so we can compare.
const updatedState = sess.state;
book.name = 'Patterns of Enterprise Application Architecture'
sess.state === updatedState
// true. If possible, future updates are applied with mutations. If you want
// to avoid making mutations to a session state, take the session state
// and start a new session with that state.
如果可能的话,未来更新会随着突变而应用. 在这种情况下,数据库已经变了,所以指针不需要改变. 如果您想要避免变换到会话状态, 请选择会话状态, 并以此状态开始新的会话 .
自定义
就像你可以延长 Model你也可以为 QuerySet (关于模型实例集的定制方法). 您也可以自行指定整个数据库的执行(待发文档) .
洞穴
ORM抽象将永远不会像手写还原器那样表现,并且会增加您的项目的构建大小. 如果数据非常简单,没有关系,Redux-ORM可能会过度杀伤. 然而,发展便利的好处相当可观.
API 密码
机构
见ORM的全部文档 这里
证据
const orm = new ORM(); // no arguments needed.
实例方法 :
register(...models: Array<Model>): 将模型类登记到ORM举个例子.session(state: any): 开始一个新的Session与state。 。 。 .
Redux 整合
createReducer(orm: ORM):返回一个可以被插入Redux的还原器函数. 减少器会返回数据库的下个状态, 前提是已提供动作 。 在打电话之前,你需要登记你的模型.createSelector(orm: ORM, [...inputSelectors], selectorFunc): 返回用于selectorFunc。 。 。 .selectorFunc接收session作为第一个论据,然后是来自inputSelectors。请注意,第一个输入选择器必须返回 db 状态才能创建会话。 详细阅读文件全文.
型号
见文件全文 Model 这里。 。 。 .
证据: 不直接即时; 使用类方法 create 财务报告和审定财务报表 upsert 载于下文.
分类方法数字 :
withId(id): 获得 id 的模型实例id。 。 。 .idExists(id): 返回布尔表示一个有 id 的实体id在州内存在.exists(matchObj): 返回布尔表示属性匹配的实体matchObj在州内存在.get(matchObj): 获得基于匹配属性的模型实例matchObj(如果你确定只有一个匹配实例).create(props): 创建一个新的模型实例props如果你不提供身份证明,新的id将会是Math.max(...allOtherIds) + 1。 。 。 .upsert(props): 要么创建新的模型实例props或者,如果一个有相同身份证明的例子已经存在, 更新一个 -换句话说,它是 创建或更新 行为.
你几乎可以接触到所有 查询集实例方法 为方便而从类对象中选出,包括 where 和类似.
实例属性数字 :
ref: 返回直接引用代表商店中模型实例的平版JavaScript对象.
实例方法数字 :
equals(otherModel): 返回布尔表示平等otherModel平等是由对两种模式的属性进行浅析比较所决定的.set(propertyName, value): 更新propertyName改为value. 回归undefined。相当于正常任务.update(mergeObj): 合并mergeObj带有模型实例属性。 回返undefined。 。 。 .delete():删除数据库中该模型实例的记录。 回返undefined。 。 。 .
子分类数字 :
使用 ES6 语法到子类 Model。您宣布的任何实例方法都将在模型实例中提供。 您声明的任何静态方法都会在会话的模型类中可用.
有关领域的申报,无论是确定 fields 属性或声明返回此字段声明的静态获取器:
声明 fields数字 :
class Book extends Model {
static get fields() {
return {
id: attr(),
name: attr(),
author: fk('Author'),
};
}
}
// alternative:
Book.fields = {
id: attr(),
name: attr(),
author: fk('Author'),
}
所有田园 fk, (中文(简体) ), oneToOne 财务报告和审定财务报表 many 仅取一个参数,相关的模型名称。 每个字段都有属性 Model 举个例子 您可以设置相关字段, 包含相关实例的 id 值, 或相关实例本身 .
对于 fk,您可以通过 author.bookSet,在相关名称的位置 ${modelName}Set。同样的 many用于 oneToOne,反向关系可以仅通过该字段被声明的模型名称访问: author.book。 。 。 .
对于 many 字段声明,在模型实例中访问字段将返回 a QuerySet 另有两种方法: add 财务报告和审定财务报表 remove。它们需要1个或更多个参数,这些参数要么是模型实例,要么是其ID。 调用这些方法记录将在下一个状态中反映的更新.
当宣布模范课时,总是记得设置 modelName 属性。 它需要被明确设定, 因为运行您的代码通过 mangler 否则会打破功能 。 这个 modelName 将用来解决所有相关领域.
声明 modelName数字 :
class Book extends Model {
static get modelName() {
return 'Book';
}
}
// alternative:
Book.modelName = 'Book';
声明 options
如果您需要向 Redux- ORM 数据库指定选项, 您可以声明静态 options 带有对象键的模型类属性。 目前您可以指定 id 属性名称 :
// This is the default value.
Book.options = {
idAttribute: 'id',
};
查询
见文件全文 QuerySet 这里。 。 。 .
你可以直接从一个 Model 类,好像它们 是类方法 Model。在这种情况下,这些功能将在包含所有模型实例的查询组上运行.
实例方法数字 :
toRefArray(): 返回以QuerySet作为平版JavaScript对象的阵列. 物体是直接引用商店的.toModelArray(): 返回以QuerySet作为数组Model实例对象.count(): 返回编号Model实例QuerySet。 。 。 .exists(): 回转true如果实体数超过 0,则另false。 。 。 .filter(filterArg): 返回一个新的QuerySet代表通过过滤器的父查询程序的记录。 对于filterArg,您可以通过Redux-ORM试图与实体匹配的对象,也可以通过返回的函数true如果你想在新的里面拥有它QuerySet, (中文(简体) ),false如果不是的话。 该函数收到一个模型实例作为它唯一的论据.exclude返回一个新的QuerySet重置在母目录中不通过过滤器的实体 。 与filter,您可以通过一个对象进行匹配(所有匹配的实体将不会在新的QuerySet)或函数。 该函数收到一个模型实例作为它唯一的论据.all()返回一个新的QuerySet与同样的实体.at(index)返回一个Model实例index输入QuerySet。 。 。 .first()返回一个Model实例0指数.last()返回一个Model实例querySet.count() - 1指数.delete()删除以QuerySet。 。 。 .update(mergeObj)更新以QuerySet基于所提供对象。 该对象将与每个实体合并.
会议
见会议的全部文件 这里
证据:你不需要自己做到这一点。 使用 orm.session。 。 。 .
实例属性数字 :
state: 当前数据库状态.
此外, 您可以访问计划中所有注册的模型, 作为此实例的属性进行查询和更新 。 举例来说,给定一个与 Book 财务报告和审定财务报表 Author 型号,
const session = orm.session(state);
session.Book // Model class: Book
session.Author // Model class: Author
session.Book.create({id: 5, name: 'Refactoring', release_year: 1999});
更改日志
V1.0.0 之前的细微更改可以包括断开更改.
见 CHANGELOG.md。 。 。 .
许可证
麻省理工学院. 见 LICENSE。 。 。 .