Reputation: 2065
I am using loopback 4 relations. I have three models product, order, and orderProducts. The relationships are as follows
But when I try post request on OrderProducts it is giving this error :
500 Error: Navigational properties are not allowed in model data (model "OrderProducts" property "product_id")
Here is my code. Thank you in advance
order.model.ts
import { Entity, model, property, hasMany} from '@loopback/repository';
import {OrderProducts} from './order-products.model';
@model({ settings: { strict: false } })
export class Orders extends Entity {
@property({
type: 'number',
id: true,
generated: true,
})
order_id?: number;
@property({
type: 'number',
required: true,
})
total: number;
@property({
type: 'string',
})
get: string;
@hasMany(() => OrderProducts, {keyTo: 'order_id'})
orderProducts: OrderProducts[];
// Define well-known properties here
// Indexer property to allow additional data
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[prop: string]: any;
constructor(data?: Partial<Orders>) {
super(data);
}
}
export interface OrdersRelations {
// describe navigational properties here
}
export type OrdersWithRelations = Orders & OrdersRelations;
product.model.ts
import { Entity, model, property, hasMany } from '@loopback/repository';
import { OrderProducts } from './order-products.model';
@model({ settings: { strict: false } })
export class Product extends Entity {
@property({
type: 'number',
id: true,
generated: true,
})
product_id?: number;
@property({
type: 'string',
required: true,
})
name: string;
@property({
type: 'string',
})
desc?: string;
@hasMany(() => OrderProducts, { keyTo: 'product_id' })
orderProducts: OrderProducts[];
// Define well-known properties here
// Indexer property to allow additional data
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[prop: string]: any;
constructor(data?: Partial<Product>) {
super(data);
}
}
export interface ProductRelations {
// describe navigational properties here
}
export type ProductWithRelations = Product & ProductRelations;
order-product.model.ts
import {Entity, model, property, belongsTo} from '@loopback/repository';
import {Product} from './product.model';
@model({settings: {strict: false}})
export class OrderProducts extends Entity {
@property({
type: 'number',
id: true,
generated: true,
})
o_id?: number;
@property({
type: 'number',
required: true,
})
quantity: number;
@property({
type: 'number',
})
order_id?: number;
@belongsTo(() => Product)
product_id: number;
// Define well-known properties here
// Indexer property to allow additional data
// eslint-disable-next-line @typescript-eslint/no-explicit-any
[prop: string]: any;
constructor(data?: Partial<OrderProducts>) {
super(data);
}
}
export interface OrderProductsRelations {
// describe navigational properties here
}
export type OrderProductsWithRelations = OrderProducts & OrderProductsRelations;
Upvotes: 1
Views: 1329
Reputation: 26
I think the problem might be caused by the naming convention. LoopBack 4 uses camelCased variable name by default.
For any relation, there are three important attribute: the source key, foreign key, and the relation name.
Here's the link to the related files: Relation Metadata. For hasMany and hasOne relations, the source key defaults to the id property, which is Order.order_id
and Product.product_id
in you case. And the target key (foreign key) defaults 'TargetModelName'
+ 'Id'
. Which is orderProducts.orderProductsId
. And the default relation name is the one you decorated with @hasMany
decorator, which is orderProducts
in your example.
You have keyFrom
to deal with the customized names so the two hasMany relations are fine.
For belongsTo relation, the default source key is the id property of the target model, which is Product.product_id
in your case. The source key is the one decorated by @belongsTo
decorator, which is OrderProducts.product_id
. And the default relation name is .. here is the tricky part, it is product_id
in you case. Idealy, the source key is supposed to be OrderProducts.productId
, and the default relation name will be product
. In your case, the property has the same name as your relation name. This is caused by the way LB4 generates relation names. That's why it complains the navigational properties.
To fix it, you need to change two files, orderProducts.model.ts
:
... // other properties
@belongsTo(() => Product, {name: 'product'}) // use a different name from the property name
product_id: number; // let's use 'product' here
...
and OrderProductsRepository
:
export class OrderProductsRepository extends DefaultCrudRepository<
OrderProducts,
typeof OrderProducts.prototype.o_id,
OrderProductsRelations
> {
public readonly product: BelongsToAccessor< // use the relation name
Product,
typeof OrderProducts.prototype.o_id
>;
constructor(
@inject('datasources.db') dataSource: DbDataSource,
@repository.getter('ProductRepository')
protected productRepositoryGetter: Getter<ProductRepository>,
) {
super(OrderProducts, dataSource);
// make sure the name is correct
this.product = this.createBelongsToAccessorFor(
'product',
productRepositoryGetter,
);
// make sure the name is correct
this.registerInclusionResolver(
'product',
this.product.inclusionResolver,
);
}
}
Reference: BelongsTo Relation Metadata.
If you have any questions about LB4, you can also open an issue on our GitHub community.
Upvotes: 1