Removing join table data in sequelize returned value - orm

I am currently trying to remove a joint table data added when retrieving an association data.
The query is done by sequelize using a method added to the model through specifying model relationships(sequelize magic methods), for some reason, I'm not able to do that.
I have currently tried passing in attributes: {exclude: ['...']} to the method but the field still persists.
Current association
// Class sequelize model
Class.belongsToMany(models.Subject, {
through: 'ClassSubject',
foreignKey: 'class_id',
otherKey: 'subject_id',
as: 'subjects'
})
// Subject sequelize model
Subject.belongsToMany(models.Class, {
through: 'ClassSubject',
foreignKey: 'subject_id',
otherKey: 'class_id',
as: 'classes'
});
Query and Response
const subjects = await dbClass.getSubjects(); // dbClass is a Class model instance
// Response
[
{
"id": "1b89d44c-2caa-452d-a1f8-7faa11970917",
"name": "Mathematics",
"code": "MATHS",
"summary": "Mathematics for class 1",
"ClassSubject": {
"class_id": "637afc7b-40f6-478e-b35e-859ca462e2e7",
"subject_id": "1b89d44c-2caa-452d-a1f8-7faa11970917"
}
}
]
Desired output
// Response
[
{
"id": "1b89d44c-2caa-452d-a1f8-7faa11970917",
"name": "Mathematics",
"code": "MATHS",
"summary": "Mathematics for class 1"
}
]
I have tried passing options to the method as specified below but to no avail
const subjects = await dbClass.getSubjects({
attributes: { exclude: ['ClassSubject'] }
});
But it still doesn't work.

Try using the joinTableAttributes option and pass empty array to exclude everything in joint table.
const subjects = await dbClass.getSubjects({ joinTableAttributes: [] });

Related

Shopware 6: how to delete all products via admin api

How to delete all products via admin api?
To achieve the goal i try to use the Bulk Payloads | Deleting entities
The doc says:
[...] To delete entities, the payload of an operation contains the IDs. [...]
Questions:
to delete all products i have to read first all product.id's?
or is there a alternative way with a type of "wildcard"?
My current request body (using Postman) ...:
{
"delete-product": {
"entity": "product",
"action": "delete",
"payload": []
}
}
... response with (products remains in db):
{
"extensions": [],
"success": true,
"data": {
"delete-product": {
"extensions": [],
"result": []
}
},
"deleted": [],
"notFound": []
}
EDIT #1
With id's provided...:
...
const obj = {
"delete-products": {
"entity": "product",
"action": "delete",
"payload": [
{"id": "73af65014974440b95450f471b3afed8"},
{"id": "784f25a29e034fad9a416923f964ba8a"}
]
}
}
apiClient.request({
"url": "/_action/sync",
"method": "POST",
obj
})
...
... the request fails in class Symfony\\Component\\Serializer\\Encoder\\JsonDecode with message:
detail: "Syntax error"
Debugging the request, payload is missing (empty content):
What is wrong with the configuration of the /api/_action/sync call?
Indeed, what it means is that you will need a low impacting query to get all product id's, store it into a variable & delete them. Use includes:["id"] filter to just get the ID's.
Here is an example of me deleting some products in Postman.
Request body:
{
"delete-product": {
"entity": "product",
"action": "delete",
"payload": {{gen_dynamic_products}}
}
}
Pre-request script (you'll need to adjust this sightly to get your ID's):
const map = new Array(30).fill(0).map((val, index) => {
return { id: pm.environment.get('gen_product_list_sub_' + index) };
});
pm.variables.set('gen_dynamic_products', JSON.stringify(map));
to delete all products i have to read first all product.id's?
Yes, that is what you'll have to do. This is necessary to maintain the extendibility of the platform. The core or other plugins may react to the deletion of products by subscribing to an entity lifecycle event. This event includes the id of the deleted entity. Hence why it is necessary to explicitly provide the ids of the entities in the first place.

How to update the Strapi GraphQL cache, after creating new data?

