Browse Source

Доделал комментарии и "регистрацию"

master
Дмитрий 3 years ago
parent
commit
2688a92a0a
  1. 6
      blog/comments/admin.py
  2. 15
      blog/comments/migrations/0001_initial.py
  3. 18
      blog/comments/migrations/0002_alter_comment_reply.py
  4. 57
      blog/comments/models.py
  5. 54
      blog/comments/views.py
  6. BIN
      blog/db.sqlite3
  7. 46
      blog/posts/templates/posts/post.html
  8. 7
      blog/posts/views.py
  9. 14
      blog/static/css/main.css
  10. 29
      blog/static/js/comment_reply.js
  11. 1
      blog/templates/base.html

6
blog/comments/admin.py

@ -1,3 +1,7 @@
from django.contrib import admin from django.contrib import admin
from .models import Comment
# Register your models here.
@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
pass

15
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 from django.db import migrations, models
import django.db.models.deletion import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration): class Migration(migrations.Migration):
@ -18,19 +19,21 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('author_name', models.CharField(max_length=60, verbose_name='Имя автора')), ('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( migrations.CreateModel(
name='Comment', name='Comment',
fields=[ fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('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()), ('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')), ('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')),
], ],
), ),
] ]

18
blog/comments/migrations/0002_alter_comment_reply.py

@ -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),
),
]

57
blog/comments/models.py

@ -1,20 +1,8 @@
from django.db import models from django.db import models
from posts.models import Post from posts.models import Post
from datetime import datetime
from django.utils import timezone 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): class CommentAuthor(models.Model):
"""Валидация автора коммента. """Валидация автора коммента.
@ -25,4 +13,47 @@ class CommentAuthor(models.Model):
""" """
author_name = models.CharField("Имя автора", max_length=60) 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]}"

54
blog/comments/views.py

@ -1,13 +1,17 @@
from django.shortcuts import render, reverse from django.shortcuts import render, reverse
from posts.models import Post from posts.models import Post
from django.http import Http404, HttpResponseRedirect from django.http import Http404, HttpResponseRedirect
from .models import Comment from .models import Comment, CommentAuthor
from datetime import datetime from datetime import datetime
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.db import IntegrityError
import hashlib
from typing import Union
def leave_comment(request, post_id): def leave_comment(request, post_id):
parent: Comment parent: Union[Comment, None]
try: try:
post = Post.objects.get(id=post_id) post = Post.objects.get(id=post_id)
@ -30,10 +34,46 @@ def leave_comment(request, post_id):
except ObjectDoesNotExist: except ObjectDoesNotExist:
parent = None parent = None
post.comment_set.create( # обработка секрета Перед записью в базу, нужно извлечь записи по
author_name=request.POST["name"], # нику. Где поле author_secret_hash не null, нужно вычислить и
comment_text=request.POST["text"], # сравнить хэши; если хеши валидные то пишем в базу (еще раз уник не запишется);
reply=parent,
) # если не передан секрет, то пользователь 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,))) 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

BIN
blog/db.sqlite3

Binary file not shown.

46
blog/posts/templates/posts/post.html

