Defining model relations in Loopback for Postgres

995 views
Skip to first unread message

Jennifer Louthan

unread,
Jul 2, 2014, 6:23:25 PM7/2/14
to loopb...@googlegroups.com
Hi. I have been following the docs as closely as I can, and still cannot figure out the best way to define model relations in Loopback. I'm using the Postgresql connector. In my example, I want a one-to-many relationship between users and inventories.

In my most recent attempt, I defined the relations in the models/<modelname>.json files:

{"name": "user",
    "options": {
      "base": "User",
      "relations": {
        "accessTokens": {
          "model": "accessToken",
          "type": "hasMany",
          "foreignKey": "userId"
        },
        "inventories": {
          "model": "Inventory",
          "type": "hasMany",
          "foreignKey": "userId"
        }
      },
      ...
      ...

{"name": "Inventory",
"options": {
      "idInjection": false,
      "postgresql": {
        "schema": "public",
        "table": "inventory"
      },
      "relations": {
        "user": {
          "model": "user",
          "type": "belongsTo",
          "foreignKey": "userId"
        }
      }
    }
    ...
    ...

Running autoupdate() or automigrate() after these additions didn't do anything, so I ran some JavaScript:

Inventory.belongsTo('user', {model: User, foreignKey: 'userId'});
User.hasMany('inventories', {model: Inventory, foreignKey: 'userId'});

User.create({firstName: 'John', email: 'f...@bar.com', password: 'password'}, function(err, user){
user.inventories.create({id: 'id1', available: 31, locationId: 'blah'}, function(err, inv){
console.log('inventory is ' + inv);
}
});


I ran it and got the error:

error: column "userid" of relation "inventory" does not exist


I was expecting the column to be created automatically, but I went ahead and added a userId column to my inventory table. Then the above code ran successfully and created a new inventory row with the column "userId" populated correctly with the id of the user. Up until this point I was feeling good.

However, now when I try to get the inventories for a user (GET to /users?filter[include][inventories]) I get an error message

"Relation \"inventories\" is not defined for user model"


and then I try getting from /api/users/1/inventories and get this error

Shared class \"user\" has no method handling GET /1/inventories




I want to create proper relations and expose their corresponding API methods like I see in the docs http://docs.strongloop.com/display/LB/Creating+model+relations. What am I doing wrong?

Thanks for the help!

Raymond Feng

unread,
Jul 2, 2014, 6:42:38 PM7/2/14
to Jennifer Louthan, loopb...@googlegroups.com
What LoopBack version do you use? 1.9.x or 2.x?

In 1.9.x, you need to define all models in models.json.

Please also note that foreign key properties can be added related models. Running autoupdate too early will miss such properties.

Thanks,

---
Raymond Feng
Co-Founder and Architect @ StrongLoop, Inc.

StrongLoop makes it easy to develop APIs in Node, plus get DevOps capabilities like monitoring, debugging and clustering.

--
You received this message because you are subscribed to the Google Groups "LoopbackJS" group.
To unsubscribe from this group and stop receiving emails from it, send an email to loopbackjs+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jennifer Louthan

unread,
Jul 2, 2014, 6:55:04 PM7/2/14
to loopb...@googlegroups.com, jbllo...@gmail.com
Thanks for getting back to me so quickly.

I am using loopback version 1.9.1. All other properties and options of my models are being found and run correctly using separate files in the models/ directory. For example, I define inventory in models/inventory.json and have a models/inventory.js file that contains

var config = require('./inventory.json');
var app = require('../app');
var db = app.dataSources.db;

var Inventory = module.exports = db.createModel(
  'inventory',
  config.properties,
  config.options
);

app.model(Inventory);


Everything besides relations is working fine (creating tables, adding properties, adding access control, etc.) with my app structured this way.

Are relations the only feature that will not work if I separate all my models like this? 


And I'm not sure I understand the second part of your response. I am running automigrate() for user and inventory immediately after adding the relations in JavaScript. It looks exactly like this:

Inventory.belongsTo('user', {model: User, foreignKey: 'userId'});
User.hasMany('inventories', {model: Inventory, foreignKey: 'userId'});

dataSource.automigrate('inventory', function(err){

});

dataSource.automigrate('user', function(err){
User.create({firstName: 'John', email: 'f...@bar.com', password: 'password'}, function(err, user){
user.inventories.create({id: 'id2', available: 31, locationId: 'blarg'}, function(err, inv){
console.log('inventory is ' + inv);
});
});
});

And the inventory in the callback is created successfully. Are you saying this is the wrong place to automigrate()?

Thanks!

Raymond Feng

unread,
Jul 2, 2014, 7:01:27 PM7/2/14
to Jennifer Louthan, loopb...@googlegroups.com
I see what you have now: a JS file in models folder that requires the json definitions and calls createModel() to create models by code. That should work. The JS files under models will be loaded automatically.

I think you have a wrong syntax for inclusion:

GET to /users?filter[include][inventories])

Should be:

GET to /users?filter[include]=inventories

or 

GET to /users?filter={“include”: “inventories”}
 
Thanks,

---
Raymond Feng
Co-Founder and Architect @ StrongLoop, Inc.

StrongLoop makes it easy to develop APIs in Node, plus get DevOps capabilities like monitoring, debugging and clustering.

Jennifer Louthan

unread,
Jul 2, 2014, 7:24:18 PM7/2/14
to loopb...@googlegroups.com, jbllo...@gmail.com
GET to /users?filter[include]=inventories and GET to GET to /users?filter={“include”: “inventories”} both give me the same error as before:

"Relation \"inventories\" is not defined for user model"


It feels like I'm missing a step needed to expose the methods the relation gives. Any ideas? 

Raymond Feng

unread,
Jul 2, 2014, 7:32:09 PM7/2/14
to Jennifer Louthan, loopb...@googlegroups.com
When you create the inventory model, it’s named ‘inventory’:

var Inventory = module.exports = db.createModel(
  'inventory',
  config.properties,
  config.options
);

To reference it in user relations, you should use the same name:

"inventories": {
          "model": "Inventory”,  ==> should be “inventory"
          "type": "hasMany",
          "foreignKey": "userId"
        }
Thanks,

---
Raymond Feng
Co-Founder and Architect @ StrongLoop, Inc.

StrongLoop makes it easy to develop APIs in Node, plus get DevOps capabilities like monitoring, debugging and clustering.

Jennifer Louthan

unread,
Jul 3, 2014, 1:17:25 PM7/3/14
to loopb...@googlegroups.com, jbllo...@gmail.com
That did the trick. Thanks so much, Raymond!!
Reply all
Reply to author
Forward
0 new messages