如何使用带有 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?

我正在使用 PostgresqlKnex.js 和 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 中的图表。