@ -1,5 +1,6 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load tz %} {% load tz %}
{% load static %}
{% block title %}{{ post.title }}{% endblock %} {% block title %}{{ post.title }}{% endblock %}
{% block content %} {% block content %}
{% autoescape off %} {% autoescape off %}
@ -17,44 +18,63 @@
<div class="subtitle is-6">Опубликован: {{ post.pub_date | date:'Y-m-d, H:i' }}</div> <div class="subtitle is-6">Опубликован: {{ post.pub_date | date:'Y-m-d, H:i' }}</div>
<div class="content is-medium">{{ post.text }}</div> <div class="content is-medium">{{ post.text }}</div>
<br> <hr>
<p>Комменты</p> <h2 class="title is-5">Комментарии ({{ comments_count }})</h2>
{% for comment in comments %} {% for comment in comments %}
<div class="box"> <div class="box">
{% if comment.reply %} {% if comment.reply %}
<p>[{{comment.id}}] <strong>{{ comment.author_name }}</strong> отвечает {{ comment.reply.author_name }}:</p> <p><strong id="{{ comment.id }}_author_name">{% if comment.is_anonym %}
{{ comment.nickname }}
{% else %}
<span class="is-auth-user" title="Этот пользователь ввел имя и хеш">{{ comment.author }}</span>
{% endif %}
</strong> отвечает {{ comment.reply.author_name }}:</p>
<div class="content"> <div class="content">
<blockquote>{{ comment.reply.comment_text }}</blockquote> <blockquote>{{ comment.reply.comment_text }}</blockquote>
{% else %} {% else %}
<p>[{{comment.id}}] <strong>{{ comment.author_name }}</strong> говорит:</p> <p><strong id="{{ comment.id }}_author_name">
{% if comment.is_anonym %}
{{ comment.nickname }}
{% else %}
<span class="is-auth-user" title="Этот пользователь ввел имя и хеш">{{ comment.author }}</span>
{% endif %}
</strong> говорит:</p>
<div class="content"> <div class="content">
{% endif %} {% endif %}
<p>{{ comment.comment_text }}</p> <p>{{ comment.comment_text }}</p>
</div> </div>
<a href="javascript:reply()">Ответить</a> <a href="javascript:void(reply({{ comment.id }}));" id="comment_{{ comment.id }}">Ответить</a>
</div> </div>
{% endfor %} {% endfor %}
<form action="{% url 'comments:leave_comment' post.id %}" method="POST"> <form action="{% url 'comments:leave_comment' post.id %}" method="POST" id="comment_form">
{% csrf_token %} {% csrf_token %}
<input type="hidden" id="reply_to" name="reply_to">
<div class="field"> <div class="field">
<label class="label">Имя</label> <label class="label">Имя</label>
<input class="input" type="text" requiered placeholder="Имярек" name="name"> <input class="input" type="text" placeholder="Имярек" name="name" required>
</div> </div>
<div class="field"> <div class="field">
<label class="label">Секрет</label> <label class="label">Секрет</label>
<input class="input" type="text" requiered placeholder="пока что тут поле ответа" name="reply_to"> <input class="input" type="text" placeholder="секрет" name="secret" >
</div> </div>
</div> </div>
<div class="field"> <div class="field">
<label class="label">Комментарий</label>
<label class="label" id="reply_to_name_field" style="display: none;">
<!-- <a class="button is-danger is-small" href="javascript:abort_reply();">X</a> -->
<a class="is-red" href="javascript:abort_reply();" title="Отменить создание ответа, просто написать комментарий">X</a>
Ответ для <span id="reply_to_name" class="is-green"></span></label>
<label class="label" id="pure_comment_label">Комментарий</label>
<div class="control"> <div class="control">
<textarea class="textarea" name='text' requiered placeholder="Текст"></textarea> <textarea class="textarea" name='text' placeholder="Текст" id="comment_text_form" required></textarea>
</div> </div>
</div> </div>
<div class="control"> <div class="control">
@ -63,6 +83,10 @@
</form> </form>
</section> </section>
<script type="text/javascript" src="{% static 'js/comment_reply.js' %}"></script>
{% endautoescape %} {% endautoescape %}
{% endblock %} {% endblock %}

7
blog/posts/views.py

@ -18,4 +18,9 @@ def detail(request, post_id):
# вынести отсюда все. Также надо обработать текст поста тут. # вынести отсюда все. Также надо обработать текст поста тут.
post = get_object_or_404(Post, pk=post_id) post = get_object_or_404(Post, pk=post_id)
comments = post.comment_set.all() 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},
)

14
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;
}

29
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";
}

1
blog/templates/base.html

@ -8,6 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="HTML5 Example Page"> <meta name="description" content="HTML5 Example Page">
<link rel="stylesheet" href="{% static 'css/bulma.css' %}"> <link rel="stylesheet" href="{% static 'css/bulma.css' %}">
<link rel="stylesheet" href="{% static 'css/main.css' %}">
</head> </head>
<body> <body>
{% block content %}{% endblock %} {% block content %}{% endblock %}

Loading…
Cancel
Save