diff --git a/blog/comments/admin.py b/blog/comments/admin.py index 8c38f3f..f769d34 100644 --- a/blog/comments/admin.py +++ b/blog/comments/admin.py @@ -1,3 +1,7 @@ from django.contrib import admin +from .models import Comment -# Register your models here. + +@admin.register(Comment) +class CommentAdmin(admin.ModelAdmin): + pass diff --git a/blog/comments/migrations/0001_initial.py b/blog/comments/migrations/0001_initial.py index 568a138..c8ed12b 100644 --- a/blog/comments/migrations/0001_initial.py +++ b/blog/comments/migrations/0001_initial.py @@ -1,7 +1,8 @@ -# Generated by Django 4.0.3 on 2022-04-09 12:38 +# Generated by Django 4.0.3 on 2022-04-12 13:43 from django.db import migrations, models import django.db.models.deletion +import django.utils.timezone class Migration(migrations.Migration): @@ -18,19 +19,21 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('author_name', models.CharField(max_length=60, verbose_name='Имя автора')), - ('author_secret_hash', models.CharField(max_length=256, verbose_name='Хеш секрета')), + ('author_secret_hash', models.CharField(blank=True, max_length=256, null=True, verbose_name='Хеш секрета')), ], + options={ + 'unique_together': {('author_name', 'author_secret_hash')}, + }, ), migrations.CreateModel( name='Comment', fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('author_name', models.CharField(max_length=60, verbose_name='имя автора')), - ('author_secret', models.CharField(blank=True, max_length=128, verbose_name='секретная строка')), - ('reply', models.IntegerField(blank=True)), ('comment_text', models.TextField()), - ('date', models.DateTimeField()), + ('date', models.DateTimeField(default=django.utils.timezone.now)), + ('author', models.ForeignKey(default='аноним', on_delete=django.db.models.deletion.CASCADE, to='comments.commentauthor')), ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='posts.post')), + ('reply', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='comments.comment')), ], ), ] diff --git a/blog/comments/migrations/0002_alter_comment_reply.py b/blog/comments/migrations/0002_alter_comment_reply.py deleted file mode 100644 index cc7f019..0000000 --- a/blog/comments/migrations/0002_alter_comment_reply.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.0.3 on 2022-04-09 13:01 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('comments', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='comment', - name='reply', - field=models.IntegerField(null=True), - ), - ] diff --git a/blog/comments/models.py b/blog/comments/models.py index b3912fa..d067d97 100644 --- a/blog/comments/models.py +++ b/blog/comments/models.py @@ -1,20 +1,8 @@ from django.db import models from posts.models import Post -from datetime import datetime from django.utils import timezone -class Comment(models.Model): - """Класс для комента.""" - - post = models.ForeignKey(Post, on_delete=models.CASCADE) - author_name = models.CharField("имя автора", max_length=60) - author_secret = models.CharField("секретная строка", max_length=128, blank=True) - reply = models.ForeignKey("self", on_delete=models.SET_NULL, blank=True, null=True) - comment_text = models.TextField() - date = models.DateTimeField(default=timezone.now) - - class CommentAuthor(models.Model): """Валидация автора коммента. @@ -25,4 +13,47 @@ class CommentAuthor(models.Model): """ author_name = models.CharField("Имя автора", max_length=60) - author_secret_hash = models.CharField("Хеш секрета", max_length=256) + author_secret_hash = models.CharField( + "Хеш секрета", max_length=256, blank=True, null=True + ) + + def __str__(self): + return self.author_name + + class Meta: + # сделать сочетание полей уникальным + unique_together = (("author_name"),) + + def is_valid(self) -> bool: + if self.author_name and self.author_secret_hash: + return True + + +def get_anonym_id() -> int: + # тут бы еще проверку на существование такого пользователя. Если + # его нет - создать + obj, result = CommentAuthor.objects.get_or_create(author_name="Anonym") + return obj.id + + +class Comment(models.Model): + """Класс для комента.""" + + post = models.ForeignKey(Post, on_delete=models.CASCADE) + author = models.ForeignKey( + CommentAuthor, on_delete=models.CASCADE, default=get_anonym_id + ) + nickname = models.CharField("имя автора", max_length=60, blank=True, null=True) + reply = models.ForeignKey("self", on_delete=models.SET_NULL, blank=True, null=True) + comment_text = models.TextField() + date = models.DateTimeField(default=timezone.now) + + def is_anonym(self) -> bool: + if self.author.id == get_anonym_id(): + return True + + else: + return False + + def __str__(self): + return f"{self.author_name}: {self.comment_text[:100]}" diff --git a/blog/comments/views.py b/blog/comments/views.py index 6c8caea..0c8a2b2 100644 --- a/blog/comments/views.py +++ b/blog/comments/views.py @@ -1,13 +1,17 @@ from django.shortcuts import render, reverse from posts.models import Post from django.http import Http404, HttpResponseRedirect -from .models import Comment +from .models import Comment, CommentAuthor from datetime import datetime from django.core.exceptions import ObjectDoesNotExist +from django.db import IntegrityError +import hashlib + +from typing import Union def leave_comment(request, post_id): - parent: Comment + parent: Union[Comment, None] try: post = Post.objects.get(id=post_id) @@ -30,10 +34,46 @@ def leave_comment(request, post_id): except ObjectDoesNotExist: parent = None - post.comment_set.create( - author_name=request.POST["name"], - comment_text=request.POST["text"], - reply=parent, - ) + # обработка секрета Перед записью в базу, нужно извлечь записи по + # нику. Где поле author_secret_hash не null, нужно вычислить и + # сравнить хэши; если хеши валидные то пишем в базу (еще раз уник не запишется); + + # если не передан секрет, то пользователь Anonym + # если секрет не совпал, то польозхватель Anonym + + author = get_or_create_author(request.POST["name"], request.POST["secret"]) + nickname = request.POST["name"] + + if not author: + post.comment_set.create( + nickname=nickname, + comment_text=request.POST["text"], + reply=parent, + ) + else: + post.comment_set.create( + author=author, + nickname=nickname, + comment_text=request.POST["text"], + reply=parent, + ) return HttpResponseRedirect(reverse("posts:detail", args=(post.id,))) + + +def get_or_create_author(name: str, secret: str) -> Union[CommentAuthor, None]: + + if secret == "": + return None + + hash = hashlib.md5(secret.encode("utf-8")).hexdigest() + + try: + author, result = CommentAuthor.objects.get_or_create( + author_name=name, author_secret_hash=hash + ) + except IntegrityError: + # если имя/хеш не совпадают, то игнорируем такого автора и отображаем ником + author = None + + return author diff --git a/blog/db.sqlite3 b/blog/db.sqlite3 index 31134cd..61d65d2 100644 Binary files a/blog/db.sqlite3 and b/blog/db.sqlite3 differ diff --git a/blog/posts/templates/posts/post.html b/blog/posts/templates/posts/post.html index 42f0755..fd009c8 100644 --- a/blog/posts/templates/posts/post.html +++ b/blog/posts/templates/posts/post.html @@ -1,5 +1,6 @@ {% extends 'base.html' %} {% load tz %} +{% load static %} {% block title %}{{ post.title }}{% endblock %} {% block content %} {% autoescape off %} @@ -17,44 +18,63 @@
Опубликован: {{ post.pub_date | date:'Y-m-d, H:i' }}
{{ post.text }}
-
-

