Full-Stack Project (HTML5 CSS3, Bootstrap, Django, Python, JavaScript, jQuery, PostgreSQL, Cloudinary)
Developer: Aleksander Kisielewicz
Photo Adventures Website was created as Portfolio Project #4 (Full-Stack Toolkit) for Diploma in Full Stack Software Development at Code Institute.
Project purpose was to build a full-stack site using agile methodology to plan and design web application using MVC framework and related contemporary technologies. Appplication offers users full CRUD (create, read, update, delete) functionality.
Application offers such functionalities as:
- Secure user registration - Users can register with captcha protection and e-mail verification
- Password reset via email - Users can easily reset their forgotten password with just a few clicks
- Sign in/out - Users can conveniently sign in and out of their account
- Role-based access - Access to functionalities is granted based on the user's assigned role
- User dashboard - Users can view their own personalized dashboard, complete with user content
- Post management - Users can view, add, edit, and delete their posts with ease (CRUD)
- Saving drafts and publishing - Users can save their edited posts as drafts or publish them (send to modaration)
- Moderation - Admins can moderate posts and comments
- Commenting - Users can leave comments on posts and have their Gravatar user picture displayed
- Liking - Users can add likes to posts to show their appreciation
- Messages/feedback - Users receive feedback and confirmation to their actions
- Social media sharing: - Posts can be easily shared on Facebook and Twitter
- Contact form: - Users can send messages to the admin through a contact form that includes captcha protection
- Project
- User Experience (UX/UI)
- Logic and features
- Technology
- Testing
- Deployment
- Possible future development
- Credits
I chose to develop an web application that can be used in real life. My main focus was always on providing an excellent user experience, which is why I decided to create a web application that is both practical and engaging. Photo Adventures is a user-friendly blog-style website where users can share their photo adventures by creating visually appealing blog posts that include both images and text. Application should have clean and intuitive user interface and offer easy access and navigation to all functionalities. Application should also be responsive on devices of all screen sizes.
The website's target audience is anyone who is passionate about photography and enjoys sharing their experiences and stories through pictures. It is inclusive of both amateur and professional photographers who are looking for a platform to showcase their work and connect with like-minded individuals. Whether it's capturing stunning landscapes, wildlife images, or simply capturing moments of everyday life, Photo Adventures Website welcomes anyone who wants to share their love of photography with the world.
To achieve the strategy goals I implemented following features:
- a clean and intuitive user interface for ease of navigation and readability,
- a menu that provides easy access to all website sections, including personalized content via user dashboard,
- cloud hosting of images for optimized website speed and user experience,
- media queries to ensure responsiveness across all types of devices,
- feedback messages to users for all important actions taken on the website.
I used agile methodology for the first time when planning full-stack django website with a focus on delivering the basic functionalities. I prioritized features by labeling them as "must-have" or "could-have" and moved some less critical ones to future development. To guide my development process, I created user stories for both the admin user and regular visitors. These stories helped to define the features and functionalities that were most important to project's target audience.
As a solo developer who was learning a lot during development, I faced challenges in estimating the time required for each task and only had a basic concept of what I would create. Therefore, I kept things simple and focused on achievable goals. As the project grew, I was able to add more advanced features and group user stories into milestones. However, I could not find the "epics" feature in GitHub Projects, only milestones (it provides only milestones and issues). Epics are supposed to be larger in scope than milestones, representing a significant amount of work. Milestones, on the other hand, are meant to mark significant points in time in terms of project completion. In this document, I added epics, but on the project board, I used only milestones to stay in order with GitHub's features.
To keep track of progress, I used a kanban board divided into following sections: "to do", "in progress" "done", "future enhancements" and "bugs" that allowed to visualize all tasks and prioritize next steps.
By using agile methodology, I was able to stay organized and focused on delivering the most important features, while also allowing flexibility for future development. This experience gave me valuable insight and lessons that I can apply to future projects.
Epic | Milestone | User stories |
---|---|---|
Epic 1: Basic Site Functionality | Milestone 1: Authentication | #2, #4 |
Milestone 2: View and select posts | #1, #5, #17 | |
Milestone 3: Post CRUD operations | #3, #10, #12, #15, #26 | |
Milestone 4: Commenting and Liking | #7, #8, #9, #11, #23, #30 | |
Epic 2: User Management | Milestone 1: User profile and dashboard | #20, #21, #25 |
Milestone 2: Contact form and spam prevention | #16, #33 | |
Milestone 3: Validation | #39, #40 | |
Milestone 4: Error handling and feedback | #31, #38 | |
Epic 3: Content Management | Milestone 1: Draft posts and featured posts | #10, #41 |
Milestone 2: Categories and tags | #13, #14, #28, #29, #34, #35 | |
Epic 4: Social media Integration | Milestone 1: Share posts on social media | #37 |
Milestone 2: Popular posts and likes display | #6, #19, #41 |
- #2 As Site Admin, I can log in to admin panel so that I can manage posts
- #3 As Site Admin, I can perform CRUD operations on posts so that I can manage the content on the blog
- #6 As Site Admin, I can view the number of likes on posts so that I can see which is the most popular
- #7 As Site Admin, I can view comments on posts so that I can read the conversation
- #11 As Site Admin, I can approve/delete users comments so that I can moderate inappropriate ones
- #13 As Site Admin, I can add posts in categories so that I can group photos by this criteria
- #14 As Site Admin, I can add tags to the posts so that others can easily find posts related to specific topics
- #23 As Site Admin, I can prevent users from liking their own posts so that posts can be liked only by other users
- #30 As Site Admin, I want to allow users to have a profile image displayed alongside their comments on the blog so it's easier for readers to identify who is commenting
- #31 As Site Admin, I want to present the user with a custom 404 page if he navigates to a page that doesn't exist so that he can see a user-friendly message that offers some guidance or suggestions
- #33 As Site Admin, I want to ensure that only human users can submit the contact form so that I can prevent spam or bot submissions
- #38 As Site Admin, I want to display success, error, and warning messages to my users using Bootstrap toasts so that they always receive easy-to-understand feedback regarding their actions
- #39 As Site Admin, I want to validate inputs in add/edit post forms so that I can have control on what input values are allowed
- #40 As Site User, I can check image dimensions, format, and file size before user upload new image so that I have full control on what files are uploaded to the cloud
- #41 As Site Admin, I can distinguish specific post as featured post so that I can direct user's attention to that post
- #1 As Site User, I can view a list of all posts so that I can select one to read
- #4 As Site User, I want to be able to create an account on the blog so that I can add posts, comments, and likes
- #5 As Site User, I can select a post so that I can read it
- #6 As Site User, I can view the number of likes on posts so that I can see which is the most popular
- #7 As Site User, I can view comments on posts so that I can read the conversation
- #8 As Site User, I can write comments on posts so that I can be the part of the conversation
- #9 As Site User, I can like/unlike posts so that I can give feedback in form of likes
- #10 As Site User, I can create draft posts so that I can finish publishing later
- #12 As Site User, I can create blog posts with photos and text so that I can share my photography adventures
- #13 As Site User, I can add posts in categories so that I can group photos by this criteria
- #14 As Site User, I can add tags to the posts so that others can easily find posts related to specific topics
- #15 As Site User, I can edit an delete my posts so that I can update them or remove if necessary
- #16 As Site User, I can use the contact form so that I can contact the website owner if needed
- #17 As Site User, I can view page content so that I can browse the website and interact with the content
- #19 As Site User, I can see the posts with the highest number of likes so that I know which posts are the most popular
- #20 As Site user, I can see my account page so that I can manage my posts
- #21 As Site User, I can view all posts added by me so that I keep track of my blogging activity
- #25 As Site User, I can view my drafts in my dashboard so that I can select them and continue editing
- #26 As Site user, I can click on the edit button in full post view so that I can edit post quickly without the need of searching for the post in the dashboard
- #29 As Site User, I can get recommendations to read similar posts on the blog so that I can read stories that are in my interest range
- #35 As Site User, I can view a list of all posts written in specific category so that I can read the posts from categories that I am interested in
- #37 As Site User, I can share interesting posts on popular social media platforms so that my friends can learn about posts that I enjoyed reading
Index - main page
Blog
Single full post
About
Contact
All accounts pages
Colour palette was selected using coolors.co generator.
I created CSS classes and assigned them roles (examples):
- #04100D "dark" - main text, headers, post titles
- #003718 "green" - links inside paragraphs (text and hover outline), social icons, accordion buttonsm, buttons
- #CDCECF "silver" - post tags bacground
- #C9E365 "accent-green" - edit post icon (hover)
- #FFBD59 "accent-orange" - links (hover), social icons (hover), navigation items (hover), user dashboard background
The Merriweather font is the main font used throughout the whole website with Sans Serif as the fallback. Merriweather is a clean, modern looking and well known font. It is sourced from Google fonts and it's linked to css document via @import method.
The Gloock is a contemporary high-contrast serif typeface intended for display use. It draws inspiration from newspaper's headlines but with a contemporary approach. I used this font to use for headings and post titles. Serif font is set as fallback.
Lobster font is used as Photo Adventures text logo, cursive font is set as fallback.
Project uses cloud-based PostgreSQL database provided by ElephantSQL as a service. ElephantSQL is known for its ease of use, reliability, and is a popular choice for Django projects that requires PostgreSQL database. It offers web interface with console for SQL queries. Database URL including API key is stored as enviromental variable in heroku.
To generate model diagrams I used django-extensions with python library pygraphviz. They show relationship between models.
All django apps including generic/default apps:
Custom app "blog" with models Post, Comment and TaggedPost.
Model | Description |
---|---|
Post | This model represents a blog post and contains fields for the post's title, author, category, excerpt, featured image, location, content, creation and update times, status, and likes. It also has a slug field to generate unique URLs for each post and uses the TaggableManager (taggit library) to add tags. |
TaggedPost | This model is used to associate tags with posts and is created automatically by the TaggableManager (taggit library). |
Comment | This model represents a comment on a blog post and contains fields for the post it belongs to, the commenter's name and email, the comment's body, and creation time. It also has an approved field that can be used to moderate comments by admin. |
Bootstrap navbar component was used to create navigation bar. It is always visible and stays fixed at the top of the screen. Navbar consists of image logo, text logo and links to main areas of the site (home, blog, about and contact pages). There is also place for Sign up and Sign in buttons which are displayed to unauthenticated user.
Nabar displayed to authenticated user includes user name, gravatar and button link leading to user dashboard.
Navbar displayed to staff/admin user includes button with link leading to admin panel (accounts).
Mobile navbar for medium screen devices with hamburger menu button and collapsible menu.
Mobile navbar for small screen devices, without image logo to save screen space.
The footer consists of 3 columns. The first column contains copyright info and address. The second one has quick navigation links for easy navigation. The third one includes site logo and social media links with icons, allowing visitors to connect with the brand on popular social media platforms.
On small devices all elements are centered in one column that takes all width of the screen.
User is welcomed with image slideshow with pictures related to such categories as Landscape, Nature, Architecture and Travel. Bootstrap carousel component was used to showcase different types of pictures and provide users with a visually engaging and dynamic experience. Slideshow has automatic transition but users can take control of the slides using "previous" and "next" buttons. Each slide picture has header and short slogan description located in overlay. Carousel takes all viewport width and height on big screens and is reduced on small devices that are usually used in portrait mode.
Hero section is designed to catch visitor's attention and provide them basic introduction to site content and purpose. Hero section consists of two columns, one with text and second with image. Unauthorised users can see "Sign up" and "Visit blog" call-to-action buttons. Authenticated user can see "Add post" and "Visit blog" buttons instead.
Below hero section there are animated counters that present "page in numbers" statistics. They show total number of posts, comments, reactions and registered users.
Most liked posts is a section that presents three posts with highest numer of "likes". Posts are displayed as bootstrap cards, they include links to full articles.
Blog page is a list of all posts. Each post is presented as bootstrap card that includes featured image (or default placeholder if picture is not provided by user), post title, brief excerpt. In addition to this elements user can also find post details such as its location, author, creation date and number of likes and comments. The posts are paginated by 5 articles per page.
It is possible to browse posts by tags.
When viewing a post in full, users will see either a featured image or a placeholder if the author hasn't provided one. As on the blog page, the post title and other details are displayed beneath the image. On the left side of the picture, an overlay shows the location information. In the top right corner, users will find a like button. Only authenticated users can like posts, and if the user is also the author of the post, they will see an edit button below the like button. In the bottom right corner of the picture, there are social media sharing buttons, allowing users to easily share the post on Facebook or Twitter.
In the full post view, users can also see post tags, which can be clicked to bring the user to a list of related posts. This feature helps readers discover other content on the same topic and explore related ideas.
Beneath the main content of the article, you'll find a comment section. On the left-hand side of the page, all comments are displayed, along with the author's Gravatar, username, and date of the comment. For users who do not have a Gravatar account, a placeholder image will be displayed instead. This ensures that all comments are visually consistent. To post a comment, users must be authenticated and can use the form provided in the right-hand column. A link to guidelines and posting rules is also included to ensure that all comments are respectful and related to topic.
The About page is divided into three paragraphs. The first paragraph introduces who we are as a group, while the second highlights passion for photography. The third paragraph outlines the mission and what we hope to achieve. Images are included within the text to visually enhance the content.
At the bottom of the page, a bootstrap accordion houses a FAQ section. The answers to common questions are provided in a concise format, making it easy for users to quickly find the information they need.
The Contact page is designed with a two-column layout, featuring the contact form on the left and the location, address, and Google Maps on the right. Users can use the contact form to get in touch with the site admin directly. To facilitate sending emails through client-side technologies, the Email.js library has been integrated. The form allows users to fill in their name, email address, and message, and authenticated users can have their name and email fields pre-filled for convenience.
To prevent spam, a Google reCAPTCHA v2 widget has been implemented.
On the right-hand side, the location, address, and Google Maps provide users with a visual representation of where we are located and make it easy for them to find us.
The admin panel provides access to various details related to blog posts, including the post title, slug, author, category, tags, status, and creation date. These details make it easy for the admin to manage and organize posts effectively. There is also existing "featured" field in the model, this functionality was left as potential future enhancement and has been described in project's user stories.
In addition, actions have been registered that allow the admin to select multiple posts and change their status in one click. This feature saves time and helps to manage larger number of posts.
Also a list of filters have been registered in the admin view, admin can sort and display posts by author, status, etc.
The page displays a dashboard for the user with basic statistics about their blog posts and gives user the ability to manage them (full CRUD functionality). The user's gravatar is displayed in the top left of the page, along with a welcome message that greets the user by name. In this section user can also find "Add post" button to click and start writing new post. The form of keypoints inform users that they can manage their content from the dashboard and can view their post history and monitor engagement such as likes and comments.
Three sections below show all posts written by user in a form of bootstrap cards. They are divideded into tabs: drafts, awaiting moderation and published posts. The sections stay hidden until there is related content to be presented. Each card can be clicked and overlay contains relevant control buttons allowing viewing, editing or deleteing posts.
Form of defensive development, post deletion confirmation screen.
All accounts subpages are based on one template designed for this purpose. It's a narrow container with header image and form or other relevant information.
User can sign up using registration form. Google reCaptcha v2 widget was implemented to ensure that only human users can register and to protect site against spam and bots. Users who have already registered can click a link at the bottom of the page to sign in.
Account verification setting has been set to "mandatory". Gmail SMTP service has been configured to provide email backend service for this functionality. Upon successful registration, user receives e-mail verification message.
User is asked to click the link to activate the account.
The link (if valid) brings user to confirmation page. On successful confirmation user is automatically signed in and redirected to home page.
If the link is invalid or the token has expired, the user will be prompted to log in with their email and password to receive a new confirmation email.
If a user who already has an account attempts to sign up again using the same email, they will receive a notification email.
Returning registered user can sign in.
Forgotten password can be reset.
Message that user receives if requested password reset. Again, if link/token is valid user can proceed with password change, if not, will see "bad token" error page and can repeat the process.
On successful password change user is asked to go to sign in page to continue.
Users can access the "Add Post" page from the user dashboard. The form on the page is used for submitting a new post. Basic validation has been implemented into the form using JavaScript.
The form fields reflect the model post fields: Title, Slug, Category, Tags, Excerpt, Image, Location, Content, and Status. All fields are required; however if users don't upload an image, a placeholder will be present in the template.
Slug field is being filled automatically (and converted to dash-separated lowercase words) when user type in title field, user still needs to interact with this field in order to make it valid. Event listener "input" is attached.
Fields that are not validated with JavaScript:
- Image field: I left this feature as possible future enhancement and described here.
- Content field: Summernote WYSIWYG widget is used for this field. Django only checks if field is not empty, this is sufficient as any character would be allowed in post body.
Field | Allowed input | Matching charset, regular expression | Length (min, max) |
---|---|---|---|
Title | Alphanumeric, space, coma | /^[a-zA-Z0-9 ,]+$/ | 5 - 50 |
Slug | Lowercase alphanumeric, dash | /^[a-z0-9]+(?:-[a-z0-9]+)*$/ | 5 - 50 |
Category (select) | One option from dropdown menu | Any option (no default is selected) | N/A |
Tags | Comma-separated words with alpha char, space | /^([a-zA-Z]{3,}\s*,\s*)*[a-zA-Z]{3,}$/ | 3 - 60, the shortest tag - 3 |
Excerpt | Alphanumeric, space, special | any | 10 - 200 |
Location | Alpha, space, coma | /^[a-zA-Z ,]+$/ | 4 - 40 |
Status (radio checkbox) | Any option from two available | Valid if any is checked | N/A |
The validation functions have been connected to the field inputs using "input" event listeners. The script checks if the form is valid and then uses the Bootstrap classes "is-valid" or "is-invalid" to highlight the field in green or red. This gives the user feedback about whether the input is correct or incorrect. When all fields become valid and highlighted with green, the "submit" button becomes active and form can be send. Submit button is disabled by default when page is loaded. The same script and mechanism works on "Edit page", user need to interact with the fields, make them valid in order to be able to save changes.
Django messages are implemented in the website's app. These messages are displayed using the Bootstrap toast component on several occasions when feedback is needed, such as signing in/out, commenting, saving drafts, and editing changes to posts.
- HTML5 - markup language used for structuring webpage content
- CSS3 - stylesheet language
- JavaScript - high-level, imperative, programming language.
- Python - high-level, imperative, general-purpose programming language.
- Markdown - markup language used to write README and TESTING documents.
- Animate On Scroll AOS - library used to animate containers on the website
- Bootstrap 5 - CSS framework developed by Twitter
- Balsamiq - used to create project wireframes
- Cloudinary - cloude-based image and video API
- Canva - used to design logo picture
- Convertio.io - used to convert images to webp format
- Coolors.co - was used to create colour palette for terminal display page
- Django - python-based web framework with MTV architectural pattern
- ElephantSQL - PostgreSQL database as a service
- EmailJS - JavaScript library used to send emails usung only client-side technologies
- Favicon.io - tool used to create favicon
- Font Awesome: - Font Awesome icons were used for social links in terminal display page
- Git - Git was used for version control by utilizing the Gitpod terminal to commit to Git and Push to GitHub
- GitHub - GitHub is used to store the project's code after being pushed from Git
- Google DevTools - used for developing HTML/CSS/DOM navigating/JavaScript console
- Google Lighthouse - used for testing website performance
- Google Maps - google map used in contact page
- Google reCaptcha v2 - captcha widget used to prevent spam
- Gmail - SMTP service used for sending emails
- Gravatar - global avatar, allows to display user profile picture related to email
- Heroku - online app used to deploy project
- Jinja - web template engine for python/django apps
- jQuery - JavaScript library
- Pexels - was used to source bacground picture for terminal display page
- Techsini.com - website mockup generator
- WAVE - web accessibility online tool
- WebAIM - online tool to check colour contrast/accesibility
- os - built-in Pythod module - used to save and import env variables
- cloudinary - cloud hosting used to store pictures
- dj3-cloudinary-storage - provides django integration with Cloudinary service
- django-crispy-forms - renders elegant div based forms
- CrispyBootstrap5 - template pack for django-crispy-forms
- dj-database-url - utility that allows configure environment variable to a connection string that includes the database engine
- django-allauth - integrated django accounts management
- django-social-share - used for facebook and twitter share buttons
- django-summernote - WYSIWYG editor widget used for writing/editing post content
- django-taggit - used for post categorizing by tag name
- gunicorn - Python WSGI HTTP Server for UNIX
- oauthlib - framework for implementing open standard for authorization
- psycopg2 - PostgresSQL database adapter for Python language
- PyJWT - Python library for encoding, decoding and veryfying JSON Web Tokens
- pytz - library that provides support for working with time zones, converts the dates between zones
- requests-oauthlib - OAuth library suppor for python requests
- sqlparse - non-validating SQL parser for Python. It provides support for parsing, splitting and formatting SQL statements.
- pygraphviz - used to generate visualization of database model
Details of manual testing can be found in TESTING.md file.
Minor syntax and spelling errors were eliminated during development, below is a list of registered issues.
Issue | Problem | Solution |
---|---|---|
#22 | Bootstrap cards for posts without uploaded images are not displayed correctly | If/else statement added in the template to diplay placeholder image if featured image is not available |
#27 | When the user sends the form with a new post or edits an existing post draft, the tag field stays empty after saving. The database is not being updated as expected. | Save many to many method was added within if statement |
#36 | Error 404 - not found occurs when sending a comment on full_post.html. | Queryset was changed to filted only published posts |
#43 | 2 template syntax errors found using django-extensions validate_templates. One in account/email.html and one in account/verified_email_required.html | Misspelled "load" in templates/accounts/email.html was corrected, head_title block was removed from accounts/verified_email_required.html (screenshot added to issue) |
#44 | During the tests, I found out that Google Maps iframe element (third party code) on contact.html page generates a console errors (screenshot added to issue) | It appears that the errors are linked to the use of ad blockers in the browser. After testing with filters turned off and in private mode, I was able to browse without encountering any errors. |
App was deployed to heroku for the first time when django installation was completed to make sure that everything is working correctly.
- Navitate to ElephantSQL website, log in to your account
- In top-right corner click on green button "Create New Instance".
- Enter database name, leave plan field as is, optionaly enter tags.
- Select region, click on "Review" and then on "Create instance".
- Go to your dashboard, find newly created database instance, click on it.
- Copy URL starting with "postgress://"
- Paste this URL into env.py file as DATABASE_URL value and save the file.
os.environ["DATABASE_URL"] = "postgres://yourLinkFromDatabaseDashboard"
- Navigate to https://cloudinary.com/ and log in to your account.
- Navigate to dashboard/console https://console.cloudinary.com/console/ and copy API Enviroment variable starting with "cloudinary://".
- Paste copied url into env.py file as CLOUDINARY_URL value and save the file.
os.environ["CLOUDINARY_URL"] = "cloudinary://yourLinkFromCloudinaryDashboard"
In order to protect django app secret key it was set as anviroment variable and stored in env.py. Please change your password accordingly.
os.environ["SECRET_KEY"] = "yourSecretKey"
Warning: Gitpod doesn't allow to use smtp service, emails won't be send if you run http server on Gitpod. If debug mode is switched to True, emails will be visible in the console. Emails will be sent correctly on deployed project assuming all enviromental variables are configured correctly.
In order to have google smtp service running in local env, make sure you add following variables to env.py:
import os
os.environ["EMAIL_HOST_USER"] = "[email protected]"
os.environ["EMAIL_HOST_PASSWORD"] = "yourpassword"
os.environ["DEFAULT_FROM_EMAIL"] = "Photo Adventures"
Email password needs to be generated in order to use gmail with third party apps. Go to https://myaccount.google.com/ navigate to security section to generate password. If you need more help on that, please follow this tutorial.
Note: Repository was created using Code Institute template: https://github.com/Code-Institute-Org/gitpod-full-template
-
Login to Github and navigate to repository: https://github.com/alexkisielewicz/photo-adventures
-
Click on "Fork button" in upper-right corner and create a new form in your own account.
-
Open your repository in local IDE or using Gitpod. Preferred way is to used Chrome Gitpod Extension. When you install extension, green "Gitpod" button appears in your repository. Click on it to cread new workspace.
-
Go to workspace terminal and install all requirements using command: "pip install -r requirements.txt". All te packages will be installed. requirements.txt content:
asgiref==3.6.0 cloudinary==1.32.0 crispy-bootstrap5==0.7 dj-database-url==0.5.0 dj3-cloudinary-storage==0.0.6 Django==3.2.18 django-allauth==0.52.0 django-crispy-forms==2.0 django-social-share==2.3.0 django-summernote==0.8.20.0 django-taggit==3.1.0 gunicorn==20.1.0 oauthlib==3.2.2 psycopg2==2.9.5 PyJWT==2.6.0 python3-openid==3.2.0 pytz==2022.7.1 requests-oauthlib==1.3.1 sqlparse==0.4.3
-
Local env.py file should be configured as on example below:
import os # Env vars os.environ["DATABASE_URL"] = "postgres://yourLinkCopiedFromElephantSQLDashboard" os.environ["SECRET_KEY"] = "YourSecretKey" os.environ["CLOUDINARY_URL"] = "cloudinary://yourLinkCopiedFromCloudinaryDashboard" # Gmail vars os.environ["EMAIL_HOST_USER"] = "[email protected]" os.environ["EMAIL_HOST_PASSWORD"] = "passwordObtainedFromGoogleAccount" os.environ["DEFAULT_FROM_EMAIL"] = "Photo Adventures"
-
In order to save django changes in database migration needs to be made.
-
Use terminal commands:
python3 manage.py makemigrations python3 manage.py migrate
-
Create superuser to access admin area using terminal command (email is optional, password won't be visible when typing, confirm password twice):
python3 manage.py createsuperuser
-
App can be run in gitpod enviroment using terminal command:
python3 manage.py runserver
-
Go to Heroku and follow further instructions below.
-
Navigate to https://heroku.com/ login to your account and open dashboard. Click button "New" and select "Create new app" button.
-
Enter app name, I used "photo-adventures", chose your region and click on "Create app" button.
-
Click on newly created app and go to "Deploy" tab and then to "Deployment method" section. Authorize and connect your GitHub account, then find and select your repository.
-
Go to the "Settings" tab, click on "Reveal Config Vars" and add the following keys and values (all values should be strings without any quotation marks):
NOTE: DISABLE_COLLECTSTATIC variable should be set to "1" for initial deployment. Before final deployment it should be removed.
Key Value CLOUDINARY_URL cloudinary url beginning with cloudinary:// DATABASE_URL postgress url beginning with postgress:// DEFAULT_FROM_EMAIL Photo Adventures DISABLE_COLLECTSTATIC 1 EMAIL_HOST_PASSWORD YourPassword obtained from Google account EMAIL_HOST_USER [email protected] PORT 8000 SECRET_KEY YourSecretKey, the same as in env.py -
Return to your Gitpod workspace and navigate to the file
photoadventures/settings.py
. Change allowed hosts including the name of the app that you created in previous steps. In my case, it was 'photo-adventures.herokuapp.com'. Save the file. -
Procfile required to run project on Heroku was already created but if you change your app's name please make sure that this change is reflected in Procfile. It can be found in your project's main directory. In my case Procfile looks as below:
web: gunicorn photoadventures.wsgi
-
After adding enviromental variables and editing Procfile project is ready for deployment. In Heroku app's dashboard navigate to "Deploy" tab, scroll down to "Manual deploy" section. Select main branch from dropdown menu and click on "Deploy Branch".
-
Step required for final deployment: Navigate again to app's settings, reveal config vars and delete DISABLE_COLLECTSTATIC entry if it was set before.
-
After built is done, you should be able to see the button with the link leading to deployed app. In my case http://photo-adventures.herokuapp.com.
If I had more time or decide to develop app further I would add/improve following functionalities that I moved to Future enhancements column on project board:
- #29 Similar posts recommendation to suggest user next read
- #34 Post list by category
- #35 Post list by author
- #40 File size/format/dimension validation of uploaded images
- #41 Featured post - functionality that allows admin to mark selected post as featured and highlight it on main page.
- send_email.js - basic configuration was done as in Code Institute walkthrough and then further developed,
- in order to implement gravatar I used method suggested in gravatar docs, that required to create and register custom template tag as described in django docs,
- idea for counters animation on the main page found in this snippet,
- patterns for regular expressions used for input validaiton found here.
- Photos Pawel Zygmunt - thanks to my friend Pawel for 3 photos from Tenerife, Connemara and Dolomites.
- Alek Kisielewicz Photography - all other post images are my own photographs.
- Pexels.com - post placeholder image/background image/accounts background.
- Code Institute course and learning platform
- The book "Python Crash Course, 2nd Edition: A Hands-On, Project-Based Introduction To Programming"
- StackOverflow
- W3Schools
- Django - Django documentation.
- Bootstrap - Bootstrap documentation.
- Documentation of python modules and libraries used in the project.
- My Mentor Reuben Ferrante for helpful feedback and guidance at all stages of the project.
- Code Institute Slack Community for being invaluable knowledge base.
- Photo Adventures Website was created for educational purpose only.