Loopback: Local Model hasOne remote model uses incorrect model id

Created on 14 Jan 2015  路  12Comments  路  Source: strongloop/loopback

I have a model Mat with HasOne relation Origin. I have local model LocalMat in the client.

Calling localMat.origin() causes Unknown "origin" id "undefined".

It seams that https://github.com/strongloop/loopback-datasource-juggler/blob/master/lib/relation-definition.js#L1432 is useing the model name 'localMat' to create the foreign key 'localMatId' which is incorrect.

This does not seem to affect hasMany, belongsTo relationships.

bug major team-apex

Most helpful comment

Any news on this one ? I think I have the same problem...
I have 2 models with a hasone relation :

  • an appointment model
  • an custom field model

In appointment.json :

...
"relations": {
    "CustomFields": {
      "type": "hasOne",
      "model": "CustomField",
      "foreignKey": "id_rdv"
    }
  },
...

When I try GET /Appointments/{id}/CustomFields here's the response :

{
  "error": {
    "name": "Error",
    "status": 404,
    "message": "Unknown \"SubscriberCustomField\" id \"undefined\".",
    "statusCode": 404,
    "code": "MODEL_NOT_FOUND",
    "stack": "Error: Unknown \"SubscriberCustomField\" id \"undefined\".\n    at convertNullToNotFoundError (/Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/lib/model.js:456:17)\n    at invokeRestAfter (/Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/strong-remoting/lib/rest-adapter.js:453:25)\n    at /Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/async/lib/async.js:607:21\n    at /Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/async/lib/async.js:246:17\n    at iterate (/Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/async/lib/async.js:146:13)\n    at /Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/async/lib/async.js:157:25\n    at /Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/async/lib/async.js:248:21\n    at /Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/async/lib/async.js:612:34\n    at /Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/strong-remoting/lib/remote-objects.js:685:7\n    at execStack (/Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/strong-remoting/lib/remote-objects.js:488:7)"
  }
}

I didn't understand how to "set the foreign key in your model-config.json." Tried it but didn't make any difference. Could you please help I've hit a wall there...
Thanks in advance.

All 12 comments

This sounds like a bug. I think we need to inherit the foreign key from the parent model. /cc @raymondfeng

@kraman have you run into this?

@BerkeleyTrue a potential workaround is to set the foreign key in your model-config.json.

@ritch Haven't run into this situation yet but I probably will soon or later.

@ritch Yes, this is what I have been doing in the mean time.

Any news on this one ? I think I have the same problem...
I have 2 models with a hasone relation :

  • an appointment model
  • an custom field model

In appointment.json :

...
"relations": {
    "CustomFields": {
      "type": "hasOne",
      "model": "CustomField",
      "foreignKey": "id_rdv"
    }
  },
...

When I try GET /Appointments/{id}/CustomFields here's the response :

{
  "error": {
    "name": "Error",
    "status": 404,
    "message": "Unknown \"SubscriberCustomField\" id \"undefined\".",
    "statusCode": 404,
    "code": "MODEL_NOT_FOUND",
    "stack": "Error: Unknown \"SubscriberCustomField\" id \"undefined\".\n    at convertNullToNotFoundError (/Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/lib/model.js:456:17)\n    at invokeRestAfter (/Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/strong-remoting/lib/rest-adapter.js:453:25)\n    at /Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/async/lib/async.js:607:21\n    at /Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/async/lib/async.js:246:17\n    at iterate (/Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/async/lib/async.js:146:13)\n    at /Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/async/lib/async.js:157:25\n    at /Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/async/lib/async.js:248:21\n    at /Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/async/lib/async.js:612:34\n    at /Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/strong-remoting/lib/remote-objects.js:685:7\n    at execStack (/Users/airzebeth/Documents/Kaamelote/Work/wzagendasynchro/node_modules/loopback/node_modules/strong-remoting/lib/remote-objects.js:488:7)"
  }
}

I didn't understand how to "set the foreign key in your model-config.json." Tried it but didn't make any difference. Could you please help I've hit a wall there...
Thanks in advance.

+1

+1

+1

fixed this on my fork yesterday

replace the includeOneToOne function in loopback-datasource-juggler with https://gist.github.com/BoLaMN/6590e8849d23f6373a177d5ccc42ef23

replaces the hard coded

relation.keyFrom

definitions with

var lookupKey = (relation.type === 'belongsTo') ? relation.keyFrom : relation.keyTo;

cheers

@BoLaMN Thanks for providing details on how you patched this. I found an issue with your patch though.

You changed the object being used to pull the targetId in the linkOneToMany function. And that does not work correctly when the subquery pulls in more than one result.

The obj should be target.

      function linkOneToMany(target, next) {
        var targetId = obj[lookupKey];
        var objList = objTargetIdMap[targetId.toString()];
        async.each(objList, function(obj, next) {
          if (!obj) return next();
          obj.__cachedRelations[relationName] = target;
          processTargetObj(obj, next);
        }, next);
      }

Should be:

      function linkOneToMany(target, next) {
        var targetId = target[lookupKey];
        var objList = objTargetIdMap[targetId.toString()];
        async.each(objList, function(obj, next) {
          if (!obj) return next();
          obj.__cachedRelations[relationName] = target;
          processTargetObj(obj, next);
        }, next);
      }

Hey @humblepie

You are correct, there was an issue with my fix i forgot to update the details here to include it, i found a few other places which needed to be patched aswell

i ripped out the required changes into a diff patch for you.

https://gist.github.com/BoLaMN/e2fb1473cae8081ac255fdd0cbe927ba

includes a issue with including embeds to do a sub include on embedded models too

Cheers

found one more in relation-definition.js too for queries with the 'related' function.

eg

inst = new User()

inst.profile(function(err, profile) {
  console.log(err, profile);
});

have added the diff patch too https://gist.github.com/BoLaMN/e2fb1473cae8081ac255fdd0cbe927ba

Cheers

In the common model defining a foreignKey property in the relation resolves the issue of a localInstance using the wrong key when trying to query for data. (As per @ritch's comment)

// mat.json
...
"relations": {
    "origin": {
      "type": "hasOne",
      "model": "Origin",
      "foreignKey": "originId"
    }
  },
...

I also looked into implementing a code fix such as the one suggested in the patch but swapping the pk and fk in the relation definition is a breaking change and would require a major release.

An alternate solution I considered was to introduce a flag in the relation definition such as generateIdFromParent which if set would generate the id based on Parent Model name for extended models in the absence of a foreign key. Since there is a documented workaround already this seems unnecessary.

As a result of the above investigation I am closing this issue. If there are concerns still regarding this please feel free to comment / open another issue and mention me directly.

Was this page helpful?
0 / 5 - 0 ratings