TypeORM does not save relations - typeorm

I have the following entity which I want to save:
#Entity('approvals')
export class Approval {
#PrimaryGeneratedColumn()
id: string;
#ManyToOne(type => Task, task => task.approvals, {nullable: false, onDelete: 'CASCADE', lazy: true})
task: Promise<Task> | Task;
#ManyToOne(type => User, user => user.approvals, {nullable: false, onDelete: 'CASCADE', lazy: true})
user: Promise<User> | User;
#Column({ type: 'enum', enum: ApprovalState, default: ApprovalState.None })
state: ApprovalState;
constructor(partialApproval: Partial<Approval>) {
Object.assign(this, partialApproval);
}
}
If I want to save an entity which looks like (copied while debugging):
[
{
"task": {
"id": "2",
"name": "task1",
"type": "type1",
"neededTimeSeconds": 0,
"factor": 1,
"userId": "1",
"periodStart": "2019-01-01",
"periodEnd": "2019-01-31",
"done": true,
"__user__": {
"id": "1",
"username": "user1",
"password": "$2b$10$SBPIVm9p8L4YkpiUVJ.mpedIgWi5Je6MuWTM7IvgMdyhr27JYM0OG",
"credits": 0,
"gravatarHash": null
},
"__has_user__": true
},
"user": {
"id": "2",
"username": "shouldHaveApprovalUser1",
"password": "password1",
"credits": 0,
"gravatarHash": null
}
},
{
"task": {
"id": "2",
"name": "task1",
"type": "type1",
"neededTimeSeconds": 0,
"factor": 1,
"userId": "1",
"periodStart": "2019-01-01",
"periodEnd": "2019-01-31",
"done": true,
"__user__": {
"id": "1",
"username": "user1",
"password": "$2b$10$SBPIVm9p8L4YkpiUVJ.mpedIgWi5Je6MuWTM7IvgMdyhr27JYM0OG",
"credits": 0,
"gravatarHash": null
},
"__has_user__": true
},
"user": {
"id": "3",
"username": "shouldHaveApprovalUser2",
"password": "password1",
"credits": 0,
"gravatarHash": null
}
}
]
Then calling repository.save() with the above array, I get:
null value in column "taskId" violates not-null constraint
Although there is clearly the id defined in each task.
This is the Task entity class:
#Entity('tasks')
export class Task {
#PrimaryGeneratedColumn({ type: 'bigint' })
id: string;
#Column({ length: 30 })
name: string;
#Column({ default: 'help' })
type: string;
#Column({ name: 'needed_time', type: 'int', nullable: true })
neededTimeSeconds: number;
#Column({ type: 'int', default: 1 })
factor: number;
#ManyToOne(type => User, { nullable: true, lazy: true })
#JoinColumn({ name: 'user_id' })
user: Promise<User>;
#Column({ name: 'user_id' })
#RelationId((task: Task) => task.user)
userId: string;
#Column({ name: 'period_start', type: 'date', default: new Date() })
periodStart: string;
#Column({ name: 'period_end', type: 'date', default: new Date() })
periodEnd: string;
#Column({ type: 'boolean', default: false })
done: boolean;
#OneToMany(type => Approval, approval => approval.task, { nullable: true, lazy: true })
approvals: Promise<Approval[]>;
#OneToMany(type => TaskMessage, taskMessage => taskMessage.task, { cascade: ['remove'], lazy: true })
messages: Promise<TaskMessage[]>;
constructor(partialTask: Partial<Task>) {
Object.assign(this, partialTask);
}
}
Can anyone tell me why the relation task is not going to be assigned/saved?

I think you are missing cascade: true for the options of the relations, this should be set on the loose side of the relation (OneToMany) - check the example

You can use Typeorm's cascades for this: https://orkhan.gitbook.io/typeorm/docs/relations#cascades
Setting the cascade property in your relationship decorator changes the behavior of typeorm's .save() method.
#OneToMany(type => Approval, approval => approval.task, {
nullable: true,
lazy: true,
cascade: ["insert", "update", "remove"]
})
approvals: Approval[];
With cascades enabled, you can insert, update or remove related entities with a single call to .save().
To create a new approval and insert task:
await this.approvalsEntity.save({ ...approvalData, task: taskData })
To update a task on a pre-existing approval:
await this.approvalsEntity.save({
id: approvalId,
task: { id: taskId, ...approvalUpdate }
})

Related

sequelize eager loading for join