Комменты

+
+

Комментарии ({{ comments_count }})

{% for comment in comments %}
{% if comment.reply %} -

[{{comment.id}}] {{ comment.author_name }} отвечает {{ comment.reply.author_name }}:

+

{% if comment.is_anonym %} + {{ comment.nickname }} + {% else %} + {{ comment.author }} + {% endif %} + отвечает {{ comment.reply.author_name }}:

{{ comment.reply.comment_text }}
{% else %} -

[{{comment.id}}] {{ comment.author_name }} говорит:

+

+ {% if comment.is_anonym %} + {{ comment.nickname }} + {% else %} + {{ comment.author }} + {% endif %} + говорит:

{% endif %} +

{{ comment.comment_text }}

- Ответить + Ответить
{% endfor %} -
+ {% csrf_token %} +
- +
- +
-
- +
+ + + +
- +
@@ -63,6 +83,10 @@ + + + + {% endautoescape %} {% endblock %} diff --git a/blog/posts/views.py b/blog/posts/views.py index de993e5..42702c5 100644 --- a/blog/posts/views.py +++ b/blog/posts/views.py @@ -18,4 +18,9 @@ def detail(request, post_id): # вынести отсюда все. Также надо обработать текст поста тут. post = get_object_or_404(Post, pk=post_id) comments = post.comment_set.all() - return render(request, "posts/post.html", {"post": post, "comments": comments}) + comments_count = comments.count() + return render( + request, + "posts/post.html", + {"post": post, "comments": comments, "comments_count": comments_count}, + ) diff --git a/blog/static/css/main.css b/blog/static/css/main.css new file mode 100644 index 0000000..3d90349 --- /dev/null +++ b/blog/static/css/main.css @@ -0,0 +1,14 @@ +.is-red { + color: #eb2c65; +} + +.is-green { + color: #3846BA; + text-color: #3846BA; +} + +.is-auth-user { + color: #3FBE7B; + text-color: #3FBE7B; + +} diff --git a/blog/static/js/comment_reply.js b/blog/static/js/comment_reply.js new file mode 100644 index 0000000..f5300d2 --- /dev/null +++ b/blog/static/js/comment_reply.js @@ -0,0 +1,29 @@ +// Получить элемент e +function reply(el) { + + document.getElementById("reply_to").value = el; + + repl_name = document.getElementById(el + "_author_name").innerText + console.log(repl_name); + + document.getElementById("reply_to_name").innerText = repl_name; + + document.getElementById("comment_text_form").focus(); + + // показываем лейбл для ответа + document.getElementById("reply_to_name_field").style.display = "block"; + // скрываем лейбл для простого комментария + document.getElementById("pure_comment_label").style.display = "none"; +} + +function abort_reply() { + console.log("Галя, отмена!") + document.getElementById("reply_to").value = ''; + + // показываем лейбл для ответа + document.getElementById("reply_to_name_field").style.display = "none"; + // скрываем лейбл для простого комментария + document.getElementById("pure_comment_label").style.display = "block"; + + +} diff --git a/blog/templates/base.html b/blog/templates/base.html index 4506b62..b698cfd 100644 --- a/blog/templates/base.html +++ b/blog/templates/base.html @@ -8,6 +8,7 @@ + {% block content %}{% endblock %}