convert mongoose schema to django model - django

I have below javascript schema
const mongoose = require('mongoose');
const pointSchema = new mongoose.Schema({
timestamp: Number,
coords: {
latitude: Number,
longitude: Number,
altitude: Number,
accuracy: Number,
heading: Number,
speed: Number
}
});
const trackSchema = new mongoose.Schema({
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
name: {
type: String,
default: ''
},
locations: [pointSchema]
});
mongoose.model('Track', trackSchema);
I'm trying to convert above file to Django models. wondering how to write that locations: [pointSchema] and const pointSchema in my models.py.
Is it possible to convert that ?
this is how express server saves the data. I want to achieve same
router.post('/tracks', async (req, res) => {
const { name, locations } = req.body;
if (!name || !locations) {
return res
.status(422)
.send({ error: 'You must provide a name and locations' });
}
try {
const track = new Track({ name, locations, userId: req.user._id });
await track.save();
res.send(track);
} catch (err) {
res.status(422).send({ error: err.message });
}
});

You will be using a Many-to-one relationship depicted as a ForeignKey for the first part. Taking the docs as a basis here, it would look like this:
from django.db import models
class Coords(models.Model):
latitude = models.FloatField(...)
longitude = models.FloatField(...)
...
class PointSchema(models.Model):
timestamp = models.DateTimeField(...)
coords = models.ForeignKey(Coords, on_delete=models.CASCADE)
And for trackSchema, we use a ManyToManyField. Short excerpt:
class TrackSchema(models.Model):
...
locations = models.ManyToManyField(PointSchema)
...

Related

Stubbing other repository calls in current repository test

I am trying to write test cases for an Node.js backend project
The database is using pg-promise. I run into issue when trying to stub the repository and it tries to call other repositories.
Here is the repository file user.js
async findByRefToken(refToken) {
return await this.db.oneOrNone(`SELECT id FROM useraccount WHERE username = $1`, refToken);
}
async createNormal(data) {
// generate a verify email token
const verifyToken = await this.generateToken();
// create unique ref code
const tokenid = nanoid(Number(env.REF_TOKEN_LENGTH));
const refToken = await this.generateUniqueRefToken(tokenid);
// find the referral's id from the ref token
let referredBy = null;
if(data.referred) {
referredBy = await this.findByRefToken(data.referred);
}
const meta = {
dob: data.dob,
gender: data.gender,
country: data.country,
phone: data.phone,
email_verified: false,
verify_token: verifyToken,
ref_token: refToken,
referred: referredBy ? referredBy.id : null
};
const verificationMeta = {
kyc_verified: false
};
const normalUser = {
id: data.userId,
first_name: data.firstName.trim(),
last_name: data.lastName.trim(),
active: true,
email: data.email.trim().toLowerCase(),
username: data.username.trim(),
role: 'Normal',
password: bcrypt.hashSync(data.password, Number(env.SALT_ROUNDS)),
meta: meta,
verification_meta: verificationMeta
};
const newUser = await this.db.one(CREATE_NORMAL_USER, normalUser);
return {
id: newUser.id,
firstName: data.firstName,
email: data.email,
verifyToken: verifyToken,
refToken: refToken,
referredBy: referredBy
};
}
I am trying to test the createNormal function, but it would also call other repo function like findByRefToken and await this.db.one(CREATE_NORMAL_USER, normalUser); Is there anyway to stub them away?
And here is the test written user.test.js
const chai = require("chai");
const sinon = require("sinon");
const expect = chai.expect;
const {faker} = require("#faker-js/faker");
const UserRepository = require("../../repos/user");
describe("UserRepository", function() {
const stubValue = {
dob: faker.date.birthdate(),
gender: faker.name.gender(),
country: faker.address.country(),
phone: faker.phone.number(),
email_verified: false,
id: faker.datatype.uuid(),
firstName: faker.name.firstName(),
lastName: faker.name.lastName(),
username: faker.name.fullName(),
password: faker.random.alphaNumeric(5),
email: faker.internet.email(),
verifyToken: faker.random.alphaNumeric(12),
refToken: faker.random.alphaNumeric(12),
referredBy: faker.random.alphaNumeric(12)
};
describe("create", function () {
it("should add a new user to the db", async function () {
// const stub = sinon.stub(UserRepository, "createNormal").resolves(stubValue.refToken);
const userRepository = new UserRepository();
const user = await userRepository.createNormal(stubValue);
expect(user.id).to.equal(stubValue.id);
expect(user.name).to.equal(stubValue.name);
expect(user.phone).to.equal(stubValue.phone);
expect(user.id).to.equal(stubValue.id);
expect(user.verifyToken).to.equal(stubValue.verifyToken);
});
});
});
Thank you for responding
The method creates and saves a new user in the database. What is left to test if you stub the database calls?
My recommendation would be to not mock; set up the appropriate structures in the database and use them. You can use factory-bot to make this simpler.
Note: it's strange that it doesn't return a user.
I'd extract making the user from saving the user. I'd also put all the token and referral stuff into their own methods.
async newNormal(data) {
const verifyToken = await this.generateToken();
// create unique ref code
const tokenid = this.generateTokenId();
const refToken = await this.generateUniqueRefToken(tokenid);
const referredBy = await this.findByRefToken(data.referred);
const meta = {
dob: data.dob,
gender: data.gender,
country: data.country,
phone: data.phone,
email_verified: false,
verify_token: verifyToken,
ref_token: refToken,
referred: referredBy ? referredBy.id : null
};
return {
id: data.userId,
first_name: data.firstName.trim(),
last_name: data.lastName.trim(),
active: true,
email: data.email.trim().toLowerCase(),
username: data.username.trim(),
role: 'Normal',
password: bcrypt.hashSync(data.password, Number(env.SALT_ROUNDS)),
meta: meta,
verification_meta: {
kyc_verified: false
};
};
}
async createNormal(data) {
const userData = await this.newNormal(data);
return await this.db.one(CREATE_NORMAL_USER, userData);
}
Now createNormal is an integration method. All it does is call newNormal and pass the result through to a SQL query. You can test it by mocking newNormal and db.one.
The important work is happening in newNormal, focus testing on that. You can mock generateToken, generateTokenId, generateUniqueRefToken and findByRefToken. But, again, this test would be easier and more realistic by just setting up the necessary data.

