Strapi v4: no relational fields when populating - javascript

I'm trying to populate a specific relation, using the relation name (categories) in combination with the populate parameter but it doesn't populate the categories.
When I look at my schema, I see that the relational field is present in the attributes object. But I still only get the non-relational fields in my response.
I tried every combination mentioned on the Strapi documentation but none of them worked.
The find permission is also enabled for the content-types that are being populated which in this case is categories.
/api/products?populate=*
/api/products?populate[0]=categories
/api/products?populate[categories]=*
My Product schema
{
"kind": "collectionType",
"collectionName": "products",
"info": {
"singularName": "product",
"pluralName": "products",
"displayName": "Product",
"description": ""
},
"options": {
"draftAndPublish": true
},
"pluginOptions": {},
"attributes": {
"title": {
"type": "string"
},
"images": {
"type": "media",
"multiple": true,
"required": false,
"allowedTypes": [
"images"
]
},
"categories": {
"type": "relation",
"relation": "oneToMany",
"target": "api::category.category"
}
}
}
System
Strapi version: 4.1.8
NPM version: 8.3.2
Node.js version: 16.13.2
Database: MySQL

You need to enable find permission on the product as well. You have to set the find permission on all the relationships (or sub-tables).
Use the "role" option.

Edit: Other choice is to use populate deep from strapi marketplace, just install, configure the default depth and then on every request where you want deep just add populate: deep
you will have to populate inside your controller, as in Strapi documentation
Query Engine API: Populating
strapi.db.query('api::article.article').findMany({
populate: true,
});
Entity Service API: Populating
const entries = await strapi.entityService.findMany('api::article.article', {
populate: '*',
});
REST API: Population & Field Selection
const qs = require('qs');
const query = qs.stringify({
fields: ['title', 'body'],
}, {
encodeValuesOnly: true,
});
If you want to populate everything just use populate: '*'
If you want to populate a relation or more use populate: [relationOne, relationTwo]

As an Example If you want to fetch data from db based on some conditions and populate one relationship
const transactions = await strapi
.query("api::transaction.transaction")
.findMany({
where: { user: userId },
populate: { offer: true },
});
here the result
If you want to populate all relations
const transactions = await strapi
.query("api::transaction.transaction")
.findMany({
where: { user: userId },
populate: "*",
});

Related

Modify Response Object in strapi

