diff --git a/.gitignore b/.gitignore index 97daa84..4f59cf1 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,7 @@ venv/ env/ ENV/ .env/ +.env # Testing .coverage diff --git a/Blog/2025/How I failed at writing a blog many times and what I would do differently if I could turn back time.md b/Blog/2025/How I failed at writing a blog many times and what I would do differently if I could turn back time.md index 6ce3679..90d8797 100644 --- a/Blog/2025/How I failed at writing a blog many times and what I would do differently if I could turn back time.md +++ b/Blog/2025/How I failed at writing a blog many times and what I would do differently if I could turn back time.md @@ -122,7 +122,7 @@ I’m finally focusing on what matters: **writing**. ![obsidian blog](https://miro.medium.com/v2/resize:fit:1400/1*Hfi8Lapum4YdQoFAzAiU2A.png) -My new ugly website. Source: [https://frodigo.com/Garage](https://frodigo.com/Garage) +My new ugly website. Source: [https://frodigo.com](https://frodigo.com) So now I’m writing — and I’ve made a deal with myself: no replatforming for the next year. diff --git a/Blog/2025/Why teaching kids how to program is not easy.md b/Blog/2025/Why teaching kids how to program is not easy.md index 03088cd..eee63d8 100644 --- a/Blog/2025/Why teaching kids how to program is not easy.md +++ b/Blog/2025/Why teaching kids how to program is not easy.md @@ -116,7 +116,5 @@ Teaching programming to kids requires luck, considerable strength, and abundant And may your child become an even better programmer than you! -If you are interested in learning how to teach kids programming, please check out my learning roadmap: [https://frodigo.com/Garage/Programming+for+Kids](https://frodigo.com/Garage/Programming+for+Kids) - --- *Published 07/04/2025* #blog #ProgrammingFundamentals #Python #BestPractices #ConceptExplanation #CaseStudy #Beginner diff --git a/Now/History/1. 25-03-2025.md b/Now/History/1. 25-03-2025.md index 3c6c98b..e452cd9 100644 --- a/Now/History/1. 25-03-2025.md +++ b/Now/History/1. 25-03-2025.md @@ -10,7 +10,6 @@ My son has repeatedly asked me what I do in my work, so I showed him some code a Connected resources: -- [Programming for Kids](Programming%20for%20Kids.md) - [Why teaching kids how to program is not easy](Why%20teaching%20kids%20how%20to%20program%20is%20not%20easy.md) --- diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/.env.example b/Projects/Testtrack/M1 - Django Learning/mysite/.env.example new file mode 100644 index 0000000..42b9fcf --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/.env.example @@ -0,0 +1,3 @@ +EMAIL_HOST_USER= +EMAIL_HOST_PASSWORD= +DEFAULT_FROM_EMAIL= diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/.gitignore b/Projects/Testtrack/M1 - Django Learning/mysite/.gitignore new file mode 100644 index 0000000..4c49bd7 --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/.gitignore @@ -0,0 +1 @@ +.env diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/admin.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/admin.py index a6157cd..095f9e4 100644 --- a/Projects/Testtrack/M1 - Django Learning/mysite/blog/admin.py +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/admin.py @@ -1,5 +1,5 @@ from django.contrib import admin -from .models import Post +from .models import Post, Comment @admin.register(Post) @@ -12,3 +12,10 @@ class PostAdmin(admin.ModelAdmin): date_hierarchy = 'published_at' ordering = ('status', 'published_at') show_facets = admin.ShowFacets.ALWAYS + + +@admin.register(Comment) +class CommentAdmin(admin.ModelAdmin): + list_display = ('name', 'email', 'post', 'created_at', 'active') + list_filter = ('active', 'created_at', 'updated_at') + search_fields = ('name', 'email', 'body') diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/feeds.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/feeds.py new file mode 100644 index 0000000..ba0971f --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/feeds.py @@ -0,0 +1,23 @@ +import markdown +from django.contrib.syndication.views import Feed +from django.template.defaultfilters import truncatewords_html +from django.urls import reverse_lazy +from .models import Post + + +class LatestPostsFeed(Feed): + title = "My Blog" + link = reverse_lazy('blog:post_list') + description = "Updates on the latest blog posts." + + def items(self): + return Post.published.all()[:5] + + def item_title(self, item): + return item.title + + def item_description(self, item): + return truncatewords_html(markdown.markdown(item.body), 30) + + def item_pubdate(self, item): + return item.published_at diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/forms.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/forms.py new file mode 100644 index 0000000..b98117e --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/forms.py @@ -0,0 +1,18 @@ +from django import forms +from .models import Comment + + +class EmailPostForm(forms.Form): + name = forms.CharField(max_length=25) + email = forms.EmailField() + to = forms.EmailField() + comments = forms.CharField( + required=False, + widget=forms.Textarea + ) + + +class CommentForm(forms.ModelForm): + class Meta: + model = Comment + fields = ('name', 'email', 'body') diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0002_alter_post_slug.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0002_alter_post_slug.py new file mode 100644 index 0000000..2f3c474 --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0002_alter_post_slug.py @@ -0,0 +1,19 @@ +# Generated by Django 5.2.7 on 2025-10-06 05:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='post', + name='slug', + field=models.SlugField( + max_length=250, unique_for_date='published_at'), + ), + ] diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0003_comment.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0003_comment.py new file mode 100644 index 0000000..ab10940 --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0003_comment.py @@ -0,0 +1,33 @@ +# Generated by Django 5.2.7 on 2025-10-06 13:05 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0002_alter_post_slug'), + ] + + operations = [ + migrations.CreateModel( + name='Comment', + fields=[ + ('id', models.BigAutoField(auto_created=True, + primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=80)), + ('email', models.EmailField(max_length=254)), + ('body', models.TextField()), + ('created_at', models.DateTimeField(auto_now_add=True)), + ('updated_at', models.DateTimeField(auto_now=True)), + ('active', models.BooleanField(default=True)), + ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, + related_name='comments', to='blog.post')), + ], + options={ + 'ordering': ('created_at',), + 'indexes': [models.Index(fields=['created_at'], name='blog_commen_created_4e025c_idx')], + }, + ), + ] diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0004_post_tags.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0004_post_tags.py new file mode 100644 index 0000000..f212111 --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/migrations/0004_post_tags.py @@ -0,0 +1,21 @@ +# Generated by Django 5.2.7 on 2025-10-07 11:52 + +import taggit.managers +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('blog', '0003_comment'), + ('taggit', '0006_rename_taggeditem_content_type_object_id_taggit_tagg_content_8fc721_idx'), + ] + + operations = [ + migrations.AddField( + model_name='post', + name='tags', + field=taggit.managers.TaggableManager( + help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags'), + ), + ] diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/models.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/models.py index 9d8e590..441ae16 100644 --- a/Projects/Testtrack/M1 - Django Learning/mysite/blog/models.py +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/models.py @@ -1,6 +1,8 @@ from django.db import models +from django.urls import reverse from django.utils import timezone from django.conf import settings +from taggit.managers import TaggableManager class PublishedManager(models.Manager): @@ -15,7 +17,10 @@ class Status(models.TextChoices): PUBLISHED = 'PB', 'Published' title = models.CharField(max_length=250) - slug = models.SlugField(max_length=250) + slug = models.SlugField( + max_length=250, + unique_for_date='published_at' + ) author = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, @@ -30,6 +35,7 @@ class Status(models.TextChoices): objects = models.Manager() published = PublishedManager() + tags = TaggableManager() class Meta: ordering = ('-published_at',) @@ -39,3 +45,37 @@ class Meta: def __str__(self): return self.title + + def get_absolute_url(self): + return reverse( + 'blog:post_detail', + args=[ + self.published_at.year, + self.published_at.month, + self.published_at.day, + self.slug + ] + ) + + +class Comment(models.Model): + post = models.ForeignKey( + Post, + on_delete=models.CASCADE, + related_name='comments' + ) + name = models.CharField(max_length=80) + email = models.EmailField() + body = models.TextField() + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) + active = models.BooleanField(default=True) + + class Meta: + ordering = ('created_at',) + indexes = [ + models.Index(fields=['created_at']), + ] + + def __str__(self): + return f'Comment by {self.name} on {self.post}' diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/sitemaps.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/sitemaps.py new file mode 100644 index 0000000..dbbd97a --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/sitemaps.py @@ -0,0 +1,13 @@ +from django.contrib.sitemaps import Sitemap +from .models import Post + + +class PostSitemap(Sitemap): + changefreq = 'weekly' + priority = 0.9 + + def items(self): + return Post.published.all() + + def lastmod(self, obj): + return obj.updated_at diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/base.html b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/base.html index 2e4bba1..ebc5f98 100644 --- a/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/base.html +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/base.html @@ -1,3 +1,4 @@ +{% load blog_tags %} {% load static %} @@ -9,11 +10,26 @@
- {% block content %}{% endblock %} + {% block content %}{% endblock %}
diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/comment.html b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/comment.html new file mode 100644 index 0000000..00c9b7f --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/comment.html @@ -0,0 +1,12 @@ +{% extends "blog/base.html" %} + +{% block title %}Add a comment{% endblock %} + +{% block content %} + {% if comment %} +