How do you test express controllers that use mongoose models?

In my controller i have a function that creates a user but also checks to make sure that the user does not already exist and then a dashboard function which gets the user from the request and returns any petitions that have been created by that user.
I've looked at mocha, chai and sinon to carry out the tests along with various online resources but have no idea how to begin testing these two functions since they rely on models. Can anyone point me in the right direction to testing the controller or know of any resources which maybe able to help me?
Controller:
const bcrypt = require('bcryptjs');
const passport = require('passport');
const Users = require('../models/Users');
const Petitions = require('../models/Petitions');
const UserController = {
async register(req, res) {
const {name, email, password, passwordCon} = req.body;
let errors = []
// check required fields
if (!name || !email || !password || !passwordCon) {
errors.push({ msg: 'Please enter all fields' });
}
// check passwords match
if (password !== passwordCon) {
errors.push({ msg: 'Passwords do not match' });
}
// check password length
if (password.length < 6) {
errors.push({ msg: 'Password must be at least 6 characters' });
}
// if validation fails, render messages
if (errors.length > 0) {
res.render('user/register', {
errors,
name,
email,
password,
passwordCon
})
} else {
// validation passed
Users.findOne({email: email})
.then(user => {
if (user) {
// user exists
errors.push({msg: 'Email already in use'});
res.render('user/register', {
errors,
name,
email,
password,
passwordCon
});
} else {
const newUser = new Users({
name: name,
email: email,
password: password
});
// hash password
bcrypt.genSalt(10, (error, salt) =>
bcrypt.hash(newUser.password, salt, (error, hash) => {
if (error) throw error;
// set password to hashed
newUser.password = hash;
// save user
newUser.save()
.then(user => {
req.flash('success_msg', 'Registration Success');
res.redirect('/user/login');
})
.catch(error => console.log(error));
}))
}
});
}
},
async dashboard(req, res) {
const user = req.user;
const petitions = await Petitions.find({createdBy: user._id});
console.log('here');
res.render('user/dashboard', {
user: req.user,
petitions: petitions
})
}
};
module.exports = UserController;
Models:
const mongoose = require('mongoose');
const UserSchema = new mongoose.Schema({
createdOn: {
type: Date,
default: Date.now
},
name: {
type: String,
required: true
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
},
petitions: [
{ type: mongoose.Schema.Types.ObjectId, ref: 'Petitions' }
]
})
const Users = mongoose.model('Users', UserSchema);
module.exports = Users;
const mongoose = require('mongoose');
const PetitionSchema = new mongoose.Schema({
createdOn: {
type: Date,
default: Date.now
},
title: {
type: String,
required: true
},
signaturesNeeded: {
type: String,
required: true
},
description: {
type: String,
required: true
},
createdBy: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Users'
},
signatures: [
{ type: mongoose.Schema.Types.ObjectId, ref: 'Users' }
]
})
const Petitions = mongoose.model('Petitions', PetitionSchema);
module.exports = Petitions;

