Add StreamBlock child items programmatically in Wagtail - wagtail

I have the following StructBlock and StreamBlock below:
class AccordionItemBlock(StructBlock):
title = CharBlock()
text = RichTextBlock()
class AccordionRepeaterBlock(StreamBlock):
accordion_item = AccordionItemBlock()
I need to programmatically add it and multiple "item" CharBlocks to this page:
class BasicPage(Page):
body = StreamField([
('accordion_repeater_block', AccordionRepeaterBlock()),
], null=True)
This is how I am approaching it
page.body = [
(
'accordion_repeater_block',
{
'accordion_item',
{
'title': 'Title goes here',
'text': RichText('Testing!'),
}
}
)
]
provider.save()
I get errors no matter what I put in the second tuple value after 'accordion_repeater_block'. Any ideas how to solve this?

Found the solution! You have to make the child blocks an instance of StreamValue.StreamChild:
from wagtail.wagtailcore.blocks.stream_block import StreamValue
page.body = [
('accordion_repeater_block',
[
StreamValue.StreamChild(
id=None,
block=AccordionItemBlock(),
value={
'title': 'Title goes here',
'text': RichText('Testing!'),
}
),
]
)
]
page.save()

For Wagtail 4.1, the accepted answer didn't work for me. This is what did:
accordion_item_data = {
'title': 'Title goes here',
'text': RichText('Testing!'),
}
page.body = StreamValue(
stream_block=page.body.stream_block,
stream_data=[
{
"type": "accordion_repeater_block",
"value": [{"type": "accordion_item", "value": accordion_item_data }]}
],
is_lazy=True,
)
page.save()
I had to look at the Wagtail source code and do a lot of trial-and-error as there is no hints on the documentation ćƒ½(*怂>Š”<)o悜

Related

add/modify blocks value in wagtail streamfield with listblock from shell

I have a page with one StreamField body and a custom block named CardBlock:
class CardBlock(blocks.StructBlock):
title = blocks.CharBlock()
description = blocks.CharBlock()
...
class CardListBlock(blocks.StructBlock):
cards = ListBlock(CardBlock())
class CustomPage(Page):
...
body = StreamField([
('card_block', CardBlock()),
('card_list', CardListBlock())
]),
...
I need to create a CustomPage programatically with card_list populated.
I tried the solution provided by #gasman. While it works for a normal StructBlock, when having nested ListBlock within a StructBlock, the value is not saved.
I tried the following:
page = CustomPage()
page.body = [
('card_block', {'title': 'foo', 'description': 'bar'}),
('card_list', {
'cards': [{'title': 'foo', 'description': 'bar'}]
})
]
results in AttributeError: 'list' object has no attribute 'bound_blocks'
So, I wrapped the list in ListValue from wagtail.blocks.list_block.
page.body = [
('card_block', {'title': 'foo', 'description': 'bar'}),
('card_list', {
'cards': ListValue([{'title': 'foo', 'description': 'bar'}]))
}
]
It would result in no errors, but when looked into db, the card_list would have an empty list.
I tried using StructValue:
('card_list', {
'cards': ListValue([
StructValue([('title', 'foo'), ('description', "bar")])
])
})
same result.
So, I tried to use the data result by saving the Page from wagtail admin with no avail.
('card_list', {
'cards': ListValue([{
"type": "item",
"value": {
"title": "foo",
"description": "bar"},
"id": "uuid"
}])
})
Is there something I am missing out on this?

MongoError: Found multiple array filters with the same top-level field name

