From 36a67f5140c7804f38ae98dfb9d0434d35154807 Mon Sep 17 00:00:00 2001 From: Julio Sarango Date: Fri, 17 May 2024 14:49:25 +0000 Subject: [PATCH 1/8] Finish Introduction and Django Admin --- .gitignore | 1 + blango/settings.py | 15 ++++++- blog/__init__.py | 0 blog/admin.py | 12 ++++++ blog/apps.py | 6 +++ blog/migrations/0001_initial.py | 39 ++++++++++++++++++ blog/migrations/__init__.py | 0 .../__pycache__/0001_initial.cpython-36.pyc | Bin 0 -> 1231 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 139 bytes blog/models.py | 26 ++++++++++++ blog/tests.py | 3 ++ blog/views.py | 3 ++ db.sqlite3 | Bin 0 -> 163840 bytes 13 files changed, 103 insertions(+), 2 deletions(-) create mode 100644 .gitignore create mode 100644 blog/__init__.py create mode 100644 blog/admin.py create mode 100644 blog/apps.py create mode 100644 blog/migrations/0001_initial.py create mode 100644 blog/migrations/__init__.py create mode 100644 blog/migrations/__pycache__/0001_initial.cpython-36.pyc create mode 100644 blog/migrations/__pycache__/__init__.cpython-36.pyc create mode 100644 blog/models.py create mode 100644 blog/tests.py create mode 100644 blog/views.py create mode 100644 db.sqlite3 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..c01bd8c247 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*/__pycache__/ diff --git a/blango/settings.py b/blango/settings.py index f9209bef27..9d218cf6ac 100644 --- a/blango/settings.py +++ b/blango/settings.py @@ -11,6 +11,7 @@ """ from pathlib import Path +import os # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -37,16 +38,17 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'blog' ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', +# 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', +# 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'blango.urls' @@ -123,3 +125,12 @@ # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + +ALLOWED_HOSTS = ['*'] +X_FRAME_OPTIONS = 'ALLOW-FROM ' + os.environ.get('CODIO_HOSTNAME') + '-8000.codio.io' +CSRF_COOKIE_SAMESITE = None +CSRF_TRUSTED_ORIGINS = ['https://' + os.environ.get('CODIO_HOSTNAME') + '-8000.codio.io'] +CSRF_COOKIE_SECURE = True +SESSION_COOKIE_SECURE = True +CSRF_COOKIE_SAMESITE = 'None' +SESSION_COOKIE_SAMESITE = 'None' \ No newline at end of file diff --git a/blog/__init__.py b/blog/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/blog/admin.py b/blog/admin.py new file mode 100644 index 0000000000..a1f2ce6fac --- /dev/null +++ b/blog/admin.py @@ -0,0 +1,12 @@ +from django.contrib import admin + +# Register your models here. +from blog.models import Tag, Post + +admin.site.register(Tag) + +class PostAdmin(admin.ModelAdmin): + prepopulated_fields = {"slug": ("title",)} + list_display = ("title","slug","published_at") + +admin.site.register(Post, PostAdmin) \ No newline at end of file diff --git a/blog/apps.py b/blog/apps.py new file mode 100644 index 0000000000..94788a5eac --- /dev/null +++ b/blog/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class BlogConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'blog' diff --git a/blog/migrations/0001_initial.py b/blog/migrations/0001_initial.py new file mode 100644 index 0000000000..c695c8cad6 --- /dev/null +++ b/blog/migrations/0001_initial.py @@ -0,0 +1,39 @@ +# Generated by Django 3.2.5 on 2024-05-17 14:08 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Tag', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('value', models.TextField(max_length=100)), + ], + ), + migrations.CreateModel( + name='Post', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('modified_at', models.DateTimeField(auto_now=True)), + ('published_at', models.DateTimeField(blank=True, null=True)), + ('title', models.TextField(max_length=100)), + ('slug', models.SlugField()), + ('summary', models.TextField(max_length=500)), + ('content', models.TextField()), + ('author', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL)), + ('tags', models.ManyToManyField(related_name='posts', to='blog.Tag')), + ], + ), + ] diff --git a/blog/migrations/__init__.py b/blog/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/blog/migrations/__pycache__/0001_initial.cpython-36.pyc b/blog/migrations/__pycache__/0001_initial.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..59e5e5cecd1e56ebf7124d29f692f6151a0c98b7 GIT binary patch literal 1231 zcmZuxJCoZs5GKHfD2meE*|HNmk8zPkjdY$A$z(hkJH9(7lbr4G$qgG0q=k6YfdoTP zKFh7s-Ve#|Nw;k#h5Jm3R9TQZ+eOF$xGyZe{TATY!(sFnJX!tWJI*s)khcwJTG)X66;3OPa;!1Dr%YgE;j3psyec<5A zXgvT%FCl>7%3E>hku8Q0`d4hlGY-)eZ=(nQ8y!>hkk$`vG>mrIUaRfzGzJG(oV+(a z&I~t68@+YNK5p%|x*g%R)mr-}B<;!l^}#w?-&o(Y=;6~g(;f1R;VygzpIbePEXs^0 zx8Mt6?n~^SUeh#3cP=vIIh-K>>1g6%EH;f!m$ej)1RPX#UWj^|UdS!>jjVH_@;4IW zSF&DcBhyk868%>~ZR9&3PN^YLAzr6SmRWO7E%DljV3kV+2EFInG#KsDr=p?wZpTtz zrUGb3YFkdx&^f~v4mmNdHVc)T^UlN3Uno&tQ2vcl=r?(zBzi_|GVGa6VL|7AGsnT+ zQkRV^8w@DbxvmLQUzaJJ4cSQKjYh8#nL)oIXy#40P&%6uDHIpiQdt_>D(ibx-qXo# z4AWLmQ;gD-&~B6@IZo4GH$vHJN$cfh-Vj%{ON(%9E=5&|1qFhvWC^ldZfkB`dyihu ze@tJV9luCVpB)|lg7N!^TpAoaZX0oGk8*9V4#q#^*&}Mvi6s>KbNRaI#NiP^o#%zz zdCrK6PK-`;E%U7WnR*z&T9lba4hsxh4(PIA@A->o^W(>J+&dNJcCPKbTdOO%yHUS8 z^*4l|o&J3=JJ*GrEvZ?0cB$(NQ;DUV*`8#YK02FyxQnyH!^8XOj*@A$t?8%Wgfz@o zR^XmH@;t`*n2p`;J7OcYPa|r_UDmFcggEHnPpPM?j==uT0*jXGj-)&9jnnQgFug9d fzHdvg`+q2=lfESzh7Y=iesB9`0-9pN5xV~YfT?R> literal 0 HcmV?d00001 diff --git a/blog/migrations/__pycache__/__init__.cpython-36.pyc b/blog/migrations/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6bb598b43a2d0667193d5bbe693537313c73384b GIT binary patch literal 139 zcmXr!<>i_d=#N<@{q@2XO^n4(dpRS*qnO>Awl9``ZtREkrnU`4-AFo$Xd5gm)H$SB` NC)EyQVlfai004OxA+-Pi literal 0 HcmV?d00001 diff --git a/blog/models.py b/blog/models.py new file mode 100644 index 0000000000..b2ba067c39 --- /dev/null +++ b/blog/models.py @@ -0,0 +1,26 @@ +from django.db import models + +# Create your models here. +from django.db import models +from django.conf import settings + +class Tag(models.Model): + value = models.TextField(max_length=100) + + def __str__(self): + return self.value + + +class Post(models.Model): + author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT) + created_at = models.DateTimeField(auto_now_add=True) + modified_at = models.DateTimeField(auto_now=True) + published_at = models.DateTimeField(blank=True, null=True) + title = models.TextField(max_length=100) + slug = models.SlugField() + summary = models.TextField(max_length=500) + content = models.TextField() + tags = models.ManyToManyField(Tag, related_name="posts") + + def __str__(self): + return self.title \ No newline at end of file diff --git a/blog/tests.py b/blog/tests.py new file mode 100644 index 0000000000..7ce503c2dd --- /dev/null +++ b/blog/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/blog/views.py b/blog/views.py new file mode 100644 index 0000000000..91ea44a218 --- /dev/null +++ b/blog/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/db.sqlite3 b/db.sqlite3 new file mode 100644 index 0000000000000000000000000000000000000000..8437023c283f928e4f91dbbb85a85c5fb2012638 GIT binary patch literal 163840 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 literal 0 HcmV?d00001 From 14d5b31dd69ca45db5dfd27442873cf3b7b97804 Mon Sep 17 00:00:00 2001 From: Julio Sarango Date: Fri, 17 May 2024 16:15:09 +0000 Subject: [PATCH 2/8] Finish generic relationships --- .gitignore | 2 ++ blog/admin.py | 8 +++--- blog/migrations/0002_comment.py | 27 ++++++++++++++++++ blog/migrations/0003_auto_20240517_1611.py | 25 ++++++++++++++++ .../__pycache__/0001_initial.cpython-36.pyc | Bin 1231 -> 1231 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 139 -> 139 bytes blog/models.py | 18 ++++++++++-- db.sqlite3 | Bin 163840 -> 184320 bytes 8 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 blog/migrations/0002_comment.py create mode 100644 blog/migrations/0003_auto_20240517_1611.py diff --git a/.gitignore b/.gitignore index c01bd8c247..3d49911770 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ */__pycache__/ +__pycache__/ +*.py[cod] diff --git a/blog/admin.py b/blog/admin.py index a1f2ce6fac..fb113575cf 100644 --- a/blog/admin.py +++ b/blog/admin.py @@ -1,12 +1,12 @@ from django.contrib import admin # Register your models here. -from blog.models import Tag, Post - -admin.site.register(Tag) +from blog.models import Tag, Post, Comment class PostAdmin(admin.ModelAdmin): prepopulated_fields = {"slug": ("title",)} list_display = ("title","slug","published_at") -admin.site.register(Post, PostAdmin) \ No newline at end of file +admin.site.register(Tag) +admin.site.register(Post, PostAdmin) +admin.site.register(Comment) \ No newline at end of file diff --git a/blog/migrations/0002_comment.py b/blog/migrations/0002_comment.py new file mode 100644 index 0000000000..fd3cd484f8 --- /dev/null +++ b/blog/migrations/0002_comment.py @@ -0,0 +1,27 @@ +# Generated by Django 3.2.5 on 2024-05-17 15:33 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('contenttypes', '0002_remove_content_type_name'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('blog', '0001_initial'), + ] + + operations = [ + migrations.CreateModel( + name='Comment', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('content', models.TextField()), + ('object_id', models.PositiveIntegerField()), + ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.contenttype')), + ('creator', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/blog/migrations/0003_auto_20240517_1611.py b/blog/migrations/0003_auto_20240517_1611.py new file mode 100644 index 0000000000..9323507205 --- /dev/null +++ b/blog/migrations/0003_auto_20240517_1611.py @@ -0,0 +1,25 @@ +# Generated by Django 3.2.5 on 2024-05-17 16:11 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0002_comment'), + ] + + operations = [ + migrations.AddField( + model_name='comment', + name='created_at', + field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + preserve_default=False, + ), + migrations.AddField( + model_name='comment', + name='modified_at', + field=models.DateTimeField(auto_now=True), + ), + ] diff --git a/blog/migrations/__pycache__/0001_initial.cpython-36.pyc b/blog/migrations/__pycache__/0001_initial.cpython-36.pyc index 59e5e5cecd1e56ebf7124d29f692f6151a0c98b7..f044aeea84e4654a7010fb2d7680e6515f0ec82c 100644 GIT binary patch delta 15 WcmX@ld7hKan3tEUEq^21NfrPky99>- delta 15 WcmX@ld7hKan3tF9Y05^nlPmxxz69F< diff --git a/blog/migrations/__pycache__/__init__.cpython-36.pyc b/blog/migrations/__pycache__/__init__.cpython-36.pyc index 6bb598b43a2d0667193d5bbe693537313c73384b..43910c625936c0df0a9e8da3fa2c3aef6825db3f 100644 GIT binary patch delta 14 VcmeBX>}F&$=H=yT%b&>B3IG@A1BU}F&$=H=y@7Bi8p6#y6P1B3to diff --git a/blog/models.py b/blog/models.py index b2ba067c39..62698ccab1 100644 --- a/blog/models.py +++ b/blog/models.py @@ -1,8 +1,11 @@ from django.db import models +from django.conf import settings +from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation +from django.contrib.contenttypes.models import ContentType + # Create your models here. -from django.db import models -from django.conf import settings + class Tag(models.Model): value = models.TextField(max_length=100) @@ -11,6 +14,16 @@ def __str__(self): return self.value +class Comment(models.Model): + creator = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE) + content = models.TextField() + content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) + object_id = models.PositiveIntegerField() + content_object = GenericForeignKey("content_type", "object_id") + created_at = models.DateTimeField(auto_now_add=True) + modified_at = models.DateTimeField(auto_now=True) + + class Post(models.Model): author = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT) created_at = models.DateTimeField(auto_now_add=True) @@ -21,6 +34,7 @@ class Post(models.Model): summary = models.TextField(max_length=500) content = models.TextField() tags = models.ManyToManyField(Tag, related_name="posts") + comments = GenericRelation(Comment) def __str__(self): return self.title \ No newline at end of file diff --git a/db.sqlite3 b/db.sqlite3 index 8437023c283f928e4f91dbbb85a85c5fb2012638..999f682ccb3e18e2a085ac7f1dd3174d9337208c 100644 GIT binary patch delta 1406 zcmZ`(O>7%g5Pq}XWxeY-V<#~N5-FRtO);sHUH{vZl&zZ?oK( zC8T&k^gtxk$VDqvLU5=ALPDzJNIpP;8x?|4K+A<7r2?_**olMEC+)nQ zdEdO5`QE%&sqmE#Y!~~TK|)BN^?@;;Sv?qX17W!0OA$GimZP$4Dgh}H zkYj=zP0DgIoEQm3W1(1hw9oMMwS9x>nbQ2cS}b=uM3SLUQcjG->5?5U~eCd7;&l3CtzsIlfGkhQ4#@Dfg zXKUI~bRtI+SHBspC#&o;iq*biU$STHlQr$%uBjdxe84T`)mPhzdHZPa z1D8_^YFTX?J=?~a7c<4V9i!-^!2?_-mutg@>hHZUw`lQVpvvA*taum`7{WkJ`>9+1 zbD5(1I4@%X;53ToOv(72w0Lj(6AJw@m#-8u~e z4*Hm7kXoan@CNytl*rtw7Urx5YsO$UYTA#M##ahZRO8ev)bkcEe^X~~fMT_su!;N@ zf3v1t;Fm=&L%Hhl-cbz)=N;cZ+3m#m9_n53-TZG zgVv|ntE`vF8pqC1^tGDyIULfs?Uy!pL=Ao7cKhVJT=a`eLlFLmNVIc+8L?y|6uCbapm;5a9TMs zF>V&f?_O2iaU3H0vItspTNJHzl^_|e-{5*{@eUM8yk)IC;RBzF)B>% z*7soKn0!TFTT++{XaEO)0R#U#{?q)o`1kVH@)z(g;P2kp7{Ncez z{HOT$@XzP(;;-S)-`E(=KRMrjDzgIb+{x?#jvE_8c{k6EO)p|r;CG$OegP=(mv8!q zdM1|5p7(brF!M7W-_Gv9cz|)ce+nZL)Ap0jjMg$NVhs5V)438DWdv9+vbZtJGi_or tV{Bsh&X7N`(R#c5ET)I7+XK=Vw`4LaaOX^CzrYAIR)u?e&TYmiO8^)&STz6u From 719ebb7094d0f6beca218d980ecca85404cf272c Mon Sep 17 00:00:00 2001 From: Julio Sarango Date: Fri, 17 May 2024 17:46:10 +0000 Subject: [PATCH 3/8] Finish html frameworks --- .gitignore | 5 +++++ blango/settings.py | 4 ++-- blango/urls.py | 2 ++ blog/views.py | 2 ++ templates/base.html | 17 +++++++++++++++++ templates/blog/index.html | 4 ++++ 6 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 templates/base.html create mode 100644 templates/blog/index.html diff --git a/.gitignore b/.gitignore index 3d49911770..4e5cf2d62b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,8 @@ */__pycache__/ __pycache__/ *.py[cod] + +*.log +*.pot +*.pyc +__pycache__/ diff --git a/blango/settings.py b/blango/settings.py index 9d218cf6ac..ae214fd6ab 100644 --- a/blango/settings.py +++ b/blango/settings.py @@ -56,7 +56,7 @@ TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [], + 'DIRS': [BASE_DIR / 'templates'], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ @@ -133,4 +133,4 @@ CSRF_COOKIE_SECURE = True SESSION_COOKIE_SECURE = True CSRF_COOKIE_SAMESITE = 'None' -SESSION_COOKIE_SAMESITE = 'None' \ No newline at end of file +SESSION_COOKIE_SAMESITE = 'None' diff --git a/blango/urls.py b/blango/urls.py index cde05802f9..32719b4854 100644 --- a/blango/urls.py +++ b/blango/urls.py @@ -15,7 +15,9 @@ """ from django.contrib import admin from django.urls import path +from blog.views import index urlpatterns = [ path('admin/', admin.site.urls), + path("", index) ] diff --git a/blog/views.py b/blog/views.py index 91ea44a218..91ad6a0e5a 100644 --- a/blog/views.py +++ b/blog/views.py @@ -1,3 +1,5 @@ from django.shortcuts import render # Create your views here. +def index(request): + return render(request, "blog/index.html") diff --git a/templates/base.html b/templates/base.html new file mode 100644 index 0000000000..c97a299916 --- /dev/null +++ b/templates/base.html @@ -0,0 +1,17 @@ + + + + + + + Hello, world! + + +
+ {% block content %} + + {% endblock %} +
+ + + \ No newline at end of file diff --git a/templates/blog/index.html b/templates/blog/index.html new file mode 100644 index 0000000000..e61cbbb0b6 --- /dev/null +++ b/templates/blog/index.html @@ -0,0 +1,4 @@ +{% extends "base.html" %} +{% block content %} +