TypeORM why is my relationship column undefined? foreign-key is undefined

I just use TypeORM and find the relationship column is undefined
#Entity({name: 'person'})
export class Person {
#PrimaryGeneratedColumn('uuid')
id!: string;
#OneToOne( () => User)
#JoinColumn()
user!: User;
#Column({
type: "enum",
enum: PersonTitle,
default: PersonTitle.Blank
})
title?: string;
#Column({type: 'varchar', default: ''})
first_name!: string;
#Column('varchar')
last_name!: string;
#ManyToOne(() => Organization, org => org.people, { nullable: true})
belong_organization!: Organization;
and I also have Organization entity:
export class Organization {
#PrimaryGeneratedColumn('uuid')
id!: string;
...
}
when I use Repository like:
const db = await getDatabaseConnection()
const prep = db.getRepository<Person>('person')
presult = await prep.findOne({where: {id}})
console.log(result)
my result is:
Person {
id: '75c37eb9-1d88-4d0c-a927-1f9e3d909aef',
user: undefined,
title: 'Mr.',
first_name: 'ss',
last_name: 'ls',
belong_organization: undefined, // I just want to know why is undefined? even I can find in database the column
expertise: [],
introduction: 'input introduction',
COVID_19: false,
contact: undefined
}
the database table like:
"id" "title" "first_name" "last_name" "expertise" "COVID_19" "userId" "belongOrganizationId" "introduction"
"75c37eb9-1d88-4d0c-a927-1f9e3d909aef" "Mr." "test" "tester" "nothing" "0" "be426167-f471-4092-80dc-7aef67f13bac" "8fc50c9e-b598-483e-a00b-1d401c1b3d61" "input introduction"
I want to show organization id, how typeORM do it? Foreign-Key is present undefined?
You need to either lazy load the relation or you need to specify the relation in the find
Lazy:
#Entity({name: 'person'})
class Person {
...
#ManyToOne(() => Organization, org => org.people, { nullable: true})
belong_organization!: Organization;
...
}
...
async logOrganization() {
const db = await getDatabaseConnection()
const prep = db.getRepository<Person>('person')
presult = await prep.findOne({where: {id}})
console.log(await result.belong_organization)
}
Find
const prep = db.getRepository<Person>('person')
presult = await prep.findOne({
where: { id },
relations: ["belong_organization"]
})
You could also always do an eager load, but i'd advise against this since then it would always do the join when it fetches a person.
If you want to query the belong_organizationId you need to add its field to the person entity. This field is usual something like belongOrganizationId
That would make
#Entity({name: 'person'})
class Person {
...
#Column()
belongOrganizationId:number
#ManyToOne(() => Organization, org => org.people, { nullable: true})
belong_organization!: Organization;
...
}
This would make it possible to query for its id too.
You could also query it more directly but this leaves you with some pretty ugly and unmaintainable code:
const findOptions: {
where :{
id,
'belong_organization.id': belong_organizationId
}
}

