如何使用带有 HasOneRelation 的 Postgresql、Knex.js 和 Objection.js 将默认值插入到具有关系的外部对象?
How to insert default values to foreign object with relation using Postgresql, Knex.js and Objection.js with HasOneRelation?
我正在使用 Postgresql
、Knex.j
s 和 Objection.js
设置简单的 API。我用 "location" 属性 创建了用户模型。这个"location"属性又是一个table。我如何使用 'city' 和 'country' in 'location' 属性?
中的默认值将该用户插入到数据库中
我已经尝试在模型本身中使用 'static get jsonSchema
' 并在突变中使用 'allowInsert
' 方法但是当我获取创建该用户时 'location' 仍然 'null'.
所以,假设我们有 users_table:
的迁移
exports.up = knex =>
knex.schema.createTable('users', table => {
table.increments('id').primary();
table
.string('email')
.unique()
.notNullable();
table.string('firstName').notNullable();
table.string('lastName').notNullable();
table.string('password').notNullable();
});
exports.down = knex => knex.schema.dropTable('users');
我们有 location_table:
exports.up = knex =>
knex.schema.createTable('locations', table => {
table.increments('id').primary();
table.string('country').defaultTo('USA');
table.string('city').defaultTo('San Francisco');
table
.integer('user_id')
.references('id')
.inTable('users')
.onUpdate('CASCADE')
.onDelete('CASCADE');
});
exports.down = knex => knex.schema.dropTable('locations');
这里是 objection.js 的用户模型:
export default class User extends Model {
static get tableName() {
return 'users';
}
// wrong probably
static get jsonSchema() {
return {
type: 'object',
properties: {
location: {
type: 'object',
properties: {
city: {
type: 'string',
default: 'Los Angeles',
},
country: {
type: 'string',
default: 'USA',
},
},
},
},
};
}
fullName() {
return `${this.firstName} ${this.lastName}`;
}
static get relationMappings() {
return {
location: {
relation: Model.HasOneRelation,
modelClass: Location,
join: {
from: 'users.id',
to: 'locations.user_id',
},
},
};
}
}
和位置模型:
export default class Location extends Model {
static get tableName() {
return 'locations';
}
static get relationMappings() {
return {
user: {
relation: Model.BelongsToOneRelation,
modelClass: `${__dirname}/User`,
join: {
from: 'locations.user_id',
to: 'users.id',
},
},
};
}
}
我创建新用户时的突变:
// ...
const payload = {
email,
firstName,
lastName,
password: hash,
};
const newUser = await User.query()
.allowInsert('[user, location]')
.insertAndFetch(payload);
// ...
最后查询:
// ...
User.query()
.eager('location')
.findOne({ email });
// ...
根据用户的查询,我希望看到具有默认值的定位属性的对象。示例:
{
email: 'jacklondon@gmail.com',
firstName: 'Jack',
fullName: 'Jack London',
id: '1',
lastName: 'London',
location: {
city: 'San Francisco',
country: 'USA',
},
userName: 'jacklondon1',
__typename: 'User',
}
这么简单的操作,我错在哪里了?
一对一解决方案
我认为部分问题是您的允许插入包含 user
对象。你不应该在允许插入中包含 user
因为它是隐含的,因为你在 User
模型(example). The other issue you had was that you were trying to use insertAndFetch
method. insertAndFetch
cannot be used when inserting a graph. You need to use the insertGraph
method to insert a graph (docs). Since you are using Postgres, you can chain the returning(*)
method and it will return the result without additional queries (example). Finally, since you're asking for a one-to-one relation, you have to specify a city and country every time. Objection will not know it needs to create a new row without specifying it (even if you have configured the database to have default values). The way I accomplished this for you was to use default parameters.
const createNewuser = async (email, firstName, lastName, hash, city = 'Los Angeles', country = 'USA') => {
const newUser = await User
.query()
.insertGraph({
email,
firstName,
lastName,
password: hash,
location: {
city: city,
country: country
}
})
.returning('*');
return newUser;
}
需要思考的其他想法
我不确定为什么用户和位置之间存在一对一关系。为什么不让城市、州和国家成为用户 table 的一部分,因为它已经是一对一的了?
但是,我认为您真正想要的是用户和位置之间的一对多关系。一个位置有多个用户。这将通过减少数据库中的重复数据量使您进入 3rd normal form,因为您不会在同一位置为每个用户复制 city/country。
如果您只是学习异议,我建议您阅读 documentation 中的图表。
我正在使用 Postgresql
、Knex.j
s 和 Objection.js
设置简单的 API。我用 "location" 属性 创建了用户模型。这个"location"属性又是一个table。我如何使用 'city' 和 'country' in 'location' 属性?
我已经尝试在模型本身中使用 'static get jsonSchema
' 并在突变中使用 'allowInsert
' 方法但是当我获取创建该用户时 'location' 仍然 'null'.
所以,假设我们有 users_table:
的迁移exports.up = knex =>
knex.schema.createTable('users', table => {
table.increments('id').primary();
table
.string('email')
.unique()
.notNullable();
table.string('firstName').notNullable();
table.string('lastName').notNullable();
table.string('password').notNullable();
});
exports.down = knex => knex.schema.dropTable('users');
我们有 location_table:
exports.up = knex =>
knex.schema.createTable('locations', table => {
table.increments('id').primary();
table.string('country').defaultTo('USA');
table.string('city').defaultTo('San Francisco');
table
.integer('user_id')
.references('id')
.inTable('users')
.onUpdate('CASCADE')
.onDelete('CASCADE');
});
exports.down = knex => knex.schema.dropTable('locations');
这里是 objection.js 的用户模型:
export default class User extends Model {
static get tableName() {
return 'users';
}
// wrong probably
static get jsonSchema() {
return {
type: 'object',
properties: {
location: {
type: 'object',
properties: {
city: {
type: 'string',
default: 'Los Angeles',
},
country: {
type: 'string',
default: 'USA',
},
},
},
},
};
}
fullName() {
return `${this.firstName} ${this.lastName}`;
}
static get relationMappings() {
return {
location: {
relation: Model.HasOneRelation,
modelClass: Location,
join: {
from: 'users.id',
to: 'locations.user_id',
},
},
};
}
}
和位置模型:
export default class Location extends Model {
static get tableName() {
return 'locations';
}
static get relationMappings() {
return {
user: {
relation: Model.BelongsToOneRelation,
modelClass: `${__dirname}/User`,
join: {
from: 'locations.user_id',
to: 'users.id',
},
},
};
}
}
我创建新用户时的突变:
// ...
const payload = {
email,
firstName,
lastName,
password: hash,
};
const newUser = await User.query()
.allowInsert('[user, location]')
.insertAndFetch(payload);
// ...
最后查询:
// ...
User.query()
.eager('location')
.findOne({ email });
// ...
根据用户的查询,我希望看到具有默认值的定位属性的对象。示例:
{
email: 'jacklondon@gmail.com',
firstName: 'Jack',
fullName: 'Jack London',
id: '1',
lastName: 'London',
location: {
city: 'San Francisco',
country: 'USA',
},
userName: 'jacklondon1',
__typename: 'User',
}
这么简单的操作,我错在哪里了?
一对一解决方案
我认为部分问题是您的允许插入包含 user
对象。你不应该在允许插入中包含 user
因为它是隐含的,因为你在 User
模型(example). The other issue you had was that you were trying to use insertAndFetch
method. insertAndFetch
cannot be used when inserting a graph. You need to use the insertGraph
method to insert a graph (docs). Since you are using Postgres, you can chain the returning(*)
method and it will return the result without additional queries (example). Finally, since you're asking for a one-to-one relation, you have to specify a city and country every time. Objection will not know it needs to create a new row without specifying it (even if you have configured the database to have default values). The way I accomplished this for you was to use default parameters.
const createNewuser = async (email, firstName, lastName, hash, city = 'Los Angeles', country = 'USA') => {
const newUser = await User
.query()
.insertGraph({
email,
firstName,
lastName,
password: hash,
location: {
city: city,
country: country
}
})
.returning('*');
return newUser;
}
需要思考的其他想法
我不确定为什么用户和位置之间存在一对一关系。为什么不让城市、州和国家成为用户 table 的一部分,因为它已经是一对一的了?
但是,我认为您真正想要的是用户和位置之间的一对多关系。一个位置有多个用户。这将通过减少数据库中的重复数据量使您进入 3rd normal form,因为您不会在同一位置为每个用户复制 city/country。
如果您只是学习异议,我建议您阅读 documentation 中的图表。