Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Navigational properties are not allowed in model data #4354

Closed
dexdinesh opened this issue Jan 1, 2020 · 8 comments
Closed

Navigational properties are not allowed in model data #4354

dexdinesh opened this issue Jan 1, 2020 · 8 comments

Comments

@dexdinesh
Copy link

dexdinesh commented Jan 1, 2020

As I had already used relations in loop back4 project now when I tried to perform any crud operation it returns
500 Error: Navigational properties are not allowed in model data (model "UserDevice" property "user_id")

My models are as follows:

User Model:

import { Entity, model, property, hasMany } from '@loopback/repository';

@model({
  settings: {
    strictObjectIDCoercion: true,
    strict: false,
  }
})
export class Users extends Entity {
  @property({
    type: 'string',
    id: true,
    generated: true,
    mongodb: { dataType: 'ObjectID' },
  })
  id?: string;

  @property({
    type: 'string',
    required: true,
  })
  full_name: string;

  @property({
    type: 'string',
    required: true,
  })
  email: string;

  @property({
    type: 'string',
    required: false,
    default: null,
  })
  password: string;

  @property({
    type: 'string',
    required: false,
    default: null,
  })
  company: string;

  @property({
    type: 'string',
    required: false,
    default: null,
  })
  biography: string;

  @property({
    type: 'string',
    required: false,
    default: null,
  })
  job_position: string;

  @property({
    type: 'string',
    required: true,
  })
  uid: string;

  @property({
    type: 'string',
    required: true,
  })
  year: string;

  @property({
    type: 'string',
    required: false,
    default: null,
  })
  profile_pic: string;

  @property({
    type: 'string',
    required: false,
    default: null,
  })
  profile_thumbnail: string;

  @property({
    type: 'string',
    required: false,
    default: null,
  })
  otp: string;

  @property({
    type: 'boolean',
    default: false,
  })
  login_status: boolean;

  @property({
    type: 'string',
    required: false,
    default: null,
  })
  tmp_password: string;

  @property({
    type: 'boolean',
    required: false,
    default: false,
  })
  receive_post_notification: boolean;

  @property({
    type: 'boolean',
    required: false,
    default: false,
  })
  receive_new_follower_notification: boolean;

  @property({
    type: 'string',
    default: null,
  })
  created_at: string;

  @property({
    type: 'string',
    default: null,
  })
  updated_at?: string;

  constructor(data?: Partial<Users>) {
    super(data);
  }
}

export interface UsersRelations {
  // describe navigational properties here
}

export type UsersWithRelations = Users & UsersRelations;

User Device Model:

import { Entity, model, property, belongsTo } from '@loopback/repository';
import { timeZone } from '../Helpers/TimeZone';
import { Users } from './users.model';

@model({
  settings: {
    strictObjectIDCoercion: true,
    strict: false,
  }
})
export class UserDevice extends Entity {
  @property({
    type: 'string',
    id: true,
    generated: true,
  })
  id?: string;
  @property({
    type: 'string',
    required: true,
  })
  device_id: string;

  @property({
    type: 'string',
  })
  device_name?: string;

  @property({
    type: 'number',
  })
  device_type: number;

  @property({
    type: 'string',
  })
  app_version: string;

  @property({
    type: 'string',
  })
  fcm_token: string;

  @property({
    type: 'number',
  })
  is_active: number;

  @property({
    type: 'string',
    default: timeZone(),
  })
  created_at?: string;

  @property({
    type: 'string',
    default: null
  })
  updated_at?: string;

  @belongsTo(() => Users)
  user_id: string;

  constructor(data?: Partial<UserDevice>) {
    super(data);
  }
}

export interface UserDeviceRelations {
  // describe navigational properties here
}

export type UserDeviceWithRelations = UserDevice & UserDeviceRelations;

Error I always receive on Create and Update operation:

Unhandled error in POST /users/login: 500 Error: Navigational properties are not allowed in model data (model "UserDevice" property "user_id")
at UserDeviceRepository.ensurePersistable (/home/abc/Downloads/mohit/node-apps/rosenberg/alumniapp-api2/node_modules/@loopback/repository/src/repositories/legacy-juggler-bridge.ts:585:15)
at UserDeviceRepository.entityToData (/home/abc/Downloads/mohit/node-apps/rosenberg/alumniapp-api2/node_modules/@loopback/repository/src/repositories/legacy-juggler-bridge.ts:554:17)
at UserDeviceRepository.updateAll (/home/abc/Downloads/mohit/node-apps/rosenberg/alumniapp-api2/node_modules/@loopback/repository/src/repositories/legacy-juggler-bridge.ts:434:38)
at UsersController.login (/home/abc/Downloads/mohit/node-apps/rosenberg/alumniapp-api2/src/controllers/users.controller.ts:169:41)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at MySequence.handle (/home/abc/Downloads/mohit/node-apps/rosenberg/alumniapp-api2/src/sequence.ts:44:22)
at HttpHandler._handleRequest (/home/abc/Downloads/mohit/node-apps/rosenberg/alumniapp-api2/node_modules/@loopback/rest/src/http-handler.ts:78:5)

