├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .pre-commit-config.yaml ├── Makefile ├── README.md ├── blog ├── __init__.py ├── admin.py ├── apps.py ├── feeds.py ├── forms.py ├── migrations │ ├── 0001_initial.py │ ├── 0002_comment_parent.py │ ├── 0003_remove_comment_parent.py │ ├── 0004_auto_20191015_0853.py │ ├── 0005_auto_20211014_1338.py │ └── __init__.py ├── models.py ├── sitemaps.py ├── tests.py ├── urls.py └── views.py ├── manage.py ├── mysite ├── __init__.py ├── settings.py ├── urls.py └── wsgi.py ├── requirements.txt ├── static └── css │ └── base.css └── templates ├── base.html ├── index.html ├── post_detail.html └── sidebar.html /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: Django CI 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: ubuntu-latest 13 | strategy: 14 | max-parallel: 4 15 | matrix: 16 | python-version: [3.9] 17 | 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: Set up Python ${{ matrix.python-version }} 21 | uses: actions/setup-python@v2 22 | with: 23 | python-version: ${{ matrix.python-version }} 24 | - name: Install Dependencies 25 | run: | 26 | python -m pip install --upgrade pip 27 | pip install -r requirements.txt 28 | - name: Run Tests 29 | run: | 30 | python manage.py test 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Python template 2 | # Byte-compiled / optimized / DLL files 3 | __pycache__/ 4 | *.py[cod] 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | 27 | .env.prod.db 28 | .production.env 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *.cover 48 | .hypothesis/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | deployment/.envs/production/.production.env 55 | deployment/.envs/production/.prod.db 56 | # Django stuff: 57 | staticfiles/ 58 | frontend/staticfiles/ 59 | media/ 60 | mediafiles/ 61 | cache/ 62 | # Sphinx documentation 63 | docs/_build/ 64 | db.sqlite3 65 | # PyBuilder 66 | target/ 67 | 68 | # pyenv 69 | .python-version 70 | 71 | # celery beat schedule file 72 | celerybeat-schedule 73 | 74 | # Environments 75 | .venv 76 | venv/ 77 | ENV/ 78 | 79 | # Rope project settings 80 | .ropeproject 81 | 82 | # mkdocs documentation 83 | /site 84 | 85 | # mypy 86 | .mypy_cache/ 87 | 88 | 89 | ### Node template 90 | # Logs 91 | logs 92 | *.log 93 | npm-debug.log* 94 | yarn-debug.log* 95 | yarn-error.log* 96 | 97 | # Runtime data 98 | pids 99 | *.pid 100 | *.seed 101 | *.pid.lock 102 | 103 | # Directory for instrumented libs generated by jscoverage/JSCover 104 | lib-cov 105 | 106 | # Coverage directory used by tools like istanbul 107 | coverage 108 | 109 | # nyc test coverage 110 | .nyc_output 111 | 112 | # Bower dependency directory (https://bower.io/) 113 | bower_components 114 | 115 | # node-waf configuration 116 | .lock-wscript 117 | 118 | # Compiled binary addons (http://nodejs.org/api/addons.html) 119 | build/Release 120 | 121 | # Dependency directories 122 | node_modules/ 123 | jspm_packages/ 124 | 125 | # Typescript v1 declaration files 126 | typings/ 127 | 128 | # Optional npm cache directory 129 | .npm 130 | 131 | # Optional eslint cache 132 | .eslintcache 133 | 134 | # Optional REPL history 135 | .node_repl_history 136 | 137 | # Output of 'npm pack' 138 | *.tgz 139 | 140 | # Yarn Integrity file 141 | .yarn-integrity 142 | 143 | 144 | ### Linux template 145 | *~ 146 | 147 | # temporary files which can be created if a process still has a handle open of a deleted file 148 | .fuse_hidden* 149 | 150 | # KDE directory preferences 151 | .directory 152 | 153 | # Linux trash folder which might appear on any partition or disk 154 | .Trash-* 155 | 156 | # .nfs files are created when an open file is removed but is still being accessed 157 | .nfs* 158 | 159 | 160 | ### VisualStudioCode template 161 | .vscode/* 162 | !.vscode/settings.json 163 | !.vscode/tasks.json 164 | !.vscode/launch.json 165 | !.vscode/extensions.json 166 | *.code-workspace 167 | 168 | # Local History for Visual Studio Code 169 | .history/ 170 | 171 | 172 | # Provided default Pycharm Run/Debug Configurations should be tracked by git 173 | # In case of local modifications made by Pycharm, use update-index command 174 | # for each changed file, like this: 175 | # git update-index --assume-unchanged .idea/{{cookiecutter.project_slug}}.iml 176 | ### JetBrains template 177 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm 178 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 179 | 180 | # User-specific stuff: 181 | .idea/**/workspace.xml 182 | .idea/**/tasks.xml 183 | .idea/dictionaries 184 | 185 | # Sensitive or high-churn files: 186 | .idea/**/dataSources/ 187 | .idea/**/dataSources.ids 188 | .idea/**/dataSources.xml 189 | .idea/**/dataSources.local.xml 190 | .idea/**/sqlDataSources.xml 191 | .idea/**/dynamic.xml 192 | .idea/**/uiDesigner.xml 193 | 194 | # Gradle: 195 | .idea/**/gradle.xml 196 | .idea/**/libraries 197 | 198 | # CMake 199 | cmake-build-debug/ 200 | 201 | # Mongo Explorer plugin: 202 | .idea/**/mongoSettings.xml 203 | 204 | ## File-based project format: 205 | *.iws 206 | 207 | ## Plugin-specific files: 208 | 209 | # IntelliJ 210 | out/ 211 | 212 | # mpeltonen/sbt-idea plugin 213 | .idea_modules/ 214 | 215 | # JIRA plugin 216 | atlassian-ide-plugin.xml 217 | 218 | # Cursive Clojure plugin 219 | .idea/replstate.xml 220 | 221 | # Crashlytics plugin (for Android Studio and IntelliJ) 222 | com_crashlytics_export_strings.xml 223 | crashlytics.properties 224 | crashlytics-build.properties 225 | fabric.properties 226 | 227 | 228 | ### Windows template 229 | # Windows thumbnail cache files 230 | Thumbs.db 231 | ehthumbs.db 232 | ehthumbs_vista.db 233 | 234 | # Dump file 235 | *.stackdump 236 | 237 | # Folder config file 238 | Desktop.ini 239 | 240 | # Recycle Bin used on file shares 241 | $RECYCLE.BIN/ 242 | 243 | # Windows Installer files 244 | *.cab 245 | *.msi 246 | *.msm 247 | *.msp 248 | 249 | # Windows shortcuts 250 | *.lnk 251 | 252 | 253 | ### macOS template 254 | # General 255 | *.DS_Store 256 | .AppleDouble 257 | .LSOverride 258 | 259 | # Icon must end with two \r 260 | Icon 261 | 262 | # Thumbnails 263 | ._* 264 | 265 | # Files that might appear in the root of a volume 266 | .DocumentRevisions-V100 267 | .fseventsd 268 | .Spotlight-V100 269 | .TemporaryItems 270 | .Trashes 271 | .VolumeIcon.icns 272 | .com.apple.timemachine.donotpresent 273 | 274 | # Directories potentially created on remote AFP share 275 | .AppleDB 276 | .AppleDesktop 277 | Network Trash Folder 278 | Temporary Items 279 | .apdisk 280 | 281 | 282 | ### SublimeText template 283 | # Cache files for Sublime Text 284 | *.tmlanguage.cache 285 | *.tmPreferences.cache 286 | *.stTheme.cache 287 | 288 | # Workspace files are user-specific 289 | *.sublime-workspace 290 | 291 | # Project files should be checked into the repository, unless a significant 292 | # proportion of contributors will probably not be using Sublime Text 293 | # *.sublime-project 294 | 295 | # SFTP configuration file 296 | sftp-config.json 297 | 298 | # Package control specific files 299 | Package Control.last-run 300 | Package Control.ca-list 301 | Package Control.ca-bundle 302 | Package Control.system-ca-bundle 303 | Package Control.cache/ 304 | Package Control.ca-certs/ 305 | Package Control.merged-ca-bundle 306 | Package Control.user-ca-bundle 307 | oscrypto-ca-bundle.crt 308 | bh_unicode_properties.cache 309 | 310 | # Sublime-github package stores a github token in this file 311 | # https://packagecontrol.io/packages/sublime-github 312 | GitHub.sublime-settings 313 | 314 | 315 | ### Vim template 316 | # Swap 317 | [._]*.s[a-v][a-z] 318 | [._]*.sw[a-p] 319 | [._]s[a-v][a-z] 320 | [._]sw[a-p] 321 | 322 | # Session 323 | Session.vim 324 | 325 | # Temporary 326 | .netrwhist 327 | 328 | # Auto-generated tag files 329 | tags -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | exclude: 'docs|node_modules|migrations|.git|.tox' 2 | default_stages: [commit] 3 | fail_fast: true 4 | 5 | repos: 6 | - repo: https://github.com/pre-commit/pre-commit-hooks 7 | rev: v4.0.1 8 | hooks: 9 | - id: trailing-whitespace 10 | - id: end-of-file-fixer 11 | 12 | - repo: https://github.com/psf/black 13 | rev: 21.7b0 14 | hooks: 15 | - id: black 16 | 17 | - repo: https://github.com/rtts/djhtml 18 | rev: v1.4.9 19 | hooks: 20 | - id: djhtml 21 | 22 | 23 | - repo: https://github.com/timothycrosley/isort 24 | rev: 5.9.3 25 | hooks: 26 | - id: isort -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | install: 2 | pip install -r requirements.txt 3 | 4 | test: 5 | python manage.py test 6 | 7 | makemigrations: 8 | python manage.py makemigrations 9 | 10 | migrate: 11 | python manage.py migrate 12 | 13 | run: 14 | python manage.py runserver 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Django_blog 2 | 3 | A blog application made on Django. 4 | 5 | ![ezgif com-video-to-gif](https://user-images.githubusercontent.com/38559396/55287491-12c4de80-53c7-11e9-8c6a-3f02b79ba9ca.gif) 6 | 7 | **Release 1.0** -Blog application made with Django, To learn more read https://djangocentral.com/building-a-blog-application-with-django 8 | 9 | **Release 2.0** - Comments system added. To learn more read - https://djangocentral.com/creating-comments-system-with-django/ 10 | 11 | ![Peek 2019-10-15 11-41](https://user-images.githubusercontent.com/38559396/66840502-c9fcfd80-ef85-11e9-827c-51fa4064a231.gif) 12 | 13 | **Release 2.1** - Pagination added, To learn more read - https://djangocentral.com/adding-pagination-with-django/

