From 61e03a5088569f61bff0347d3dfea7bf2febb0f0 Mon Sep 17 00:00:00 2001 From: stellie Date: Mon, 21 Dec 2020 14:52:53 -0800 Subject: [PATCH 1/3] initial status post lesson 6 --- .DS_Store | Bin 0 -> 6148 bytes blogging/.DS_Store | Bin 0 -> 6148 bytes blogging/admin.py | 3 +- blogging/fixtures/blogging_test_fixture.json | 76 +++++++++--------- blogging/migrations/0001_initial.py | 2 +- blogging/migrations/0002_category.py | 22 ----- .../migrations/0003_auto_20191104_1942.py | 17 ---- blogging/models.py | 18 +---- blogging/static/django_blog.css | 74 ----------------- blogging/templates/blogging/detail.html | 17 ---- blogging/templates/blogging/list.html | 22 ----- blogging/tests.py | 58 +------------ blogging/urls.py | 7 -- blogging/views.py | 21 +---- manage.py | 6 +- mysite/settings.py | 5 +- mysite/templates/base.html | 36 +++------ mysite/templates/login.html | 9 --- mysite/urls.py | 5 -- polling/migrations/0001_initial.py | 5 +- polling/models.py | 1 + polling/templates/polling/detail.html | 18 ++--- polling/templates/polling/list.html | 6 +- polling/urls.py | 4 +- polling/views.py | 6 +- 25 files changed, 83 insertions(+), 355 deletions(-) create mode 100644 .DS_Store create mode 100644 blogging/.DS_Store delete mode 100644 blogging/migrations/0002_category.py delete mode 100644 blogging/migrations/0003_auto_20191104_1942.py delete mode 100644 blogging/static/django_blog.css delete mode 100644 blogging/templates/blogging/detail.html delete mode 100644 blogging/templates/blogging/list.html delete mode 100644 blogging/urls.py mode change 100644 => 100755 manage.py delete mode 100644 mysite/templates/login.html diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0Ad5FS3KdkJ8oYl)$@!iddK|D%1Qw#AO)m=6p#XQ3V83O4L6C3Qa}nwfo}!;`_Sl)y>Lv7PX|Mc0K^5; zVO+;7L2RBN_QEld8JZ=Pm{h9~!;;Q?tGZq|CMF$L!-v(AttJ$Ur}O?6<*=TpC6{CgHF`Xfa@ZY0+&|c3kI_l#{d8T literal 0 HcmV?d00001 diff --git a/blogging/admin.py b/blogging/admin.py index 1175916..387350c 100644 --- a/blogging/admin.py +++ b/blogging/admin.py @@ -1,6 +1,5 @@ from django.contrib import admin -from blogging.models import Post, Category +from blogging.models import Post admin.site.register(Post) -admin.site.register(Category) diff --git a/blogging/fixtures/blogging_test_fixture.json b/blogging/fixtures/blogging_test_fixture.json index bf5269e..e384e53 100644 --- a/blogging/fixtures/blogging_test_fixture.json +++ b/blogging/fixtures/blogging_test_fixture.json @@ -1,38 +1,38 @@ -[ - { - "pk": 1, - "model": "auth.user", - "fields": { - "username": "admin", - "first_name": "Mr.", - "last_name": "Administrator", - "is_active": true, - "is_superuser": true, - "is_staff": true, - "last_login": "2013-05-24T05:35:58.628Z", - "groups": [], - "user_permissions": [], - "password": "pbkdf2_sha256$10000$1rQazFNdOfFt$6aw/uIrv2uASkZ7moXMTajSN+ySYuowBnbP6ILNQntE=", - "email": "admin@example.com", - "date_joined": "2013-05-24T05:35:58.628Z" - } - }, - { - "pk": 2, - "model": "auth.user", - "fields": { - "username": "noname", - "first_name": "", - "last_name": "", - "is_active": true, - "is_superuser": true, - "is_staff": true, - "last_login": "2013-05-24T05:35:58.628Z", - "groups": [], - "user_permissions": [], - "password": "pbkdf2_sha256$10000$1rQazFNdOfFt$6aw/uIrv2uASkZ7moXMTajSN+ySYuowBnbP6ILNQntE=", - "email": "noname@example.com", - "date_joined": "2013-05-24T05:35:58.628Z" - } - } -] +[ + { + "pk": 1, + "model": "auth.user", + "fields": { + "username": "admin", + "first_name": "Mr.", + "last_name": "Administrator", + "is_active": true, + "is_superuser": true, + "is_staff": true, + "last_login": "2013-05-24T05:35:58.628Z", + "groups": [], + "user_permissions": [], + "password": "pbkdf2_sha256$10000$1rQazFNdOfFt$6aw/uIrv2uASkZ7moXMTajSN+ySYuowBnbP6ILNQntE=", + "email": "admin@example.com", + "date_joined": "2013-05-24T05:35:58.628Z" + } + }, + { + "pk": 2, + "model": "auth.user", + "fields": { + "username": "noname", + "first_name": "", + "last_name": "", + "is_active": true, + "is_superuser": true, + "is_staff": true, + "last_login": "2013-05-24T05:35:58.628Z", + "groups": [], + "user_permissions": [], + "password": "pbkdf2_sha256$10000$1rQazFNdOfFt$6aw/uIrv2uASkZ7moXMTajSN+ySYuowBnbP6ILNQntE=", + "email": "noname@example.com", + "date_joined": "2013-05-24T05:35:58.628Z" + } + } +] diff --git a/blogging/migrations/0001_initial.py b/blogging/migrations/0001_initial.py index 5d406bf..5b69ac2 100644 --- a/blogging/migrations/0001_initial.py +++ b/blogging/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.1.1 on 2019-10-29 01:39 +# Generated by Django 2.1.1 on 2020-12-19 00:22 from django.conf import settings from django.db import migrations, models diff --git a/blogging/migrations/0002_category.py b/blogging/migrations/0002_category.py deleted file mode 100644 index 0ccbe19..0000000 --- a/blogging/migrations/0002_category.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 2.1.1 on 2019-11-05 03:35 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('blogging', '0001_initial'), - ] - - 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=128)), - ('description', models.TextField(blank=True)), - ('posts', models.ManyToManyField(blank=True, related_name='categories', to='blogging.Post')), - ], - ), - ] diff --git a/blogging/migrations/0003_auto_20191104_1942.py b/blogging/migrations/0003_auto_20191104_1942.py deleted file mode 100644 index 663c56d..0000000 --- a/blogging/migrations/0003_auto_20191104_1942.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 2.1.1 on 2019-11-05 03:42 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('blogging', '0002_category'), - ] - - operations = [ - migrations.AlterModelOptions( - name='category', - options={'verbose_name_plural': 'Categories'}, - ), - ] diff --git a/blogging/models.py b/blogging/models.py index 10d6cc3..0f8d1db 100644 --- a/blogging/models.py +++ b/blogging/models.py @@ -1,24 +1,14 @@ from django.db import models from django.contrib.auth.models import User + class Post(models.Model): + def __str__(self): + return self.title + title = models.CharField(max_length=128) text = models.TextField(blank=True) author = models.ForeignKey(User, on_delete=models.CASCADE) created_date = models.DateTimeField(auto_now_add=True) modified_date = models.DateTimeField(auto_now=True) published_date = models.DateTimeField(blank=True, null=True) - - def __str__(self): - return self.title - -class Category(models.Model): - name = models.CharField(max_length=128) - description = models.TextField(blank=True) - posts = models.ManyToManyField(Post, blank=True, related_name='categories') - - class Meta: - verbose_name_plural = 'Categories' - - def __str__(self): - return self.name diff --git a/blogging/static/django_blog.css b/blogging/static/django_blog.css deleted file mode 100644 index 45a882d..0000000 --- a/blogging/static/django_blog.css +++ /dev/null @@ -1,74 +0,0 @@ -body { - background-color: #eee; - color: #111; - font-family: "HelveticaNeue-Light", "Helvetica Neue Light", "Helvetica Neue", Helvetica, Arial, "Lucida Grande", sans-serif; - margin:0; - padding:0; -} -#container { - margin:0; - padding:0; - margin-top: 0px; -} -#header { - background-color: #333; - border-botton: 1px solid #111; - margin:0; - padding:0; -} -#control-bar { - margin: 0em 0em 1em; - list-style: none; - list-style-type: none; - text-align: right; - color: #eee; - font-size: 80%; - padding-bottom: 0.4em; -} -#control-bar li { - display: inline-block; -} -#control-bar li a { - color: #eee; - padding: 0.5em; - text-decoration: none; -} -#control-bar li a:hover { - color: #cce; -} -#content { - margin: 0em 1em 1em; -} - -ul#entries { - list-style: none; - list-style-type: none; -} -div.entry { - margin-right: 2em; - margin-top: 1em; - border-top: 1px solid #cecece; -} -ul#entries li:first-child div.entry { - border-top: none; - margin-top: 0em; -} -div.entry-body { - margin-left: 2em; -} -.notification { - float: right; - text-align: center; - width: 25%; - padding: 1em; -} -.info { - background-color: #aae; -} -ul.categories { - list-style: none; - list-style-type: none; -} -ul.categories li { - display: inline; -} diff --git a/blogging/templates/blogging/detail.html b/blogging/templates/blogging/detail.html deleted file mode 100644 index ea5b9c8..0000000 --- a/blogging/templates/blogging/detail.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "base.html" %} - -{% block content %} -Home -