folks. I'm working in Node.js with a MongoDB collection that has a field that is an array of objects, like so:
{_id: 'someIdNumber',
text: 'some text',
replies: [{_id: 'someReplyId', replyText: 'some reply text', password: 'somePassword'}, {...}]
I'm trying to update the replyText field of the replies array using the $[<identifier>] array update operator as shown in the MongoDB documentation: https://docs.mongodb.com/manual/reference/operator/update/positional-filtered/ What I'm trying to do is as follows:
db.collection('collectionName').updateOne(
{ _id: ObjectID('whateverId') },
{ $set: { "replies.$[elem].replyText": "new text" } },
{
arrayFilters: [{ "elem._id": ObjectID(req.body.reply_id)}, {"elem.password": 'whateverPassword}]
},
(err, data) => {console.log('hooray, it worked')}
This throws an error, MongoError: Found multiple array filters with the same top-level field name elem. If I get rid of one of my arrayFilters, this fixes the error, but obviously at the expense of my filtering conditions.
The MongoDB documentation's example of this process, (I've shortened the collection students2 to a single document,) is as follows:
{
"_id" : 1,
"grades" : [
{ "grade" : 80, "mean" : 75, "std" : 6 },
{ "grade" : 85, "mean" : 100, "std" : 4 },
{ "grade" : 85, "mean" : 100, "std" : 6 }
]
}
db.students2.update(
{ },
{ $inc: { "grades.$[elem].std" : -1 } },
{ arrayFilters: [ { "elem.grade": { $gte: 80 }, "elem.std": { $gt: 5 } } ], multi: true }
)
The syntax is a tiny bit different because the documentation is using the Mongo shell method, not Node.js, but otherwise, it looks to me like I'm doing what the documentation says to do. I'm using updateOne and not update because I only want to update one document, and I'm not using multi for the same reason.
Any insight on how I could get this to work with both arrayFilters intact would be much appreciated.
Thanks for your time!
Got it! Unfortunately I cannot upvote the question/comment hence adding here for others to benefit. Thanks #LuosRestil and #typesafe for getting me to the fix. I had the same syntax error where I tried to add multiple expressions for the same field in the array for the arrayFilters. It should be once expression per field.
WRONG:
arrayFilters: [
{ "elemA.<fieldName1>": "value1" },
{ "elemA.<fieldName2>": "value2" },
{ "elemA.<fieldName3>": "value3" }
];
CORRECT:
arrayFilters: [
{
"elemA.<fieldName1>": "value1",
"elemA.<fieldName2>": "value2",
"elemA.<fieldName3>": "value3"
}
];

Show objects from array which contain only a specific property

I'm new to mongodb and I'd like to help me if you can. I have a collection of documents (songs) like the following:
{
title: 'song number 1',
duration: '3:23',
text: [
{chords: 'some chords'},
{lyrics: 'some lyrics'},
{chords: 'again chords'},
{lyrics: 'again lyrics'}
]
},
{
title: 'song number 23',
duration: '4:22',
text: [
{chords: 'some chords'},
{lyrics: 'some lyrics'},
{chords: 'again chords'},
{lyrics: 'again lyrics'}
]
}
I want to retrieve for every document (song) ONLY the chords from the text array.
Any ideas please?
This aggregation query filters the text array elements where the chords field exists or not empty or not null.
db.test.aggregate( [
{
$addFields: {
text: {
$filter: {
input: "$text",
as: "txt",
cond: {
$gt: [ { $strLenCP: { $ifNull: [ "$$txt.chords", "" ] } }, 0 ]
}
}
}
}
}
] )
The result will look like this (with the first document from the question post as input):
{
"_id" : 1,
"title" : "song number 1",
"duration" : "3:23",
"text" : [
{
"chords" : "some chords"
},
{
"chords" : "again chords"
}
]
}
I finally got it! I needed this query
db.test.aggregate([
{$unwind: "$text"},
{$match: {"text.chords":{$exists:true}}},
{$project: {'text.chords':1}}
]).pretty()
This query shows only the chords subdocuments of every song, not the lyrics.
The key of the query (I think) is $unwind.
Thank you everyone for your answers and the help.

Nested query in Reactive Component - ReactJS

I try to update query of the component by using setQuery() prop. My data is structured like this.
{
root : {
category 1: {
item 1 {...},
item 2 {...},
},
category 2: {
item 1 {...},
item 2 {...},
},
...
}
}
I need to get item 1 under the category 1. When user select item 1 I update query like this. But it doesn't work.
this.props.setQuery({
"query": {
"bool": {
"must": [
{
"query_string": {
"query": 'category 1 AND item 1',
"fields": ["item", "category", "keywords"],
"analyzer": "standard"
}
}
]
}
}
});
I try find out how to solve this using nested query. But I couldn't find any use full thing. Please give me any clue to achieve this.
I found a solution for this.
this.props.setQuery({
query: {
bool: {
must: [
{
terms: {
'parentName': ['category 1']
},
},
{
terms: {
'childName': 'item 1',
},
},
],
},
},
});
I think this will helps to someone.

Can I send 2 dimension array as parameter in POSTMAN?

I need to send parameters as array of objects in POSTMAN.
"array": [
{"field1": "html", "field2": "5"},
{"field1": "css", "field2": "3"}
]
I know that array must send as array[] but how can I set one item of the array as an object?
I tried this
"array[]" : "{"field1": "html", "field2": "5"}"
But I'm getting a 500 response error.
Just send it in raw format(in json) and specify data type as application/json. Worked for me
If array of arrays works for you as well, you can send them like this:
Go to bulk edit
userdata:[{"name":"name1","email":"email1#gmail.com"},{"name":"name2","email":"email2#gmail.com"},{"name":"name3","email":"email3#gmail.com"}]
I found the solution adding raw data as JSON.
from body > raw
[
{
"text": "Buy fish",
"completed": true
},
{
"text": "Go for walk check",
"completed": false
},
{
"text": "Pick baby from school",
"completed": false
}
]
I know this ia an old post but this might help someone out there.... it's what worked for me:
In your controller: (Laravel/PHP)
$validator = Validator::make($request->all(), [
'fields' => 'nullable|array|min:1',
'fields.key1.*' => 'integer',
'fields.key2.*' => 'integer'
]);
if ($validator->fails())
{
return ...
}
Then in Postman:
fields[0]['key1']
fields[0]['key2']
.... and you can repeat this as many times as you need to, just increment the index
fields[1]['key1']
fields[1]['key2']
If you dd(fields) in your controller, you'd get:
0 => [
key1 => value1
key2 => value2
]
1 => [
key1 => value3
key2 => value4
]
.....

Resources