14 | Static assets management added, To learn more read - https://djangocentral.com/static-assets-in-django/

15 | 16 | Wysiwyg editor added, To learn more read - https://djangocentral.com/integrating-summernote-in-django/

17 | 18 | Sitemap added, To learn more read - https://djangocentral.com/creating-sitemaps-in-django/ 19 | 20 | Feeds added, To learn more read - https://djangocentral.com/creating-feeds-with-django/ 21 | 22 | Using Environment Variables In Django, To learn more read - https://djangocentral.com/environment-variables-in-django/ 23 | 24 | # Deployment 25 | 26 | How To Deploy Django App with Nginx, Gunicorn, PostgreSQL and Let’s Encrypt SSL on Ubuntu - https://djangocentral.com/deploy-django-with-nginx-gunicorn-postgresql-and-lets-encrypt-ssl-on-ubuntu/ 27 | 28 | # Contributors 29 | Contributions are welcome, and they are greatly appreciated! Every little bit helps, and credit will always be given.

30 | 31 | Please star the repo and feel free to make pull requests.

32 | Buy Me a Coffee at ko-fi.com 33 | -------------------------------------------------------------------------------- /blog/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAbhijeet/Django_blog/42d2566313121814240b36301d888a9a0239bcbb/blog/__init__.py -------------------------------------------------------------------------------- /blog/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from django_summernote.admin import SummernoteModelAdmin 3 | 4 | from .models import Comment, Post 5 | 6 | 7 | class PostAdmin(SummernoteModelAdmin): 8 | list_display = ("title", "slug", "status", "created_on") 9 | list_filter = ("status", "created_on") 10 | search_fields = ["title", "content"] 11 | prepopulated_fields = {"slug": ("title",)} 12 | 13 | summernote_fields = ("content",) 14 | 15 | 16 | @admin.register(Comment) 17 | class CommentAdmin(admin.ModelAdmin): 18 | list_display = ("name", "body", "post", "created_on", "active") 19 | list_filter = ("active", "created_on") 20 | search_fields = ("name", "email", "body") 21 | actions = ["approve_comments"] 22 | 23 | def approve_comments(self, request, queryset): 24 | queryset.update(active=True) 25 | 26 | 27 | admin.site.register(Post, PostAdmin) 28 | -------------------------------------------------------------------------------- /blog/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BlogConfig(AppConfig): 5 | name = "blog" 6 | -------------------------------------------------------------------------------- /blog/feeds.py: -------------------------------------------------------------------------------- 1 | from django.contrib.syndication.views import Feed 2 | from django.template.defaultfilters import truncatewords 3 | from django.urls import reverse 4 | 5 | from .models import Post 6 | 7 | 8 | class LatestPostsFeed(Feed): 9 | title = "My blog" 10 | link = "" 11 | description = "New posts of my blog." 12 | 13 | def items(self): 14 | return Post.objects.filter(status=1) 15 | 16 | def item_title(self, item): 17 | return item.title 18 | 19 | def item_description(self, item): 20 | return truncatewords(item.content, 30) 21 | 22 | # Only needed if the model has no get_absolute_url method 23 | # def item_link(self, item): 24 | # return reverse("post_detail", args=[item.slug]) 25 | 26 | 27 | from django.utils.feedgenerator import Atom1Feed 28 | 29 | 30 | class AtomSiteNewsFeed(LatestPostsFeed): 31 | feed_type = Atom1Feed 32 | subtitle = LatestPostsFeed.description 33 | -------------------------------------------------------------------------------- /blog/forms.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | 3 | from .models import Comment 4 | 5 | 6 | class CommentForm(forms.ModelForm): 7 | class Meta: 8 | model = Comment 9 | fields = ("name", "email", "body") 10 | -------------------------------------------------------------------------------- /blog/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.6 on 2019-10-13 10:21 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Post', 19 | fields=[ 20 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('title', models.CharField(max_length=200, unique=True)), 22 | ('slug', models.SlugField(max_length=200, unique=True)), 23 | ('updated_on', models.DateTimeField(auto_now=True)), 24 | ('content', models.TextField()), 25 | ('created_on', models.DateTimeField(auto_now_add=True)), 26 | ('status', models.IntegerField(choices=[(0, 'Draft'), (1, 'Publish')], default=0)), 27 | ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='blog_posts', to=settings.AUTH_USER_MODEL)), 28 | ], 29 | options={ 30 | 'ordering': ['-created_on'], 31 | }, 32 | ), 33 | migrations.CreateModel( 34 | name='Comment', 35 | fields=[ 36 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 37 | ('name', models.CharField(max_length=80)), 38 | ('email', models.EmailField(max_length=254)), 39 | ('body', models.TextField()), 40 | ('created_on', models.DateTimeField(auto_now_add=True)), 41 | ('status', models.IntegerField(choices=[(0, 'Draft'), (1, 'Publish')], default=0)), 42 | ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comments', to='blog.Post')), 43 | ], 44 | options={ 45 | 'ordering': ['created_on'], 46 | }, 47 | ), 48 | ] 49 | -------------------------------------------------------------------------------- /blog/migrations/0002_comment_parent.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.6 on 2019-10-13 12:05 2 | 3 | from django.db import migrations, models 4 | import django.db.models.deletion 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ('blog', '0001_initial'), 11 | ] 12 | 13 | operations = [ 14 | migrations.AddField( 15 | model_name='comment', 16 | name='parent', 17 | field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='replies', to='blog.Comment'), 18 | ), 19 | ] 20 | -------------------------------------------------------------------------------- /blog/migrations/0003_remove_comment_parent.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.6 on 2019-10-13 14:16 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('blog', '0002_comment_parent'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='comment', 15 | name='parent', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /blog/migrations/0004_auto_20191015_0853.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 2.2.6 on 2019-10-15 08:53 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('blog', '0003_remove_comment_parent'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='comment', 15 | name='status', 16 | ), 17 | migrations.AddField( 18 | model_name='comment', 19 | name='active', 20 | field=models.BooleanField(default=False), 21 | ), 22 | ] 23 | -------------------------------------------------------------------------------- /blog/migrations/0005_auto_20211014_1338.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2021-10-14 13:38 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('blog', '0004_auto_20191015_0853'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='comment', 15 | name='id', 16 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), 17 | ), 18 | migrations.AlterField( 19 | model_name='post', 20 | name='id', 21 | field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /blog/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAbhijeet/Django_blog/42d2566313121814240b36301d888a9a0239bcbb/blog/migrations/__init__.py -------------------------------------------------------------------------------- /blog/models.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.models import User 2 | from django.db import models 3 | 4 | STATUS = ((0, "Draft"), (1, "Publish")) 5 | 6 | 7 | class Post(models.Model): 8 | title = models.CharField(max_length=200, unique=True) 9 | slug = models.SlugField(max_length=200, unique=True) 10 | author = models.ForeignKey( 11 | User, on_delete=models.CASCADE, related_name="blog_posts" 12 | ) 13 | updated_on = models.DateTimeField(auto_now=True) 14 | content = models.TextField() 15 | created_on = models.DateTimeField(auto_now_add=True) 16 | status = models.IntegerField(choices=STATUS, default=0) 17 | 18 | class Meta: 19 | ordering = ["-created_on"] 20 | 21 | def __str__(self): 22 | return self.title 23 | 24 | def get_absolute_url(self): 25 | from django.urls import reverse 26 | 27 | return reverse("post_detail", kwargs={"slug": str(self.slug)}) 28 | 29 | 30 | class Comment(models.Model): 31 | post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name="comments") 32 | name = models.CharField(max_length=80) 33 | email = models.EmailField() 34 | body = models.TextField() 35 | created_on = models.DateTimeField(auto_now_add=True) 36 | active = models.BooleanField(default=False) 37 | 38 | class Meta: 39 | ordering = ["created_on"] 40 | 41 | def __str__(self): 42 | return "Comment {} by {}".format(self.body, self.name) 43 | -------------------------------------------------------------------------------- /blog/sitemaps.py: -------------------------------------------------------------------------------- 1 | from django.contrib.sitemaps import Sitemap 2 | 3 | from .models import Post 4 | 5 | 6 | class PostSitemap(Sitemap): 7 | changefreq = "weekly" 8 | priority = 0.8 9 | 10 | def items(self): 11 | return Post.objects.filter(status=1) 12 | 13 | def lastmod(self, obj): 14 | return obj.updated_on 15 | 16 | # def location(self, item): 17 | # return reverse(item) 18 | -------------------------------------------------------------------------------- /blog/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /blog/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import include, path 2 | 3 | from . import views 4 | from .feeds import AtomSiteNewsFeed, LatestPostsFeed 5 | 6 | urlpatterns = [ 7 | path("feed/rss", LatestPostsFeed(), name="post_feed"), 8 | path("feed/atom", AtomSiteNewsFeed()), 9 | path("", views.PostList.as_view(), name="home"), 10 | # path('/', views.PostDetail.as_view(), name='post_detail'), 11 | path("/", views.post_detail, name="post_detail"), 12 | ] 13 | -------------------------------------------------------------------------------- /blog/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import get_object_or_404, render 2 | from django.views import generic 3 | 4 | from .forms import CommentForm 5 | from .models import Post 6 | 7 | 8 | class PostList(generic.ListView): 9 | queryset = Post.objects.filter(status=1).order_by("-created_on") 10 | template_name = "index.html" 11 | paginate_by = 3 12 | 13 | 14 | # class PostDetail(generic.DetailView): 15 | # model = Post 16 | # template_name = 'post_detail.html' 17 | 18 | 19 | def post_detail(request, slug): 20 | template_name = "post_detail.html" 21 | post = get_object_or_404(Post, slug=slug) 22 | comments = post.comments.filter(active=True).order_by("-created_on") 23 | new_comment = None 24 | # Comment posted 25 | if request.method == "POST": 26 | comment_form = CommentForm(data=request.POST) 27 | if comment_form.is_valid(): 28 | 29 | # Create Comment object but don't save to database yet 30 | new_comment = comment_form.save(commit=False) 31 | # Assign the current post to the comment 32 | new_comment.post = post 33 | # Save the comment to the database 34 | new_comment.save() 35 | else: 36 | comment_form = CommentForm() 37 | 38 | return render( 39 | request, 40 | template_name, 41 | { 42 | "post": post, 43 | "comments": comments, 44 | "new_comment": new_comment, 45 | "comment_form": comment_form, 46 | }, 47 | ) 48 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /mysite/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheAbhijeet/Django_blog/42d2566313121814240b36301d888a9a0239bcbb/mysite/__init__.py -------------------------------------------------------------------------------- /mysite/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for mysite project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.1.7. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.1/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | TEMPLATES_DIR = os.path.join(BASE_DIR, "templates") 18 | 19 | 20 | # Quick-start development settings - unsuitable for production 21 | # See https://docs.djangoproject.com/en/2.1/howto/deployment/checklist/ 22 | 23 | # SECURITY WARNING: keep the secret key used in production secret! 24 | SECRET_KEY = "0x!b#(1*cd73w$&azzc6p+essg7v=g80ls#z&xcx*mpemx&@9$" 25 | 26 | # SECURITY WARNING: don't run with debug turned on in production! 27 | DEBUG = True 28 | 29 | ALLOWED_HOSTS = [] 30 | 31 | 32 | # Application definition 33 | 34 | INSTALLED_APPS = [ 35 | "django.contrib.admin", 36 | "django.contrib.auth", 37 | "django.contrib.contenttypes", 38 | "django.contrib.sessions", 39 | "django.contrib.messages", 40 | "django.contrib.staticfiles", 41 | "django.contrib.sitemaps", 42 | 'debug_toolbar', 43 | "blog", 44 | "crispy_forms", 45 | "django_summernote", 46 | ] 47 | 48 | 49 | 50 | 51 | MIDDLEWARE = [ 52 | "django.middleware.security.SecurityMiddleware", 53 | "django.contrib.sessions.middleware.SessionMiddleware", 54 | 'debug_toolbar.middleware.DebugToolbarMiddleware', 55 | "django.middleware.common.CommonMiddleware", 56 | "django.middleware.csrf.CsrfViewMiddleware", 57 | "django.contrib.auth.middleware.AuthenticationMiddleware", 58 | "django.contrib.messages.middleware.MessageMiddleware", 59 | "django.middleware.clickjacking.XFrameOptionsMiddleware", 60 | ] 61 | 62 | ROOT_URLCONF = "mysite.urls" 63 | 64 | # Django Debug Toolbar https://django-debug-toolbar.readthedocs.io/en/latest/installation.html 65 | INTERNAL_IPS = [ 66 | '127.0.0.1', 67 | ] 68 | 69 | 70 | TEMPLATES = [ 71 | { 72 | "BACKEND": "django.template.backends.django.DjangoTemplates", 73 | "DIRS": [TEMPLATES_DIR], 74 | "APP_DIRS": True, 75 | "OPTIONS": { 76 | "context_processors": [ 77 | "django.template.context_processors.debug", 78 | "django.template.context_processors.request", 79 | "django.contrib.auth.context_processors.auth", 80 | "django.contrib.messages.context_processors.messages", 81 | ], 82 | }, 83 | }, 84 | ] 85 | 86 | WSGI_APPLICATION = "mysite.wsgi.application" 87 | 88 | 89 | # Database 90 | # https://docs.djangoproject.com/en/2.1/ref/settings/#databases 91 | 92 | DATABASES = { 93 | "default": { 94 | "ENGINE": "django.db.backends.sqlite3", 95 | "NAME": os.path.join(BASE_DIR, "db.sqlite3"), 96 | } 97 | } 98 | 99 | 100 | # Password validation 101 | # https://docs.djangoproject.com/en/2.1/ref/settings/#auth-password-validators 102 | 103 | AUTH_PASSWORD_VALIDATORS = [ 104 | { 105 | "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", 106 | }, 107 | { 108 | "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", 109 | }, 110 | { 111 | "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", 112 | }, 113 | { 114 | "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", 115 | }, 116 | ] 117 | 118 | 119 | # Internationalization 120 | # https://docs.djangoproject.com/en/2.1/topics/i18n/ 121 | 122 | LANGUAGE_CODE = "en-us" 123 | 124 | TIME_ZONE = "UTC" 125 | 126 | USE_I18N = True 127 | 128 | USE_L10N = True 129 | 130 | USE_TZ = True 131 | 132 | 133 | # Static files (CSS, JavaScript, Images) 134 | # https://docs.djangoproject.com/en/2.1/howto/static-files/ 135 | 136 | STATIC_URL = "/static/" 137 | 138 | # Location of static files 139 | STATICFILES_DIRS = [ 140 | os.path.join(BASE_DIR, "static"), 141 | ] 142 | 143 | 144 | STATIC_ROOT = os.path.join(BASE_DIR, "staticfiles") 145 | 146 | 147 | CRISPY_TEMPLATE_PACK = "bootstrap4" 148 | 149 | 150 | # Media paths 151 | 152 | # Base url to serve media files 153 | MEDIA_URL = "/media/" 154 | 155 | # Path where media is stored 156 | MEDIA_ROOT = os.path.join(BASE_DIR, "media/") 157 | 158 | 159 | DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" 160 | X_FRAME_OPTIONS = "SAMEORIGIN" 161 | -------------------------------------------------------------------------------- /mysite/urls.py: -------------------------------------------------------------------------------- 1 | """mysite URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.1/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.conf import settings 17 | from django.conf.urls.static import static 18 | from django.contrib import admin 19 | from django.contrib.sitemaps.views import sitemap 20 | from django.urls import include, path 21 | import debug_toolbar 22 | 23 | from blog.sitemaps import PostSitemap 24 | 25 | sitemaps = { 26 | "posts": PostSitemap, 27 | } 28 | 29 | urlpatterns = [ 30 | path("admin/", admin.site.urls), 31 | path("", include("blog.urls"), name="blog-urls"), 32 | path("summernote/", include("django_summernote.urls")), 33 | path('__debug__/', include(debug_toolbar.urls)), 34 | path("sitemap.xml", sitemap, {"sitemaps": sitemaps}, name="sitemap"), 35 | ] 36 | 37 | if settings.DEBUG: 38 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 39 | -------------------------------------------------------------------------------- /mysite/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for mysite project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.4.1 2 | autopep8==1.5.7 3 | Django==3.2.12 4 | django-crispy-forms==1.13.0 5 | django-debug-toolbar==3.2.2 6 | django-summernote==0.8.11.6 7 | pycodestyle==2.8.0 8 | pytz==2021.3 9 | sqlparse==0.4.2 10 | toml==0.10.2 11 | -------------------------------------------------------------------------------- /static/css/base.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Roboto", sans-serif; 3 | font-size: 17px; 4 | background-color: #fdfdfd; 5 | } 6 | 7 | .shadow { 8 | box-shadow: 0 4px 2px -2px rgba(0, 0, 0, 0.1); 9 | } 10 | 11 | .btn-danger { 12 | color: #fff; 13 | background-color: #f00000; 14 | border-color: #dc281e; 15 | } 16 | 17 | .masthead { 18 | background: #3398E1; 19 | height: auto; 20 | padding-bottom: 15px; 21 | box-shadow: 0 16px 48px #E3E7EB; 22 | padding-top: 10px; 23 | } 24 | 25 | .card { 26 | box-shadow: 0 16px 48px #E3E7EB; 27 | } 28 | -------------------------------------------------------------------------------- /templates/base.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Django Central 11 | 12 | 13 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 51 | 52 | 53 | {% block content %} 54 | 55 | {% endblock content %} 56 | 57 | 58 |
59 |

