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 .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
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')),
],
),
]

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 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]}"

54
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

BIN
blog/db.sqlite3

Binary file not shown.

46
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 @@
<div class="subtitle is-6">Опубликован: {{ post.pub_date | date:'Y-m-d, H:i' }}</div>
<div class="content is-medium">{{ post.text }}</div>
<br>
<p>Комменты</p>
<hr>
<h2 class="title is-5">Комментарии ({{ comments_count }})</h2>
{% for comment in comments %}
<div class="box">
{% 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">
<blockquote>{{ comment.reply.comment_text }}</blockquote>
{% 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">
{% endif %}
<p>{{ comment.comment_text }}</p>
</div>
<a href="javascript:reply()">Ответить</a>
<a href="javascript:void(reply({{ comment.id }}));" id="comment_{{ comment.id }}">Ответить</a>
</div>
{% 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 %}
<input type="hidden" id="reply_to" name="reply_to">
<div class="field">
<label class="label">Имя</label>
<input class="input" type="text" requiered placeholder="Имярек" name="name">
<input class="input" type="text" placeholder="Имярек" name="name" required>
</div>
<div class="field">
<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 class="field">
<label class="label">Комментарий</label>
<div class="field">
<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">
<textarea class="textarea" name='text' requiered placeholder="Текст"></textarea>
<textarea class="textarea" name='text' placeholder="Текст" id="comment_text_form" required></textarea>
</div>
</div>
<div class="control">
@ -63,6 +83,10 @@
</form>
</section>
<script type="text/javascript" src="{% static 'js/comment_reply.js' %}"></script>
{% endautoescape %}
{% endblock %}

7
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},
)

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="description" content="HTML5 Example Page">
<link rel="stylesheet" href="{% static 'css/bulma.css' %}">
<link rel="stylesheet" href="{% static 'css/main.css' %}">
</head>
<body>
{% block content %}{% endblock %}

Loading…
Cancel
Save