A portfolio website developed for an interior architect and designer using Django 2.2.
This portfolio website was developed to provide a platform for the client, an interior architect and designer, to showcase her work. The site content, including Instagram posts, blogs, and detailed projects, is frequently updated so the client needed a site that was easy to maintain.
User stories for potential visitors to the website include:
After talking about it for years, I'll decided to build an extension to my house. A friend recommended Colette's site. I go straight to the Portfolio page and select a project similar to mine. Having read the story behind the design and build I purchase one of the drawings. I'm taken to a Stripe payment page, I prefer that as it feels more secure. Once complete I see a summary of my order and take note of my reference number. By email I receive both a receipt from Stripe and the drawing pdf from Colette. After mulling it over for a few days, I go back to the site and fill out the contact form leaving a description of my dream extension. Colette responds with useful advice on planning and designing to maximise light.
Having been gifted a site by my Uncle, I've decided to build a holiday home. I would like the house to be as sustainable as possible, but my funds are limited. Having seen an Instagram post by Colette on budget builds, I check out her site. I start with browsing blogs on sustainability and budgeting. I visit some of the portfolio projects. I fill out the contact form outlining my requirements. I'm so impressed by the reply from Colette that we agree to meet to discuss further. She's now drawing up my dream holiday home.
I'm currently enrolled in an interior design course but struggling with a colour rendering project. A fellow student recommended Colette's site. I visit the blog page and find two rendering blogs. I leave a like and a comment on both blogs. I'm sorted - they were just what I needed. I click on Instagram to start following Colette as she often posts when new blogs are uploaded. I know I'll be revisiting the site and recommending it to fellow students.
My firm is looking to hire an architectural technician. I've received an application from Colette and she's provided a link to her site. I review the about section outlining her skills, education and employment. She noted some portfolio projects in her application that have free drawing downloads. From these I can review her design and technical skills. Happy with her skills and breath of experience, I call her for interview.
The client receives an email flag whenever a new comment, contact or purchase is made. She opens the site and clicks admin at the bottom of the home page. On login she can easily see and respond to outstanding comments and contacts. She simply clicks exclude on any comment or contact she would like to close. To update the site or fulfil an order, she enters admin at the end of the URL and logs into the Django admin interface. Styled and customised to look similar to the main site, the interface feels bespoke for the client.
Simplicity is key with the look and flow of the site designed to provide a neutral background for the client's work.
As a portfolio site, the client's name is centred and prominent above the navbar.
The main site is divided into four distinct sections, about, blog, portfolio and contact highlighted by a pared back navbar.
On small devices, the prominent name above the navbar is swapped for one in the navbar when it reaches the top of the screen. From this point the navbar is sticky.
A minimal colour scheme of black, white and stone, provides a neutral background for the client's content. The Google font Montserrat, is used in uppercase for all headings. While another Google font Ibarra Real Nova is used, in both regular and italics styles, for all remaining text. Italics and hover colour changes are used to identify link text. Messages follow a traffic light green, orange and red colour scheme to inform, alert or warn users.
Balsamiq was used to create wireframes for large, medium and small devices. During development, the original design was tweaked based on client feedback and requirements.
Built with Django 2.2.10, the web site contains 7 apps: home, cv, blog, portfolio, contact, cart and users.
As there is no facility for users to register, a guest account has been created for assessment, username: guest password: &B/82a"x To ease of assessment please note:
- About Page: The BA education has a project example.
- About Page: The KOBW employment has a project example.
- Project Page: The 1960s extension project has costed downloads.
- Project Page: The Fordson project has a free download.
The Home App provides a landing page for the site, introducing the client and her work. Portfolio images, blogs, and Instagram posts, entice the visitor to look further. Linked to other internal and external sources, content on this page updates automatically.
| TYPE | NAME | DESCRIPTION |
|---|---|---|
| View | index | Includes 3 projects and 3 blogs. |
| Template | index | Owner introduction, portfolio carousel, most liked blog links, Instagram feed and admin login link. |
| include-intro | Used to vary position of client photo and introductory text, depending on device size. | |
| Static | Main CSS | Juicer Instagram feed formatting. |
| Main JS | Set first carousel item as active. |
The main challenge of the CV app was to present the clients CV in a novel way that could be easily viewed on all devices. Divided into 3 sections, expertise, education and employment, animated data circles and accordions help to break up the content. Updates are through Django's admin interface.
| TYPE | NAME | DESCRIPTION |
|---|---|---|
| Model | Role | Individual role. Job field differentiates employment from education. |
| Point | Bullet point for an individual role. | |
| Example | Link between a project and an individual role. | |
| Skill | Skill level to display in animated data circle. | |
| View | About | Renders about page. Includes all roles as jobs or studies, depending on job field. Includes all skills. |
| Template | About | Owner introduction, animated expertise, education timeline and accordion, cv download and employment timeline and accordion. |
| Static | Main CSS | Timeline and data circle formatting. |
| Main JS | Data circles and accordion animations. |
The Blog App affords the client the opportunity to create blogs with varying formats and site visitors the opportunity to like and comment on blogs. Although the client doesn't expect a lot of traffic, she is anxious to reply to all comments promptly so receives an email flag on new post. The comments page allows her to review and reply to all outstanding comments. Outside of comment replies, updates are through Django's admin interface.
| TYPE | NAME | DESCRIPTION |
|---|---|---|
| Model | Category | Blog category list item. |
| Blog | Individual blog. | |
| Section | Section data for an individual blog. Fields set to blank=True allow for maximum flexibility. | |
| Comment | Visitor comment and admin reply for an individual blog. | |
| Form | CommentForm | Create a comment by adding data to the content field. |
| ReplyForm | Update a comment by adding data to the reply and/or exclude fields. | |
| View | Blogs | Renders an ordered list of blogs on the blogs page. Paginates after every 6 blogs. |
| Blog | Renders an individual blog, its sections and comments, on the blog page. Renders a comment form. Creates an individual comment on valid from post. Sends email flag on valid form post. | |
| Like | Increments like field for an individual blog. | |
| Comments | View accessed by site admin only, login required. Renders outstanding comments (i.e. not excluded and no reply), oldest first, on the comments page. Renders reply form for each comment. Updates individual comment on valid form post. | |
| Template | Blogs | Blog cards with links to individual blogs. Paginates after 6 blogs. |
| Blog | Blog introduction and sections. Blog like count and upvote. Previous visitor blog comments and replies. New blog comment form. | |
| Comments | Login required. Outstanding blog comments. Exclude buttons and reply textboxes to update comments. |
Through this app the client presents her project portfolio. Additional content, costed and free to download, is available for some projects. Updates are through Django's administration interface.
| TYPE | NAME | DESCRIPTION |
|---|---|---|
| Model | Category | Project category list item. |
| Project | Individual project. | |
| Section | Section data for an individual project. Fields set to blank=True allow for maximum flexibility. | |
| Download | PDF download available for an individual project. Price set to null=True and blank=True as some downloads are free. | |
| Form | ContactForm | Create a contact. |
| ReplyForm | Update a contact by adding data to the reply and/or exclude fields. | |
| View | Projects | Renders an ordered list of projects on portfolio page. Paginates after every 6 projects. |
| Project | Renders an individual project, its sections and downloads, on project page. | |
| Template | Projects | Project cards with links to individual projects. Paginates after 6 projects. |
| Project | Project introduction and sections. Downloads, if applicable, to purchase or download for free. |
When it came to the Cart App, client requirements drove design decisions.
Although the app gives the client an opportunity to sell project content, it's not the main purpose of the site, and she wanted a subtle approach. On the project detail page, if downloads are available, costed downloads appear with a purchase button and free downloads with a download button. The cart navbar menu item only appears after clicking purchase on a costed download.
Although the client doesn't imagine a lot of sales, she was interested in knowing what visitors do and do not end up checking out. For these reasons, just the cart id is stored in session, with the remaining data is stored in the cart table and available to the client through the Django admin interface.
Stripe Checkout was chosen as the payment option for the site. According to the Stripe website it creates a secure, Stripe-hosted payment page that lets you collect payments with just a few lines of code. It works across devices and is designed to increase your conversion. The client really appreciated:
- As a Stripe hosted option she doesn't have to worry about payment security.
- Stripe Checkout meets the 2019 European Strong Customer Authentication (SCA) requirements.
- A customisable interface still allowed her logo and colour scheme to be incorporated.
- Stripe notifications settings allowed the clients to receive an email after every successful payment.
- Stripe email settings allowed the client's customers to receive receipts directly from Stripe.
| TYPE | NAME | DESCRIPTION |
|---|---|---|
| Model | Cart | Individual cart. Stripe field indicates checked out. Fulfilled field indicates order complete. |
| View | Cart | Renders cart page. If it exists, gets current cart id from session. |
| Add | Adds a download to a cart. If it exists, gets current cart id from session. If it doesn't, creates a cart and adds its id to session. If passed download is not already in current cart, updates download and total in cart, and cart count in session. | |
| Remove | Removes a download from a cart. Gets current cart id from session. Removes passed download from cart. Updates cart total in cart, and cart count in session. | |
| Charge | Renders charge page and redirects payment to Stripe. Gets current cart id from session. Creates list of all downloads for current cart id. Creates Stripe checkout session as per Stripe documentation. Makes Stripe's session id available in context. | |
| Success | Renders success page. Gets Stripe's session id from URL and uses to retrieve Stripe's session data. Gets current cart id from session. Saves Stripe's session id to cart as reference. Removes cart data from session to prevent repurchase errors. Makes Stripe's session data available in context. | |
| Template | Cart | Lists downloads in cart. Remove button to remove downloads from cart. Checkout button to proceed with purchase. |
| Charge | Redirects to Stripe from this page. Makes the Stripe session id available for Stripe JS. | |
| Success | Confirmation of success. Summary of order and reference from Stripe session data. | |
| Static | Stripe JS | Stripe redirect. |
Diagram: Cart Flow For Successful Purchase
Stripe outline on their site "not rely on the redirect to the success_url alone for fulfilling purchases as: Malicious users could directly access the success_url without paying and gain access to your goods or services. Customers may not always reach the success_url after a successful payment. It is possible they close their browser tab before the redirect occurs." For these reasons the success page order summary shows Stripe's session data. For now order fulfilment is manual and my client relies on her Stripe account dashboard to verify payment success before sending pdfs.
Site visitors can contact the client by submitting a contact form. New contacts are flagged to the client and outstanding contacts pages gives her the facility to save and send email replies.
| TYPE | NAME | DESCRIPTION |
|---|---|---|
| Model | Contact | Individual visitor contact and admin reply. |
| View | Contact | Renders contact form on contact page. Creates a contact on valid form post. Sends email flag on valid form post. |
| Contacts | View accessed by site admin only, login required. Renders outstanding contacts (i.e. not excluded and no reply), oldest first, on contacts page. Renders reply form for each contact. Updates individual contact on valid form post. If update is reply, sends email, either text or html template. | |
| Template | Contact | Contact form with highlighted required fields. |
| Contacts | Login required. Outstanding contacts. Exclude buttons and reply textboxes to update contacts. | |
| Reply Email | Contact reply formatted for email. |
The user app is currently limited to login and logout templates using Django's User model. For this site, the client is the user, logging in using the admin link on the landing page to access the outstanding comments and contacts pages.
| TYPE | NAME | DESCRIPTION |
|---|---|---|
| Template | Login | Login for site owner. |
| Logout | Logout for site owner |
The client needed an interface to allow content updates. Having walked her through Django's admin interface, she felt comfortable with this option. To improve usability, app admin files were used to add inlines, order fields, filter, make editable etc. To tie in with the style of the main site, the admin interface was customised using HTML and CSS.
Diagram: Customised Django Admin Interface
Search Engine Optimisation The next thing to work on before my client launches her site is SEO.
Automated Fulfilment Currently fulfilment for paid downloads is manual but we would like to take advantage of automated fulfilment using webhooks as outlined in Stripe's documentation.
Search Facility As the client is currently building up her portfolio and blogs she did not want a search facility, feeling it would emphasis the lack of content. As content grows, I really feel this is something the site will need.
Email Currently emails are sent from a Gmail that was setup specifically for the site. In time the client would like emails to come from coletteosullivan.com
- Visual Studio Code IDE used.
- Git Used to track changes during development.
- GitHub Used to host the version control system and website content.
- SQLite Default Django database used for development.
- PostgreSQL Database used for production on deployment to Heroku.
- Heroku Cloud based hosting service.
- AWS S3 Cloud based storage for media files saved to database.
- Go Daddy Domain hosting service.
- Stripe Checkout Used to manage online payments.
- Google Fonts Used for all fonts.
- Font Awesome Used for all icons.
- Bootstrap Used for responsive layout and styling.
- jQuery Used for navbar, carousel, accordion, data circles & stripe functionality.
- jquery-circle-progress Used for animated data circles.
- Juicer Used to link and display client's Instagram feed.
- pip Used to install Python modules.
- Django Web framework used.
- Django Heroku Used to improve deployment.
- Gunicorn A Python WSGI HTTP server for UNIX.
- Django Crispy Forms Used to render Django forms.
- Django Math Filters Used to provide math filters for Django.
- Pillow A Python imaging library.
- AWS Django Storages A custom storage backend for Django.
- Boto3 Used to create, configure, and manage AWS services.
- Psycopg2 A PostgreSQL database adapter for Python.
- Whitenoise Used to allow the web app to serve its own static files.
- Stripe Checkout Used to manage online payments.
- Balsamiq Used to develop wireframes for the website.
- Microsoft Access & Publisher Used to create README diagrams.
- Affinity Designer & Photo Used to edit images and identify hex colours for fonts and backgrounds.
Testing detailed in TESTING.md.
The website was developed in Visual Studio Code using a virtual environment and deployed to Heroku via Git.
The following instructions to clone and deploy assume the user has:
- IDE
- Python 3
- Pip
- Virtual Environment
- GitHub Account
- Heroku Account
- Heroku CLI
- AWS S3 Account & Bucket
- Stripe Account
The following instructions were taken from GitHib Help.
- Open the interior-architect repository.
- Click the clone or download button.
- In the clone with HTTPs pop-up, click the copy icon.
- Open git bash.
- Change the current working directory to where you want the cloned directory to be made.
- Type git clone and paste the URL copied earlier.
- Press enter.
- Create a virtual environment for your Python project.
- Create a env.py file in the iaproject folder.
- Add the following variables to the env.py file.
os.environ['DEBUG_VALUE']='False'
os.environ['EMAIL_HOST_USER']
os.environ['EMAIL_PASSWORD']
os.environ['SECRET_KEY']
os.environ['STRIPE_PUBLISHABLE']
os.environ['STRIPE_SECRET']
os.environ['AWS_ACCESS_KEY_ID']
os.environ['AWS_SECRET_ACCESS_KEY']
os.environ['AWS_STORAGE_BUCKET_NAME']
os.environ['DATABASE_URL']
os.environ['ALLOWED_HOSTS']
- Use
pip install -r requirements.txtto install Python required modules.
- On the Heroku website log into your account.
- Click new and create new app.
- Give your app a name (it must be unique), select a region and click create app.
- Under resources choose add on postgres hobby free.
- Click reveal id.
- Return to your IDE and fill the values for your environment variables in the env.py file.
- Return to the Heroku dashboard and under settings/ config vars click reveal vars. Add the key values for all your environment variables.
- In your console type
heroku login, and when prompted click any key to open the browser and log into your heroku account. - Type
heroku git:remote -a appname. - Type
git subtree push --prefix iaproject heroku masterto push the code to Heroku. - Cd into iaproject and type
python manage.py makemigrationsandpython manage.py migrateto create and run migrations. - Type
python manage.py createsuperuserto create a super user. - Open the Heroku app address adding
/adminto the end of the URL and login with the super user credentials. - Use this Django admin interface to add data to populate the blog, cv and portfolio apps.
- Site concept and design by website developer.
- Site content provided by client.
- Favicon/logo by Blasko purchased from Vector Stock.
- Work desk photo by Jessica Arends from Unsplash.
- Aligning images from Stack Overflow.
- Remove Bootstrap card header border radius Stack Overflow.
- Timeline Bootsnipp.
- Semi transparent borders Stack Overflow.
- Background images in Django Stack Overflow.
- Sticky element W3 Schools.
- jquery-circle-progress Kottenator.
- Determine if item is at top of screen Stack Overflow.
- Carousel set first loop image as active Stack Overflow.
- $.each Stack Overflow.
- Check if element is hidden in jQuery Stack Overflow.
- Show, hide elements by data attribute Stack Overflow.
- Get data attributes in jQuery Stack Overflow.
- Brad Traversy - YouTube Django Crash Course.
- Brad Traversy - Udemy Django Dev To Development.
- Pretty Printed - YouTube Django Playlist.
- Corey Schafer - YouTube Django Playlist.
- Real Python - Build A Portfolio App.
- Coding Point - Django Ecommerce Web App.
- Django 2.2 Documentation.
- Related names Simple Is Better Then Complex.
- Choice Fields Stack Overflow.
- Limiting query results Stack Overflow.
- F strings Real Python.
- Bare excepts Stack Overflow
- Python Django Tutorial Part 6 - User Registration Corey Schafer.
- Send POST data from inside for loop Stack Overflow.
- Save instance Real Python.
- Customising model form fields Django Docs.
- Form widgets Coding Entrepreneurs.
- Multiply in template tag Stack Overflow.
- Foreign key in template tag Stack Overflow.
- Django Tutorial for Beginners Part 22, Cart App Coding Point.
- Stripe Checkout documentation Stripe.
- Capture URL parameters Stack Overflow.
- Clear session Stack Overflow.
- How to send emails in Django Data Flair.
- Sending a confirmation email using Gmail Coding Entrepreneurs.
- Gmail less secure apps Google.
- Admin.py Inlines Stack Overflow.
- Admin.py Display, filters, edits Brad Traversy.
- Admin.py Order Stack Overflow.
- Admin CSS Brad Traversy.
- Admin HTML Brad Traversy.
- Admin HTML Setting favicon Stack Overflow.
- Many thanks to my client for her wonderful content, endless faith and infectious enthusiasm! To my mentor Ali Ashik, and all on Code Institute's Slack channel for making this journey possible.