- Table of Contents
Hello there,
and welcome to my final Code Institute (CI) school project.
In this project I should be able to show that I can create a web application using Python 3 and Django.
I decided to create my own project.
- Use the following guidelines when developing your project:
- Build a web app that fulfils some actual (or imagined) real-world need. This can be of your choosing and may be domain specific.
- Write a README file for your project that explains what the project does and the need that it fulfils. It should also describe the functionality of the project, as well as the technologies used. Detail how the project was deployed and tested and if some of the work was based on other code, explain what was kept and how it was changed to fit your need. A project submitted without a README file will FAIL.
- The project must be a brand-new Django project, composed of multiple apps (an app for each reusable component in your project).
- The project should include an authentication mechanism, allowing a user to register and log in, and there should be a good reason as to why the users would need to do so. e.g., a user would have to register to persist their shopping cart between sessions (otherwise it would be lost).
- At least one of your Django apps should contain some e-commerce functionality using Stripe. This may be a shopping cart checkout, subscription-based payments or single payments, etc.
- Include at least one form with validation that will allow users to create and edit models in the backend (in addition to the authentication mechanism).
- The project will need to connect to a database (e.g., sqlite or Postgres) using Django’s ORM
- The UI should be responsive, use either media queries or a responsive framework such as Bootstrap to make sure that the site looks well on all commonly-used devices.
- As well as having a responsive UI, the app should have a great user experience.
- The frontend should contain some JavaScript logic to enhance the user experience.
- Whenever relevant, the backend should integrate with third-party Python/Django packages, such as Django Rest Framework, etc. Strive to choose the best tool for each purpose and avoid reinventing the wheel, unless your version of the wheel is shinier (and if so, consider also releasing your wheel as a standalone open source project).
- Make sure to test your project extensively. In particular, make sure that no unhandled exceptions are visible to the users, under any circumstances. Use automated Django tests wherever possible. For your JavaScript code, consider using Jasmine tests.
- Use Git & GitHub for version control. Each new piece of functionality should be in a separate commit.
- Deploy the final version of your code to a hosting platform such as Heroku.
The general idea is that only registered users can add / edit / delete property in the database. Also only registered users should be able to contact other users.
Role | View Property | Add / Edit / Delete Property | Access admin area |
---|---|---|---|
Anonymous | Yes | No | No |
Logged in | Yes | Yes* | No |
Logged in as Admin | Yes | Yes** | Yes |
*User should be able to edit / delete only his own property.
**Admin and CI should be able to delete any property.
Create 6 - 7 pages for the project.
- Any page
- Navigation
- Logo should lead the user to "home" page
- Create functionality for user to access All properties
- Create functionality for user to search for existing properties
- Anonymous
- Create functionality for user to be able to create new account.
- Create functionality for user to be able to log-in to existing account.
- Logged in
- Create functionality for user to be able log out from current session
- Create functionality for user to access Add new property
- Create functionality for user to access Profile page
- Footer
- Show general information about the company (e.g social links, address and contact details)
- Navigation
- Landing page (index.html)
- Welcome new or existing user
- Show user few examples of properties (up to 5) with short information’s (newest properties)
- Add section for recent feedbacks
- Add contact form for user to be able to send messages (e.g "General message", "Property enquiry"(registered users only) and "Feedback")
- All properties
- Create functionality for user to be able to search for properties.
- Show user available properties (if any)
- Show base information about each of them
- Add pagination (if too many properties to display)
- Single property
- Show user detail information’s about targeted property
- Title
- Price
- Owner
- Picture
- More pictures (if any)
- Address
- Map
- Date posted
- Logged in
- Create functionality for user to be able log out from current session
- Show user detail information’s about targeted property
- Add property
- Allow logged in user to add new property to database
- Create functionality for user to pay small fee via Stripe
The idea is to let user to pay small (£5-£10) onetime fee for posting
- Edit property
- Allow logged in user to edit his own property
- Profile page
- Show user avatar (if any)
- Show properties added by user
- Let user to edit his profile
- Let user to be able to change his password
- Django admin
- Show registered users and they information
- Show properties by users
- Show all messages
Design | Importance |
---|---|
Functionality | 7 |
User experiences | 6 |
HTML / CSS | 5 |
- aph (The project)
- accounts allow user to:
All Django default auth templates has been redisigned.- register new account together with extended profile
- log in with existing account
- profile
- view detailed information about his profile
- access edit profile view
- view his own listings / access add new listing view
- generate invoice
- view the listing
- change password
- reset forgoten password (real email address required)
- edit his profile
- admin users (ci user has been added as su)
- access Django admin area
I decided to change the styles a bit to match at least the colour scheme
- access Django admin area
- enquiries allow user to:
- view unread messages in
nav
Refresh every 3s - send direct message to admins only
- to contact owner of the property
- exchange messages between users
- to delete messages and conversations
- view unread messages in
- fake_data_gen
- purely created for testing purposes...
- generate fake
User
,UserProfile
andListing
which are stored directly to database
- generate fake
- purely created for testing purposes...
- listings allow user to:
- view all (paginated) listings (visible) with image and short information
- search_ by
Keywords
(description),City
,State
,Bedrooms
andMax Price
- search for
City
,State
,Bedrooms
,Sqft
,Garage
andUser
via tags in short info
All queries are sorted in descending order
- search for
- view detailed information about viewed property
- if owner user can access edit / delete views
- if logged in but not the owner user can contact the owner via direct message
- edit listing (if owner)
- delete listing (if owner)
- add new listing (if user is authenticated)
I decided to go for 3 step process...add_house
- main view adding new house. The data are also stored in session till user did not pay for the listing to prevent losing the data and re-entering it again after
preview_house
- preview submitted data
- go back to edit the form data
- continue to pay fee
pay_fee
- one time fee of $10 is charged to the user processed via Stripe
For testing use CN: 4242424242424242, CVV: 111 and Exp Date: Any valid date
- one time fee of $10 is charged to the user processed via Stripe
- pages
- only to hold
index
Originally I had more use for this app but I decided to scrap them all.
- only to hold
- accounts allow user to:
For more detail information please visit Changelog and Fixes
I decided to use Postgres database.
- Models
- Listing (
Listing
) - Enquiries (user messages) (
ContactMessage
) - Enquiries (user conversations) (
PropertyEnquire
) - Default Django auth User (
User
) - User profile (extend
User
model from Djangoauth
)
- Listing (
-
aph (The project)
- enquiries
- if user send message to admins this should create a new conversation as admin is forced to create new conversation manually in admin area to be able to reply the user
- unfortunately this app only works for "property enquiries". Could use the system for admin messages as well as for confirmation after payment / registration
- listings
- add more fields to listing form (first line of address and so on)
- generated invoices needs better design. Unfortunately I had many problems with parsing Embedded CSS while generating the pdf. In almost every case I was forced to use Inline CSS styles. As every template comes with Embedded CSS I was forced to create something simple with inline styles.
- all houses in database has the same (hardcoded value). As I faked all data the addresses does not exist. Therefore the Google map is for showing the layout only
- search form is very basic
- add / edit listing
- every time there is a validation error or the form is edited the images must be uploaded again. This is very bad approach. The user should not need to do this if he does not wish to change the image. Also user should have access to his uploaded images.
- even if the listing is deleted the images still persist
- enquiries
-
Others
- Automated testing as 99% tests has been done manually
- CSS styles are very basic as I was focusing on the functionality and UX of the project.
- More JS for better UX and animations as the page is very static
- More form validations across all forms
- More comments across all files
- More content across all pages
- Media folder is pushed to git just for the purpose of testing project locally
- Unfortunately due to limited time the last sections of README is not in many details as write all down will require much more time with this project
- Bootstrap 4.0
- used to build the layout and style the project
- Font Awesome 5.3
- used for icons
- jQuery 3.2
- used for DOM / Sending requests to server
- Light box
- used to create slide show of additional images (if any)
- jQ cookie
- used to retrieve
csrf_token
from session
- used to retrieve
- Stripe
- used to fake real payments
- Django 2.1
- used build the project
- Django Bootstrap 4
- mainly used for form rendering and styling including form errors
- Sendgrid Django 4.2
- used to send real emails to user together with Sendgrid transactional template.
- also used for Django auth reset password emails
- xhtml2pdf
- used generate invoice from HTML template to PDF file
- render the template as request
- attach a PDF file to an email and send it to user
- used generate invoice from HTML template to PDF file
- Faker 0.9
- to fake the data in database
Git has been used for version control.
-
There are 12 different branches:
-
master branch used in production.
- *The application is built from this branch on Digital Ocean
-
11 other branches has been created for development purpose only. Where each branch represent different version of the application.
-
Before version 1 I did not use separated branches.
-
0.0
- Created basic file structure, installed minimum requirements, started the project "APH"
- Added setting for
static
files and folders
-
0.1
- Created fundamental for
pages
app (for pages such asindex.html
orabout.html
) - Added fundamentals for
base.html
- Created fundamental for
-
0.2
- Created fundamental for
listings
app (for pages such ashouses.html
,house.html
orsearch.html
) - separated
templates
andstatic
for each of the app - added testing via Django build in tests
- Created fundamental for
-
0.3
As I did not have enough data as well as I thought that this project will be too similar to my 4th project I decided to change the project slightly.- Changed
plants.html
andplant.html
to match the project definition - Added venv variables to hide keys
- Added
if
statement to swap between testing and production database - Created single listing model
- Added fundamental for
accounts
app - Create
User
model - Created small up to fake users and listing using
django faker
- Changed
-
0.4
- Added img to db
- Created fundamentals for
houses.html
- Customized the Admin dashboard
- Added Showcase section to
index.html
- Added More Information section to
index.html
- Added fundamentals Contact us section to
index.html
- Added
_page-header.html
-
0.5
- Extended the
User
model - Added section for Bootstrap messages
- Added user registration and creating profile
- Added Log in functionality for user
- Added Log out functionality for user
- Added functionality for user to be able to change his password
- Added fundamentals for user to be able to reset his password
- Extended the
-
0.6
- Added
enquiries
app for user to be able to contact the company
- Added
-
0.7
- Finished
house.html
for single house listing - Added Light Box for secondary images in single house listing
- Added fundamentals for
edit_house
view
- Finished
-
0.8
- Removed Whitenoise as no longer required
- Deployed the project to Digital Ocean for testing
- Connected the
fake_data_gen
app to the project - Added more styles to
index.html
andhouse.html
- Separated settings for production and development
- Removed
/media
from.gitignore
as used on Digital Ocean and for local testing (CI)
Please note that I will normally store the images elsewhere or upload to the images directly to Digital Ocean.
I only storing them in GitHUb for easy local testing.
-
0.9
- Added first step to add new listing
At this point I got back to the project.
- Changelog
- Created proper
README
template and adjusted it to fit the project - Tidied up static files
- Added favicon
- index.html
- Added more CSS styles to
nav
- Added CSS transitions to
#listings
section
- Added more CSS styles to
- Created proper
- Fixes
- index.html
- fixed
overflow
issues (on small screens) in#more-info
section
- fixed
- index.html
- Changelog
- fake_data_gen
- added
testing_models.py
to not repeat myself while creatingUser
,UserProfile
orListing
- added
- added more test across the project
- Listing app
- added fundamentals for
preview_house()
- created partial template as _large_listing.html is now used in preview_house.html as well as house.html
- moved progression bar to separate partial template _progress_bar.html
- added fundamentals for
- fake_data_gen
- Fixes
- Listings app
add_house()
is_valid()
now properly validate the form includingclean_zipcode()
- added separate
return
statement to prevent the form to reset after an error
forms.py
- zipcode is now striped of all white spaces and lower cased in
clean_zipcode()
for more security - added more validations to form (
max
andmin
) bedrooms, bathrooms and garage field
- zipcode is now striped of all white spaces and lower cased in
- Pages app
index()
- now properly search only houses which are published instead off all
- Listings app
- Changelog
- added functionality for Stripe payments in last step of adding new listing (pay_fee.html)
- Fixes
- moved lightbox.min.js to listing app as it does not have to be loaded on every page
- Changelog
- added functionality for editing a existing house
- added functionality for user to be able to delete his own house
- created separate form class for edit house (
EditListingForm()
)
- Fixes
- Listing app
- removed
unique=True
fromzipcode
in models.py as this was throwing form error while using the same model for edit hose.
This does not effectAddListingForm
due toclean_zipcode()
. - added simple
if
statment toedit_house()
to prevent user accidentally overwrite other user listing while entering zipcode which already exist in database
Did not useclean_zipcode()
as this was throwing validation error while rendering the form.
- removed
- Listing app
- Changelog
- added more styles to
nav
- APH
- added JS modal alerts
- Enquire app
- added separate view for contact messages
- added functionality to contact the owner of the house in house.html
- added "pooling" (via JQ Ajax) user messages and enquiries from the server for logged in users
- added _user_messages_modal.html to display unread / read messages and enquiries to user
- added create_conversations.py
Create conversations from received and sent messages.
- Nav
- added separate
.user-messages
btn to _navbar.html.
One for mobile and for large screens.
- added separate
- added more styles to
- Fixes
- Listings app
- fixed 500 error in house.html due to injecting _delete_house_modal.html and _enquire_modal.html even when user was not authenticated
- Enquire app
- _user_messages_modal.html
- added loader as there is 3s delay before messages are inhered to the modal
- models.py
- PropertyEnquire
- changed house_id to IntegerField due to the 500 error when listing has been deleted
- changed sender_id
on_delete
to models.CASCADE as messages should not be kept when the user does not exist anymore
- PropertyEnquire
- _user_messages_modal.html
- Listings app
- Changelog
- Listing app
- added simple pagination to _small_listing.html via build in Django
Paginator
- added simple pagination to _small_listing.html via build in Django
- Listing app
- Changelog
- Listing app
- added houses.css to separate the code
- added
search()
functionality to views.py- added pagination
- added search information based on user input
- added
search_by_links()
to views.py to let user search by square_feet, garage, bedrooms or bathrooms - added
search_by_user()
to views.py to let user search by user listings - added _search_form.html to houses.html
- Listing app
- Fixes
- _navbar.html
- fixed issue when message btn was showing even when user was not authenticated in mobile view
- _search_form.html
- removed countries from and decided to use just states as this makes the app much simpler to work with
- removed currency selection from now and working with dollars only as again I did not want to go trough converting currencies depends on where the user is based.
- user is now properly redirected back to last visited page after login / register (
next_url
)
- _navbar.html
- Changelog
- Fixes
- views.py
- fixed issue with incorrect redirect when user is authenticated when viewing
register()
- fixed issue with incorrect redirect when user is authenticated when viewing
- fixed issue with
nav
in mobile view. Thenav
will always turn white when the.navbar-toggler
is pressed to avoid hard reading ofnav
content - added more styles across the page to fix many styles issues
- views.py
- Changelog
- profile.html
- added user information section to display his details
- added listing section to display the user's listings (if any) or let user to create one
- edit_profile.html
- added functionality for user to edit his
User
model and / orUser Profile
- added functionality for user to edit his
- _footer.html
- added social links to this section
- profile.html
- Fixes
- views.py
- decided not to use
next_url
whenever user creates new account. Instead user will be redirected to profile.html
- decided not to use
- views.py
- Changelog
- 404.html
- added custom 404 error page
- 500.html
- added custom 500 error page
- Listing app
- invoice.html
- serve as template for rendering invoices for users
- invoice.py
render_to_request()
render
invoice from HTML template andreturn
it asHttpResponse
(pdf file)
render_to_file()
- save generated invoice to pdf file. Used for
send_pdf()
- save generated invoice to pdf file. Used for
send_pdf()
- attach generated pdf to email and send the email to user with email template
- pay_fee.html submit btn is now disabled after submitting the form to prevent user to accidentally pay twice
- 404.html
- Fixes
- _navbar.html
- added
.active
class for user to see on which page he is on - added
if
statement to check for user rights for access to Django admin - removed unnecessary links
- added
- Listing app
- house.html decided to change the short description to links for user to be able search by clicked tag
add_house()
added initial values for better UXpay_fee()
- user is now redirected to
index
if he already paid to prevent the user pay twice - email with attached invoice as pdf is now send to user after successful payment is made
- user is now redirected to
preview_house()
user is now redirected toindex
if he already paid to prevent the user pay twiceAddListingForm
added more form validations such asclean_price()
andclean_bedrroms()
- added
if
statment to _large_listing.html to check if additional images exist before injecting the section
- Accounts app
- profile.html
.listings-overlay
- now display to user if the listing has been approved by admins yet
- allow user to visit the listing page
- allow user to view invoice as pdf in separate window
- confirmation email is now send to user after registration. Example of this email can be viewed here
- profile.html
- _navbar.html
- Changelog
- Pages app
- index.html
- replaced dummy text and add more content to this section
- index.html
- modal.html
- added mainly to warn user if he has a unsaved listing
- Enquires app
- slightly changed the fields names in
ContactForm
for better understanding
- slightly changed the fields names in
- validated HTML / CSS / JS
- Pages app
- Fixes
- Enquires app
send_contact_message()
added form validation
- Enquires app
-
Front End
-
- 1 error due to BS4 rendering info
-
- Sorry! We found the following errors (32)
- Due to the BS4 and Lightbox css
- Warnings (1023)
- Due to the BS4 and Lightbox css
- Sorry! We found the following errors (32)
-
JSHint (Report of all custom JS functions)
- Metrics
- There are 46 functions in this file.
- Function with the largest signature take 2 arguments, while the median is 0.
- Largest function has 17 statements in it, while the median is 3.
- The most complex function has a cyclomatic complexity value of 7 while the median is 1.
- Five unused variables
- As their are called from templates
- Metrics
-
-
Back End
-
Fixed issue where alerts prevent user to click on nav links
-
pages app
- tested views
- index
- Fixed issue with
alert
to only shows on top of the page
- Fixed issue with
-
listings app
- tested views
- tested listing model
- added
max-height
to.card-img-overlay
as it was covering the<a>
-
accounts app
- tested User model
- tested UserProfile model
- tested views
- Fixed
clean_email()
now properly check if user already exist - Fixed issue when user was not redirected to last page visited
- Fixed
- Fixed issue where alerts prevent user to click on nav links
-
pages app
- tested views
- index
- Fixed issue with
alert
to only shows on top of the page
- Fixed issue with
-
listings app
- tested views
- tested listing model
- added
max-height
to.card-img-overlay
as it was covering the<a>
-
accounts app
- tested User model
- tested UserProfile model
- tested views
- Fixed
clean_email()
now properly check if user already exist - Fixed issue when user was not redirected to last page visited
I decided to deploy the project to Digital Ocean as I wanted to learn something new in process.
-
created [requirements.txt]
-
Pre-requirements
- pushed the latest changes to GitHub
- created new Ubuntu 18.04 x64 droplet
- generated new ssh key to connect to the server
-
Users
- created new user and gave him root privileges
- added the new ssh key to the created user
- disabled the default root user for security reasons
-
Security
- added simple firewall for better security
-
Software and packages installation
- updated the packages on the server
- installed Python 3
- installed python3-venv
-
Postgres Database
- installed Postgres
- created new Postgres database
- created new user in the database and gave him all privileges
-
Folders set up
- created new dir
- cloned the repository to the created dir
-
Virtual Environment
- created new venv and active it
- installed requirements to the venv from requirements.txt
-
Cloud (production) setting
- created .bash_profile and added all necessary variables to it
- added try block to seettings.py to look for the cloud setting first
-
Server set up
- migrated to new created database
- add superuser
- collected static files
- allowed port 8000 (default for Django) to the firewall
-
Gunicorn
- deactivated the venv
- installed Gunicorn via pip
- added gunicorn.socket and gunicorn.service configuration as per Digital Ocean documentation
- enabled Gunicorn socket and checked for existence of it
-
NGINX
- installed NGINX
- created new folder in the project to hold the setting
- added NGINX setting as per documentation and adjust it to the project
- added 10Mb max rule to NGINX config to allow users to upload large images
- tested NGINX config
- removed port 8000 from firewall and add new port 80 (NGINX)
-
restarted Gunicorn and NGINX
-
Custom Domain
Please note that not every feature of the project can be run locally as I do not provide me secret keys.
Due to the security reasons I do not publish any of those and therefore the project can not be really run locally.
If you like to test those features please visit the website
Also the project comes with local setting and pre-pulated database with users thier profiles and listings.
- su
- username: ci
- password: codeinstitute
- Download and install Python 3
- Clone or download the project Please note that if you downloaded the project manually you must unpack it after
- Open your Command line (CLI) inside the project root or navigate to it
- Create virtual environment (venv) (optional)
- Activate venv
source venv/bin/activate
where "venv" is the name of your virtual environment
- Activate venv
- Install required packages via CLI
pip install -r requirements.txt
- Set venv variables
- IP
0.0.0.0
- PORT
8000
- DEVELOPMENT
True
- STRIPE_PUBLISHABLE
your_key
- STRIPE_SECRET
your_key
- SENDGRID_API_KEY
your_key
- IP
- Database
You can skip this steps if you use the database suplied with the projectpython manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
python manage.py collectstatic
- Run the application
python manage.py runserver
- The application should now run on your
localhost:8000
- Fake data
- make sure that the
DEBUG
is set toTRUE
- visit
localhost:8000/fake-data/
- go back to terminal and enter how many records you wish to have
- make sure that the
- Additional requirements
- you will need to own or register new account at sendgrid.com
- add 2 Transactional Templates one for registration-email-template-example.html and one for invoice-email-template-example.html. You can import each of them via
.txt
files in /assets/email_templates - lastly you will need to change your templates id in both
registration_email_template_id
andinvoice_template_id
-
- for his incredible Django 2 and Python tutorials
-
everyone for finding few minutes to test the project!
All of you gave me constructive feedback which made the project better 😊
- all images has been borowed from pexels.com
- favicon was generated at favicon-generator.org