Website Relaunch / Python, Flask & Ghost

14 April 2018 in general

Website Relaunch / Python, Flask & Ghost

My new website launched these weekend and i think its a lot lot... better now.
The design is based on a purchased HTML5 / CSS3 / JQuery Template and integrated in a python flask app and a ghost blog.


I was thinking about some portfolio platforms like semplice or templatesystems for wordpress. Since my last website was completly made with squarespace i wanted a new alernative with more control.

Sadly, my PHP skills are a little bit rusty and wordpress is relativly big and slow. So, i decided to make a little python webapp and serve it via nginx.

The structure is:

  • simple Python web app with Flask
  • Serving via uwsgi to:
  • nginx

I love writing in markdown and i think the ghost platform is a great and fast way to archieve this. Luckily the purchased template had some basic blog post support. Some little tweaks and adapting the template to a ghost blog theme was enough to make it work.


  • Ghost blog via ghost-cli
  • serving/proxy via nginx

The Flask App and the ghost blog have 2 different root directories on the same domain via nginx. I think the illusion of one main website is good enough. The only thing to remember is:

Change the navigation and footer on both sites/apps. :)


I dont need a full featured python web app with admin interface (There are some very good flask boilerplates out). The main feature is:
Load Images and display the images in a cool way.

The cool display is given by the template. So, we need only some basic python code to make it work together.

A little example of my (not cleaned up and finished) Project Site code:

def getProjects():
    global projects
    projects = []
    # projectFolders = os.listdir(projectPath)

    with open(os.path.join(projectPath, 'sort.txt')) as f:
        projectFolders = f.readlines()
    projectFolders = [x.strip() for x in projectFolders]

    # projectFolders = [s for s in os.listdir(projectPath) if os.path.isdir(os.path.join(projectPath, s))]
    # projectFolders.sort(key=lambda s: os.path.getctime(os.path.join(projectPath, s)), reverse=False)

    for projectFolder in projectFolders:

        if os.path.isdir(os.path.join(projectPath, projectFolder)):
            global projectImages
            global projectVideos
            projectImages = []
            projectVideos = []

            for projectFile in os.listdir(os.path.join(projectPath, projectFolder)):

                if 'title' in projectFile and projectFile.endswith('.jpg'):
                    projectTitleImage = os.path.join('/static', 'img', 'portfolio', 'projects', projectFolder, projectFile).replace('\\', '/').replace(' ', '%20')

                if projectFile.endswith('.ini'):
                    info = configparser.ConfigParser()
          , projectFolder, projectFile), encoding='utf-8')
                        projectClient = info['info']['client']
                        projectClient = 'NA'
                        projectAgency = info['info']['agency']
                        projectAgency = 'NA'
                        projectPhotographer = info['info']['photographer']
                        projectPhotographer = 'NA'
                        projectProduction = info['info']['production']
                        projectProduction = 'NA'
                        projectWork = info['info']['work']
                        projectWork = 'NA'
                        projectPosition = info['info']['position']
                        projectPosition = 'NA'
                        projectRole = info['info']['role']
                        projectRole = 'NA'
                        projectDescription = BeautifulSoup(info['info']['description'], 'html.parser')
                        projectDescription = 'NA'

                    projectCaption = 'Client: ' \
                                     + projectClient \
                                     + ' | Agency: ' \
                                     + projectAgency \
                                     + ' | Photographer: ' \
                                     + projectPhotographer \
                                     + ' | Production: ' \
                                     + projectProduction
                    projectCaption = projectCaption


                if projectFile.endswith('.jpg'):
                    projectImages.append(os.path.join('/static', 'img', 'portfolio', 'projects', projectFolder, projectFile).replace('\\', '/').replace(' ', '%20'))
                if projectFile.endswith('.mp4'):
                    projectVideos.append(os.path.join('/static', 'img', 'portfolio', 'projects', projectFolder, projectFile).replace('\\', '/').replace(' ', '%20'))

            projectLink = os.path.join('/portfolio/projects', projectFolder)

                'title': projectFolder,
                'image': projectTitleImage,
                'caption': projectCaption,
                'client': projectClient,
                'agency': projectAgency,
                'photographer': projectPhotographer,
                'production': projectProduction,
                'work': projectWork,
                'position': projectPosition,
                'role': projectRole,
                'link': projectLink,
                'description': projectDescription,
                'imageLinks': projectImages,
                'videoLinks': projectVideos

Basic description

  • We setup the path to our Project directory
  • We open the file sort.txt (Projectfolder name per line to sort it manually)
  • We are going to every project folder and check it for images (in sort.txt order)
  • If an image is found with "title" in the name it becomes the titelimage (project overview)
  • We read the ini file in each project (infos about the project)
  • We pushing all the data for every project to a variable

Then we can render our template site and give the parameters/infos:

def portfolioProjectOverview():
    global projects

    return render_template('pages/placeholder.portfolioOverview.html',
                           title='PORTFOLIO PROJECTS OVERVIEW' + __title__)

Basic description

  • We setup the webserver path
  • We get the variable from the project function
  • We render our template with the parameters

For the dynamic Project Single view we can make something like that:

def portfolioProjectView(projectname):

    for project in projects:
        projectDetails = []
        if projectname in project['title']:
            return render_template('pages/placeholder.projectView.html',
                                   title='PORTFOLIO OVERVIEW' + __title__)
            projectDetails = ''

This getting the url from the user and checks if the project is exisiting (previous function).


I think with a little bit python knowledge we can make some really great simple portfolio websites. It is also very fast if you know a little bit about caching etc.