A modern Django application for showcasing your development projects. Built with Django 5.1 and styled with Tailwind CSS, this application provides a clean and responsive interface to display your portfolio of projects.
All the About
, Skills
, Social links
and Projects
are fully dynamic
from the database, no code editing should be needed to get your profile up and
running.
There is a live example of this portfolio app at https://www.gnramsay.com
django-tailwind-cli
to make the
integration easier. This uses the Tailwind CLI and does NOT require Node.js to
be installeddjango-cotton
and django-shadcn
uv
, pre-commit
, ruff
, mypy
)Other package requirements will be automatically installed with the dependencies.
git clone https://github.com/seapagan/django-projects
cd django-projects
uv venv
uv sync --no-dev
source .venv/bin/activate # On Windows: .venv\Scripts\activate
git clone https://github.com/seapagan/django-projects
cd django-projects
python -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -r requirements.txt
The application uses further environment variables for configuration and security. Most of them do have a default setting that will be used if not specified.
Key settings:
DJANGO_SECRET_KEY
: Your Django secret key. This MUST be set to a secure
string.DJANGO_DEBUG
: Set to 1 for development, 0 for productionDJANGO_SECURE_MODE
: Set to 1 to enable enhanced security features for
production (see Security Features section)DJANGO_CSRF_TRUSTED_ORIGINS
: JSON array of trusted origins for CSRF
protection, e.g. ["https://www.myserver.com"]
DJANGO_ALLOWED_HOSTS
: JSON array of allowed hosts, e.g. [".myserver.com"]
DJANGO_STATIC_ROOT
: Path where static files will be collected in production
modeDJANGO_USE_CACHE
: Set to 1 to enable caching for the whole application. This
uses memcached
and that needs to be installed locally. Defaults to 0 (better
for development) See the Caching secton below.DJANGO_CACHE_TIMEOUT
: Cache expiry length, in seconds (Defaults to 600, 10
minutes)DJANGO_PROTECT_ADMIN
: Set to 1 to enable IP-based admin access restriction. If not set or set to 0, all IPs can access the admin panelDJANGO_ADMIN_IPS_ALLOWED
: JSON array of IP addresses allowed to access the
admin panel, e.g. ["127.0.0.1", "192.168.1.100"]
. If not set, defaults to
["127.0.0.1", "localhost"]
. Only used when DJANGO_PROTECT_ADMIN
is set to 1[!NOTE]
If you do specify a value of
DJANGO_ADMIN_IPS_ALLOWED
, the defaults are REMOVED. Should you still want the defaults, they need to be manually added to the env var along with your custom addresses.
DJANGO_SECURE_PROXY
: Set to 1 if you are using a secure reverse proxy (which
passes the X_FORWARDED_FOR
header) to serve this appRECAPTCHA_SITE_KEY
: Your Google reCAPTCHA v2 site keyRECAPTCHA_SECRET_KEY
: Your Google reCAPTCHA v2 secret keyBy default, the application uses SQLite for the database. However, you can configure it to use PostgreSQL instead by setting the following environment variables:
DJANGO_USE_POSTGRES
: Set to 1 to use PostgreSQL instead of SQLiteDJANGO_POSTGRES_DB
: PostgreSQL database nameDJANGO_POSTGRES_USER
: PostgreSQL usernameDJANGO_POSTGRES_PASSWORD
: PostgreSQL passwordDJANGO_POSTGRES_HOST
: PostgreSQL host (default: localhost)DJANGO_POSTGRES_PORT
: PostgreSQL port (default: 5432)[!NOTE]
The PostgreSQL user and database must already exist, and the user needs to have ownership (or access) to that database.
For SQLite, the local database file will be created when the migrations are run.
[!IMPORTANT]
This project uses the binary version of
psycopg
which is not compatible with some older systems (primarily older Macs or if using PyPy Python). If you encounter compatibility issues, you should remove thepsycopg[binary]
package and use the plainpsycopg
package instead, or compile it locally. For more information, see the psycopg documentation on supported systems.
You can configure the email (SMTP) server to send contact emails using the below environment variables:
USE_LIVE_EMAIL
: Set to 1 to send actual emails, 0 or unset to output to
consoleEMAIL_HOST
: SMTP server host (required if USE_LIVE_EMAIL=1)EMAIL_PORT
: SMTP server port (required if USE_LIVE_EMAIL=1)EMAIL_HOST_USER
: SMTP username (required if USE_LIVE_EMAIL=1)EMAIL_HOST_PASSWORD
: SMTP password (required if USE_LIVE_EMAIL=1)EMAIL_USE_TLS
: Set to 1 to use TLS for SMTP (optional, defaults to 1)DEFAULT_FROM_EMAIL
: Default sender email address (required if
USE_LIVE_EMAIL=1)CONTACT_FORM_RECIPIENT
: Email address where contact form submissions will be
sent (required if USE_LIVE_EMAIL=1).env
FileCreate an .env
file in the project root with the following content, or set the
environment variables directly:
DJANGO_SECRET_KEY=your-secret-key
DJANGO_DEBUG=1 # sets debug mode
DJANGO_SECURE_MODE=0 # set to 1 for production security features
# Database settings (optional - defaults to SQLite if not set)
DJANGO_USE_POSTGRES=0 # set to 1 to use PostgreSQL
DJANGO_POSTGRES_DB=mydatabase
DJANGO_POSTGRES_USER=myuser
DJANGO_POSTGRES_PASSWORD=your-postgres-password
DJANGO_POSTGRES_HOST=localhost
DJANGO_POSTGRES_PORT=5432
# Production settings (required when DJANGO_DEBUG=0)
DJANGO_CSRF_TRUSTED_ORIGINS=["https://www.myserver.com"]
DJANGO_ALLOWED_HOSTS=[".myserver.com"]
DJANGO_STATIC_ROOT="/var/www/myproject/static/"
# cache settings
DJANGO_USE_CACHE=0 # set to 0 for development, 1 for production when the database rarely changes
DJANGO_CACHE_TIMEOUT=3600 # defaults to 600 (10 minutes) if not set
# admin IP whitelist
DJANGO_PROTECT_ADMIN=1 # set to 1 to enable IP-based admin access restriction
DJANGO_ADMIN_IPS_ALLOWED=["127.0.0.1", "192.168.1.100"] # IP addresses allowed to access admin panel
# enable secure proxy forwarding. This ALSO needs 'DJANGO_SECURE_MODE=1'
DJANGO_SECURE_PROXY=0 # set to 1 if you are serving behind a secure proxy (ie nginx etc)
# recaptcha settings
RECAPTCHA_SITE_KEY=your-recaptcha-site-key
RECAPTCHA_SECRET_KEY=your-recaptcha-secret-key
# Email settings (optional - if USE_LIVE_EMAIL=0, emails will output to console only)
USE_LIVE_EMAIL=1
EMAIL_HOST=smtp.example.com
EMAIL_PORT=587
[email protected]
EMAIL_HOST_PASSWORD=your-smtp-password
EMAIL_USE_TLS=1
[email protected]
[email protected]
[!NOTE]
There is an
.example.env
file in the project root you can rename and modify to your own settings
To get your reCAPTCHA keys (required for the contact form functionality):
RECAPTCHA_SITE_KEY
and "Secret Key" to
RECAPTCHA_SECRET_KEY
[!NOTE]
For a Production or User-Facing project, ALWAYS set
DJANGO_DEBUG=0
python manage.py migrate
python manage.py createsuperuser
python manage.py tailwind runserver
You can customize the portfolio with your own skills and projects through the Django Admin interface at http://localhost:8000/admin
You can add and manage about sections through the Django admin interface in the Site Configuration section. Each about section has the following field:
About sections are linked to your site configuration and allow you to provide
detailed information about yourself, your background, skills, and experience.
Each about
record will be in a separate paragraph - you can use limited
HTML tags in this field (a
,strong
,em
,ul
,ol
,li
,sub
,sup
- all
others will be stripped.)
If you have no about sections added, the About area will not be displayed.
You can add projects by clicking on the Projects section in the admin pages. Each project has the following fields:
Projects are displayed on your portfolio page in the following order:
priority
value are shown first, sorted by priority (lower
numbers appear first and duplicate priorities are sorted by their
created_at
date.)priority
value are then displayed, sorted by creation
date (oldest to newest)You can set a project's priority in the admin interface. This allows you to highlight your most important projects by giving them lower priority numbers.
The application provides additional configuration options through the Django admin interface at the Site Configuration section . These settings allow you to customize various aspects of your portfolio:
Site Content
owner_name
: The Owner of the site. This will be the page title and the
text in the header of the page content (default: "The Developer")hero_title
: The main title displayed on your portfolio (default: "Full
Stack Developer")hero_info
: Primary text content for the hero section (up to 500
characters, no default)hero_secondary
: Secondary text content for the hero section (up to 500
characters, no default)Social Media Links
github_username
: Your GitHub usernametwitter_username
: Your Twitter/X usernamelinkedin_username
: Your LinkedIn usernameyoutube_username
: Your YouTube channel usernamemedium_username
: Your Medium usernameAny social media usernames left blank will not be displayed on your portfolio.
About Section
As mentioned above you can add/edit your 'About' info here too.
Languages and Frameworks
You can manage your programming languages and frameworks through the Django
admin interface by editing the Site Configuration
database.
Languages : Add programming languages you work with
name
: Name of the programming language (e.g., "Python", "JavaScript")Frameworks (/admin/app/framework/
): Add frameworks and libraries you
work with
name
: Name of the framework (e.g., "Django", "React")Both languages and frameworks will be displayed on your portfolio to showcase your technical stack. If you have none added, the whole My SKills section will not be displayed.
These settings can be modified at any time through the admin interface and changes will be reflected immediately on your portfolio (though, see the note below on Caching)
The contact form will save each contact into the database and you can view these in the Admin pages in the Contact Submissions section.
Optionally, if you have set up the email, each contact will also be emailed to you as it is submitted.
This Django application is set up for optional caching which is disabled by default. Generally it is not much use when you are setting up or customizing the database since any changes will take 10 minutes to actually be visible in the front-end. It is a bit overkill for this application but a good skill to learn.
[!NOTE]
Probably best not to enable this until your database is set up to your liking, as the default is a 10 minute cache time-out. See below how to change this.
I have chosen to use memcached for this, though you can use Redis, File Caching, Local Memory or any other method that Django supports. See the Django docs on Caching for more info.
You need to install memcached for this to work. If this is not installed and the cache enabled your application will crash.
See the link above for how to install for your server, under Debian/Ubuntu it is available as a package:
sudo apt install memcached
Finally, to enable the caching, set the below variable in your .env
file or
environment:
DJANGO_USE_CACHE=1
If this variable is 0 or missing, caching will be disabled.
You can change the length of time that the database is cached by setting the below variable:
DJANGO_CACHE_TIMEOUT=1200 # Default is 600 (10 Minutes)
For production deployment, it's recommended to:
DJANGO_DEBUG=0
to disable debug modeDJANGO_SECURE_MODE=1
to enable security features[!CAUTION]
The Secure Mode settings may need tweaking for your exact use-case. As it stands, they work well for an application served behind an nginx reverse-proxy (which is a GREAT way to serve your Django App) but may cause issues in other cases. Checkout the
config/settings.py
file around the end to see what settings are set and add/adjust any that you need for your specific setup.
DJANGO_USE_POSTGRES=1
and
configuring the related database settingsDJANGO_PROTECT_ADMIN=1
to enable IP-based admin access restriction and configure DJANGO_ADMIN_IPS_ALLOWED
with trusted IP addressesDJANGO_CSRF_TRUSTED_ORIGINS
: Your domain(s) as a JSON arrayDJANGO_ALLOWED_HOSTS
: Your domain(s) as a JSON arrayDJANGO_STATIC_ROOT
: The path where static files will be collectedYou should also run the collectstatic files before deployment:
python manage.py collectstatic
[!TIP]
This is a customized
collectstatic
command which, as well as the usual functionality will also:
- build the Tailwind production file
- minimize any Javascript files in the
assets/js
folder.You no longer need to run the
python manage.py tailwind build
command seperately.
The application includes gunicorn for production deployment. A typical command to run the application in production would be:
gunicorn config.wsgi:application --bind 0.0.0.0:8000
For a proper production setup, you should:
For a detailed guide on setting up Django with Nginx and Gunicorn in production, see: https://realpython.com/django-nginx-gunicorn/
When DJANGO_SECURE_MODE=1
(and DJANGO_DEBUG=0
), the following security
features are enabled:
[!CAUTION]
Be very careful when setting the HSTS timeout to a longer value (like 180 days). Once set, it can be almost impossible to change later as browsers will remember this setting for the specified duration. However, setting a longer timeout is highly recommended once you've verified that HSTS is working correctly for your site.
DJANGO_PROTECT_ADMIN=1
, only allows specified IP addresses to
access the admin panelDJANGO_SECURE_PROXY=1
, your proxy needs to
support this.These features follow Django security best practices and help protect your application against common web vulnerabilities.
āāā app/ # Main application code
āāā assets/ # Static assets
ā āāā css/ # CSS files
ā āāā js/ # JavaScript files
ā āāā favicon.ico # Put your favicon here or use the existing one
āāā config/ # Project configuration
āāā templates/ # HTML templates
ā āāā app/ # Application templates
ā āāā cotton/ # Component templates
āāā manage.py # Django management script
git checkout -b feature/amazing-feature
)git commit -m 'Add amazing feature'
)git push origin feature/amazing-feature
)This project uses several development tools to maintain code quality:
pre-commit
hooks for code formatting and lintingruff
for Python lintingmypy
for type-checkingdjango-browser-reload
for live developmentdjango-stubs
for improved type checkingInstall development dependencies and set up pre-commit:
Using uv (Recommended):
uv sync
pre-commit install
Using pip:
pip install -r requirements-dev.txt
pre-commit install
This project is licensed under the MIT License - see the LICENSE file for details.