Hello in a Container

+{% endblock %} \ No newline at end of file From 888f3ce5024fa2810e66dda089eb7bc9736e0a3f Mon Sep 17 00:00:00 2001 From: Julio Sarango Date: Mon, 20 May 2024 16:37:03 +0000 Subject: [PATCH 4/8] Finish custom filters --- .../__pycache__/0001_initial.cpython-36.pyc | Bin 1231 -> 0 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 139 -> 0 bytes blog/templatetags/__init__.py | 0 blog/templatetags/blog_extras.py | 30 ++++++++++++++++++ blog/views.py | 6 +++- db.sqlite3 | Bin 184320 -> 184320 bytes templates/blog/index.html | 17 +++++++++- 7 files changed, 51 insertions(+), 2 deletions(-) delete mode 100644 blog/migrations/__pycache__/0001_initial.cpython-36.pyc delete mode 100644 blog/migrations/__pycache__/__init__.cpython-36.pyc create mode 100644 blog/templatetags/__init__.py create mode 100644 blog/templatetags/blog_extras.py diff --git a/blog/migrations/__pycache__/0001_initial.cpython-36.pyc b/blog/migrations/__pycache__/0001_initial.cpython-36.pyc deleted file mode 100644 index f044aeea84e4654a7010fb2d7680e6515f0ec82c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1231 zcmZuxJCoZs5GKKgD2meE*|HNmk8zPkjdY$A$z(hkJH9(7lbr4G$qgG0q=k6UfdoTP zKFh5$>F$T*_oUl4mHSMJR9TQZ+eOF$xGyZe{TATY!(sfpezN*AaGbxKj~<`ohcwK0 zG=T%Iatb$fT`M!i3NQ5vKlNMPQ$ZR&alnV*HwOaYt$T01v3aUt`XH&T}d!$N~l=IPSLT|WmTJfva%p-DKY3r@Ms?I=pn5i+GrT`DGUxCw~aWpN4d6F2a_LU{)k$1VhP3mT)b{Nad<>f=dutx z&lyqCiSdc9h0M#JsfQt~d6{cuu)wJ0fG!L6p1*iDKYl#Ny;ELp=i1J@wYn0!8})}% ze?th`>E8#lb6tqplA5Jwm%6?%6<>;(?Mbfbqx0E^yEr>MJiMRnD4ABii0V0v9@ feczT~_y15#Cw)scj2?6i{oeM?gfu0DBl7+O?c{3= diff --git a/blog/migrations/__pycache__/__init__.cpython-36.pyc b/blog/migrations/__pycache__/__init__.cpython-36.pyc deleted file mode 100644 index 43910c625936c0df0a9e8da3fa2c3aef6825db3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 139 zcmXr!<>hM2cTZygg2x~N1{i@12OutH0TL+;!3>&=ek&P@K*9*(mxX>ter~FMa(+r? zzJ7UrQFd`bVsff}QchxCdOncKPuI`QOfO0-$;{6y){l?R%*!l^kJl@xyv1RYo1ape MlWGStu^5OM0D74rxBvhE diff --git a/blog/templatetags/__init__.py b/blog/templatetags/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/blog/templatetags/blog_extras.py b/blog/templatetags/blog_extras.py new file mode 100644 index 0000000000..17777cb2d2 --- /dev/null +++ b/blog/templatetags/blog_extras.py @@ -0,0 +1,30 @@ +from django import template +from django.contrib.auth import get_user_model +from django.utils.html import format_html + +user_model = get_user_model() + +register = template.Library() + +@register.filter +def author_details(author, current_user): + if not isinstance(author, user_model): + return "" + + if author == current_user: + return format_html("me") + + if author.first_name and author.last_name: + name = f"{author.first_name} {author.last_name}" + else: + name = f"{author.username}" + + if author.email: + prefix = format_html('', author.email) + suffix = format_html("") + else: + prefix = "" + suffix = "" + + return format_html('{}{}{}', prefix, name, suffix) + diff --git a/blog/views.py b/blog/views.py index 91ad6a0e5a..b9a239b543 100644 --- a/blog/views.py +++ b/blog/views.py @@ -1,5 +1,9 @@ from django.shortcuts import render +from django.utils import timezone + +from blog.models import Post # Create your views here. def index(request): - return render(request, "blog/index.html") + posts = Post.objects.filter(published_at__lte=timezone.now()) + return render(request, "blog/index.html", {"posts": posts}) diff --git a/db.sqlite3 b/db.sqlite3 index 999f682ccb3e18e2a085ac7f1dd3174d9337208c..7be98a4c445af70fe1eff889907c03e853fdd939 100644 GIT binary patch delta 524 zcmZozz}>KbdxA8h*+dyf0Rbm~L5EnUYgxl$D>JSYDD?lxvb#mTgjIl9FVco@HoKG?{t+W-kj9 zlXR=>5VQCcKffZ!0>@-mC-?BYq#S3Lyre9P$cP+MzlzdI3m^Y*gLt#3BNJT% zQ(a?21(>t+EDQ`SOwBgmo~Oyk!Nk9vfqy&yoy~#;tNB$`n0*-uIfH5Qo%I?Y_*nTL zGw?s=f64!w|11A{p!SRW(?8TRu`sc)PJg+cQ5vY=1q1&d{_p&s_}}us04lh~&%(mY z$vFAVeF;8RzA6U(J^Xe2R(yB)sx~%e@Fgd*axysbi&rvoGQu2fXbyI?iIst&o{^D} zg^{@-I839fm69`np_-baWTjB8l$M#AlTr+1Mk~2x78RE$QDGLalZ0Wz^*6)ynHello in a Container +

Blog Posts

+ {% for post in posts %} +
+
+

{{ post.title }}

+ By {{ post.author|author_details:request.user }} on {{ post.published_at|date:"M, d Y" }} + +

{{ post.summary }}

+

+ ({{ post.content|wordcount }} words) + Read More +

+
+
+ {% endfor %} {% endblock %} \ No newline at end of file From 3ae069a366e9edb0962e5df513af776a358cc08a Mon Sep 17 00:00:00 2001 From: Julio Sarango Date: Mon, 20 May 2024 20:37:49 +0000 Subject: [PATCH 5/8] Finish custom template tags --- blango/urls.py | 5 +++-- blog/templatetags/blog_extras.py | 23 +++++++++++++++++++++++ blog/views.py | 6 +++++- db.sqlite3 | Bin 184320 -> 184320 bytes templates/blog/index.html | 13 ++++++------- templates/blog/post-byline.html | 2 ++ templates/blog/post-detail.html | 21 +++++++++++++++++++++ templates/blog/post-list.html | 6 ++++++ 8 files changed, 66 insertions(+), 10 deletions(-) create mode 100644 templates/blog/post-byline.html create mode 100644 templates/blog/post-detail.html create mode 100644 templates/blog/post-list.html diff --git a/blango/urls.py b/blango/urls.py index 32719b4854..9e73371b75 100644 --- a/blango/urls.py +++ b/blango/urls.py @@ -15,9 +15,10 @@ """ from django.contrib import admin from django.urls import path -from blog.views import index +from blog.views import index, post_detail urlpatterns = [ path('admin/', admin.site.urls), - path("", index) + path("", index), + path("post//", post_detail, name="blog-post-detail") ] diff --git a/blog/templatetags/blog_extras.py b/blog/templatetags/blog_extras.py index 17777cb2d2..4618cd0971 100644 --- a/blog/templatetags/blog_extras.py +++ b/blog/templatetags/blog_extras.py @@ -1,6 +1,7 @@ from django import template from django.contrib.auth import get_user_model from django.utils.html import format_html +from blog.models import Post user_model = get_user_model() @@ -28,3 +29,25 @@ def author_details(author, current_user): return format_html('{}{}{}', prefix, name, suffix) +@register.simple_tag +def row(extra_classes=""): + return format_html('
', extra_classes) + + +@register.simple_tag +def endrow(): + return format_html("
") + +@register.simple_tag +def col(extra_classes=""): + return format_html('
', extra_classes) + + +@register.simple_tag +def endcol(): + return format_html("
") + +@register.inclusion_tag("blog/post-list.html") +def recent_posts(post): + posts = Post.objects.exclude(pk=post.pk)[:5] + return {"title": "Recent Posts", "posts": posts} \ No newline at end of file diff --git a/blog/views.py b/blog/views.py index b9a239b543..ca66b172dd 100644 --- a/blog/views.py +++ b/blog/views.py @@ -1,4 +1,4 @@ -from django.shortcuts import render +from django.shortcuts import render, get_object_or_404 from django.utils import timezone from blog.models import Post @@ -7,3 +7,7 @@ def index(request): posts = Post.objects.filter(published_at__lte=timezone.now()) return render(request, "blog/index.html", {"posts": posts}) + +def post_detail(request, slug): + post = get_object_or_404(Post, slug=slug) + return render(request, "blog/post-detail.html", {"post": post}) \ No newline at end of file diff --git a/db.sqlite3 b/db.sqlite3 index 7be98a4c445af70fe1eff889907c03e853fdd939..52abbfc98a5d5ca905b2bd60b6e232504679ad24 100644 GIT binary patch delta 4643 zcmc&&ZEPF$8NYYiu`f?MG)e|EM}x#lYsbE`eLia=n563{bft7aMK!e-`*1wYcbQ*W zr5(G5^wYkKk`8}L#RoQNAGRtoO*M@{p@Jb0MF_TOyt8W45Jgn%O=3gr`Ts8ojdM|w zsslxOo>T7rzvutFKaUST8a@1I^r5TS!qIC6*~0X`Q@x?r;daPF-}JZ$lYRZ)VCBhu z{a;18zkf)Q)``5vwx(_NiYp@w!$=Gxck{jMuuTq38Y)U4Ki(KVDVK6aGpenXy#9i1MZh<*LR4ZOI0 zVppMV>dwJtd2GKm>=>D1=75n?$J|QAHA_jYl+!2Qo@#G1jC%_Co$l83cZcpxH=WGg zy9!SI+vc5FH`grhzJKeGecP7g{_@CJyi&^y-LgeZs_A$#8&7H%-9^vpdb&L`^&PH@ zhld$B1#iN`?X{yvVB_`tc6Pao@Z_V(C#RJ58z(kiST94HfphRayalhov+xwO;Xm+C z_z3<8FT$@8_9(P2c$}~~B#S)DhGT?=qLg;g|3v6>m_CQ-v(!3wg1ru*Pc!ge_&0nE zAHv^I$lLHIcnw~L7f{e?ggy?N1wO*cgv1Dm64FITgpe>HAwncVCeOcf)p|MB%fJ)x zBgl8Z)qSvgll+nVQ+cN>#vYH2#CoU4KaF}@~_G6I3pdcx3?nVgobI1S6L zlr5)NF$tDOz)ZUXEW9I1zy<@sQbs~o(->lz!KUeyT%}nzYl=B)7Dp__tkmwa-2($1 zfT3+&!MF4BYC4}Z5~`X}^-Owa^UN<4tKNau33f|_fNc!89b)MTEoW%yY-Zt&Ixxb! z!vy0lF91Z#YsmyAk&)H4!87eDv3uPtx}!GL)2I-o=O5P|LqL{@W& zw5DZp>9l`#Z^z;Ac8MGw2so_f(>aU>BcrP77F#iE4GS-3EA@t38&lA&cEu@oz=cj7 zJj?$0I?TPt82B@sfyXeCAA!F0e1Tm$UwQI+^6BGWYrXdw+qXu7V+~L3I0HlSmC-$s!=W8we0sb`^p5mO zF{V(^G;IR^f)gU8XS3PBR7vT2YVjL2D0!+hpW-DeKA%tDsyLo$+h|D)!#dqBZNQHrkJ ztU8L>pr?o3M#HTt?tMO%S++KN^fQ!?AC*eVnA@yTfCaayIQ2@&N+@?)RjWE|)yTz0 z&3b+AR>fwmW;qSz4!m6gm0E>P9%_Esm0E>P-pZ6Zk`QT>3>Jc;gcPWhVQ9vpD4Bg@ zsEghtbu4DyQq`Jm4eJL@EG+eoP+7hv zcuGJ~xa^i#I%K^ss?aT=lXvNbZX(B1_ujQ@yl=64XFGaQ)jGX9|0avx^~eA_?_L#$ z>6NtqqFZ&b4a`jl==IMhgt*`GmNxkUi+d~SEi1%Y2~=mB@>bZVmT=#td+*35j(E@0 z1_pvkx*=li*o)C`bBatrrK|+S(3~t;1z)j$< zxE%ih&cbu>B+S6C;OB50L4F83LIO)HE&H60Q7(#6p9@`-3u46QLYQ)12vLrf_Bkg? z0*jJ`)`xq!b?D=nx%-|+(eJ|RxR;-W=W!W%0%lO~F@NbFk?7Jd(WPIcOTS2$et|Ci z0$uufy7Y4rS^YVY(m-8L5|JOkzc8#Wz&r38{4wBF_#KAV({K_+PWmEsUnGB#NP&tJ WFA^zGkr+l)B(?4-{E=eK9sdL5bzoos delta 465 zcmYL_JxCjI7{>n}cg8d39!1npTa%DNX`x(Hi1cVdF*vo7&K(RcbqGXg>5#$Z)=tWS z|4DIokmJBxN@H=92px1utF3WRNHF*bR%l;QaCmv)eSYu9^OlywrRDHkA5YIe9OG#( z7sNtUyoQaW=jd>g)=MtkoYl>B_mh=rk-fSc;>yeOt{#Ii7NTf3Wz3k$7v4|K6h3_P zkE};2Ez6MgoN=@|&v}G7uZG;Wh3W9XaC|Uj4-VL=w7*$+5f6#T8Z)brAdwR*5r1uQ zLbM753;}-noii)Y9?Lh}{k9nQtE*3&rj8mz4M+Hka~u=#1FE*l6C@lmT;mcaXyA~9 z9jNs(*WCJE)YQ!)!&l70Hh-B#&zUxZ;uimspj8_T``92pfYvWZx%++Tf%@~0C)*?{ zjAs2th14=s1!A~`Bg2sK92+VV6YB)k8Ln`OIzhWobxTAE+Gg0M!X~t5@|~f|G4W(! zL};X3)0KW_=@{MgHz^IH?Ixqa$k7=Z)U*fHm=wJ<`sB_L6%TIRM5QJTF7Er9H^>E= MgzO_|eij|s|C@}F8~^|S diff --git a/templates/blog/index.html b/templates/blog/index.html index a868015c74..390681770c 100644 --- a/templates/blog/index.html +++ b/templates/blog/index.html @@ -1,19 +1,18 @@ {% extends "base.html" %} {% load blog_extras %} {% block content %} -

Blog Posts

+

Blog Posts

{% for post in posts %} -
+ {% row "border-bottom" %}
-

{{ post.title }}

- By {{ post.author|author_details:request.user }} on {{ post.published_at|date:"M, d Y" }} - +

{{ post.title }}

+ {% include "blog/post-byline.html" %}

{{ post.summary }}

({{ post.content|wordcount }} words) - Read More + Read More

-
+ {% endrow %} {% endfor %} {% endblock %} \ No newline at end of file diff --git a/templates/blog/post-byline.html b/templates/blog/post-byline.html new file mode 100644 index 0000000000..0a4633b9b7 --- /dev/null +++ b/templates/blog/post-byline.html @@ -0,0 +1,2 @@ +{% load blog_extras %} +By {{ post.author|author_details:request.user }} on {{ post.published_at|date:"M, d Y" }} \ No newline at end of file diff --git a/templates/blog/post-detail.html b/templates/blog/post-detail.html new file mode 100644 index 0000000000..024d70f0c2 --- /dev/null +++ b/templates/blog/post-detail.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} +{% load blog_extras %} +{% block content %} +

{{ post.title }}

+{% row %} +
+ {% include "blog/post-byline.html" %} +
+{% endrow %} +{% row %} +
+ {{ post.content|safe }} +
+{% endrow %} + +{% row %} + {% col %} + {% recent_posts post %} + {% endcol %} +{% endrow %} +{% endblock %} \ No newline at end of file diff --git a/templates/blog/post-list.html b/templates/blog/post-list.html new file mode 100644 index 0000000000..35e5572af5 --- /dev/null +++ b/templates/blog/post-list.html @@ -0,0 +1,6 @@ +

{{ title }}

+ \ No newline at end of file From acff8dd8194e535a9d43963eff58499b689a6228 Mon Sep 17 00:00:00 2001 From: Julio Sarango Date: Tue, 21 May 2024 13:57:45 +0000 Subject: [PATCH 6/8] Finish crispy forms --- blango/settings.py | 8 +++++++- blog/forms.py | 17 +++++++++++++++++ blog/views.py | 23 +++++++++++++++++++++-- db.sqlite3 | Bin 184320 -> 184320 bytes templates/blog/post-comments.html | 29 +++++++++++++++++++++++++++++ templates/blog/post-detail.html | 1 + 6 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 blog/forms.py create mode 100644 templates/blog/post-comments.html diff --git a/blango/settings.py b/blango/settings.py index ae214fd6ab..277f02e379 100644 --- a/blango/settings.py +++ b/blango/settings.py @@ -38,7 +38,9 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - 'blog' + 'blog', + 'crispy_forms', + 'crispy_bootstrap5' ] MIDDLEWARE = [ @@ -134,3 +136,7 @@ SESSION_COOKIE_SECURE = True CSRF_COOKIE_SAMESITE = 'None' SESSION_COOKIE_SAMESITE = 'None' + +CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" +CRISPY_TEMPLATE_PACK = "bootstrap5" + diff --git a/blog/forms.py b/blog/forms.py new file mode 100644 index 0000000000..3a57c689f9 --- /dev/null +++ b/blog/forms.py @@ -0,0 +1,17 @@ +from django import forms + +from crispy_forms.layout import Submit +from crispy_forms.helper import FormHelper + +from blog.models import Comment + + +class CommentForm(forms.ModelForm): + class Meta: + model = Comment + fields = ["content"] + + def __init__(self, *args, **kwargs): + super(CommentForm, self).__init__(*args, **kwargs) + self.helper = FormHelper() + self.helper.add_input(Submit('submit', 'Enviar Comentario')) \ No newline at end of file diff --git a/blog/views.py b/blog/views.py index ca66b172dd..ea1336019a 100644 --- a/blog/views.py +++ b/blog/views.py @@ -1,7 +1,9 @@ -from django.shortcuts import render, get_object_or_404 +from django.shortcuts import render, get_object_or_404, redirect from django.utils import timezone from blog.models import Post +from blog.forms import CommentForm + # Create your views here. def index(request): @@ -10,4 +12,21 @@ def index(request): def post_detail(request, slug): post = get_object_or_404(Post, slug=slug) - return render(request, "blog/post-detail.html", {"post": post}) \ No newline at end of file + if request.user.is_active: + if request.method == "POST": + comment_form = CommentForm(request.POST) + + if comment_form.is_valid(): + comment = comment_form.save(commit=False) + comment.content_object = post + comment.creator = request.user + comment.save() + return redirect(request.path_info) + else: + comment_form = CommentForm() + else: + comment_form = None + + return render( + request, "blog/post-detail.html", {"post": post, "comment_form": comment_form} + ) \ No newline at end of file diff --git a/db.sqlite3 b/db.sqlite3 index 52abbfc98a5d5ca905b2bd60b6e232504679ad24..5b80425db612955fdc5808c0d843e60dd097793d 100644 GIT binary patch delta 581 zcmZozz}>KbdxA8h+e8^>Mz@U#uk{#NHv8xoI`A1<85&xd80(psTbh`gZ$6!Gslda+ zox{L)n|B+}MDCo8jWOIBO-d}RzRkgoX~pIFMMX))rIjW*Sy@0Gi6z+vsmX>(rlx5Y z7M8`6@6FxpW$EbYWfhWR7!(j@l$@TN_;VTfbNRRP-{GIMu`!lkQ-#@= zk&sJRK#C^MoTJdZZTlytVfabI^@IT@|%)f-cbz@^G|MVsQ83lwW=2MK7BI=Y{Z~H|mjF=7M+W}i{NMOL z0+l@GpMGFJBa0*p3o|DpGl*c~Wnf@n=4WN#-@+fp&$_YkJ72v&3kQQCCnKk$V{mGE zXiVn3%*cb1-;=4RkFo zNmWQKRw&I=C`rvtRLCu@RLIOLNi9k(P6Se5(+zYDfW{kuL)*y8z*x`H*uc=#1a3T< NjD_j;E&G{#1OO9Iqmcjr delta 191 zcmZozz}>KbdxA8h<3t%}M#qf_uk{$2Hv8xoI`A1-85vrcTIg9A85)@xZ$6!Gsldg| zGm(MsHt#l`i5nZ6c{Wd+Q_Rf8%s+{Ne>?vj{z;n!3tISDRG57kC(oRt(7bK^_HFAK z{TzTQmN4)?;y=v41gK&f|Mb@Xi~^Ha%;npD=s%-V0~;g%F9!Z!n*|%*^D{DR|JBdL qB>)ur&A|T~EH-`LenvKKCSC>x21foZ4E$SwhOFY>zGXj?j{pFB=s*Comments +{% for comment in post.comments.all %} +{% row "border-top pt-2" %} + {% col %} +
Posted by {{ comment.creator }} at {{ comment.created_at|date:"M, d Y h:i" }}
+ {% endcol %} +{% endrow %} +{% row "border-bottom" %} + {% col %} +

{{ comment.content }}

+ {% endcol %} +{% endrow %} +{% empty %} + {% row "border-top border-bottom" %} + {% col %} +

No comments.

+ {% endcol %} + {% endrow %} +{% endfor %} +{% if request.user.is_active %} +{% row "mt-4" %} + {% col %} +

Add Comment

+ {% crispy comment_form %} + {% endcol %} +{% endrow %} +{% endif %} \ No newline at end of file diff --git a/templates/blog/post-detail.html b/templates/blog/post-detail.html index 024d70f0c2..62d4a7c969 100644 --- a/templates/blog/post-detail.html +++ b/templates/blog/post-detail.html @@ -13,6 +13,7 @@

{{ post.title }}

{% endrow %} +{% include "blog/post-comments.html" %} {% row %} {% col %} {% recent_posts post %} From 858c8a4e5772d879258a643ae539a3aa96342f81 Mon Sep 17 00:00:00 2001 From: Julio Sarango Date: Thu, 23 May 2024 21:09:02 +0000 Subject: [PATCH 7/8] Finish django configurations --- blango/settings.py | 210 ++++++++++++++++++++++++--------------------- blango/wsgi.py | 5 +- manage.py | 5 +- requirements.txt | 50 +++++++++++ 4 files changed, 166 insertions(+), 104 deletions(-) create mode 100644 requirements.txt diff --git a/blango/settings.py b/blango/settings.py index 277f02e379..c236eb6419 100644 --- a/blango/settings.py +++ b/blango/settings.py @@ -12,131 +12,141 @@ from pathlib import Path import os +from configurations import Configuration, values +import dj_database_url + -# Build paths inside the project like this: BASE_DIR / 'subdir'. -BASE_DIR = Path(__file__).resolve().parent.parent +class Dev(Configuration): + # 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/3.2/howto/deployment/checklist/ -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'django-insecure-+sn%dpa!086+g+%44z9*^j^q-u4n!j(#wl)x9a%_1op@zz2+1-' + # Quick-start development settings - unsuitable for production + # See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = True + # SECURITY WARNING: keep the secret key used in production secret! + SECRET_KEY = 'django-insecure-+sn%dpa!086+g+%44z9*^j^q-u4n!j(#wl)x9a%_1op@zz2+1-' -ALLOWED_HOSTS = [] + # SECURITY WARNING: don't run with debug turned on in production! + DEBUG = values.BooleanValue(True) + ALLOWED_HOSTS = [] -# Application definition -INSTALLED_APPS = [ - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'blog', - 'crispy_forms', - 'crispy_bootstrap5' -] + # Application definition -MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', -# 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', -# 'django.middleware.clickjacking.XFrameOptionsMiddleware', -] + INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'blog', + 'crispy_forms', + 'crispy_bootstrap5' + ] -ROOT_URLCONF = 'blango.urls' + MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + # 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + # 'django.middleware.clickjacking.XFrameOptionsMiddleware', + ] -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [BASE_DIR / 'templates'], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - ], - }, - }, -] + ROOT_URLCONF = 'blango.urls' -WSGI_APPLICATION = 'blango.wsgi.application' + TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [BASE_DIR / 'templates'], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, + ] + WSGI_APPLICATION = 'blango.wsgi.application' -# Database -# https://docs.djangoproject.com/en/3.2/ref/settings/#databases -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': BASE_DIR / 'db.sqlite3', - } -} + # Database + # https://docs.djangoproject.com/en/3.2/ref/settings/#databases + DATABASES = { + "default": dj_database_url.config(default=f"sqlite:///{BASE_DIR}/db.sqlite3"), + "alternative": dj_database_url.config( + "ALTERNATIVE_DATABASE_URL", + default=f"sqlite:///{BASE_DIR}/alternative_db.sqlite3", + ), + } -# Password validation -# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators -AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, -] + # Password validation + # https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators + AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, + ] -# Internationalization -# https://docs.djangoproject.com/en/3.2/topics/i18n/ -LANGUAGE_CODE = 'en-us' + # Internationalization + # https://docs.djangoproject.com/en/3.2/topics/i18n/ -TIME_ZONE = 'UTC' + LANGUAGE_CODE = 'en-us' -USE_I18N = True + TIME_ZONE = values.Value("UTC") -USE_L10N = True + USE_I18N = True -USE_TZ = True + USE_L10N = True + + USE_TZ = True -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/3.2/howto/static-files/ - -STATIC_URL = '/static/' - -# Default primary key field type -# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field - -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' - -ALLOWED_HOSTS = ['*'] -X_FRAME_OPTIONS = 'ALLOW-FROM ' + os.environ.get('CODIO_HOSTNAME') + '-8000.codio.io' -CSRF_COOKIE_SAMESITE = None -CSRF_TRUSTED_ORIGINS = ['https://' + os.environ.get('CODIO_HOSTNAME') + '-8000.codio.io'] -CSRF_COOKIE_SECURE = True -SESSION_COOKIE_SECURE = True -CSRF_COOKIE_SAMESITE = 'None' -SESSION_COOKIE_SAMESITE = 'None' - -CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" -CRISPY_TEMPLATE_PACK = "bootstrap5" - + # Static files (CSS, JavaScript, Images) + # https://docs.djangoproject.com/en/3.2/howto/static-files/ + + STATIC_URL = '/static/' + + # Default primary key field type + # https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field + + DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + + ALLOWED_HOSTS = values.ListValue(["localhost", "0.0.0.0", ".codio.io"]) + + X_FRAME_OPTIONS = 'ALLOW-FROM ' + os.environ.get('CODIO_HOSTNAME') + '-8000.codio.io' + CSRF_COOKIE_SAMESITE = None + CSRF_TRUSTED_ORIGINS = ['https://' + os.environ.get('CODIO_HOSTNAME') + '-8000.codio.io'] + CSRF_COOKIE_SECURE = True + SESSION_COOKIE_SECURE = True + CSRF_COOKIE_SAMESITE = 'None' + SESSION_COOKIE_SAMESITE = 'None' + + CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" + CRISPY_TEMPLATE_PACK = "bootstrap5" + +class Prod(Dev): + DEBUG = False + SECRET_KEY = values.SecretValue() diff --git a/blango/wsgi.py b/blango/wsgi.py index 83565cf12c..eca8ef5433 100644 --- a/blango/wsgi.py +++ b/blango/wsgi.py @@ -9,8 +9,9 @@ import os -from django.core.wsgi import get_wsgi_application - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'blango.settings') +os.environ.setdefault("DJANGO_CONFIGURATION", "Prod") + +from configurations.wsgi import get_wsgi_application application = get_wsgi_application() diff --git a/manage.py b/manage.py index c66b327f71..9d1addf718 100644 --- a/manage.py +++ b/manage.py @@ -7,8 +7,9 @@ def main(): """Run administrative tasks.""" os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'blango.settings') - try: - from django.core.management import execute_from_command_line + os.environ.setdefault("DJANGO_CONFIGURATION", "Dev") + try: + from configurations.management import execute_from_command_line except ImportError as exc: raise ImportError( "Couldn't import Django. Are you sure it's installed and " diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000000..94be970cd4 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,50 @@ +ansible==2.7.5 +asn1crypto==0.24.0 +backports.functools-lru-cache==1.6.1 +backports.shutil-get-terminal-size==1.0.0 +certifi==2018.1.18 +chardet==3.0.4 +cryptography==2.1.4 +cycler==0.10.0 +decorator==4.1.2 +enum34==1.1.6 +httplib2==0.9.2 +idna==2.6 +ipaddress==1.0.17 +ipython==5.5.0 +ipython-genutils==0.2.0 +Jinja2==2.10 +keyring==10.6.0 +keyrings.alt==3.0 +kiwisolver==1.1.0 +MarkupSafe==1.0 +matplotlib==2.2.4 +numpy==1.16.5 +paramiko==2.0.0 +pathlib2==2.3.0 +pexpect==4.2.1 +pickleshare==0.7.4 +prompt-toolkit==1.0.15 +pyasn1==0.4.2 +pycodestyle==2.5.0 +pycrypto==2.6.1 +pygame==1.9.6 +Pygments==2.2.0 +pygobject==3.26.1 +pyOpenSSL==17.5.0 +pyparsing==2.4.5 +python-apt==1.6.5+ubuntu0.3 +python-dateutil==2.8.1 +pytz==2019.3 +pyxdg==0.25 +PyYAML==3.12 +requests==2.18.4 +scandir==1.7 +scipy==1.2.2 +SecretStorage==2.3.1 +simplegeneric==0.8.1 +six==1.13.0 +subprocess32==3.5.4 +traitlets==4.3.2 +urllib3==1.22 +wcwidth==0.1.7 From 9ccbd6b89389a7adb9d946f8c6999af55d325745 Mon Sep 17 00:00:00 2001 From: Julio Sarango Date: Mon, 27 May 2024 16:30:15 +0000 Subject: [PATCH 8/8] Finish logging --- blango/settings.py | 41 +++++++++++++++++++++++++++++++++++++++++ blog/views.py | 11 +++++++++++ db.sqlite3 | Bin 184320 -> 184320 bytes 3 files changed, 52 insertions(+) diff --git a/blango/settings.py b/blango/settings.py index c236eb6419..b2c5546cd2 100644 --- a/blango/settings.py +++ b/blango/settings.py @@ -147,6 +147,47 @@ class Dev(Configuration): CRISPY_ALLOWED_TEMPLATE_PACKS = "bootstrap5" CRISPY_TEMPLATE_PACK = "bootstrap5" + LOGGING = { + "version": 1, + "disable_existing_loggers": False, + "filters": { + "require_debug_false": { + "()": "django.utils.log.RequireDebugFalse", + }, + }, + "formatters": { + "verbose": { + "format": "{levelname} {asctime} {module} {process:d} {thread:d} {message}", + "style": "{", + }, + }, + "handlers": { + "console": { + "class": "logging.StreamHandler", + "stream": "ext://sys.stdout", + "formatter": "verbose", + }, + "mail_admins": { + "level": "ERROR", + "class": "django.utils.log.AdminEmailHandler", + "filters": ["require_debug_false"], + }, + }, + "loggers": { + "django.request": { + "handlers": ["mail_admins"], + "level": "ERROR", + "propagate": True, + }, + }, + "root": { + "handlers": ["console"], + "level": "DEBUG", + } + } + + DJANGO_ADMINS="Ben Shaw,ben@example.com;Leo Lucio,leo@example.com" + class Prod(Dev): DEBUG = False SECRET_KEY = values.SecretValue() diff --git a/blog/views.py b/blog/views.py index ea1336019a..caa4c5ba5b 100644 --- a/blog/views.py +++ b/blog/views.py @@ -1,17 +1,25 @@ from django.shortcuts import render, get_object_or_404, redirect from django.utils import timezone +import logging + from blog.models import Post from blog.forms import CommentForm + # Create your views here. def index(request): + logger = logging.getLogger(__name__) + posts = Post.objects.filter(published_at__lte=timezone.now()) + logger.debug("Got %d posts", len(posts)) + return render(request, "blog/index.html", {"posts": posts}) def post_detail(request, slug): post = get_object_or_404(Post, slug=slug) + logger = logging.getLogger(__name__) if request.user.is_active: if request.method == "POST": comment_form = CommentForm(request.POST) @@ -21,6 +29,9 @@ def post_detail(request, slug): comment.content_object = post comment.creator = request.user comment.save() + logger.info( + "Created comment on Post %d for user %s", post.pk, request.user + ) return redirect(request.path_info) else: comment_form = CommentForm() diff --git a/db.sqlite3 b/db.sqlite3 index 5b80425db612955fdc5808c0d843e60dd097793d..4dcd45093578aa6644ca5cb8f8b24ae76bd431cb 100644 GIT binary patch delta 766 zcmZozz}>KbdxA8h-$WT_M!$^-uk{$&Hv8!3IS8677@Aob8dw>d>KU3_n3$VxKAmr= zAi&0{#K3o(cN@<{?i?<6PNj{Fe4H9hN^GpY&B2b9MroC%$tC$&8OdeE>6J#6CCNG2 z#U@p06($vB*`>*o@6Fv@@047YWmOX7X6|Sh@9iJzQRJ4LlkQ@fQWBLH>Fnj^?Wk++ zQCJk@?HBG;WR_}}Ze(C&qHAELYiNLQk&&sXxv_DR5-Zr+q^k7d3?tK|vXa77ON;dU z;-s{Sj9hcGiYzlj%dFf&y;QG?uq@wV|ME=7(8;goF7pDqA=App+$1z5u%u8o-^J3x z$Jebe-Y=^t(XBKuIm-hPn{(H1&t1RDJAm>Qel zk})%NXXRi}gd3Bnke6SQh;ERfm4Shtxv_<%nK9fTXj+C!SsIuaZD-xj^jiP`5LDZS delta 189 zcmV;u07CzOpbLPY3y>QDMv)vt0Yn;HVvrI2;KoKw?GBG+aF*-CeE;KheG&eW1 z%WpX#3