How to update the cache, after creating new data?
Error message from Apollo
Store error: the application attempted to write an object with no provided id but the store already contains an id of UsersPermissionsUser:1 for this object. The selectionSet that was trying to be written is:
{
"kind": "Field",
"name": { "kind": "Name", "value": "user" },
"arguments": [],
"directives": [],
"selectionSet": {
"kind": "SelectionSet",
"selections": [
{ "kind": "Field", "name": { "kind": "Name", "value": "username" }, "arguments": [], "directives": [] },
{ "kind": "Field", "name": { "kind": "Name", "value": "__typename" } }
]
}
}
Nativescript-vue Front-end Details
1- Watch Book Mobile app in action on YouTube: https://youtu.be/sBM-ErjXWuw
2- Watch Question video for details on YouTube: https://youtu.be/wqvrcBRQpZg
{N}-vue AddBook.vue file
apolloClient
.mutate({
// Query
mutation: mutations.CREATE_BOOK,
// Parameters
variables: {
name: this.book.name,
year: this.book.year,
},
// HOW TO UPDATE
update: (store, { data }) => {
console.log("data ::::>> ", data.createBook.book);
const bookQuery = {
query: queries.ALL_BOOKS,
};
// TypeScript detail: instead of creating an interface
// I used any type access books property without compile errors.
const bookData:any = store.readQuery(bookQuery);
console.log('bookData :>> ', bookData);
// I pin-pointed data objects
// Instead of push(createBook) I've pushed data.createBook.book
bookData.books.push(data.createBook.book);
store.writeQuery({ ...bookQuery, data: bookData })
},
})
.then((data) => {
// I can even see ID in Result
console.log("new data.data id ::::: :>> ", data.data.createBook.book.id);
this.$navigateTo(App);
})
.catch((error) => {
// Error
console.error(error);
});
What are these "Book:9": { lines in the cache?
console.log store turns out:
"Book:9": {
"id": "9",
"name": "Hadi",
"year": "255",
"__typename": "Book"
},
"$ROOT_MUTATION.createBook({\"input\":{\"data\":{\"name\":\"Hadi\",\"year\":\"255\"}}})": {
You can see all front-end GitHub repo here
Download Android apk file
Our goal is to update the cache. Add Book Method is in here:
https://github.com/kaanguru/mutate-question/blob/c199f8dcc8e80e83abdbcde4811770b766befcb5/nativescript-vue/app/components/AddBook.vue#L39
Back-end details
However, this is a frontend question a running Strapi GraphQL Server is here: https://polar-badlands-01357.herokuapp.com/admin/
GraphQL Playground
USER: admin
PASSWORD: passw123
You can see GraphQL documentation
I have so much simple Strapi GrapQL Scheme:
If you want to test it using postman or insomnia you can use;
POST GraphQL Query URL: https://polar-badlands-01357.herokuapp.com/graphql
Bearer Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MSwiaWF0IjoxNTkwODI3MzE0LCJleHAiOjE1OTM0MTkzMTR9.WIK-f4dkwVAyIlP20v1PFoflpwGmRYgRrsQiRFgGdqg
NOTE: Don't get confused with $navigateTo() it's just a custom method of nativescript-vue.
It turns out;
all code was correct accept bookData.push(createBook);
// HOW TO UPDATE
update: (store, { data }) => {
console.log("data ::::>> ", data.createBook.book);
const bookQuery = {
query: queries.ALL_BOOKS,
};
// TypeScript detail: instead of creating an interface
// I used any type access books property without compile errors.
const bookData:any = store.readQuery(bookQuery);
console.log('bookData :>> ', bookData);
// I pin-pointed data objects
// Instead of push(createBook) I've pushed data.createBook.book
bookData.books.push(data.createBook.book);
store.writeQuery({ ...bookQuery, data: bookData })
},
})
Typescipt was helping
The point is; I shouldn't trust TypeScript errors, or at least I should read more about what it really says.
Typescript just asked me to be more specific while saying: Property 'push' does not exist on type 'unknown'
TypeScript was trying to tell me I need to be more specific while calling ROOT_MUTATION data. It said: Cannot find name 'createBook' But again I ignored it.
Solution Github Branch
https://github.com/kaanguru/mutate-question/tree/solution
Sources
how to update cache
Create interface for object Typescript

Select from multiple tables in sequelize