{{ post }}

- -
- {{ post.text }} -
-
    - {% for category in post.categories.all %} -
  • {{ category }}
  • - {% endfor %} -
-{% endblock %} diff --git a/blogging/templates/blogging/list.html b/blogging/templates/blogging/list.html deleted file mode 100644 index d8aa919..0000000 --- a/blogging/templates/blogging/list.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends "base.html" %}{% block content %} -

Recent Posts

- {% comment %} here is where the query happens {% endcomment %} - {% for post in posts %} -
-

- {{ post }} -

- -
- {{ post.text }} -
-
    - {% for category in post.categories.all %} -
  • {{ category }}
  • - {% endfor %} -
-
- {% endfor %} -{% endblock %} diff --git a/blogging/tests.py b/blogging/tests.py index 4250226..547e52f 100644 --- a/blogging/tests.py +++ b/blogging/tests.py @@ -1,11 +1,6 @@ -import datetime - from django.test import TestCase from django.contrib.auth.models import User -from django.utils.timezone import utc - from blogging.models import Post -from blogging.models import Category class PostTestCase(TestCase): @@ -15,58 +10,7 @@ def setUp(self): self.user = User.objects.get(pk=1) def test_string_representation(self): - expected = "This is a title" + expected = 'This is a title' p1 = Post(title=expected) actual = str(p1) self.assertEqual(expected, actual) - - -class CategoryTestCase(TestCase): - - def test_string_representation(self): - expected = "A Category" - c1 = Category(name=expected) - actual = str(c1) - self.assertEqual(expected, actual) - - -class FrontEndTestCase(TestCase): - """test views provided in the front-end""" - fixtures = ['blogging_test_fixture.json', ] - - def setUp(self): - self.now = datetime.datetime.utcnow().replace(tzinfo=utc) - self.timedelta = datetime.timedelta(15) - author = User.objects.get(pk=1) - for count in range(1, 11): - post = Post(title="Post %d Title" % count, - text="foo", - author=author) - if count < 6: - # publish the first five posts - pubdate = self.now - self.timedelta * count - post.published_date = pubdate - post.save() - - def test_list_only_published(self): - resp = self.client.get('/') - # the content of the rendered response is always a bytestring - resp_text = resp.content.decode(resp.charset) - self.assertTrue("Recent Posts" in resp_text) - for count in range(1, 11): - title = "Post %d Title" % count - if count < 6: - self.assertContains(resp, title, count=1) - else: - self.assertNotContains(resp, title) - - def test_details_only_published(self): - for count in range(1, 11): - title = "Post %d Title" % count - post = Post.objects.get(title=title) - resp = self.client.get('/posts/%d/' % post.pk) - if count < 6: - self.assertEqual(resp.status_code, 200) - self.assertContains(resp, title) - else: - self.assertEqual(resp.status_code, 404) diff --git a/blogging/urls.py b/blogging/urls.py deleted file mode 100644 index 172a39c..0000000 --- a/blogging/urls.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.urls import path -from blogging.views import list_view, detail_view - -urlpatterns = [ - path('', list_view, name="blog_index"), - path('posts//', detail_view, name="blog_detail"), -] diff --git a/blogging/views.py b/blogging/views.py index b4bab4f..91ea44a 100644 --- a/blogging/views.py +++ b/blogging/views.py @@ -1,22 +1,3 @@ from django.shortcuts import render -from django.http import HttpResponse, HttpResponseRedirect, Http404 -from django.template import loader -from blogging.models import Post - - -def detail_view(request, post_id): - published = Post.objects.exclude(published_date__exact=None) - try: - post = published.get(pk=post_id) - except Post.DoesNotExist: - raise Http404 - context = {'post': post} - return render(request, 'blogging/detail.html', context) - - -def list_view(request): - published = Post.objects.exclude(published_date__exact=None) - posts = published.order_by('-published_date') - context = {'posts': posts} - return render(request, 'blogging/list.html', context) +# Create your views here. diff --git a/manage.py b/manage.py old mode 100644 new mode 100755 index 390c767..3eb648f --- a/manage.py +++ b/manage.py @@ -8,8 +8,8 @@ from django.core.management import execute_from_command_line except ImportError as exc: raise ImportError( - "Couldn't import Django. Are you sure it's installed and " - "available on your PYTHONPATH environment variable? Did you " - "forget to activate a virtual environment?" + 'Couldn\'t import Django. Are you sure it\'s installed and ' + 'available on your PYTHONPATH environment variable? Did you ' + 'forget to activate a virtual environment?' ) from exc execute_from_command_line(sys.argv) diff --git a/mysite/settings.py b/mysite/settings.py index 14e4a11..3a45464 100644 --- a/mysite/settings.py +++ b/mysite/settings.py @@ -20,7 +20,7 @@ # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = 'xak=ca*e6hvh8q5hgfz5l9ees)_pxjif0)ui!ikifg4!enjk+7' +SECRET_KEY = 'ijm-qa2dxu0=u4a6d6wge-!3%ndaqoa#)!8%9yogrd*!$c$d@c' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True @@ -120,6 +120,3 @@ # https://docs.djangoproject.com/en/2.1/howto/static-files/ STATIC_URL = '/static/' - -LOGIN_URL = '/login/' -LOGIN_REDIRECT_URL = '/' diff --git a/mysite/templates/base.html b/mysite/templates/base.html index 4bb0230..cb7edaa 100644 --- a/mysite/templates/base.html +++ b/mysite/templates/base.html @@ -1,27 +1,15 @@ -{% load staticfiles %} - - My Django Blog - - - - {# header ends here #} -
-
- {% block content %} - [content will go here] - {% endblock %} -
-
- + + My Django Blog + + +
+
+ {% block content %} + [content will go here] + {% endblock %} +
+
+ diff --git a/mysite/templates/login.html b/mysite/templates/login.html deleted file mode 100644 index 1566d0f..0000000 --- a/mysite/templates/login.html +++ /dev/null @@ -1,9 +0,0 @@ -{% extends "base.html" %} - -{% block content %} -