Battery.belongsTo(TagType, { constraints: true, onDelete: "CASCADE" });
Tag.belongsTo(TagType, { constraints: true, onDelete: "CASCADE" });
const battery = sequelize.define("Battery", {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
voltage: {
type: Sequelize.INTEGER,
allowNull: false,
},
percetange: {
type: Sequelize.INTEGER,
allowNull: false,
},
});
const tag = sequelize.define("Tag", {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
allowNull: false,
primaryKey: true,
},
name: {
type: Sequelize.STRING,
allowNull: false,
unique: true,
},
battery: {
type: Sequelize.INTEGER,
allowNull: true,
},
});
//api
const percetangeAndTags = [];
const tags = await Tag.findAll({
include: [
{
model: TagType,
attributes: ["name"],
},
],
});
error.catchError("notFound", tags);
await Promise.all(
tags.map(async (tag) => {
const batteryVoltageValue = await Battery.findOne({
where: {
tagTypeId: tag.tagTypeId,
voltage: { [Op.lte]: tag.battery },
},
attributes: [
[Sequelize.fn("max", Sequelize.col("voltage")), "max"],
],
});
const battery = await Battery.findOne({
where: {
tagTypeId: tag.tagTypeId,
voltage: batteryVoltageValue.dataValues.max,
},
});
percetangeAndTags.push({
id: tag.id,
name: tag.name,
tag_type: tag.tag_type.name,
percetange: battery?.percetange,
});
})
);
res.status(200).json(percetangeAndTags);
How can I write this code in the most efficient way?
Because the explanation is short, it doesn't accept the question, so I wrote a poem instead of randomly throwing it.
Keleci bilen kişinin yüzünü ağ ede bir söz
Sözü pişirip diyenin işini sağ ede bir söz
Söz ola kese savaşı söz ola bitire başı
Söz ola ağılı aşı bal ile yağ ede bir söz

How to add multiple examples to a Swagger schema?

I'm using Swagger with OAS3, since I need anyOf support. I have an API call that can take one of 2 possible schemas, an account or an address. The schema works well in Swagger: it shows, and validates. But the example value shows only the first schema. I created an examples array, per the documentation, but I have no idea where to add it:
const accountSchema = {
description: 'schema for adding a new account',
type: 'object',
properties: {
account: {
type: 'object',
properties: {
userId: {type: 'number'},
platformId: {type: 'number'},
name: {type: 'string'},
key: {type: 'string'},
secret: {type: 'string'},
test: {type: 'boolean'},
},
required: ['userId', 'platformId', 'name', 'key', 'secret']
}
},
};
const ethereumAddressSchema = {
description: 'schema for adding a new ethereum address',
type: 'object',
properties: {
ethereum: {
type: 'object',
properties: {
userId: {type: 'number'},
name: {type: 'string'},
address: {type: 'string'},
chainId: {type: 'number'},
},
required: ['userId','name', 'address']
}
}
};
const examples = [
{
account: {
"userId": 0,
"platformId": 0,
"name": "string",
"key": "string",
"secret": "string",
"test": true
},
},
{
ethereum: {
"userId": 0,
"address": '0xfffffffffffffff',
"name": "string",
"chainId": 1,
}
}
];
const body = {
anyOf: [accountSchema, ethereumAddressSchema]
};
const response = {
type: 'object',
properties: {
accountId: {type: 'number'},
reason: {type: 'string'}
},
required: []
};
const addAccountSchema = {
description: 'Add a new account',
tags: ['account'],
produces: ['application/json'],
summary: 'Add a new account',
body,
response: {
200: {
description: 'Account request valid',
...response
},
404: {
description: 'Account request parameters not found',
...response
},
422: {
description: 'Account request invalid',
...response
}
}
};
module.exports = addAccountSchema;
Where should I add the examples array, or is there a better way to show the user the 2 possible schemas in the UI?

Typeorm many-to-one / one-to-many resulting in unexpected results

I am new to Typeorm but I feel like I have a fairly simple example that is resulting in some unexpected results. Lets say I have a Client. Each Client can have many ClientUsers. Each ClientUser can have only one Client.
Example:
Client Schema:
module.exports = new EntitySchema({
name: "Client",
target: Client,
columns: {
id: {
primary: true,
type: "uuid",
generated: "uuid"
},
name: {
type: "varchar"
},
telephone: {
type: "varchar"
},
email: {
type: "varchar"
}
},
relations: {
client_users: {
target: "ClientUser",
type: "one-to-many",
cascade: true
}
}
});
ClientUser Schema:
module.exports = new EntitySchema({
name: "ClientUser",
target: ClientUser,
columns: {
id: {
primary: true,
type: "uuid",
generated: "uuid"
},
first_name: {
type: "varchar"
},
last_name: {
type: "varchar"
},
title: {
type: "varchar"
},
email: {
type: "varchar"
},
username: {
type: "varchar"
},
password: {
type: "varchar"
}
},
relations: {
client: {
target: "Client",
type: "many-to-one",
joinColumn: true,
cascade: true
}
}
});
This result in the ClientUser table having a column called clientid, which is always null. If I change ClientUser schema relation to the following (update client to client_id):
relations: {
client_id: {
target: "Client",
type: "many-to-one",
joinColumn: true,
cascade: true
}
}
I get a column named clientIdId but the column is correctly populated with the related client id.
I am obviously doing something really simply wrong here but I've been looking at it so long, it escapes me. Please help me understand how I can have a logical column name, that gets populated as expected.