I'm struggling in how to select from two tables using the Sequelize.
Actually I'm trying to do it:
SELECT * FROM users, clients WHERE user.id = clients.user_id
I have no idea how to user two tables as I described, the only thing I did that got some results were:
const clients = await Client.findAll({
attributes: ["user_id"],
});
const users = [];
for (const client of clients) {
let user = await User.findAll({
where: {
id: {
[Op.eq]: client.user_id
}
}
});
users.push(user);
}
Which return me something:
[
[
{
"id": 1,
"first_name": "Velda",
"middle_name": "Zboncak",
"last_name": "Kris",
"email": "vkris10#hotmail.com",
"created_at": "2020-02-07T20:09:29.484Z",
"updated_at": "2020-02-07T20:09:29.484Z"
}
]
];
Model and Assossiation
First of all, you need to create the correct associations in the model of your table. In this case for the User and the Client, it's supposed to be an Client.belongsTo(...)
Take a look at User model:
const { Model, DataTypes } = require("sequelize");
class User extends Model {
static init(sequelize) {
super.init({
first_name: DataTypes.STRING,
middle_name: DataTypes.STRING,
last_name: DataTypes.STRING,
email: DataTypes.STRING
}, { sequelize });
}
}
module.exports = User;
Take a look at Client model:
const { Model, DataTypes } = require("sequelize");
class Client extends Model {
static init(sequelize) {
super.init({
user_id: DataTypes.INTEGER // The foreign key
}, { sequelize });
}
static associate(models) {
Client.belongsTo(models.User, {
foreignKey: "id", // Column name of associated table
as: "user" // Alias for the table
});
}
}
module.exports = Client;
When associating tables you need to have in mind those values inside the associate method, being the foreignKey: "id" the column name inside the models.ModelName, which will be used to make the joins, and the as: "user" which are used as an alias for the table like SELECT t.column1 FROM table AS t;
Controller
Okay, now you have set your models, you need to set your controller, where the magic happens. As you said you want to fetch results using:
SELECT * FROM users, clients WHERE user.id = clients.user_id
But to achieve the same result you can follow the sql join method to fetch the results from db, so it will be something like this:
SELECT
"user"."first_name", "user"."middle_name", "user"."last_name", "user"."email"
FROM "clients" AS "client"
LEFT JOIN "users" AS "user"
ON "client"."id" = "user"."id";
Knowing that we can talk about including tables in sequelize, which is the same as associations
const Client = require("./path/to/models/Client");
module.exports = {
async fetchAll(req, res) {
const results = await Client.findAll({
limit: 25,
include: [
{
association: "user",
attributes: ["first_name", "middle_name", "last_name", "email"]
}
]
});
return res.json(results);
},
};
Now lets talk about what is going on in the code:
The Model.findAll({}) will fetch all the result inside the specified table, in this case clients table.
The limit: 25 will limit your results in only 25 rows, you are free to remove or edit as you need.
The include: [], it will do the joins through the tables you specify, as you need only the users table, we are going to use only one object, so the assossiation: "user" will make this connection between tables, you must use the same alias you set inside the model. And at least the attributes: ["columns"] is where you set all the fields you want to fetch.
And that's it, you make you request, and the result of this will be exactly the same join as I mentioned. And the results will be:
[
{
"id": 1,
"user_id": 1,
"user": {
"first_name": "John",
"middle_name": "Ironsight",
"last_name": "Doe",
"email": "johndoe#example.com"
}
}, {...}
]
Can use where in include. Find the document at here
let user_id = client.user_id;
users = await User.findAll({
include: [
{
model: Client,
as: 'client',
where: {
user_id: user_id
}
}
]
});

Ember-data hasMany relationship not working (JSONAPIAdapter)