Your comment has been added.

+

Back to post

+ {% else %} + {% include "blog/post/includes/comment_form.html" %} + {% endif %} +{% endblock %} diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/detail.html b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/detail.html index 73f1030..188c0ae 100644 --- a/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/detail.html +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/detail.html @@ -1,4 +1,5 @@ {% extends "blog/base.html" %} +{% load blog_tags %} {% block title %}{{ post.title }}{% endblock %} @@ -7,5 +8,34 @@

{{ post.title }}

Published on {{ post.published_at }} by {{post.author}}

- {{ post.body|linebreaks }}

+ {{ post.body|markdown }} +

+ Share via email +

+

Similar posts

+ {% for post in similar_posts %} +

+ + {{ post.title }} + +

+ {% empty %} +

No similar posts found.

+ {% endfor %} + {% with comments.count as total_comments %} +

+ {{ total_comments }} Comment{{ total_comments|pluralize }} +

+ {% endwith %} + {% for comment in comments %} +
+

+ Comment {{ forloop.counter }} by {{ comment.name }} on {{ comment.created_at }} +

+ {{ comment.body|linebreaks }} +
+ {% empty %} +

No comments yet.

+ {% endfor %} + {% include "blog/post/includes/comment_form.html" %} {% endblock %} diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/includes/comment_form.html b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/includes/comment_form.html new file mode 100644 index 0000000..ed3e169 --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/includes/comment_form.html @@ -0,0 +1,14 @@ +