How to use data from Model to bind as kendo datasource

i have an empty div that i want to initialize into a kendo grid using data from Model..it should be something like the following but i am unable to load data
$("#mapsDiv").kendoGrid({
sortable: true,
dataSource: {
transport: {
read:"/Home/About",
dataType: "odata"
},
pageSize: 5
},
pageable: true,
resizable: true,
columnMenu: true,
scrollable:true,
navigatable: true,
editable: "incell"
});
About.cshtml
#model List<KendoExample.Entities.ShortStudent>
<div class="row">
<div class="col-md-12 table-responsive" id="mapsDiv">
</div>
My Home Controller is as follows
List<ShortStudent> students = new List<ShortStudent>();
ShortStudent student1 = new ShortStudent();
student1.birthdate = new DateTime(1999, 4, 30);
student1.classname = "1B";
student1.firstname = "Fredie";
student1.surname = "Fletcher";
student1.studentid = 1;
ShortStudent student2 = new ShortStudent();
student2.birthdate = new DateTime(2010, 5, 4);
student2.classname = "1B";
student2.firstname = "Lee";
student2.surname = "Hobbs";
student2.studentid = 2;
students.Add(student1);
students.Add(student2);
return View(students);
I have seen examples using json but not odata...
Also, there are examples to use it like
#(Html.Kendo().Scheduler<MeetingViewModel>()
.Name("scheduler")
.Editable(false)
.DataSource(ds => ds
.Custom()
.Batch(true)
.Schema(schema => schema
.Model(m =>
{
m.Id(f => f.MeetingID);
m.Field("title", typeof(string)).DefaultValue("No title").From("Title");
m.Field("start", typeof(DateTime)).From("Start");
m.Field("end", typeof(DateTime)).From("End");
m.Field("description", typeof(string)).From("Description");
m.Field("recurrenceID", typeof(int)).From("RecurrenceID");
m.Field("recurrenceRule", typeof(string)).From("RecurrenceRule");
m.Field("recurrenceException", typeof(string)).From("RecurrenceException");
m.Field("isAllDay", typeof(bool)).From("IsAllDay");
m.Field("startTimezone", typeof(string)).From("StartTimezone");
m.Field("endTimezone", typeof(string)).From("EndTimezone");
}))
.Transport(new {
//the ClientHandlerDescriptor is a special type that allows code rendering as-is (not as a string)
read = new Kendo.Mvc.ClientHandlerDescriptor() {HandlerName = "customRead" }
})
)
)
which i am unable to understand/implement so please ignore this kind of a solution.
Currently i see a grid footer that says (1 - 2 of 4852 items) without any header or content(datarows) on my screen. What am I doing wrong?
UPDATE
var dataSource = new kendo.data.DataSource(
{
transport: {
read: {
url: '#Url.Action("About", "Home")',
contentType: "application/json",
dataType: "json"
}
},
schema: {
model: {
fields: {
firstname: { type: "string" },
surname: { type: "string" },
birthdate: { type: "date" },
classname: { type: "string" }
}
}
},
type: "json",
serverPaging: false,
serverFiltering: true,
serverSorting: false
}
);
$("#mapsDiv")
.kendoGrid(
{
sortable: true,
dataSource: {
transport: {
read: dataSource
},
pageSize: 2
},
pageable: true,
resizable: false,
columnMenu: true,
scrollable:true,
navigatable: true,
editable: "incell",
columns:[{
field: "firstname",
},{
field: "surname",
},{
field: "classname",
},{
field: "age",
}]
});
HomeController
public ActionResult About()
{
....
return View(students);
}
Now the grid with header is there but no data is present..
If i change action to json, it returns plain json on the page
public ActionResult About()
{
....
return Json(students, JsonRequestBehavior.AllowGet);
}
Have you tried adding the fields to the grid?
$("#mapsDiv")
.kendoGrid(
{
sortable: true,
dataSource: {
transport: {
read:"/Home/About",
dataType: "odata"
},
pageSize: 5
},
columns: [
{
field: "classname",
title: "Class Name"
},
{
field: "firstname",
title: "First name"
},
{
field: "surname",
title: "Last name"
}
],
pageable: true,
resizable: true,
columnMenu: true,
scrollable:true,
navigatable: true,
editable: "incell"
});
I just visit demo of telerik. Try following. Hope to help, my friend. Or you can visit this link to refer more: http://demos.telerik.com/kendo-ui/grid/remote-data-binding.
$("#mapsDiv")
.kendoGrid(
{
sortable: true,
dataSource: {
transport: {
read:"/Home/About",
dataType: "odata"
},
pageSize: 5
},
schema: {
model: {
fields: {
studentid: { type: "number" },
birthdate : { type: "date" },
classname : { type: "string" },
firstname : { type: "date" },
surname : { type: "string" }
}
}
},
pageable: true,
resizable: true,
columnMenu: true,
scrollable:true,
navigatable: true,
editable: "incell"
});
So here is what i found what should have been straight forward :)
var values = #Html.Raw(Json.Encode(#Model));
$("#MapDetails")
.kendoGrid(
{
sortable: true,
dataSource: {
data:values,
pageSize: 2
},
pageable: true,
resizable: false,
columnMenu: true,
scrollable:true,
navigatable: true,
editable: "incell",
columns:[{
field: "firstname",
},{
field: "surname",
},{
field: "classname",
},{
field
: "age",
}]
});

