Problem with ORM relations


#1

Here is my model setup

class SyndicateMembership extends Model {
  syndicate(){
    return this.belongsTo('App/Models/Syndicate')
  }
  user(){
    return this.belongsTo('App/Models/User')
  }
  membershipRate() {
    return this.belongsTo('App/Models/SyndicateMembershipRate')
  }
}

The result is

[
    {
        "id": 1,
        "user_id": 1,
        "rate_id": 2,
        "syndicate_id": 1,
        "next_payment_date": "2018-12-15T23:58:07.000Z",
        "created_at": "2018-12-15 23:58:08",
        "updated_at": "2018-12-15 23:58:09",
        "syndicate": {
            "id": 1,
            "syndicate_name": "SSS",
            "user_id": 1,
            "is_active": 1,
            "created_at": "2018-12-15 23:57:35",
            "updated_at": "2018-12-15 23:57:36"
        },
        "membershipRate": null
    }
]

As you can see membershipRate is null.

If I use knex to build a query manually with joins it works fine.

This code

 const memberships = await db.table({sm:'syndicate_memberships'})
    .where('sm.user_id', authUser.id)
    .joinRaw('left join syndicates s on s.id = sm.syndicate_id')
    .joinRaw('left join syndicate_membership_rates mr on mr.id = sm.rate_id')

produces

[
    {
        "id": 2,
        "user_id": 1,
        "rate_id": 2,
        "syndicate_id": 1,
        "next_payment_date": "2018-12-15T23:58:07.000Z",
        "created_at": "2018-12-16T00:46:51.000Z",
        "updated_at": "2018-12-16T00:46:51.000Z",
        "syndicate_name": "SSS",
        "is_active": 1,
        "rate_name": "Weekly",
        "interval_price": 15,
        "interval_uom": "month",
        "interval_quantity": 1
    }
]

Any idea why the ORM gives me null for that one relationship?


#2

Best way is to turn on database debugging and see the queries being generated by the ORM


#3

This was also mentioned in discord, but… because the foreign key differs from what lucid expects, and you’re not telling it otherwise, it will never match. Lucid assumes the foreign key will be syndicate_membership_rate_id not rate_id as your schema has it defined. As such, you need to either specify the keys when defining the relationships in your model, or override the default foreignKey method in your SyndicateMembershipRate model to return rate_id instead of the default.


#4

Thank you I will look at that.

Do the docs discuss overriding the default foreignKey?


#5

No, but if you look at the model class in the core code node_modules/@adonisjs/lucid/src/Lucid/Model/index.js you can see the default foreignKey method you’d want to override:

/**
   * The foreign key for the model. It is generated
   * by converting model name to lowercase and then
   * snake case and appending `_id` to it.
   *
   * @attribute foreignKey
   *
   * @return {String}
   *
   * @example
   * 
   * User - user_id
   * Post - post_id
   * 
   */
  static get foreignKey () {
    return util.makeForeignKey(this.name)
  }

While detail about how to override this is not in the documentation, information about how the default is determined is in the documentation… :wink:

foreignKey
Defaults to tableName_primaryKey of the current model. The singular form of the table name is used.

Anyway, in your case you’d want to override the foreignKey method thusly:

static get foreignKey() {
  return 'rate_id'
}

Or, simply define the keys as part of your relationship definitions… so instead of:

membershipRate() {
  return this.belongsTo('App/Models/SyndicateMembershipRate')
}

this:

membershipRate() {
  return this.belongsTo('App/Models/SyndicateMembershipRate',
                        'rate_id',
                        'id')
}