Add new comment

+
+
+ {{ form.name.as_field_group }} +
+
+ {{ form.email.as_field_group }} +
+ {{ form.body.as_field_group }} + {% csrf_token %} +

+ +

+
diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/latest_posts.html b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/latest_posts.html new file mode 100644 index 0000000..15060c6 --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/latest_posts.html @@ -0,0 +1,9 @@ + diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/list.html b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/list.html index 8f182df..cc95c82 100644 --- a/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/list.html +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/list.html @@ -1,20 +1,31 @@ {% extends "blog/base.html" %} +{% load blog_tags %} {% block title %}Blog Posts{% endblock %} {% block content %}

Blog Posts

+ {% if tag %} +

Posts tagged with "{{ tag.name }}"

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

- + {{ post.title }}

+

+ Tags: + {% for tag in post.tags.all %} + {{ tag.name }}{% if not forloop.last %}, {% endif %} + {% endfor %} +

Published on {{ post.published_at }} by {{post.author}}

- {{ post.body|truncatewords:30|linebreaks }}

+ {{ post.body|markdown|truncatewords_html:30 }}

{% empty %}

No posts available.

{% endfor %} + {% include "pagination.html" with page=page_obj %} {% endblock %} diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/share.html b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/share.html new file mode 100644 index 0000000..710fe0f --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/blog/post/share.html @@ -0,0 +1,20 @@ +{% extends "blog/base.html" %} + +{% block title %}Share Post{% endblock %} + +{% block content %} + +{% if sent %} +

Message has been sent.

+

+ Sending post {{ post.title }} was successful. +