Extjs Grid Paging Toolbar Shows All Data and No Page Number

I am using extjs paging toolbar on my grid. I have pagesize 5, but it shows all data(default is I guess 25)... but when I click next page, it still shows the first 25 data. and also have issue with page number where it's blank of 2...when it's suppose to say 1 of 2... Click here to see what it looks like
this is my grid...
var myPageSize = 5;
store.load({
params: {
start: 0,
limit: myPageSize
}
});
this.grid = Ext.create('Ext.grid.Panel', {
title: 'GridView App',
store: store,
loadMask: true,
columns: [{
header: 'Q1',
sortable: true,
dataIndex: 'Q1',
flex: 1,
}, {
header: 'Q2',
sortable: true,
dataIndex: 'Q2',
flex: 1,
}, {
header: 'Q3',
sortable: true,
dataIndex: 'Q3',
flex: 1,
}, {
header: 'Q4',
sortable: true,
dataIndex: 'Q4',
flex: 1,
}, {
header: 'Improvements',
flex: 1,
sortable: true,
dataIndex: 'Improvements'
}, {
header: 'Comments',
flex: 1,
sortable: true,
dataIndex: 'Comments'
}],
bbar: Ext.create('Ext.PagingToolbar', {
style: 'border:1px solid #99BBE8;',
store: store,
displayInfo: true,
preprendButtons: true,
displayMsg: 'Displaying Surveys {0} - {1} of {2}',
emptyMsg: "No Surveys to display"
}),
stripeRows: true,
trackover: true,
renderTo: Ext.getBody()
});
and this is my store
var store = Ext.create('Ext.data.JsonStore', {
storeId: 'myData',
scope: this,
fields: [{
name: 'Q1',
type: 'int'
}, {
name: 'Q2',
type: 'int'
}, {
name: 'Q3',
type: 'int'
}, {
name: 'Q4',
type: 'int'
}, {
name: 'Q5',
type: 'int'
}, {
name: 'Improvements',
type: 'string'
}, {
name: 'Comments',
type: 'string'
}],
sorters: [{
property: 'Q1',
direct: 'ASC'
}],
proxy: {
type: 'ajax',
url: 'GridView/writeRecord',
reader: new Ext.data.JsonReader({
root: 'myTable',
totalProperty: 'count'
})
}
});
And my Json looks like this, please click here
UPDATE JSON RESULT
{
"count": 30,
"myTable": [
{
"Q1": "1",
"Q2": "1",
"Q3": "1",
"Q4": "1",
"Improvements": "",
"Comments": "1"
},
{
"Q1": "1",
"Q2": "2",
"Q3": "3",
"Q4": "4",
"Improvements": "Iphone5",
"Comments": "Iphone14"
},
{
"Q1": "1",
"Q2": "1",
"Q3": "3",
"Q4": "3",
"Improvements": "This is Comment1-3",
"Comments": "This is Comment2-3"
},
The issue is with the data you're returning from server. With that store configuration you must return a json formatted like this (note that records fields are different that yours)
{
"success" : true,
"count" : 2,
"myTable" :[{
"id" : 1,
"cod" :"100001"
},{
"id" : 2,
"cod" :"100001"
}]
}
Store's root parameter is where extjs expects to have the array of records, success parameter is something you can handle to display server comunication errors and totalProperty is where you tell to extjs how many total records you have fetched. (Answer based on extjs 4.x, but as i can remember with 3.x is the same)

Resources