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

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?

Related

Mongoose find all documents that have a string in an array

I've a question:
How i can find all documents that have a string in an array using mongoose?
For example, my document:
<Model>.findMany(/* code that i need */).exec() // return all documents that have an array called "tags" that includes tag "test"
{
"_id": {
"$oid": "61b129b7dd0906ad4a2efb74"
},
"id": "843104500713127946",
"description": "Server di prova",
"tags": [
"developers",
"programming",
"chatting",
"ita"
],
"shortDescription": "Siamo un server nato per chattare e aiutare programmatori su Discord!",
"invite": "https://discord.gg/NdqUqHBxz9",
"__v": 0
}
For example, if I need to get all documents with ita tag, I need to get this document.
If the document doesn't have ita tag in array tag, I don't need it and the code will not return it.
Thanks in advance and sorry for bad english.
Actually you can just request tags to be test since mongoose looks for every tags entry
so this:
await Test.insertMany([{ tags: ["test"] }, { tags: ["Notest"] }])
let res = await Test.find({ "tags": "test" })
console.log(res)
}
returns that:
[
{
_id: new ObjectId("61b8af02c3effad21a5d7187"),
tags: [ 'test' ],
__v: 0
}
]
2 more neat things to know:
This works no matter how many entries tags has, as long test is one of ethem
This also enables you to change the entrie containing the "test" by using positional $ operator, so something like {$set: "tags.$": "smthNew"} will change all the test entries
Example for 2nd:
let res = await Test.updateMany({ "tags": "test" }, { $set: { "tags.$": "new" } }, { new: true })

I need to get a nested element in a json array