Foreign Key with Sequelize not working as expected

I was trying to create an association between two tables and I wanted to add a foreign key.
The two models are User and Companies
User.associate = (models) => {
User.belongsTo(models.Companies, { foreignKey: 'Company' });
};
My expectation of the code above was that a Company ID field gets added in the user table which references the Company ID of the Companies table.
On running the code above, I don't see any additional columns getting created. I tried checking if a foreign key association is created in the DB and that also is missing.
However, if I try to add a column with the same name while keeping the association code, I get a name conflict. This seems to suggest that the association is getting created but I am unable to see it.
Could someone help me understand what I am doing wrong? Thanks for the help!
models/company.js
module.exports = (sequelize, DataTypes) => {
var Company = sequelize.define('company', {
company: { type: DataTypes.STRING, primaryKey: true },
});
Company.associate = (models) => {
Company.hasMany(models.user, { as: 'users' });
};
Company.sync();
return Company;
};
models/user.js
const uuid = require('uuid/v4');
'use strict';
module.exports = (sequelize, DataTypes) => {
var User = sequelize.define('user', {
id: { type: DataTypes.UUID, primaryKey: true },
name: { type: DataTypes.STRING, allowNull: false }
});
User.associate = (models) => {
User.belongsTo(models.company);
};
User.beforeCreate((user, _ ) => {
user.id = uuid();
return user;
});
User.sync();
return User;
};
models/index.js
'use strict';
var fs = require('fs');
var path = require('path');
var Sequelize = require('sequelize');
var basename = path.basename(__filename);
var env = process.env.NODE_ENV || 'development';
// var config = require(__dirname + '/../config/config.js')[env];
var db = {};
// if (config.use_env_variable) {
// var sequelize = new Sequelize(process.env[config.use_env_variable], config);
// } else {
// var sequelize = new Sequelize(config.database, config.username, config.password, config);
// }
const sequelize = new Sequelize('postgres://postgres:user#localhost:5432/mydb');
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
var model = sequelize['import'](path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;
I was able to get this resolved.
The issue was with regard to the sequence in which the sync was called. In my original code, I was calling sync inside each model. Even though I added the options force and alter, I think the foreign keys were not getting added. So, I removed the sync code from inside the models, and added it in a separate loop inside index.js.
This gave me a new issue. Tables were getting created in an order that is not consistent with the order in which tables should be created for foreign keys to work since tables should pre-exist. I resolved it by manually providing the sequence of sync and now I see the columns getting created.
To summarise: model defn -> model association -> model sync in sequence
Thank you for your suggestions, members of SO.
Your model is fine! you must remove sync from models file , then check migration file for models with foreign key that foregin key is there,
for Migration User :
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Users', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.UUID
},
name: {
type: Sequelize.STRING
},
companyId: {
type: Sequelize.UUID,
references: {
model: 'Company',// company migration define
key: 'id'
}
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Users');
}
};
for create automate table from index.js and models you must install sequelize-cli
by type npm install --save sequelize-cli
then you must run this command for create models table in db
sequelize db:migrate
By using foreignKey: 'Company' you are telling it to associate with a column named Company. You typically also want to use singular table names, so company with an association of companies. By default Sequelize will use the primary key for the association, so you only need to specify foreignKey if you want to change it or set other parameters.
const User = sequelize.define(
'user',
{ /* columns */ },
{ /* options */ }
);
User.associate = (models) => {
User.belongsTo(models.Company);
};
const Company = sequelize.define(
'company',
{ /* columns */ },
{ /* options */ }
);
Company.associate = (models) => {
Company.hasMany(models.User, { as: 'users' });
};
This will create the following tables Company (id) and User (id, company_id).
Query all User records associated to a single Company:
const user = await User.findAll({ include: { model: Company } });
/*
user = {
id: 1,
company_id: 1,
company: {
id: 1,
},
};
*/
Query all Company records with multiple associated User records via users:
const company = await User.findAll({ include: { model: User, as: 'users' } });
/*
company = {
id: 1,
users: [{
id: 1
company_id: 1,
}],
};
*/
My guess is that the associate method is not getting called, and therefore, your association does not get created. Keep in mind that associate is not a built-in Sequelize method, but it is just a pattern used by the community. (More info on this thread)
There are various approaches to handle calling associate, here is one example. You have a models.js file that handles your association and you initialize that inside your main app.js file.
// app.js (aka your main application)
const models = require('./models')(sequelize, DataTypes);
// models.js
module.exports = (sequelize, DataTypes) => {
const models = {
user: require('./userModel')(sequelize, DataTypes),
company: require('./companyModel')(sequelize, DataTypes)
};
Object.keys(models).forEach(key => {
if (models[key] && models[key].associate) {
models[key].associate(models);
}
});
};
// companyModel.js
module.exports = (sequelize, DataTypes) => {
var Company = sequelize.define('company', {...});
Company.associate = (models) => {
Company.hasMany(models.user, { as: 'users' });
};
Company.sync();
return Company;
};
// userModel.js
module.exports = (sequelize, DataTypes) => {
var User = sequelize.define('user', {...});
User.sync();
return User;
};
Also, FYI, You probably know this but sync should only be used for experimenting or testing, not for a production app.

