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

Back to Part 1

Back to Part 2

Sie haben sich erfolgreich bei xBopp registriert
Willkommen zurück! Ihre Anmeldung war erfolgreich.
Super! Ihre Registrierung war erfolgreich.
Gemacht! Ihre neue Email-Adresse wurde erfolgreich registriert.
Ihr Link ist abgelaufen
Super! Prüfen Sie Ihren Email-Eingang auf unseren Link zur Anmeldung.