Copyright © Django Central

60 |
61 | 62 | 65 | 68 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /templates/index.html: -------------------------------------------------------------------------------- 1 | {% extends "base.html" %} 2 | 3 | {% block content %} 4 | 5 | 6 |
7 |
8 |
9 |
10 |
11 |
12 |

Welcome to my awesome Blog

13 |

We Love Django As much as you do   14 |

15 |
16 |
17 |
18 |
19 | 20 |
21 | 22 |
23 |
24 | 25 | 26 |
27 | {% for post in post_list %} 28 |
29 |
30 |

{{ post.title }}

31 |

{{ post.author }} | {{ post.created_on}}

32 | 33 |

{{post.content|safe|slice:":200" }}

34 | Read More → 35 |
36 | 37 |
38 | {% endfor %} 39 | 40 |
41 | {% block sidebar %} 42 | {% include 'sidebar.html' %} 43 | {% endblock sidebar %} 44 |
45 |
46 | {% if is_paginated %} 47 | 48 | 57 | 58 | 59 | {% endif %} 60 | {%endblock%} 61 | -------------------------------------------------------------------------------- /templates/post_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} {% block content %} 2 | {% load crispy_forms_tags %} 3 | 4 |
5 |
6 |
7 |
8 |

{% block title %} {{ post.title }} {% endblock title %}