Get server's answer after saving model

I use Ember data with a node js server; the model looks very simple:
Gmcontrolpanel.Product = DS.Model.extend({
name: DS.attr('string'),
description: DS.attr('string'),
});
Once the node server receives the product.save(), it persists the record in the mysql db, managing the record ID and answers like this:
{
product
{
id: 1,
name: "aaa",
description "bbb"
}
}
I need to get the id of the server's response (not the promise returned by save().then(), where id is null); how can i get it?
Update:
The node server, using express:
GMserver.post('/products', function (req, res) {
rootName = "product";
queryString = 'INSERT INTO products (id, name, descriptions ) VALUES ( '+ counters.prodcuts +', "' + req.body.product.name + '", "' + req.body.product.description + '")';
executeQuery(req, res, queryString);
responseToPost(counters.products, req.body.product, rootName, res);
counters.products++;
});
function executeQuery (req, res, querystring) {
connection.query(queryString, function(err, rows, fields){
if (err) throw err;
});
}
function responseToPost (id, data, rootName, res) {
var result = new Object();
result[rootName] = new Object();
var i = 0;
var answer;
result[rootName].id = id;
for(var key in data)
{
result[rootName][key] = data[key];
}
answer = JSON.stringify(result, null, '\t');
console.log(answer);
res.send(answer);
}
I can see by the log of answer here, that answer is the one written above;
I tried to change responseToPost to send always a static value like this:
result[rootName][key] = 'aaa';
but in Ember, doing
product.save().then(function(savedProduct) {
console.log(savedProduct.get('name'));
}
i get the sumbmitted value of name, not 'aaa' as I expected...
Second Update:
doing in Ember
product.save().then(function(savedProduct) {
console.log(savedProduct);
}
to see what savedProduct is, in Chrome i see the result of the log:
Class {id: null, store: Class, container: Container, currentState: (...), errors: Class…}
__ember1395755543625: "ember548"
__ember1395755543625_meta: Object
__nextSuper: undefined
_attributes: Object
_changesToSync: Object
_data: Object
__ember1395755543625_meta: Meta
_super: function superFunction(){
name: "asdf"
description: "asdfa"
__proto__: Object
_deferredTriggers: Array[0]
_inFlightAttributes: Object
_relationships: Object
_suspendedRelationships: false
_updatingRecordArraysLater: false
container: Container
currentState: (...)
get currentState: function () {
set currentState: function (value) {
data: (...)
errors: Class
id: null
isError: false
store: Class
toString: function () { return ret; }
__proto__: Object
where "asdf" and "asdfa" are the values i typed in the insert form on the app
The record should be updated if that's the JSON returned.
product.save().then(function(record){ //record is the same as product here
console.log(record.get('id'));
});