I would like to get a modified response object. For example I dont know how to get the user object without the roles.
The default response is:
{
"id": 6,
"username": "username",
"email": "user#email.com",
"provider": "local",
"confirmed": true,
"blocked": false,
"role": {
"id": 2,
"name": "Authenticated",
"description": "Default role given to authenticated user.",
"type": "authenticated"
}
}
Now I want to get the same response without the role attribute.
{
"id": 6,
"username": "username",
"email": "user#email.com",
"provider": "local",
"confirmed": true,
"blocked": false
}
Currently you cannot do this in the Rest API unless you change the UserController provided by permissions plugin, which is not recommended.
What you can do then is to use the GraphQL plugin provided by Strapi, so you can query only the fields you need on client side.
The docs about how to use GraphQL plugin are here.
For anyone still struggling with this problem:
The latest versions of strapi do support custom queries, you can pass an array containing all the names of relations you wish to populate (only relations!).
If you don't want to populate any relationships, you can keep it empty, your controller would then look something like this:
module.exports = {
UserWithoutRoles: ctx => {
return strapi.query('user').findOne({ id: ctx.params.id }, ['']);
}
}
If you do wish to populate it, it would be like this:
module.exports = {
UserWithoutRoles: ctx => {
return strapi.query('user').findOne({ id: ctx.params.id }, ['role']);
}
}
Also see:
[https://strapi.io/documentation/3.0.0-beta.x/concepts/queries.html#api-reference][1]

Firebase Fanout Structure List Query

For the Firebase fanout data structure example of users and groups, what would be the most efficient way to retrieve user/member ("users") detail data for a given group from the "groups" list? Let's say the goal was to display the "name" property for each member of group "techpioneers"?
{
"users": {
"alovelace": {
"name": "Ada Lovelace",
"groups": {
"techpioneers": true,
"womentechmakers": true
}
},
},
"groups": {
"techpioneers": {
"name": "Historical Tech Pioneers",
"members": {
"alovelace": true,
"ghopper": true
}
},
...
}
}
Would using a combination of orderByChild() and equalTo() be the best approach to find users who have "techpioneers" key and get at each of their data such as the "name" property? How would you access a property such as "name" once you've determined which users are part of the target group?
let usersRef = firebase.database().ref('users')
usersRef.orderByChild("groups").equalTo('techpioneers').on('child_added', (snapshot) => {
// how would you access user "alovelace" name property?
console.log(snapshot.val().name);
});
Thank you for any help you can provide.

Ember.js findRecord() called with id null for model with alternative primary key

I have an issue with my Ember.js application. It uses the JSONAPI{Adapter,Serializer} and the following model:
models/node.js
App.Node = DS.Model.extend(
{
// (node 'name' field used as primary key for serialization)
info: DS.attr('string'),
children: DS.hasMany('node', { inverse: null })
}
Which represents a tree of named nodes.
The JSONAPIAdapter (adapters/application.js) implements the functions queryRecord(), query(), findRecord(), findAll() to translate the Ember.js queries from the server. This all works fine.
The JSONAPISerializer implements the function normalizeResponse() to translate the server response JSON data to json:api format. In the serializer, the primary key is defined to be the 'name' field of the node:
serializers/application.js
App.ApplicationSerializer = DS.JSONAPISerializer.extend(
{
primaryKey: 'name',
normalizeResponse(store, primaryModelClass, payload, id, requestType)
{
// ...
}
});
A sample of the json:api data generated by the serializer is:
{
"data": [
{
"type": "node",
"attributes": {
"info": "Root node"
},
"relationships": {
"children": {
"data": [
{
"type": "node",
"name": "Root/SubNode1"
}
]
}
},
"name": "Root"
}
],
"included": [
{
"type": "node",
"attributes": {
"info": "Subnode 1"
},
"relationships": {
"children": {
"data": [
]
}
},
"name": "Root/SubNode1"
}
]
}
I use Ember.js version 2.7.0 and Ember inspector.
Once the application is running, and data is loaded in the model, I can see the data in Ember inspector being visible in the model. However, when investigating the model data in the 'Data' view (and selecting an item), I find that Ember is invoking adapter:findRecord() with id = null, resulting in an erroneous query. Somehow it seems the model data is incorrect.
When I remove the primary key definition in the JSONAPISerializer and duplicate the name field of a node in the id field as the Ember default primary key, all is fine. What am I missing with my primary key definition? The Ember guide only states information about the primaryKey in the serializer (https://guides.emberjs.com/v2.7.0/models/customizing-serializers/#toc_ids).
Many thanks in advance!
You need to define name field to have a place to save the id.
App.Node = DS.Model.extend(
{
// (node 'name' field used as primary key for serialization)
name: DS.attr('string'),
info: DS.attr('string'),
children: DS.hasMany('node', { inverse: null })
}

Mongoose - use a post method to create a new empty collection

Libraries in use: Express, Mongoose, Express-Restify-Mongoose
Problem: I am trying to figure out how to create a POST request that will provide the schema in the req.body. I want to simply create a new collection if it does not already exist and enforce that new schema.
when I use the following code:
app.use('/api/v1', function(req, res, next) {
if(req.path[0] === '/' && -1 === req.path.indexOf('/', 1) && req.method === 'POST') {
var collection_name = req.path.substr(1, req.path.length - 1).toLowerCase();
if(mongoose.modelNames().indexOf(collection_name) === -1) {
// only create if model does not exist
console.log(req.body);
var schema = new mongoose.Schema({}, { strict: false, collection: collection_name });
var model = mongoose.model(collection_name, schema);
restify.serve(app, model, { plural: false, name: collection_name });
}
}
next();
});
It also posts an empty document to that collection. If I change the code ever so slightly so that var schema uses the post's req.body to determine the schema the POST request does not go through:
var schema = new mongoose.Schema(req.body, { strict: false, collection: collection_name });
Where the req.body from the POST is:
{
"update_count": { "type": "String", "required": "false" },
"created_date": { "type": "String", "required": "false" },
"created_by": { "type": "String", "required": "false" },
"updated_date": { "type": "String", "required": "false" },
"updated_by": { "type": "String", "required": "false" }
}
Which returns an error and does not complete the POST request because it is also trying to use that same req.body to follow the schema I've just set AND it wants to use the req.body to enter into the document.
{
"message": "testcollection3 validation failed",
"name": "ValidationError",
"errors": {
"updated_by": {
"message": "Cast to String failed for value \"[object Object]\" at path \"updated_by\"",
"name": "CastError",
"kind": "String",
"value": {
"type": "String",
"required": "false"
},
"path": "updated_by"
},
..................................
How can I set the schema with my post and also prevent a document from being created?
As you've seen, Mongoose won't create a model's collection until it needs to save a document to it. However, you can create the collection explicitly using the Db#createCollection method from the native MongoDB driver that's accessible via mongoose.connection.db:
mongoose.connection.db.createCollection(collection_name, (err) => {...});
Mongoose seems to create a still missing collection any time it is needed for some data access operation. So for me, (using v4.5.9) this works:
mongoose.connection.db.findOne({}).then(...).catch(...);

Waterline.js: Populate association from list

Does anyone know if it's possible to populate a list of IDs for another model using waterline associations? I was trying to get the many-to-many association working but I don't think it applies here since one side of the relationship doesn't know about the other. Meaning, a user can be a part of many groups but groups don't know which users belong to them. For example, I'm currently working with a model with data in mongodb that looks like:
// Group
{
_id: group01,
var: 'somedata',
},
{
_id: group02,
var: 'somedata',
},
{
_id: group03,
var: 'somedata',
}
// User
{
_id: 1234,
name: 'Jim',
groups: ['group01', 'group03']
}
And I'm trying to figure out if it's possible to setup the models with an association in such a way that the following is returned when querying the user:
// Req: /api/users/1234
// Desired result
{
id: 1234,
name: 'Jim',
groups: [
{
_id: group01,
var: 'somedata',
},
{
_id: group03,
var: 'somedata',
}
]
}
Yes, associations are supported in sails 0.10.x onwards. Here is how you can setup the models
Here is how your user model will look like:
// User.js
module.exports = {
tableName: "users",
attributes: {
name: {
type: "string",
required: true
},
groups: {
collection: "group",
via: "id"
}
}
};
Here is how your group model will look like:
// Group.js
module.exports = {
tableName: "groups",
attributes: {
name: {
type: "string",
required: "true"
}
}
};
Setting up models like this will create three tables in your DB:
users,
groups and
group_id__user_group
The last table is created by waterline to save the associations. Now go on and create groups. Once groups are created, go ahead and create user.
Here is a sample POST request for creation a new user
{
"name": "user1",
"groups": ["547d84f691bff6663ad08147", "547d850c91bff6663ad08148"]
}
This will insert data into the group_id__user_group in the following manner
{
"_id" : ObjectId("547d854591bff6663ad0814a"),
"group_id" : ObjectId("547d84f691bff6663ad08147"),
"user_groups" : ObjectId("547d854591bff6663ad08149")
}
/* 1 */
{
"_id" : ObjectId("547d854591bff6663ad0814b"),
"group_id" : ObjectId("547d850c91bff6663ad08148"),
"user_groups" : ObjectId("547d854591bff6663ad08149")
}
The column user_groups is the user id. And group_id is the group id. Now if you fetch the user using GET request, your response will look like this:
{
"groups": [
{
"name": "group1",
"createdAt": "2014-12-02T09:23:02.510Z",
"updatedAt": "2014-12-02T09:23:02.510Z",
"id": "547d84f691bff6663ad08147"
},
{
"name": "group2",
"createdAt": "2014-12-02T09:23:24.851Z",
"updatedAt": "2014-12-02T09:23:24.851Z",
"id": "547d850c91bff6663ad08148"
}
],
"name": "user1",
"createdAt": "2014-12-02T09:24:21.182Z",
"updatedAt": "2014-12-02T09:24:21.188Z",
"id": "547d854591bff6663ad08149"
}
Please note that groups are not embedded in the user collection. Waterline does the fetch from groups, users and group_id__user_group to show this result to you.
Also, if you want to do this in your controller, you will need to execute like this
User.findOne({'id': "547d854591bff6663ad08149"})
.populate('groups')
.exec(function (err, user){
// handle error and results in this callback
});
Without populate('groups'), you won't get the groups array. Hope this serves your purpose

Categories

Resources