There is some list of elements in json format, it looks like this:
[{
'id': 0,
"name": "Category name1",
"services": [{
"id": 0,
"name": "Product name1"
}, {
"id": 1,
"name": "Product name2"
}]
},
{
'id': 1,
'name': "Category name2",
"services": [{
"id": 0,
"name": "Product name1"
}, {
"id": 1,
"name": "Product name2"
}]
}
]
I'm trying to get only the entire "services" array from the first category. Conditionally, I'm trying to get it as follows:
this.class = this.http.get('/assets/products.json');
this.class.forEach(element => {
if (element.id == ID) //The ID is obtained when calling the function in which this code is executed
{
console.log(element.services);
}
}
However, this gives me absolutely nothing and "undefined" is output to the console, however, with the same array and under the same conditions on the site https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach it (foreach and console.log) outputs everything I need.
//The same foreach only on the mozilla website
array1.forEach(item => {
if(item.id==1){ //1 вместо ID
console.log(item.services);
}});
Output to the console: Array [Object { id: 0, name: "Product name1"}, Object { id: 1, name: "Product name2"}].
P.S. I don't really need this list of products in the console, I'm trying to pass a variable, but since undefined is passed to the variable, as well as to the console, I can't use the variable and call it to display products on the page. All this is used in the typescript angular project.
The HttpClient methods like get return observables, to which you need to subscribe to in order for the request to even get executed. In your situation, the class property is only holding a reference to the observable returned by calling this.http.get. Try subscribing to the observable and use the result to extract the data that you need.
this.http.get<any[]>('/assets/products.json').subscribe((data) => {
this.class = data;
this.class.forEach((element) => {
if (element.id == ID) {
console.log(element.services);
}
});
});

How to access forgin key value in react from django api

I have django api in which i have post model which is linked to comments and categories table by forgin key now i am feching data for post detail and when i try to access category of that post it return s id and i want to access name of category this is my post list view
{
"id": 4,
"title": "BLOG PAGE",
"body": "testing",
"owner": "ankit",
"comments": [],
"categories": [
2
],
"created_at": "2021-05-07T17:22:32.989706Z"
},
{
"id": 5,
"title": "Test Post",
"body": "This is a test Post",
"owner": "ankit",
"comments": [],
"categories": [
2
],
and this is my categories
[
{
"id": 2,
"name": "Python",
"owner": "ankit",
"posts": [
4,
5,
6,
8
]
}
]
and this is my post detail component
export class PostDetail extends React.Component {
constructor(props) {
super(props);
const ID = this.props.match.params.id
this.state = {
data: [],
loaded: false,
placeholder: "Loading"
};
}
formatDate(dateString){
const options = { year: "numeric", month: "long", day: "numeric" }
return new Date(dateString).toLocaleDateString(undefined, options)
}
componentDidMount() {
fetch(`${Url}posts/${this.props.match.params.id}`)
.then(response => {
if (response.status > 400) {
return this.setState(() => {
return { placeholder: "Something went wrong!" };
});
}
return response.json();
})
.then(data => {
this.setState(() => {
return {
data,
loaded: true
};
});
});
}
render(){
return(
<>
<h1 className="main-title">{this.state.data.title}</h1>
<div className="container">
<div className="box1">
<h2>Categories</h2>
<div className="categories">{this.state.data.categories}</div>
</div>
</>
);
}
}
and i am getting output as 2 when i try to get data like mention above
i thought i can access it by putting . in front of categories eg. categories.name but it returns TypeError error
TypeError: Cannot read property 'name' of undefined
this are my serializers
class CategorySerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
posts = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Category
fields = ['id', 'name', 'owner', 'posts']
class PostSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
comments = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
model = Post
fields = ['id', 'title', 'body', 'owner', 'comments', 'categories','created_at']
i try to access category of that post it return s id and i want to access name of category this is my post list view
1. For getting only the name of the category.
You can use the SlugRelatedField.
Modify your PostSerializer like so:
class PostSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
comments = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
categories = serializers.SlugRelatedField(many=True, read_only=True, slug_field='name')
class Meta:
model = Post
fields = ['id', 'title', 'body', 'owner', 'comments', 'categories','created_at']
Example JSON response:
{
"id": 4,
"title": "BLOG PAGE",
"body": "testing",
"owner": "ankit",
"comments": [],
"categories": [
"Python"
],
"created_at": "2021-05-07T17:22:32.989706Z"
},
2. To nest full Category objects
class PostSerializer(serializers.ModelSerializer):
owner = serializers.ReadOnlyField(source='owner.username')
comments = serializers.PrimaryKeyRelatedField(many=True, read_only=True)
categories = CategorySerializer(many=True)
class Meta:
model = Post
fields = ['id', 'title', 'body', 'owner', 'comments', 'categories','created_at']
Example JSON response:
{
"id":4,
"title":"BLOG PAGE",
"body":"testing",
"owner":"ankit",
"comments":[],
"categories":[
{
"id":2,
"name":"Python",
"owner":"ankit",
"posts":[
4,
5,
6,
8
]
}
],
"created_at":"2021-05-07T17:22:32.989706Z"
}

Add StreamBlock child items programmatically in 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゜

How to check before updating an array element in MongoDB/NodeJS

In my sample document, I have a campaign document that contains the _id of the document and an importData array. importData is an array of objects containing a unique date and source value.
My goal is to have an object updated with a unique date/source pair. I would like to have the new object replace any matching object. In the example below, Fred may have originally donated a TV, but I want my application to update the object to reflect he donated both a TV and a radio.
// Events (sample document)
{
"_id" : "Junky Joe's Jubilee",
"importData" : [
{
"date": "2015-05-31",
"source": "Fred",
"items": [
{item: "TV", value: 20.00},
{item: "radio", value: 5.34}
]
},
{
"date": "2015-05-31",
"source": "Mary",
"items": [
{item: "Dresser", value: 225.00}
]
}
]
}
My original thought was to do something like the code below, but not only am I updating importData with Fred's donations, I'm also blowing away anything else in the importData array:
var collection = db.collection("events");
collection.update(
{_id: "Junky Joe's Jubilee",
importData: {
date: "2015-05-31",
source: 'Fred'
},
}, // See if we can find a campaign object with this name
{
$set:
{"importData":
{
date: "2015-05-31",
source: 'Fred',
items: [
{item: "TV", value: 20.00},
{item: "radio", value: 5.34}
]
}
}
},
{upsert: true}); // Create a document if one does not exist for this campaign
When I tried pushing (instead of $set), I was getting multiple entries for the date/source combos (e.g. Fred would appear to have donated two items multiple times on "2015-05-31").
How would I go about doing that with the MongoDB native driver and NodeJS?
Try this
var collection = db.collection("events");
collection.update(
{_id: "Junky Joe's Jubilee",
importData: {
date: "2015-05-31",
source: 'Fred'
},
}, // See if we can find a campaign object with this name
{
$set:
{"importData.$":
{
date: "2015-05-31",
source: 'Fred',
items: [
{item: "TV", value: 20.00},
{item: "radio", value: 5.34}
]
}
}
},
{upsert: true}); // Create a document if one does not exist for this campaign
According to the documentation under Array update operators this should only modify the first element in the array, which matches the query.

Resources