Ghost API & Python 3.x, #3
In this third part we create, update and delete content.
First we create a post. We add the method createPost().
def createPost(self, title, body, bodyFormat='html', excerpt = None, tags=None, authors=None, status='draft', featured=False, featureImage=None, slug=None):
"""
Args:
body (string): the content of a post
bodyFormat (string): 'html', or 'markdown'
excerpt (string): the excerpt for a post
tags (list): a list of dictionaries e.g. [{'name':'my new tag', 'description': 'a very new tag'}]
authors (list): a list of dictionaries e.g. [{'name':'Jacques Bopp', 'slug': 'jacques'}]
status (string): 'published' or 'draft'
featured (bool): if the post should be featured
featureImage (string): the image url (e.g. "content/images/2020/09/featureImage1.jpg" -> see uploadImage()
Returns:
result (string): if the creation was successful or not
"""
content = {'title': title}
if bodyFormat == 'markdown': content['mobiledoc'] = json.dumps({ 'version': '0.3.1', 'markups': [], 'atoms': [], 'cards': [['markdown', {'cardName': 'markdown', 'markdown': body}]], 'sections': [[10, 0]]});
else: content['html'] = body
if excerpt != None: content['custom_excerpt'] = excerpt
if tags != None: content['tags'] = tags
if authors != None: content['authors'] = authors
content['status'] = status
content['featured'] = featured
if featureImage != None: content['feature_image'] = self.site['url']+featureImage
if slug != None: content['slug'] = slug
url = self.site['url']+'ghost/api/v3/admin/posts/'
params = {'source': 'html'}
result = requests.post(url, params=params, json={"posts": [content]}, headers=self.headers)
if result.ok: result = 'success: post created (status_code:'+str(result.status_code)+')'
else: result = 'error: post not created (status_code:'+str(result.status_code)+')'
return result
All arguments are described in the code block above. Our first example doesn't use images:
if __name__ == '__main__':
ga = GhostAdmin('somedomain1')
title = 'new post'
body = """<div>Lorem ipsum dolor sit amet</div>"""
excerpt = 'this post is about ...'
tags = [{'name':'my new tag', 'description': 'a new tag'}]
authors = [{'name':'Jacques Bopp', 'slug': 'jacques'}]
slug = 'my-new-post'
result = ga.createPost(title, body, bodyFormat='html', excerpt = excerpt, tags=tags, authors=authors, status='draft', featured=False, featureImage=None, slug=slug)
createPost() returns a string. This either starts with 'success: ' or with 'error: ' and shows the status code.
If we want to include images, we have to upload them first to our Ghost site. We add two methods to our class. loadImage(imagePathAndName) and imageUpload(imageName, imageObject). With loadImage() we load an image e.g. from our harddisk into an image object. This image object we then send to imageUpload() together with its name. We receive a result string starting with either 'success:' plus the full url of the image or 'error:' plus the status code.
Ghost automatically saves the image within '/ghost/content/yyyy/mm/' where 'yyyy/mm' shows the year and month of the upload.
def loadImage(self, imagePathAndName):
image = open(imagePathAndName, 'rb')
imageObject = image.read()
image.close()
image = BytesIO(imageObject)
return image
def imageUpload(self, imageName, imageObject):
url = self.site['url'] + 'ghost/api/v3/admin/images/upload/'
files = {"file": (imageName, imageObject, 'image/jpeg')}
params = {'purpose': 'image', 'ref': imageName} # 'image', 'profile_image', 'icon'
result = requests.post(url, files=files, params=params, headers=self.headers)
if result.ok: result = 'success: '+json.loads(result.text)['images'][0]['url']
else: result = 'error: upload failed (' + str(result.status_code) + ')'
return result
Let's upload an image:
if __name__ == '__main__':
ga = GhostAdmin('somedomain1')
image = ga.loadImage('c:/tmp/image1.jpg')
result = ga.imageUpload('featureImage1.jpg', image)
The result is: 'success: https://somedomain1/content/images/2020/12/featureImage1.jpg'
We now can use the part after 'somedomain1' 'content/images/2020/12/featureImage1.jpg' when we want to create a post with a feature image:
if __name__ == '__main__':
ga = GhostAdmin('somedomain1')
title = 'new post 2'
body = """<div>Lorem ipsum invidunt invidunt invidunt invidunt ut labore et dolore magna aliquyam</div>"""
excerpt = 'this is my second post ...'
tags = [{'name':'my second new tag', 'description': 'a second new tag'},{'name':'my third new tag', 'description': 'a third new tag'}]
authors = [{'name':'Jacques Bopp', 'slug': 'jacques'},{'name':'Ghost', 'slug': 'ghost'}]
featureImage = 'content/images/2020/12/featureImage1.jpg'
result = ga.createPost(title, body, bodyFormat='html', excerpt=excerpt, tags=tags, authors=authors, status='draft', featured=False, featureImage=featureImage)
The code for updating a post is nearly identical with the one for creating a post:
def updatePostByTitle(self, oldTitle, newTitle, body, bodyFormat='html', excerpt = None, tags=None, authors=None, status='draft', featured=False, featureImage=None):
posts = self.getPostByTitle(oldTitle)
if len(posts) > 1: result = 'error: more than 1 post found'
elif len(posts) == 0: result = 'error: no post found'
else:
post = posts[0]
content = {'title': newTitle}
if bodyFormat == 'markdown': content['mobiledoc'] = json.dumps({ 'version': '0.3.1', 'markups': [], 'atoms': [], 'cards': [['markdown', {'cardName': 'markdown', 'markdown': body}]], 'sections': [[10, 0]]});
else: content['html'] = body
if excerpt != None: content['custom_excerpt'] = excerpt
if tags != None: content['tags'] = tags
if authors != None: content['authors'] = authors
content['status'] = status
content['featured'] = featured
if featureImage != None: content['feature_image'] = self.site['url']+featureImage
content['updated_at'] = post['updated_at']
url = self.site['url']+'ghost/api/v3/admin/posts/'+post['id']+'/'
result = requests.put(url, json={"posts": [content]}, headers=self.headers)
if result.ok: result = 'success: post updated (status_code:' + str(result.status_code) + ')'
else: result = 'error: post not updated (status_code:' + str(result.status_code) + ')'
return result
The only difference are the 2 first arguments: oldTitle and newTitle. oldTitle is used to find the post. If more than 1 post is found an error is returned. newTitle can equal oldTitle.
Finally we add the method deletePostById():
def deletePostById(self, id):
url = self.site['url'] + 'ghost/api/v3/admin/posts/' + id + '/'
result = requests.delete(url, headers=self.headers)
if result.ok: result = 'success: post deleted (status_code:' + str(result.status_code) + ')'
else: result = 'error: post NOT deleted (status_code:' + str(result.status_code) + ')'
return result
The file ghostAdmin.py with the class GhostAdmin and all its methods and examples is here.
I hope this little tutorial was helpful. For any question, comments you can contact me: JacquesBopp