9 |

{{ post.author }} | {{ post.created_on }}

10 |

{{ post.content | safe }}

11 |
12 |
13 | {% block sidebar %} {% include 'sidebar.html' %} {% endblock sidebar %} 14 | 15 |
16 |
17 | 18 | {% with comments.count as total_comments %} 19 |

{{ total_comments }} comments

20 | 21 |

22 | {% endwith %} {% for comment in comments %} 23 |

24 | 25 |
26 |

27 | {{ comment.name }} 28 | 29 | {{ comment.created_on }} 30 | 31 |

32 | {{ comment.body | linebreaks }} 33 |
34 | 35 | {% endfor %} 36 |
37 |
38 |
39 |
40 | {% if new_comment %} 41 | 44 | {% else %} 45 |

Leave a comment

46 |
47 | {{ comment_form | crispy }} 48 | {% csrf_token %} 49 | 50 |
51 | {% endif %} 52 |
53 |
54 |
55 |
56 | 57 | {% endblock content %} 58 | -------------------------------------------------------------------------------- /templates/sidebar.html: -------------------------------------------------------------------------------- 1 | {% block sidebar %} 2 | 3 | 4 | 5 | 6 |
7 |
8 |
About Us
9 |
10 |

This awesome blog is made with our Favourite full stack Framework Django, 11 | follow up the tutorial to learn how we made it

12 | Know more 13 |
14 |
15 |
16 | 17 | {% endblock sidebar %} 18 | --------------------------------------------------------------------------------