My Blog Login

-
{% csrf_token %} - {{ form.as_p }} -

-
-{% endblock %} diff --git a/mysite/urls.py b/mysite/urls.py index 446cd8f..c52489c 100644 --- a/mysite/urls.py +++ b/mysite/urls.py @@ -15,13 +15,8 @@ """ from django.contrib import admin from django.urls import path, include -from django.contrib.auth.views import LoginView, LogoutView - urlpatterns = [ - path('', include('blogging.urls')), path('polling/', include('polling.urls')), path('admin/', admin.site.urls), - path('login/', LoginView.as_view(template_name='login.html'), name="login"), - path('logout/', LogoutView.as_view(next_page='/'), name="logout"), ] diff --git a/polling/migrations/0001_initial.py b/polling/migrations/0001_initial.py index 2be60e7..ecb6829 100644 --- a/polling/migrations/0001_initial.py +++ b/polling/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.1.1 on 2019-10-29 01:24 +# Generated by Django 2.1.1 on 2020-12-18 23:41 from django.db import migrations, models @@ -14,7 +14,8 @@ class Migration(migrations.Migration): migrations.CreateModel( name='Poll', fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('id', models.AutoField(auto_created=True, primary_key=True, + serialize=False, verbose_name='ID')), ('title', models.CharField(max_length=128)), ('text', models.TextField(blank=True)), ('score', models.IntegerField(default=0)), diff --git a/polling/models.py b/polling/models.py index 6a940d2..97d9967 100644 --- a/polling/models.py +++ b/polling/models.py @@ -1,5 +1,6 @@ from django.db import models + class Poll(models.Model): title = models.CharField(max_length=128) text = models.TextField(blank=True) diff --git a/polling/templates/polling/detail.html b/polling/templates/polling/detail.html index bb18db0..025182b 100644 --- a/polling/templates/polling/detail.html +++ b/polling/templates/polling/detail.html @@ -1,19 +1,17 @@ -{# polling/templates/polling/detail.html #} - -{% extends "base.html" %} +{% extends 'base.html' %} {% block content %}

{{ poll.title }}

-
+
{{ poll.text }}
-
+
Current score: {{ poll.score }}
-
-
+
+ {% csrf_token %} - - + +
-{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/polling/templates/polling/list.html b/polling/templates/polling/list.html index 9cf4282..f215457 100644 --- a/polling/templates/polling/list.html +++ b/polling/templates/polling/list.html @@ -1,11 +1,11 @@ -{% extends "base.html" %} +{% extends 'base.html' %} {% block content %}

Polls

{% for poll in polls %} -
+ {% endfor %} -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/polling/urls.py b/polling/urls.py index 9c4e2fd..00b1630 100644 --- a/polling/urls.py +++ b/polling/urls.py @@ -2,6 +2,6 @@ from polling.views import list_view, detail_view urlpatterns = [ - path('', list_view, name="poll_index"), - path('polls//', detail_view, name="poll_detail"), + path('', list_view, name='poll_index'), + path('polls//', detail_view, name='poll_detail'), ] diff --git a/polling/views.py b/polling/views.py index 6339808..6f2f252 100644 --- a/polling/views.py +++ b/polling/views.py @@ -2,18 +2,20 @@ from django.http import Http404 from polling.models import Poll + def list_view(request): context = {'polls': Poll.objects.all()} return render(request, 'polling/list.html', context) + def detail_view(request, poll_id): try: poll = Poll.objects.get(pk=poll_id) except Poll.DoesNotExist: raise Http404 - if request.method == "POST": - if request.POST.get("vote") == "Yes": + if request.method == 'POST': + if request.POST.get('vote') == 'Yes': poll.score += 1 else: poll.score -= 1 From 87e6c4057a5e92b4cc51a008096c6047a876d13f Mon Sep 17 00:00:00 2001 From: stellie Date: Mon, 21 Dec 2020 14:53:54 -0800 Subject: [PATCH 2/3] status post lesson 7 with changes and additions --- .DS_Store | Bin 6148 -> 10244 bytes blogging/.DS_Store | Bin 6148 -> 6148 bytes blogging/admin.py | 4 +- blogging/migrations/0002_category.py | 25 ++++++++ blogging/models.py | 12 ++++ blogging/static/django_blog.css | 74 ++++++++++++++++++++++++ blogging/templates/.DS_Store | Bin 0 -> 6148 bytes blogging/templates/blogging/.DS_Store | Bin 0 -> 6148 bytes blogging/templates/blogging/detail.html | 17 ++++++ blogging/templates/blogging/list.html | 20 +++++++ blogging/tests.py | 54 ++++++++++++++++- blogging/urls.py | 7 +++ blogging/views.py | 20 ++++++- mysite/settings.py | 3 + mysite/templates/base.html | 13 +++++ mysite/templates/login.html | 9 +++ mysite/urls.py | 7 ++- 17 files changed, 260 insertions(+), 5 deletions(-) create mode 100644 blogging/migrations/0002_category.py create mode 100644 blogging/static/django_blog.css create mode 100644 blogging/templates/.DS_Store create mode 100644 blogging/templates/blogging/.DS_Store create mode 100644 blogging/templates/blogging/detail.html create mode 100644 blogging/templates/blogging/list.html create mode 100644 blogging/urls.py create mode 100644 mysite/templates/login.html diff --git a/.DS_Store b/.DS_Store index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..138592ade30b97f0f0427a5c76da3763caa009b8 100644 GIT binary patch literal 10244 zcmeHMTWl0n82F?hNh3*_mZ$wgpN} zjn^0>-Vzg0V~9^e;*$gk55_3sB_>AVE$V~5_-HVi_@w`tGh24MZK4lGgXbi3{yG2U zob!M4?KwGT0RYS3 z0bz_D$wVYaxC}$7Pkws9C={b32J&#S>&%^GB9bFq@^C;N4j7G$(Fp~^(J4+3-2oF^ zidrBb5U50eo!uo!g99F&-b>E!X~#>G?W1(gvHc(({}QVvRZpHWRgz^%o-Q9w_IrcL zzz7O{n~^`rT6>J3KOMbyd!94;Zlw3w)?iAV-R`-8VY^m747N>+Tn_cvuIUZ7c?H+> z!+w(75Dt`5wV|QLrp9``p=sT4y*|_sZ(LWeuWwvCJS9?hOSHFSFT!IyY) z7%dP`J0{t)<7I0487ED1Wxf?KIDHMtjF*kBGP2)|>*Ei!bEv9bEJF3wC{)Tct#ijt zC8f?BAOyN{f$h2eJ}d8&OsTa#q1Ku4+&y`V*?TgcQ^>l0N}Z80oJ_$n0;|<=lJ+Uf z?+vUI0ed^(d-9^IY ziZW|<&w%aQX~)`c_!-MJZMSdlplt^I2MBaGu#eeRUY2Lzjc(V-TG!{!yIs}hYYU1| z-1lazq}EBjg|vOVUPNLyM;AOC{+b)3@g>s<~RxR;;7| zquZxMqG_(VOO{o?`vGDG4^`H#lVsK0>lkUviB!!SNVT(PfTI~HTker)_VvB=*|L<6 z&(&I6yCkXCj*AK)jr0l&bn@EiPr63VDx4QjX; z>u?FiaRY9|P1u5MxD$8bZtTKtJd8&$g$DLv4v%9V13Zn7;$!$YK8erc%lHbuim&0D zcnROe%lIC?kDub__${AcRgw3*dAnHSM6}ICv^_E&ZDU*S-S#ih_UjtP)fqEq&7QNc zZu#oQ^_yEqKj)Y)SW2dlKb#;Taf-wUKVvx?>AP$5l?AcA#4qc_O*V6O7rCxZWlWM7 zOSC&;i()FH5N|GtEsLpD#B$zT5nH9GlZeA|=)6XW$JEJ0S8iRaG%0E|@tIqjm5niV z3Ne>kTa+z|3hWoh&2ns+BFTTBh3DWrT!yRgF|qFp_#S?QpW%06o{Y6Pk9ap9W4I9S z#1*&_>v1*Sjm^ZtO}H7i;x^olJBWwfxDR`9KOVpgT6hfm(GFQyz(G8L58=c32yyWV zd={T0KE6PFJcsAUu&=YoJ1+48$%=(F>veQQ`-`-FDz_X(CE>)1`}7!$ivZ$a*)E zn8>Fr0oX&sz*JgdJld8Wex+VM6J>R!N9kOecbw_E!sjxkpVHY(>{ULWXV`p15eNtb z1Ofs9fq+2ZmOx+<&q8PS|63>i|NoZARU{!05D1Kq09JP-JKD%ryh0f3#_rm6dXCb= z3iBJ`G7O=LxiEcr9ZwB*9Uoq#kS@AH3GIow5PN^D?X%+A3f$P5w! kif{u7S0Gm1SoocJGQWxw&zlbg)!)8a3!^{&KSOD(c7&`y} delta 57 zcmZoMXfc=|&e%4wP;8=}A|vC(0Ba!8qph($ diff --git a/blogging/admin.py b/blogging/admin.py index 387350c..f51c200 100644 --- a/blogging/admin.py +++ b/blogging/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin - -from blogging.models import Post +from blogging.models import Post, Category admin.site.register(Post) +admin.site.register(Category) diff --git a/blogging/migrations/0002_category.py b/blogging/migrations/0002_category.py new file mode 100644 index 0000000..e1bd005 --- /dev/null +++ b/blogging/migrations/0002_category.py @@ -0,0 +1,25 @@ +# Generated by Django 2.1.1 on 2020-12-21 19:42 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('blogging', '0001_initial'), + ] + + 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=128)), + ('description', models.TextField(blank=True)), + ('posts', models.ManyToManyField(blank=True, + related_name='categories', + to='blogging.Post')), + ], + ), + ] diff --git a/blogging/models.py b/blogging/models.py index 0f8d1db..807caa2 100644 --- a/blogging/models.py +++ b/blogging/models.py @@ -12,3 +12,15 @@ def __str__(self): created_date = models.DateTimeField(auto_now_add=True) modified_date = models.DateTimeField(auto_now=True) published_date = models.DateTimeField(blank=True, null=True) + + +class Category(models.Model): + def __str__(self): + return self.name + + class Meta: + verbose_name_plural = 'Categories' + + name = models.CharField(max_length=128) + description = models.TextField(blank=True) + posts = models.ManyToManyField(Post, blank=True, related_name='categories') diff --git a/blogging/static/django_blog.css b/blogging/static/django_blog.css new file mode 100644 index 0000000..303f14b --- /dev/null +++ b/blogging/static/django_blog.css @@ -0,0 +1,74 @@ +body { + background-color: #eee; + color: #111; + font-family: 'HelveticaNeue-Light', 'Helvetica Neue Light', 'Helvetica Neue', Helvetica, Arial, 'Lucida Grande', sans-serif; + margin:0; + padding:0; +} +#container { + margin:0; + padding:0; + margin-top: 0px; +} +#header { + background-color: #333; + border-botton: 1px solid #111; + margin:0; + padding:0; +} +#control-bar { + margin: 0em 0em 1em; + list-style: none; + list-style-type: none; + text-align: right; + color: #eee; + font-size: 80%; + padding-bottom: 0.4em; +} +#control-bar li { + display: inline-block; +} +#control-bar li a { + color: #eee; + padding: 0.5em; + text-decoration: none; +} +#control-bar li a:hover { + color: #cce; +} +#content { + margin: 0em 1em 1em; +} + +ul#entries { + list-style: none; + list-style-type: none; +} +div.entry { + margin-right: 2em; + margin-top: 1em; + border-top: 1px solid #cecece; +} +ul#entries li:first-child div.entry { + border-top: none; + margin-top: 0em; +} +div.entry-body { + margin-left: 2em; +} +.notification { + float: right; + text-align: center; + width: 25%; + padding: 1em; +} +.info { + background-color: #aae; +} +ul.categories { + list-style: none; + list-style-type: none; +} +ul.categories li { + display: inline; +} diff --git a/blogging/templates/.DS_Store b/blogging/templates/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..adbf147b4082ea6546ee6311a5a7255de3673a6d GIT binary patch literal 6148 zcmeHKyJ`b55S&e19HeoXQooQt5XSj}{6KIF#&G0{kp3!vmp?7Dk0R!h&Th;stVUYx zNN9@NTL8BC-oF5A00Z3-Cm)vP`|b;SsE85iJmY{PK5^K8eT<9jzXQ%4u)~On>|gxd zG>yI~lLAse3P=GdAO)_fK$X|w>8hvdFexAf?m+?nJ~X;xFPszO)4?HH0OE?_FwUcw zAT|#Wd*PhO2+fj8OsZ9jVM%AaRbDTg6O#_B=ELe{s}9BDcAnoN9o7>yN&zWwsla0{ z*Ixf$>HqZqmn5yEfE2hZ1#Go_+iv)zs;!I1d97{qce>}C>290{g+r8MVw7Vpyc|DA cQsy-s>pfo8d_Y08tgH@D#L4E*13Xl$^2=rI+U3^-`4@HSCG)OdPEZMW` z^W18uIG+L7>T~x9ECI~vj`;F0HGl3tv75>mkJ^Qa}nwfs+FMeQ0#YUN|Pkr-LCz0OE}4 zFs@^kAU01Bd*PVK49${COsdt0VM%AcRb4L}6O#_B;lt`=s|m&8biThuIjkotN&zWw zslaV+m)`$3^dIK`OOkd{KnnaT1#Gt7u2;NL_14Mdyw^7R6WwdR>26#Hg(2E8G1@UV g-j461DC?T9`Mej7i9u&R=tTVtxGpj&@Yf0)0jXFOLI3~& literal 0 HcmV?d00001 diff --git a/blogging/templates/blogging/detail.html b/blogging/templates/blogging/detail.html new file mode 100644 index 0000000..04c8b63 --- /dev/null +++ b/blogging/templates/blogging/detail.html @@ -0,0 +1,17 @@ +{% extends 'base.html' %} + +{% block content %} +Home +

{{ post }}

+ +
+ {{ post.text }} +
+
    + {% for category in post.categories.all %} +
  • {{ category }}
  • + {% endfor %} +
+{% endblock %} diff --git a/blogging/templates/blogging/list.html b/blogging/templates/blogging/list.html new file mode 100644 index 0000000..182d299 --- /dev/null +++ b/blogging/templates/blogging/list.html @@ -0,0 +1,20 @@ +{% extends 'base.html' %}{% block content %} +

Recent Posts

+ {% comment %} here is where the query happens {% endcomment %} + {% for post in posts %} +
+

{{ post }}

+ +
+ {{ post.text }} +
+
    + {% for category in post.categories.all %} +
  • {{ category }}
  • + {% endfor %} +
+
+ {% endfor %} +{% endblock %} diff --git a/blogging/tests.py b/blogging/tests.py index 547e52f..47124b2 100644 --- a/blogging/tests.py +++ b/blogging/tests.py @@ -1,6 +1,8 @@ +import datetime +from django.utils.timezone import utc from django.test import TestCase from django.contrib.auth.models import User -from blogging.models import Post +from blogging.models import Post, Category class PostTestCase(TestCase): @@ -14,3 +16,53 @@ def test_string_representation(self): p1 = Post(title=expected) actual = str(p1) self.assertEqual(expected, actual) + + +class CategoryTestCase(TestCase): + + def test_string_representation(self): + expected = 'A Category' + c1 = Category(name=expected) + actual = str(c1) + self.assertEqual(expected, actual) + + +class FrontEndTestCase(TestCase): + """Test views provided in the front-end""" + fixtures = ['blogging_test_fixture.json', ] + + def setUp(self): + self.now = datetime.datetime.utcnow().replace(tzinfo=utc) + self.timedelta = datetime.timedelta(15) + author = User.objects.get(pk=1) + for count in range(1, 11): + post = Post(title='Post %d Title' % count, + text='foo', + author=author) + if count < 6: # publish the first five posts + pubdate = self.now - self.timedelta * count + post.published_date = pubdate + post.save() + + def test_list_only_published(self): + resp = self.client.get('/') + # the content of the rendered response is always a bytestring + resp_text = resp.content.decode(resp.charset) + self.assertTrue('Recent Posts' in resp_text) + for count in range(1, 11): + title = 'Post %d Title' % count + if count < 6: + self.assertContains(resp, title, count=1) + else: + self.assertNotContains(resp, title) + + def test_details_only_published(self): + for count in range(1, 11): + title = 'Post %d Title' % count + post = Post.objects.get(title=title) + resp = self.client.get('/posts/%d/' % post.pk) + if count < 6: + self.assertEqual(resp.status_code, 200) + self.assertContains(resp, title) + else: + self.assertEqual(resp.status_code, 404) diff --git a/blogging/urls.py b/blogging/urls.py new file mode 100644 index 0000000..d4de3ed --- /dev/null +++ b/blogging/urls.py @@ -0,0 +1,7 @@ +from django.urls import path +from blogging.views import list_view, detail_view + +urlpatterns = [ + path('', list_view, name='blog_index'), + path('posts//', detail_view, name='blog_detail'), +] diff --git a/blogging/views.py b/blogging/views.py index 91ea44a..a0eeb98 100644 --- a/blogging/views.py +++ b/blogging/views.py @@ -1,3 +1,21 @@ from django.shortcuts import render +from django.http import HttpResponse, HttpResponseRedirect, Http404 +from django.template import loader +from blogging.models import Post -# Create your views here. + +def list_view(request): + published = Post.objects.exclude(published_date__exact=None) + posts = published.order_by('-published_date') + context = {'posts': posts} + return render(request, 'blogging/list.html', context) + + +def detail_view(request, post_id): + published = Post.objects.exclude(published_date__exact=None) + try: + post = published.get(pk=post_id) + except Post.DoesNotExist: + raise Http404 + context = {'post': post} + return render(request, 'blogging/detail.html', context) diff --git a/mysite/settings.py b/mysite/settings.py index 3a45464..24835e0 100644 --- a/mysite/settings.py +++ b/mysite/settings.py @@ -120,3 +120,6 @@ # https://docs.djangoproject.com/en/2.1/howto/static-files/ STATIC_URL = '/static/' + +LOGIN_URL = '/login/' +LOGIN_REDIRECT_URL = '/' diff --git a/mysite/templates/base.html b/mysite/templates/base.html index cb7edaa..f0420f2 100644 --- a/mysite/templates/base.html +++ b/mysite/templates/base.html @@ -1,9 +1,22 @@ +{% load staticfiles %} My Django Blog + + +
{% block content %} diff --git a/mysite/templates/login.html b/mysite/templates/login.html new file mode 100644 index 0000000..6ddac16 --- /dev/null +++ b/mysite/templates/login.html @@ -0,0 +1,9 @@ +{% extends 'base.html' %} + +{% block content %} +

My Blog Login

+
{% csrf_token %} + {{ form.as_p }} +

+
+{% endblock %} diff --git a/mysite/urls.py b/mysite/urls.py index c52489c..c657ed2 100644 --- a/mysite/urls.py +++ b/mysite/urls.py @@ -15,8 +15,13 @@ """ from django.contrib import admin from django.urls import path, include +from django.contrib.auth.views import LoginView, LogoutView urlpatterns = [ - path('polling/', include('polling.urls')), path('admin/', admin.site.urls), + path('polling/', include('polling.urls')), + path('', include('blogging.urls')), + path('login/', LoginView.as_view(template_name='login.html'), + name='login'), + path('logout/', LogoutView.as_view(next_page='/'), name='logout'), ] From 520245f11e2087d0247706b7eca74b0de2485bfa Mon Sep 17 00:00:00 2001 From: stellie Date: Mon, 21 Dec 2020 18:48:36 -0800 Subject: [PATCH 3/3] add ability to add categories to posts --- .DS_Store | Bin 10244 -> 10244 bytes blogging/admin.py | 15 +++++++++++++-- .../migrations/0003_auto_20201222_0221.py | 17 +++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 blogging/migrations/0003_auto_20201222_0221.py diff --git a/.DS_Store b/.DS_Store index 138592ade30b97f0f0427a5c76da3763caa009b8..2f875118240babc589de1a9503aa95b24da7ba4e 100644 GIT binary patch delta 33 lcmZn(XbIS0D#%p8J^6u{$YyWB$*hb9o9~PL=Lb`&i~z)=3q}9{ delta 33 lcmZn(XbIS0D#(<3ck%-