I'm using Ember-Data v.1.13.9 with Ember-CLI v.1.13.8. I'm using the JSONAPIAdapter adapter.
I have a problem with a hasMany relationship. I can see from the Ember inspector that both the main record and the related record are being loaded into the store. However, the relationship doesn't seem to be there since I cannot access the related records details in my template.
models/invoice.js
export default DS.Model.extend(
{
invNum : DS.attr('string'),
created : DS.attr('date', {defaultValue: function() { return new Date(); }}),
clientId : DS.attr('number'),
userId : DS.attr('number'),
details : DS.hasMany('invoice-detail', {async : true}),
});
models/invoice-detail.js
export default DS.Model.extend(
{
invoice : DS.belongsTo('invoice', {async : true}),
detail : DS.attr('string'),
amount : DS.attr('number'),
vat : DS.attr('number'),
});
my JSON data: (URL: /accounts/invoices/1)
{
"data": {
"id": 1,
"attributes": {
"inv-num": "A0011000001",
"created": "November, 01 2000 00:00:00",
"user-id": 2,
"client-id": 14,
"relationships": {
"details": {
"data": [
{
"id": 1,
"type": "invoice-detail"
}
]
}
}
},
"type": "invoice"
},
"included": [
{
"id": 1,
"attributes": {
"amount": 3000,
"detail": "Stage 1 delivery of 3Com Reseller Locator to\r\nFoundation Network LTD",
"vat": 525
},
"type": "invoice-detail"
}
]
}
I've tried accessing the details related array directly:
{{#each model.details as |detail index|}}
{{index}} : {{detail.detail}} £{{detail.amount}} (£{{detail.vat}} vat)
{{/each}}
And by using a controller: invoice/controller.js
export default Ember.Controller.extend({
invoiceDetails : function()
{
var invoice = this.get("model");
var details = invoice.get("details");
Ember.Logger.log("invoiceDetails",invoice,details);
return details;
}.property('model.details'),
});
and
{{#each invoiceDetails as |detail index|}}
{{index}} : {{detail.detail}} £{{detail.amount}} (£{{detail.vat}} vat)
{{/each}}
Neither is providing me with the data that I require.
What am I doing wrong?
A second related issue I am having is that I can't get it to reload data from the server. the {reload:true} makes no difference. Looking at the network traffic I can see that no call to the server is made for second and subsequent visits to this route.
invoice/route.js
export default Ember.Route.extend({
model: function(params) {
return this.store.findRecord('invoice', params.id, { reload: true });
}
})
What I actually want to do here is have one route which retrieves a list of invoices (without the details part - so it's quick to retrieve since I do not need the details on the list page). Then, when I drill down to a specific invoice, make a call to the server to get the full details for that invoice. My plan was to use the shouldReloadRecord function to check if I have details attached to this record or not. If so, use the copy from the store, if not, go to the server and then overwrite the limited "list" record I got initially. As a stepping stone in that direction I figured that just setting {reload:true} in the route would force all requests to go back to the server.
I guess I've misunderstood something somewhere...?

How to make ember work with Django REST gis

I am currently trying to setup ember to interact with Django's REST Framework using the ember-django-adapter.
This works flawless. But since I started using djangorestframework-gis, ember is not able to process the responses anymore.
I have not found anyone building geoJSON with ember except for: https://gist.github.com/cspanring/5114078 But that does not seem to be the right approach because I do not want to change the data model?
This is the api-response:
{
"type": "FeatureCollection",
"features": [
{
"id": 1,
"type": "Feature",
"geometry": {
"coordinates": [
9.84375,
53.665466308594
],
"type": "Point"
},
"properties": {
"date_created": "2014-10-05T20:08:43.565Z",
"body": "Hi",
"author": 1,
"expired": false,
"anonymous": false,
"input_device": 1,
"image": "",
"lat": 0.0,
"lng": 0.0
}
}
]
}
While ember expects something like:
[{"id":1,
"date_created":"2014-10-05T20:08:43.565Z",
"body":"Hi",
"author":1,
"expired":false,
"anonymous":false,
"input_device":1,
"image":"",
"lat":0,
"lng":0
}
]
My take on this was to write my own Serializer:
import Ember from "ember";
import DS from "ember-data";
export default DS.DjangoRESTSerializer.extend({
extractArray: function(store, type, payload) {
console.log(payload);
//console.log(JSON.stringify(payload));
var features = payload["features"];
var nPayload = [];
for (var i = features.length - 1; i >= 0; i--) {
var message = features[i];
var nmessage = {"id": message.id};
for(var entry in message.properties){
var props = message.properties;
if (message.properties.hasOwnProperty(entry)) {
var obj = {}
nmessage[entry]=props[entry];
}
}
nPayload.push(nmessage);
};
console.log(nPayload); //prints in the format above
this._super(store, type, nPayload);
},
})
But I receive the following error:
The response from a findAll must be an Array, not undefined
What am I missing here? Or is this the wrong approach? Has anyone ever tried to get this to work?
An alternative would be to handle this on the serverside and simply output a regular restframework response and set lat and long in the backend.
This is not a valid answer for the question above. I wanted to share my solution anyways,
just in case anyone ever gets into the same situation:
I now do not return a valid geoJSON, but custom lat, lng values. The following is backend code for django-rest-framework:
Model:
#models/message.py
class Message(models.Model):
def lat(self):
return self.location.coords[1]
def lng(self):
return self.location.coords[0]
And in the serializer:
#message/serializer.py
class MessageSerializer(serializers.ModelSerializer):
lat = serializers.Field(source="lat")
lng = serializers.Field(source="lng")
Ember can easily handle the format.