+{% else %} +

Share Post: {{ post.title }} via email

+
+ {{ form.as_p }} + {% csrf_token %} + +
+{% endif %} +{% endblock %} diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/pagination.html b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/pagination.html new file mode 100644 index 0000000..beca7d1 --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templates/pagination.html @@ -0,0 +1,13 @@ + diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/templatetags/__init__.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/templatetags/blog_tags.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templatetags/blog_tags.py new file mode 100644 index 0000000..7eecdd5 --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/templatetags/blog_tags.py @@ -0,0 +1,30 @@ +import markdown +from django.utils.safestring import mark_safe +from django import template +from django.db.models import Count +from ..models import Post + +register = template.Library() + + +@register.simple_tag +def total_posts(): + return Post.published.count() + + +@register.inclusion_tag('blog/post/latest_posts.html') +def show_latest_posts(count=5): + latest_posts = Post.published.order_by('-published_at')[:count] + return {'latest_posts': latest_posts} + + +@register.simple_tag() +def get_most_commented_posts(count=5): + return Post.published.annotate( + total_comments=Count('comments') + ).order_by('-total_comments')[:count] + + +@register.filter(name='markdown') +def markdown_format(text): + return mark_safe(markdown.markdown(text)) diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/urls.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/urls.py index d560190..2d67423 100644 --- a/Projects/Testtrack/M1 - Django Learning/mysite/blog/urls.py +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/urls.py @@ -1,9 +1,26 @@ from django.urls import path from . import views +from .feeds import LatestPostsFeed app_name = 'blog' urlpatterns = [ - path('', views.post_list, name='post_list'), - path('/', views.post_detail, name='post_detail') + path('', views.PostListView.as_view(), name='post_list'), + path( + 'tag//', + views.PostListView.as_view(), + name='post_list_by_tag' + ), + path( + '////', + views.PostDetailView.as_view(), + name='post_detail' + ), + path('/share/', views.post_share, name='post_share'), + path( + '/comment/', + views.post_comment, + name='post_comment' + ), + path('feed/', LatestPostsFeed(), name='post_feed') ] diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/blog/views.py b/Projects/Testtrack/M1 - Django Learning/mysite/blog/views.py index 45eb8dc..c95647f 100644 --- a/Projects/Testtrack/M1 - Django Learning/mysite/blog/views.py +++ b/Projects/Testtrack/M1 - Django Learning/mysite/blog/views.py @@ -1,25 +1,121 @@ +from django.db.models import Count from django.shortcuts import get_object_or_404, render +from django.views.generic import ListView, DetailView +from django.views.decorators.http import require_POST +from django.core.mail import send_mail +from taggit.models import Tag from .models import Post +from .forms import EmailPostForm, CommentForm -def post_list(request): - posts = Post.published.all() +class PostListView(ListView): + context_object_name = 'posts' + paginate_by = 3 + template_name = 'blog/post/list.html' + + def get_queryset(self): + queryset = Post.published.all() + tag_slug = self.kwargs.get('tag_slug', None) + + if tag_slug: + tag = get_object_or_404(Tag, slug=tag_slug) + queryset = queryset.filter(tags__in=[tag]) + self.tag = tag + else: + self.tag = None + + return queryset + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['tag'] = self.tag + return context + + +class PostDetailView(DetailView): + model = Post + template_name = 'blog/post/detail.html' + context_object_name = 'post' + + def get_object(self, queryset=None): + return get_object_or_404( + Post, + status=Post.Status.PUBLISHED, + slug=self.kwargs['post'], + published_at__year=self.kwargs['year'], + published_at__month=self.kwargs['month'], + published_at__day=self.kwargs['day'] + ) + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + post = self.get_object() + + context['comments'] = post.comments.filter(active=True) + + context['form'] = CommentForm() + + post_tags_ids = post.tags.values_list('id', flat=True) + similar_posts = Post.published.filter( + tags__in=post_tags_ids).exclude(id=post.id) + similar_posts = similar_posts.annotate(same_tags=Count( + 'tags')).order_by('-same_tags', '-published_at')[:4] + + context['similar_posts'] = similar_posts + + return context + + +def post_share(request, post_id): + post = get_object_or_404( + Post, + id=post_id, + status=Post.Status.PUBLISHED + ) + sent = False + + if request.method == 'POST': + form = EmailPostForm(request.POST) + if form.is_valid(): + cd = form.cleaned_data + post_url = request.build_absolute_uri(post.get_absolute_url()) + subject = f"{cd['name']} recommends you read " f"{post.title}" + message = ( + f"Read {post.title} at {post_url}\n\n" + f"comments: {cd['name']}: {cd['comments']}" + ) + + send_mail( + subject, + message, + from_email=None, + recipient_list=[cd['to']] + ) + sent = True + else: + form = EmailPostForm() + return render( request, - 'blog/post/list.html', - {'posts': posts} - ) + 'blog/post/share.html', + {'post': post, 'form': form, 'sent': sent}) -def post_detail(request, id): +@require_POST +def post_comment(request, post_id): post = get_object_or_404( Post, - id=id, + id=post_id, status=Post.Status.PUBLISHED ) - + comment = None + form = CommentForm(data=request.POST) + if form.is_valid(): + comment = form.save(commit=False) + comment.post = post + comment.save() return render( request, - 'blog/post/detail.html', - {'post': post} + 'blog/post/comment.html', + {'post': post, 'form': form, 'comment': comment} ) diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/db.sqlite3 b/Projects/Testtrack/M1 - Django Learning/mysite/db.sqlite3 index 221ef97..ab04634 100644 Binary files a/Projects/Testtrack/M1 - Django Learning/mysite/db.sqlite3 and b/Projects/Testtrack/M1 - Django Learning/mysite/db.sqlite3 differ diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/mysite/settings.py b/Projects/Testtrack/M1 - Django Learning/mysite/mysite/settings.py index aec6ed2..625e0e6 100644 --- a/Projects/Testtrack/M1 - Django Learning/mysite/mysite/settings.py +++ b/Projects/Testtrack/M1 - Django Learning/mysite/mysite/settings.py @@ -11,6 +11,7 @@ """ from pathlib import Path +from decouple import config # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent @@ -27,6 +28,7 @@ ALLOWED_HOSTS = [] +SITE_ID = 1 # Application definition @@ -37,7 +39,10 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', - 'blog.apps.BlogConfig', + 'django.contrib.sites', + 'django.contrib.sitemaps', + 'taggit', + 'blog.apps.BlogConfig' ] MIDDLEWARE = [ @@ -121,3 +126,11 @@ # https://docs.djangoproject.com/en/5.2/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + + +EMAIL_HOST = 'smtp.gmail.com' +EMAIL_HOST_USER = config('EMAIL_HOST_USER') +EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD') +EMAIL_PORT = 587 +EMAIL_USE_TLS = True +DEFAULT_FROM_EMAIL = config('DEFAULT_FROM_EMAIL') diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/mysite/urls.py b/Projects/Testtrack/M1 - Django Learning/mysite/mysite/urls.py index b7eb578..2adfb9a 100644 --- a/Projects/Testtrack/M1 - Django Learning/mysite/mysite/urls.py +++ b/Projects/Testtrack/M1 - Django Learning/mysite/mysite/urls.py @@ -15,9 +15,17 @@ 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin +from django.contrib.sitemaps.views import sitemap from django.urls import path, include +from blog.sitemaps import PostSitemap urlpatterns = [ path('admin/', admin.site.urls), path('blog/', include('blog.urls', namespace='blog')), + path( + 'sitemap.xml', + sitemap, + {'sitemaps': {'posts': PostSitemap}}, + name='django.contrib.sitemaps.views.sitemap' + ), ] diff --git a/Projects/Testtrack/M1 - Django Learning/mysite/requirements.txt b/Projects/Testtrack/M1 - Django Learning/mysite/requirements.txt new file mode 100644 index 0000000..4c61f68 --- /dev/null +++ b/Projects/Testtrack/M1 - Django Learning/mysite/requirements.txt @@ -0,0 +1,3 @@ +python-decouple>=3.8 +django-taggit>=6.1.0 +markdown>=3.9.0