diff --git a/python-oop/README.md b/python-oop/README.md
new file mode 100644
index 0000000000..5248955c1a
--- /dev/null
+++ b/python-oop/README.md
@@ -0,0 +1,3 @@
+# Object-Oriented Programming (OOP) in Python 3
+
+This folder provides the code examples for the Real Python tutorial [Object-Oriented Programming (OOP) in Python 3](https://realpython.com/object-oriented-programming-oop-in-python-3/).
diff --git a/python-oop/cars.py b/python-oop/cars.py
new file mode 100644
index 0000000000..62f75bbd5c
--- /dev/null
+++ b/python-oop/cars.py
@@ -0,0 +1,14 @@
+class Car:
+ def __init__(self, color, mileage):
+ self.color = color
+ self.mileage = mileage
+
+ def __str__(self):
+ return f"The {self.color} car has {self.mileage:,} miles"
+
+
+blue_car = Car(color="blue", mileage=20_000)
+red_car = Car(color="red", mileage=30_000)
+
+for car in (blue_car, red_car):
+ print(car)
diff --git a/python-oop/dog.py b/python-oop/dog.py
new file mode 100644
index 0000000000..e11a45a322
--- /dev/null
+++ b/python-oop/dog.py
@@ -0,0 +1,12 @@
+class Dog:
+ species = "Canis familiaris"
+
+ def __init__(self, name, age):
+ self.name = name
+ self.age = age
+
+ def __str__(self):
+ return f"{self.name} is {self.age} years old"
+
+ def speak(self, sound):
+ return f"{self.name} says {sound}"
diff --git a/python-oop/dogbreeds.py b/python-oop/dogbreeds.py
new file mode 100644
index 0000000000..3ff89b47cb
--- /dev/null
+++ b/python-oop/dogbreeds.py
@@ -0,0 +1,25 @@
+class Dog:
+ species = "Canis familiaris"
+
+ def __init__(self, name, age):
+ self.name = name
+ self.age = age
+
+ def __str__(self):
+ return f"{self.name} is {self.age} years old"
+
+ def speak(self, sound):
+ return f"{self.name} barks: {sound}"
+
+
+class JackRussellTerrier(Dog):
+ def speak(self, sound="Arf"):
+ return super().speak(sound)
+
+
+class Dachshund(Dog):
+ pass
+
+
+class Bulldog(Dog):
+ pass
diff --git a/python-oop/retriever.py b/python-oop/retriever.py
new file mode 100644
index 0000000000..1d08f677fa
--- /dev/null
+++ b/python-oop/retriever.py
@@ -0,0 +1,17 @@
+class Dog:
+ species = "Canis familiaris"
+
+ def __init__(self, name, age):
+ self.name = name
+ self.age = age
+
+ def __str__(self):
+ return f"{self.name} is {self.age} years old"
+
+ def speak(self, sound):
+ return f"{self.name} says {sound}"
+
+
+class GoldenRetriever(Dog):
+ def speak(self, sound="Bark"):
+ return super().speak(sound)
diff --git a/python-oop/starfleet_list.py b/python-oop/starfleet_list.py
new file mode 100644
index 0000000000..a1fd78714a
--- /dev/null
+++ b/python-oop/starfleet_list.py
@@ -0,0 +1,5 @@
+# Starfleet employees as a list
+
+kirk = ["James Kirk", 34, "Captain", 2265]
+spock = ["Spock", 35, "Science Officer", 2254]
+mccoy = ["Leonard McCoy", "Chief Medical Officer", 2266]
diff --git a/python-oop/starfleet_objects.py b/python-oop/starfleet_objects.py
new file mode 100644
index 0000000000..1fe1014a48
--- /dev/null
+++ b/python-oop/starfleet_objects.py
@@ -0,0 +1,14 @@
+# Starfleet employees as objects
+
+
+class Employee:
+ def __init__(self, name, age, position, year_started):
+ self.name = name
+ self.age = age
+ self.position = position
+ self.year_started = year_started
+
+
+kirk = Employee("James Kirk", 34, "Captain", 2265)
+spock = Employee("Spock", 35, "Science Officer", 2254)
+mccoy = Employee("Leonard McCoy", 137, "Chief Medical Officer", 2266)
diff --git a/python-qr-code/README.md b/python-qr-code/README.md
new file mode 100644
index 0000000000..3cdfe7624c
--- /dev/null
+++ b/python-qr-code/README.md
@@ -0,0 +1,10 @@
+# Generate Beautiful QR Codes With Python
+
+Supporting code for the Real Python tutorial [Generate Beautiful QR Codes With Python](https://realpython.com/python-generate-qr-code/).
+
+To run the code in this tutorial, you should have `segno`, `pillow`, and `qrcode-artistic` installed in your environment.
+You can install with the command below:
+
+```console
+$ python -m pip install segno pillow qrcode-artistic
+```
diff --git a/python-qr-code/scripts/animated_qrcode.py b/python-qr-code/scripts/animated_qrcode.py
new file mode 100644
index 0000000000..d64acb6ba2
--- /dev/null
+++ b/python-qr-code/scripts/animated_qrcode.py
@@ -0,0 +1,11 @@
+import segno
+from urllib.request import urlopen
+
+slts_qrcode = segno.make_qr("https://www.youtube.com/watch?v=hTWKbfoikeg")
+nirvana_url = urlopen("https://media.giphy.com/media/LpwBqCorPvZC0/giphy.gif")
+slts_qrcode.to_artistic(
+ background=nirvana_url,
+ target="animated_qrcode.gif",
+ light="blue",
+ scale=5,
+)
diff --git a/python-qr-code/scripts/basic_qrcode.py b/python-qr-code/scripts/basic_qrcode.py
new file mode 100644
index 0000000000..900c21df42
--- /dev/null
+++ b/python-qr-code/scripts/basic_qrcode.py
@@ -0,0 +1,4 @@
+import segno
+
+qrcode = segno.make_qr("Hello, World")
+qrcode.save("basic_qrcode.png")
diff --git a/python-qr-code/scripts/borderless_qrcode.py b/python-qr-code/scripts/borderless_qrcode.py
new file mode 100644
index 0000000000..b377bc560a
--- /dev/null
+++ b/python-qr-code/scripts/borderless_qrcode.py
@@ -0,0 +1,4 @@
+import segno
+
+qrcode = segno.make_qr("Hello, World")
+qrcode.save("borderless_qrcode.png", scale=5, border=0)
diff --git a/python-qr-code/scripts/darkblue_qrcode.py b/python-qr-code/scripts/darkblue_qrcode.py
new file mode 100644
index 0000000000..4fa5a5cdd1
--- /dev/null
+++ b/python-qr-code/scripts/darkblue_qrcode.py
@@ -0,0 +1,11 @@
+import segno
+
+qrcode = segno.make_qr("Hello, World")
+
+# Without changing the color of the quiet zone
+qrcode.save("darkblue_qrcode.png", scale=5, dark="darkblue")
+
+# With quiet zone
+qrcode.save(
+ "darkblue_qrcode.png", scale=5, dark="darkblue", quiet_zone="maroon"
+)
diff --git a/python-qr-code/scripts/formatted_rotated_qrcode.py b/python-qr-code/scripts/formatted_rotated_qrcode.py
new file mode 100644
index 0000000000..c75c7ccc31
--- /dev/null
+++ b/python-qr-code/scripts/formatted_rotated_qrcode.py
@@ -0,0 +1,8 @@
+import segno
+
+qrcode = segno.make_qr("Hello, World")
+
+qrcode_rotated = qrcode.to_pil(
+ scale=5, light="lightblue", dark="green"
+).rotate(45, expand=True)
+qrcode_rotated.save("formatted_rotated_qrcode.png")
diff --git a/python-qr-code/scripts/green_datamodules_qrcode.py b/python-qr-code/scripts/green_datamodules_qrcode.py
new file mode 100644
index 0000000000..49ce58e140
--- /dev/null
+++ b/python-qr-code/scripts/green_datamodules_qrcode.py
@@ -0,0 +1,22 @@
+import segno
+
+qrcode = segno.make_qr("Hello, World")
+
+# Changing the color of only the dark data modules
+qrcode.save(
+ "green_datadark_qrcode.png",
+ scale=5,
+ light="lightblue",
+ dark="darkblue",
+ data_dark="green",
+)
+
+# Changing the color of the dark and light data modules
+qrcode.save(
+ "green_datamodules_qrcode.png",
+ scale=5,
+ light="lightblue",
+ dark="darkblue",
+ data_dark="green",
+ data_light="lightgreen",
+)
diff --git a/python-qr-code/scripts/lightblue_qrcode.py b/python-qr-code/scripts/lightblue_qrcode.py
new file mode 100644
index 0000000000..032336e148
--- /dev/null
+++ b/python-qr-code/scripts/lightblue_qrcode.py
@@ -0,0 +1,4 @@
+import segno
+
+qrcode = segno.make_qr("Hello, World")
+qrcode.save("lightblue_qrcode.png", light="lightblue", scale=5)
diff --git a/python-qr-code/scripts/rotated_qrcode.py b/python-qr-code/scripts/rotated_qrcode.py
new file mode 100644
index 0000000000..4b9613a663
--- /dev/null
+++ b/python-qr-code/scripts/rotated_qrcode.py
@@ -0,0 +1,11 @@
+import segno
+
+qrcode = segno.make_qr("Hello, World")
+
+# Rotated QR code but truncated
+qrcode_rotated = qrcode.to_pil().rotate(45)
+qrcode_rotated.save("rotated_qrcode.png")
+
+# Setting expand to True
+qrcode_rotated = qrcode.to_pil().rotate(45, expand=True)
+qrcode_rotated.save("rotated_qrcode.png")
diff --git a/python-qr-code/scripts/scaled_qrcode.py b/python-qr-code/scripts/scaled_qrcode.py
new file mode 100644
index 0000000000..913b2597ba
--- /dev/null
+++ b/python-qr-code/scripts/scaled_qrcode.py
@@ -0,0 +1,4 @@
+import segno
+
+qrcode = segno.make_qr("Hello, World")
+qrcode.save("scaled_qrcode.png", scale=2)
diff --git a/python-qr-code/scripts/wide_border_qrcode.py b/python-qr-code/scripts/wide_border_qrcode.py
new file mode 100644
index 0000000000..e4333b99b3
--- /dev/null
+++ b/python-qr-code/scripts/wide_border_qrcode.py
@@ -0,0 +1,4 @@
+import segno
+
+qrcode = segno.make_qr("Hello, World")
+qrcode.save("wide_border_qrcode.png", scale=5, border=10)
diff --git a/rp-portfolio/README.md b/rp-portfolio/README.md
new file mode 100644
index 0000000000..5fe29e8d7f
--- /dev/null
+++ b/rp-portfolio/README.md
@@ -0,0 +1,44 @@
+# Get Started With Django: Build a Portfolio App
+
+Follow the [step-by-step instructions](https://realpython.com/get-started-with-django-1/) on Real Python to build your own portfolio project with Django.
+
+## Images
+
+You can find the example images for the projects in the [uploads/project_images](uploads/project_images) folder.
+
+## Setup
+
+You can run the provided example project on your local machine by following the steps outlined below.
+
+Create a new virtual environment:
+
+```bash
+$ python3 -m venv venv
+```
+
+Activate the virtual environment:
+
+```bash
+$ source venv/bin/activate
+```
+
+Install the dependencies for this project if you haven't installed them yet:
+
+```bash
+(venv) $ python -m pip install -r requirements.txt
+```
+
+Make and apply the migrations for the project to build your local database:
+
+```bash
+(venv) $ python manage.py makemigrations
+(venv) $ python manage.py migrate
+```
+
+Run the Django development server:
+
+```bash
+(venv) $ python manage.py runserver
+```
+
+Navigate to `http://localhost:8000/` or `http://localhost:8000/projects` to see your portfolio project in action.
diff --git a/rp-portfolio/blog/admin.py b/rp-portfolio/blog/admin.py
deleted file mode 100644
index ac64b86daa..0000000000
--- a/rp-portfolio/blog/admin.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from django.contrib import admin
-from blog.models import Post, Category
-
-
-class PostAdmin(admin.ModelAdmin):
- pass
-
-
-class CategoryAdmin(admin.ModelAdmin):
- pass
-
-
-admin.site.register(Post, PostAdmin)
-admin.site.register(Category, CategoryAdmin)
diff --git a/rp-portfolio/blog/apps.py b/rp-portfolio/blog/apps.py
deleted file mode 100644
index 10038974b1..0000000000
--- a/rp-portfolio/blog/apps.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from django.apps import AppConfig
-
-
-class BlogConfig(AppConfig):
- name = "blog"
diff --git a/rp-portfolio/blog/forms.py b/rp-portfolio/blog/forms.py
deleted file mode 100644
index 3fdf2c7e31..0000000000
--- a/rp-portfolio/blog/forms.py
+++ /dev/null
@@ -1,15 +0,0 @@
-from django import forms
-
-
-class CommentForm(forms.Form):
- author = forms.CharField(
- max_length=60,
- widget=forms.TextInput(
- attrs={"class": "form-control", "placeholder": "Your Name"}
- ),
- )
- body = forms.CharField(
- widget=forms.Textarea(
- attrs={"class": "form-control", "placeholder": "Leave a comment!"}
- )
- )
diff --git a/rp-portfolio/blog/migrations/0001_initial.py b/rp-portfolio/blog/migrations/0001_initial.py
deleted file mode 100644
index a8b9d85f76..0000000000
--- a/rp-portfolio/blog/migrations/0001_initial.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Generated by Django 2.1.4 on 2018-12-16 19:11
-
-from django.db import migrations, models
-import django.db.models.deletion
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ]
-
- operations = [
- migrations.CreateModel(
- name='Category',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True,
- serialize=False, verbose_name='ID')),
- ('name', models.CharField(max_length=20)),
- ],
- ),
- migrations.CreateModel(
- name='Comment',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True,
- serialize=False, verbose_name='ID')),
- ('author', models.CharField(max_length=60)),
- ('body', models.TextField()),
- ('created_on', models.DateTimeField(auto_now_add=True)),
- ],
- ),
- migrations.CreateModel(
- name='Post',
- fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True,
- serialize=False, verbose_name='ID')),
- ('title', models.CharField(max_length=255)),
- ('body', models.TextField()),
- ('created_on', models.DateTimeField(auto_now_add=True)),
- ('last_modified', models.DateTimeField(auto_now=True)),
- ('categories', models.ManyToManyField(related_name='posts',
- to='blog.Category')),
- ],
- ),
- migrations.AddField(
- model_name='comment',
- name='post',
- field=models.ForeignKey(
- on_delete=django.db.models.deletion.CASCADE,
- to='blog.Post'
- ),
- ),
- ]
diff --git a/rp-portfolio/blog/migrations/__init__.py b/rp-portfolio/blog/migrations/__init__.py
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/rp-portfolio/blog/models.py b/rp-portfolio/blog/models.py
deleted file mode 100644
index a28ccd2734..0000000000
--- a/rp-portfolio/blog/models.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from django.db import models
-
-
-class Category(models.Model):
- name = models.CharField(max_length=20)
-
-
-class Post(models.Model):
- title = models.CharField(max_length=255)
- body = models.TextField()
- created_on = models.DateTimeField(auto_now_add=True)
- last_modified = models.DateTimeField(auto_now=True)
- categories = models.ManyToManyField("Category", related_name="posts")
-
-
-class Comment(models.Model):
- author = models.CharField(max_length=60)
- body = models.TextField()
- created_on = models.DateTimeField(auto_now_add=True)
- post = models.ForeignKey("Post", on_delete=models.CASCADE)
diff --git a/rp-portfolio/blog/templates/blog_category.html b/rp-portfolio/blog/templates/blog_category.html
deleted file mode 100644
index 324c31eda5..0000000000
--- a/rp-portfolio/blog/templates/blog_category.html
+++ /dev/null
@@ -1,20 +0,0 @@
-{% extends "base.html" %}
-{% block page_content %}
-
-
{{ category | title }}
-
- {% for post in posts %}
-
-
- {{ post.created_on.date }} |
- Categories:
- {% for category in post.categories.all %}
-
- {{ category.name }}
-
- {% endfor %}
-
-
{{ post.body | slice:":400" }}...
- {% endfor %}
-
-{% endblock %}
diff --git a/rp-portfolio/blog/templates/blog_detail.html b/rp-portfolio/blog/templates/blog_detail.html
deleted file mode 100644
index a0791a3686..0000000000
--- a/rp-portfolio/blog/templates/blog_detail.html
+++ /dev/null
@@ -1,36 +0,0 @@
-{% extends "base.html" %}
-{% block page_content %}
-
-
{{ post.title }}
-
- {{ post.created_on.date }} |
- Categories:
- {% for category in post.categories.all %}
-
- {{ category.name }}
-
- {% endfor %}
-
-
{{ post.body | linebreaks }}
-
Leave a comment:
-
-
Comments:
- {% for comment in comments %}
-
- On {{comment.created_on.date }}
- {{ comment.author }} wrote:
-
-
{{ comment.body }}
-
- {% endfor %}
-
-{% endblock %}
diff --git a/rp-portfolio/blog/templates/blog_index.html b/rp-portfolio/blog/templates/blog_index.html
deleted file mode 100644
index 8b80d37760..0000000000
--- a/rp-portfolio/blog/templates/blog_index.html
+++ /dev/null
@@ -1,21 +0,0 @@
-{% extends "base.html" %}
-
-{% block page_content %}
-
-
Blog Index
-
- {% for post in posts %}
-
-
- {{ post.created_on.date }} |
- Categories:
- {% for category in post.categories.all %}
-
- {{ category.name }}
-
- {% endfor %}
-
-
{{ post.body | slice:":400" }}...
- {% endfor %}
-
-{% endblock %}
diff --git a/rp-portfolio/blog/tests.py b/rp-portfolio/blog/tests.py
deleted file mode 100644
index a79ca8be56..0000000000
--- a/rp-portfolio/blog/tests.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# from django.test import TestCase
-
-# Create your tests here.
diff --git a/rp-portfolio/blog/urls.py b/rp-portfolio/blog/urls.py
deleted file mode 100644
index afd1c42b27..0000000000
--- a/rp-portfolio/blog/urls.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from django.urls import path
-from . import views
-
-urlpatterns = [
- path("", views.blog_index, name="blog_index"),
- path("/", views.blog_detail, name="blog_detail"),
- path("/", views.blog_category, name="blog_category"),
-]
diff --git a/rp-portfolio/blog/views.py b/rp-portfolio/blog/views.py
deleted file mode 100644
index 75808bde6b..0000000000
--- a/rp-portfolio/blog/views.py
+++ /dev/null
@@ -1,37 +0,0 @@
-from django.shortcuts import render
-
-from blog.forms import CommentForm
-from blog.models import Post, Comment
-
-
-def blog_index(request):
- posts = Post.objects.all().order_by("-created_on")
- context = {"posts": posts}
- return render(request, "blog_index.html", context)
-
-
-def blog_category(request, category):
- posts = Post.objects.filter(categories__name__contains=category).order_by(
- "-created_on"
- )
- context = {"category": category, "posts": posts}
- return render(request, "blog_category.html", context)
-
-
-def blog_detail(request, pk):
- post = Post.objects.get(pk=pk)
- comments = Comment.objects.filter(post=post)
-
- form = CommentForm()
- if request.method == "POST":
- form = CommentForm(request.POST)
- if form.is_valid():
- comment = Comment(
- author=form.cleaned_data["author"],
- body=form.cleaned_data["body"],
- post=post,
- )
- comment.save()
-
- context = {"post": post, "comments": comments, "form": form}
- return render(request, "blog_detail.html", context)
diff --git a/rp-portfolio/manage.py b/rp-portfolio/manage.py
index bd3c534b5b..ff792ca5eb 100755
--- a/rp-portfolio/manage.py
+++ b/rp-portfolio/manage.py
@@ -1,8 +1,11 @@
#!/usr/bin/env python
+"""Django's command-line utility for administrative tasks."""
import os
import sys
-if __name__ == "__main__":
+
+def main():
+ """Run administrative tasks."""
os.environ.setdefault(
"DJANGO_SETTINGS_MODULE", "personal_portfolio.settings"
)
@@ -15,3 +18,7 @@
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/rp-portfolio/blog/__init__.py b/rp-portfolio/pages/__init__.py
similarity index 100%
rename from rp-portfolio/blog/__init__.py
rename to rp-portfolio/pages/__init__.py
diff --git a/rp-portfolio/pages/apps.py b/rp-portfolio/pages/apps.py
new file mode 100644
index 0000000000..4b6237c55b
--- /dev/null
+++ b/rp-portfolio/pages/apps.py
@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class PagesConfig(AppConfig):
+ default_auto_field = "django.db.models.BigAutoField"
+ name = "pages"
diff --git a/rp-portfolio/pages/templates/pages/home.html b/rp-portfolio/pages/templates/pages/home.html
new file mode 100644
index 0000000000..846d734fb1
--- /dev/null
+++ b/rp-portfolio/pages/templates/pages/home.html
@@ -0,0 +1,5 @@
+{% extends "base.html" %}
+
+{% block page_content %}
+ Hello, World!
+{% endblock page_content %}
diff --git a/rp-portfolio/pages/urls.py b/rp-portfolio/pages/urls.py
new file mode 100644
index 0000000000..56268a2247
--- /dev/null
+++ b/rp-portfolio/pages/urls.py
@@ -0,0 +1,6 @@
+from django.urls import path
+from pages import views
+
+urlpatterns = [
+ path("", views.home, name="home"),
+]
diff --git a/rp-portfolio/pages/views.py b/rp-portfolio/pages/views.py
new file mode 100644
index 0000000000..35737bc4df
--- /dev/null
+++ b/rp-portfolio/pages/views.py
@@ -0,0 +1,5 @@
+from django.shortcuts import render
+
+
+def home(request):
+ return render(request, "pages/home.html", {})
diff --git a/rp-portfolio/personal_portfolio/asgi.py b/rp-portfolio/personal_portfolio/asgi.py
new file mode 100644
index 0000000000..a274c9bcf6
--- /dev/null
+++ b/rp-portfolio/personal_portfolio/asgi.py
@@ -0,0 +1,16 @@
+"""
+ASGI config for personal_portfolio project.
+
+It exposes the ASGI callable as a module-level variable named ``application``.
+
+For more information on this file, see
+https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
+"""
+
+import os
+
+from django.core.asgi import get_asgi_application
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "personal_portfolio.settings")
+
+application = get_asgi_application()
diff --git a/rp-portfolio/personal_portfolio/settings.py b/rp-portfolio/personal_portfolio/settings.py
index 3f1a01e8e5..311a542347 100644
--- a/rp-portfolio/personal_portfolio/settings.py
+++ b/rp-portfolio/personal_portfolio/settings.py
@@ -1,26 +1,28 @@
"""
Django settings for personal_portfolio project.
-Generated by 'django-admin startproject' using Django 2.1.4.
+Generated by 'django-admin startproject' using Django 4.2.4.
For more information on this file, see
-https://docs.djangoproject.com/en/2.1/topics/settings/
+https://docs.djangoproject.com/en/4.2/topics/settings/
For the full list of settings and their values, see
-https://docs.djangoproject.com/en/2.1/ref/settings/
+https://docs.djangoproject.com/en/4.2/ref/settings/
"""
-import os
+from pathlib import Path
-# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
-BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+# Build paths inside the project like this: BASE_DIR / 'subdir'.
+BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
-# See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/
+# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
-SECRET_KEY = "!^_6%0so9$a@u-w22nc56xcp0^spoo4k^3q!j016o5hll+#c#o"
+SECRET_KEY = (
+ "django-insecure-31@!bxz-__i#0_lt_oy4*$e0q$a5h9e32-^yso51gydj^a!v#9"
+)
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
@@ -31,14 +33,14 @@
# Application definition
INSTALLED_APPS = [
+ "pages.apps.PagesConfig",
+ "projects.apps.ProjectsConfig",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
- "projects",
- "blog",
]
MIDDLEWARE = [
@@ -56,7 +58,9 @@
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
- "DIRS": ["personal_portfolio/templates/"],
+ "DIRS": [
+ BASE_DIR / "templates/",
+ ],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
@@ -64,50 +68,46 @@
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
- ]
+ ],
},
- }
+ },
]
WSGI_APPLICATION = "personal_portfolio.wsgi.application"
# Database
-# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
+# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
- "NAME": os.path.join(BASE_DIR, "db.sqlite3"),
+ "NAME": BASE_DIR / "db.sqlite3",
}
}
# Password validation
-# https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators
+# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
- "NAME": "django.contrib.auth.password_validation"
- + ".UserAttributeSimilarityValidator"
+ "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
- "NAME": "django.contrib.auth.password_validation"
- + ".MinimumLengthValidator"
+ "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
- "NAME": "django.contrib.auth.password_validation"
- + ".CommonPasswordValidator"
+ "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
- "NAME": "django.contrib.auth.password_validation"
- + ".NumericPasswordValidator"
+ "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# Internationalization
-# https://docs.djangoproject.com/en/2.1/topics/i18n/
+# https://docs.djangoproject.com/en/4.2/topics/i18n/
LANGUAGE_CODE = "en-us"
@@ -115,12 +115,18 @@
USE_I18N = True
-USE_L10N = True
-
USE_TZ = True
# Static files (CSS, JavaScript, Images)
-# https://docs.djangoproject.com/en/2.1/howto/static-files/
+# https://docs.djangoproject.com/en/4.2/howto/static-files/
+
+STATIC_URL = "static/"
+
+# Default primary key field type
+# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
+
+DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
-STATIC_URL = "/static/"
+MEDIA_ROOT = BASE_DIR / "uploads/"
+MEDIA_URL = "media/"
diff --git a/rp-portfolio/personal_portfolio/templates/base.html b/rp-portfolio/personal_portfolio/templates/base.html
deleted file mode 100644
index df3a7d0cbc..0000000000
--- a/rp-portfolio/personal_portfolio/templates/base.html
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
-
-
-
- {% block page_content %}{% endblock %}
-
-
-
-
-
diff --git a/rp-portfolio/personal_portfolio/urls.py b/rp-portfolio/personal_portfolio/urls.py
index 2c2022ed03..6fd49c525f 100644
--- a/rp-portfolio/personal_portfolio/urls.py
+++ b/rp-portfolio/personal_portfolio/urls.py
@@ -1,23 +1,10 @@
-"""personal_portfolio URL Configuration
-
-The `urlpatterns` list routes URLs to views. For more information please see:
- https://docs.djangoproject.com/en/2.1/topics/http/urls/
-Examples:
-Function views
- 1. Add an import: from my_app import views
- 2. Add a URL to urlpatterns: path('', views.home, name='home')
-Class-based views
- 1. Add an import: from other_app.views import Home
- 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
-Including another URLconf
- 1. Import the include() function: from django.urls import include, path
- 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
-"""
from django.contrib import admin
from django.urls import path, include
+from django.conf import settings
+from django.conf.urls.static import static
urlpatterns = [
path("admin/", admin.site.urls),
+ path("", include("pages.urls")),
path("projects/", include("projects.urls")),
- path("blog/", include("blog.urls")),
-]
+] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
diff --git a/rp-portfolio/personal_portfolio/wsgi.py b/rp-portfolio/personal_portfolio/wsgi.py
index 8255492c15..32e0bfdc04 100644
--- a/rp-portfolio/personal_portfolio/wsgi.py
+++ b/rp-portfolio/personal_portfolio/wsgi.py
@@ -4,7 +4,7 @@
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
-https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/
+https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
"""
import os
diff --git a/rp-portfolio/projects/admin.py b/rp-portfolio/projects/admin.py
index 4185d360e9..0afa67bf68 100644
--- a/rp-portfolio/projects/admin.py
+++ b/rp-portfolio/projects/admin.py
@@ -1,3 +1,9 @@
-# from django.contrib import admin
+from django.contrib import admin
+from projects.models import Project
-# Register your models here.
+
+class ProjectAdmin(admin.ModelAdmin):
+ pass
+
+
+admin.site.register(Project, ProjectAdmin)
diff --git a/rp-portfolio/projects/apps.py b/rp-portfolio/projects/apps.py
index 20f1a5a832..c992f4a7a4 100644
--- a/rp-portfolio/projects/apps.py
+++ b/rp-portfolio/projects/apps.py
@@ -2,4 +2,5 @@
class ProjectsConfig(AppConfig):
+ default_auto_field = "django.db.models.BigAutoField"
name = "projects"
diff --git a/rp-portfolio/projects/migrations/0001_initial.py b/rp-portfolio/projects/migrations/0001_initial.py
index 9f99ce9ca3..8e3fd37a42 100644
--- a/rp-portfolio/projects/migrations/0001_initial.py
+++ b/rp-portfolio/projects/migrations/0001_initial.py
@@ -1,4 +1,4 @@
-# Generated by Django 2.1.4 on 2018-12-16 13:45
+# Generated by Django 4.2.4 on 2023-08-25 10:09
from django.db import migrations, models
@@ -14,12 +14,11 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='Project',
fields=[
- ('id', models.AutoField(auto_created=True, primary_key=True,
- serialize=False, verbose_name='ID')),
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=100)),
('description', models.TextField()),
('technology', models.CharField(max_length=20)),
- ('image', models.FilePathField(path='/img')),
+ ('image', models.FileField(blank=True, upload_to='project_images/')),
],
),
]
diff --git a/rp-portfolio/projects/models.py b/rp-portfolio/projects/models.py
index 1a1edde1bc..27b26eaec2 100644
--- a/rp-portfolio/projects/models.py
+++ b/rp-portfolio/projects/models.py
@@ -5,4 +5,4 @@ class Project(models.Model):
title = models.CharField(max_length=100)
description = models.TextField()
technology = models.CharField(max_length=20)
- image = models.FilePathField(path="/img")
+ image = models.FileField(upload_to="project_images/", blank=True)
diff --git a/rp-portfolio/projects/templates/project_detail.html b/rp-portfolio/projects/templates/projects/project_detail.html
similarity index 71%
rename from rp-portfolio/projects/templates/project_detail.html
rename to rp-portfolio/projects/templates/projects/project_detail.html
index 2c92be07f4..2acff4ed56 100644
--- a/rp-portfolio/projects/templates/project_detail.html
+++ b/rp-portfolio/projects/templates/projects/project_detail.html
@@ -1,11 +1,12 @@
{% extends "base.html" %}
-{% load static %}
{% block page_content %}
{{ project.title }}
-
+ {% if project.image %}
+
+ {% endif %}
About the project:
@@ -15,5 +16,4 @@
Technology used:
{{ project.technology }}
-
-{% endblock %}
+{% endblock page_content %}
diff --git a/rp-portfolio/projects/templates/project_index.html b/rp-portfolio/projects/templates/projects/project_index.html
similarity index 76%
rename from rp-portfolio/projects/templates/project_index.html
rename to rp-portfolio/projects/templates/projects/project_index.html
index 3811b74b8f..a43519acfb 100644
--- a/rp-portfolio/projects/templates/project_index.html
+++ b/rp-portfolio/projects/templates/projects/project_index.html
@@ -1,12 +1,14 @@
{% extends "base.html" %}
-{% load static %}
+
{% block page_content %}
Projects
{% for project in projects %}
-
+ {% if project.image %}
+
+ {% endif %}
{{ project.title }}
{{ project.description }}
@@ -17,6 +19,6 @@
{{ project.title }}
- {% endfor %}
+{% endfor %}
-{% endblock %}
+{% endblock page_content %}
diff --git a/rp-portfolio/projects/tests.py b/rp-portfolio/projects/tests.py
deleted file mode 100644
index a79ca8be56..0000000000
--- a/rp-portfolio/projects/tests.py
+++ /dev/null
@@ -1,3 +0,0 @@
-# from django.test import TestCase
-
-# Create your tests here.
diff --git a/rp-portfolio/projects/urls.py b/rp-portfolio/projects/urls.py
index 231abb5839..3bce1cbefb 100644
--- a/rp-portfolio/projects/urls.py
+++ b/rp-portfolio/projects/urls.py
@@ -1,5 +1,5 @@
from django.urls import path
-from . import views
+from projects import views
urlpatterns = [
path("", views.project_index, name="project_index"),
diff --git a/rp-portfolio/projects/views.py b/rp-portfolio/projects/views.py
index 51a2ed42da..eb05a040b7 100644
--- a/rp-portfolio/projects/views.py
+++ b/rp-portfolio/projects/views.py
@@ -5,10 +5,10 @@
def project_index(request):
projects = Project.objects.all()
context = {"projects": projects}
- return render(request, "project_index.html", context)
+ return render(request, "projects/project_index.html", context)
def project_detail(request, pk):
project = Project.objects.get(pk=pk)
context = {"project": project}
- return render(request, "project_detail.html", context)
+ return render(request, "projects/project_detail.html", context)
diff --git a/rp-portfolio/requirements.txt b/rp-portfolio/requirements.txt
new file mode 100644
index 0000000000..57104a52a0
--- /dev/null
+++ b/rp-portfolio/requirements.txt
@@ -0,0 +1,3 @@
+asgiref==3.7.2
+Django==4.2.4
+sqlparse==0.4.4
diff --git a/rp-portfolio/templates/base.html b/rp-portfolio/templates/base.html
new file mode 100644
index 0000000000..4704d566c5
--- /dev/null
+++ b/rp-portfolio/templates/base.html
@@ -0,0 +1,14 @@
+
+
+
+
+ {% block title %}My Personal Portfolio{% endblock title %}
+
+
+
+{% block page_content %}{% endblock page_content %}
+
+
diff --git a/rp-portfolio/projects/static/img/project1.png b/rp-portfolio/uploads/project_images/project1.png
similarity index 100%
rename from rp-portfolio/projects/static/img/project1.png
rename to rp-portfolio/uploads/project_images/project1.png
diff --git a/rp-portfolio/projects/static/img/project2.png b/rp-portfolio/uploads/project_images/project2.png
similarity index 100%
rename from rp-portfolio/projects/static/img/project2.png
rename to rp-portfolio/uploads/project_images/project2.png
diff --git a/rp-portfolio/projects/static/img/project3.png b/rp-portfolio/uploads/project_images/project3.png
similarity index 100%
rename from rp-portfolio/projects/static/img/project3.png
rename to rp-portfolio/uploads/project_images/project3.png