@LonguCodes
Copy link

LonguCodes commented Jan 3, 2020

This is desired behaviour, discussed in #3439.
I have no idea why they decided on that and I have another question. How should we update relation properties? @bajtos

@achrinza
Copy link
Member

This is an intended feature to prevent errors such as invalid foreign keys from being keyed into the database. AFAIK, updating navigational properties requires sending another HTTP request directly to the controller that you'd like to modify the navigational property of.

Hence,

  1. Send a request to the "parent" model controller with the registered inclusionResolver.
  2. Send a request to the "child" model controller that you'd like to modify the navigational property by filtering by the child model's ID (and potentially the parent model's ID if the child model is a weak entity).

See:

@ludohenin
Copy link
Contributor

ludohenin commented Apr 3, 2020

@dexdinesh this is because your not using the buildin default property format for your FK in

export class UserDevice extends Entity {
  // ...
  @belongsTo(() => Users)
  user_id: string; // <-- expected to be userId
}

otherwise you have to configure your relation

export class UserDevice extends Entity {
  // ...
  @belongsTo(() => Users, {keyTo: 'id', keyFrom: 'user_id'})
  user_id: string;
}

But I can't get this working either.
@bajtos Has the relation to configured in the repository (accessor) everytime a relation is declared in a model ?
I'm getting the same error since I updated LB dependencies
"@loopback/repository": "^1.14.0" to "@loopback/repository": "^1.19.1"
also updated to lastest release with any more success

@ludohenin
Copy link
Contributor

ludohenin commented Apr 3, 2020

when I think I'm solving stuffs, I'm getting this error

"statusCode":500,
"name":"TypeError",
"message":"Cannot read property 'type' of undefined","stack":"TypeError: Cannot read property 'type' of undefined\n    at Object.resolveHasManyMetadata (/home/ludohen/github/ta-api-core-2/node_modules/@loopback/repository/dist/relations/has-many/has-many.helpers.js:22:22) [...]

EDIT:

found the reason for that, when it's not following the naming convention (using the id), all options have to be set (keyTo, KeyFrom and name)

@fabianorodrigo
Copy link

This is an intended feature to prevent errors such as invalid foreign keys from being keyed into the database. AFAIK, updating navigational properties requires sending another HTTP request directly to the controller that you'd like to modify the navigational property of.

Hence,

  1. Send a request to the "parent" model controller with the registered inclusionResolver.
  2. Send a request to the "child" model controller that you'd like to modify the navigational property by filtering by the child model's ID (and potentially the parent model's ID if the child model is a weak entity).

See:

@achrinza, what is the recommendation if I need a atomic transaciton? How is the community handling this use case?

@achrinza
Copy link
Member

achrinza commented Apr 13, 2020

@achrinza, what is the recommendation if I need a atomic transaciton? How is the community handling this use case?

@fabianorodrigo Common LoopBack 4 SQL connectors support database-level transaction isolation. Unfortunately I don’t have a concrete example to share right now. Hope this helps!

@fabianorodrigo
Copy link

@achrinza, what is the recommendation if I need a atomic transaciton? How is the community handling this use case?

@fabianorodrigo Common LoopBack 4 SQL connectors support database-level transaction isolation. Hope this helps!

Sorry @achrinza, but when you suggest two requests as a workaround to the problem "Navigational properties are not allowed in model", one to the parent model and another to the child, this requests are made by the client side, right? The transactions supported by connectors are just applicable if I received a request with all the data (parent and child) and then open transaction, insert in the parent, insert in the child and commit transaction.
My doubt is exactly that: How can I preserv atomicity with different requests? How is the community dealing with this chalenge?

@achrinza
Copy link
Member

achrinza commented Sep 20, 2020

Sorry for the late reply; this issue slipped through my radar.

The atomicity can be handled at the server side by making 2 repository calls within a single request. This does mean a bit of extra logic in the controller, but this ensures atomicity.

The issue of implementing navigational properties is that LoopBack 4 does not have first-class support for database-level foreign key constraints. This enables cross-database relations, though requires LoopBack 4 to re-implement certain database logic. Some connectors such as PostgreSQL support foreign key definitions in @model, but not all.

@bajtos bajtos closed this as completed Mar 11, 2021
@loopbackio loopbackio locked and limited conversation to collaborators Mar 11, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants