Evgenii Legotckoi
Evgenii LegotckoiҚыр. 30, 2016, 12:42 Т.Қ.

Джанго - 011-сабақ. Джанго көмегімен сайтқа түсініктемелер қосу

Мен Django сайтында түсініктемелерді енгізуді қолға алған кезде, мен Django түсініктемелерді енгізу үшін ешқандай модуль ұсынбағанына таң қалдым. Дәлірек айтқанда, ол оны бұрын берген, бұл django.contrib.comments модулі болды, бірақ 1.7 нұсқасында ол ескірген деп жарияланды және оны өзіңіз қиюды немесе Disqus сияқты нәрсені пайдалануды ұсынды. Жарайды, бұл код синтаксисін бөлектеуді де қолдайтын сияқты, бірақ... мақалаларда бір бөлектеу бар, түсініктемелерде басқа - бұл ұсқынсыз болады.

Сондықтан, біз өз велосипедімізді іске асырамыз және қателерімізді ұстаймыз.

Түсініктемелерді орындау үшін сізге қажет:

  • Жаңа үлгі қосыңыз, оны Пікір; деп атаймыз.
  • Пікір қосуды өңдейтін көріністі қосыңыз;
  • Түсініктеме енгізу формасын қосыңыз;
  • Ағаш құрылымын ұйымдастыру үшін Materialized Path тәсілін қолданыңыз;

Түсініктеме үлгісі

Түсініктеме үлгісі келесі өрістерді қамтиды:

  • жол - түбірге толық жолды қамтитын бүтін мәндердің массивін қамтиды. Материалданған жол мақаласында айтылғандай, бұл барлық негізгі элементтердің идентификаторы;
  • article_id - түсініктеме орналасқан мақаланың сыртқы кілті;
  • автор_id - түсініктеме авторының сыртқы кілті;
  • мазмұн - түсініктеменің өзі;
  • pub_date - түсініктеме жарияланған күні мен уақыты;

Сонымен қатар, жолдың ұзындығы бойынша түсініктеменің ығысу деңгейін анықтайтын get_offset() және санын анықтайтын get_col() әдістері берілген. түсініктеме алатын тордағы бағандар, сондай-ақ әкімші панелінде түсініктеме мазмұнының бір бөлігін көрсетуге жауап беретін __str__ , қайта анықталған әдіс.

Ауыстыру және бағандар саны беттегі түсініктемелердің ағаш көрінісін ұйымдастырады, бірақ ығысу 6 бағаннан аспайды, себебі тор қазіргі уақытта 12 бағанға бөлінген.

class Comment(models.Model):
    class Meta:
        db\_table = "comments"

    path = ArrayField(models.IntegerField())
    article\_id = models.ForeignKey(Article)
    author\_id = models.ForeignKey(User)
    content = models.TextField('Комментарий')
    pub\_date = models.DateTimeField('Дата комментария', default=timezone.now)

    def \_\_str\_\_(self):
        return self.content[0:200]

    def get\_offset(self):
        level = len(self.path) - 1
        if level > 5:
            level = 5
        return level

    def get\_col(self):
        level = len(self.path) - 1
        if level > 5:
            level = 5
        return 12 - level

urls.py файлы

Түсініктеме urls.py. файлында сипатталуы керек белгілі бір мекенжайға POST сұрауы арқылы сайтқа жіберіледі.

from django.conf.urls import url

from . import views

app\_name = 'post'
urlpatterns = [
    url(r'^(?P<article\_id>[0-9]+)/$', views.EArticleView.as\_view(), name='article'),
    url(r'^comment/(?P<article\_id>[0-9]+)/$', views.add\_comment, name='add\_comment'),
]

Көптеген мақалаларда айтқанымдай, мен мақалалар мен бөлімдермен білім модулінде жұмыс істеймін, бірақ бұл жолы мақалалардың URL мекенжайларын бөлімдерге тәуелді болмайтындай етіп біріктіру және индекстеу жоғалмауы үшін мақалаларды бөлек модульге ауыстырдым. егер мақала басқа бөлімге ауыстырылса. Енді біз post модулімен жұмыс істейміз. Пікір де сонда жіберіледі. Нәтижесінде, егер пайдаланушы ID = 12 бар мақалаларға түсініктеме берсе, түсініктеме post/comment/12 жолымен жіберіледі.

Түсініктеме формасы

Түсініктеме пішіні forms.py. бөлек файлға орналастырылады.

Пішінді өңдеу және түсініктемені сақтау көріністе орындалады, сондықтан мұнда сақтау әдісі жоқ. Бұл пішін тек түсініктеме енгізу және оны серверге жіберу үшін қызмет етеді.

from django import forms

from .models import Comment


class CommentForm(forms.Form):

    parent\_comment = forms.IntegerField(
        widget=forms.HiddenInput,
        required=False
    )

    comment\_area = forms.CharField(
        label="",
        widget=forms.Textarea
    )

ата-ана_түсініктеме өрісі өте маңызды, себебі ол мақаланың астындағы пікірлердің біріне жауап бергенде автоматты түрде толтырылатын ата-аналық түсініктеменің идентификаторын қамтиды. Пайдаланушы оны толтырмайды және ол жасырылады. Сонымен қатар, оны толтыру міндетті емес, өйткені түсініктеме тікелей мақалаға сілтеме жасай алады.

Белгілі бір түсініктемеге қатынас JavaScript сценарийі арқылы жасалады.

Өкілдік

Түсініктеме қосу үшін мен өзімді ешқандай көрініссіз әдіспен шектедім, әсіресе @login_required декораторын көрініске қарағанда әдіске тіркеу оңайырақ болғандықтан.

Мақаланы көрсетуге жауапты көрініс де өзгертілді, өйткені мақаланың берілген бетінің контекстіне түсініктеме пішінін қосу қажет болды, егер, әрине, пайдаланушы рұқсат етсе.

from django.views import View
from django.shortcuts import render\_to\_response, get\_object\_or\_404, redirect
from django.contrib import auth
from django.http import Http404
from django.contrib.auth.decorators import login\_required
from django.views.decorators.http import require\_http\_methods
from django.core.exceptions import ObjectDoesNotExist
from django.template.context\_processors import csrf

from knowledge.models import Article, Comment
from knowledge.forms import CommentForm


class EArticleView(View):
    template\_name = 'post/article.html'
    comment\_form = CommentForm

    def get(self, request, *args, **kwargs):
        article = get\_object\_or\_404(Article, id=self.kwargs['article\_id'])
        context = {}
        context.update(csrf(request))
        user = auth.get\_user(request)
        # Помещаем в контекст все комментарии, которые относятся к статье
        # попутно сортируя их по пути, ID автоинкрементируемые, поэтому
        # проблем с иерархией комментариев не должно возникать 
        context['comments'] = article.comment\_set.all().order\_by('path')
        context['next'] = article.get\_absolute\_url()
        # Будем добавлять форму только в том случае, если пользователь авторизован
        if user.is\_authenticated:
            context['form'] = self.comment\_form

        return render\_to\_response(template\_name=self.template\_name, context=context)

# Декораторы по которым, только авторизованный пользователь 
# может отправить комментарий и только с помощью POST запроса
@login\_required
@require\_http\_methods(["POST"])
def add\_comment(request, article\_id):

    form = CommentForm(request.POST)
    article = get\_object\_or\_404(Article, id=article\_id)

    if form.is\_valid():
        comment = Comment()
        comment.path = []
        comment.article\_id = article
        comment.author\_id = auth.get\_user(request)
        comment.content = form.cleaned\_data['comment\_area']
        comment.save()

        # Django не позволяет увидеть ID комментария по мы не сохраним его, 
        # хотя PostgreSQL имеет такие средства в своём арсенале, но пока не будем
        # работать с сырыми SQL запросами, поэтому сформируем path после первого сохранения
        # и пересохраним комментарий 
        try:
            comment.path.extend(Comment.objects.get(id=form.cleaned\_data['parent\_comment']).path)
            comment.path.append(comment.id)
        except ObjectDoesNotExist:
            comment.path.append(comment.id)

        comment.save()

    return redirect(article.get\_absolute\_url())

Түсініктемесі бар мақала үлгісі

Мен бет орналасуы үшін django-bootstrap3 модулін қолданатынымды айттым ба? Сондықтан, бұл үлгінің қалай орналастырылғанына таң қалмаңыз.

Түсініктемелер Bootstrap Grid жүйесіндегі тұрақты жолдар болып табылады және ағаш көрінісі бағандарды ауыстыру арқылы қол жеткізіледі.

Бұл жерде әрбір жолда id={{ comment.id }} болуы өте маңызды - бұл пайдаланушы мақалаға емес, біреуге түсініктеме берсе, жасырын пішін өрісінде ауыстырылатын мәннің дәл өзі. пікірлерден.

Дәл сол идентификатор JavaScript арқылы түсініктеме пішінін беттің айналасында жылжыту үшін пайдаланылады. Пішін show_comments_form() функциясы арқылы орналастырылады. Бұл функция әрбір түсініктеме үшін «Жауап беру» сілтеме өңдегішінде, сондай-ақ тек түсініктеме жазу үшін сілтеме өңдегішінде орналастырылады. Бұл функция jQuery. кітапханасын пайдаланады, сондықтан оны негізгі үлгіге қосуды ұмытпаңыз. Мен Bootstrap. көмегімен пайдаланылатын нұсқаны қосып жатырмын.

{% extends 'home/base.html' %}
{% load bootstrap3 %}
{% block page %}
    <article>
        <h1>{{ article.article\_title }}</h1>
        {{ article.article\_content|safe }}
    </article>
    <h2>Комментарии</h2>
    {% for comment in comments %}
        <a name="comment-{{ comment.id }}"></a>
        <div class="row" id="{{ comment.id }}">
            <div class="col-md-{{ comment.get\_col }} col-md-offset-{{ comment.get\_offset }}">
                <div class="panel panel-default">
                    <div class="panel-heading">
                        <strong>{{ comment.author\_id.get\_full\_name|default:comment.author\_id.username }}</strong>&nbsp;&nbsp;
                        {{ comment.pub\_date }}
                        <a href="#comment-{{ comment.id }}">#</a>
                    </div>
                    <div class="panel-body">
                        <div>{{ comment.content|safe }}</div>
                        {% if form %}<a class="btn btn-default btn-xs pull-right"
                                        onclick="return show\_comments\_form({{ comment.id }})">
                            {% bootstrap\_icon "share-alt" %}&nbsp;&nbsp;Ответить</a>
                        {% endif %}
                    </div>
                </div>
            </div>
        </div>
    {% endfor %}
    {% if form %}
        <h3 id="write\_comment"><a onclick="return show\_comments\_form('write\_comment')">Написать комментарий</a></h3>
        <form id="comment\_form" action="{% url 'post:add\_comment' article.id %}" method="post">
        {% csrf\_token %}
        {% bootstrap\_form form %}
        {% buttons %}
            <button type="submit" class="btn btn-primary">{% bootstrap\_icon "comment" %}&nbsp;&nbsp;Комментировать</button>
        {% endbuttons %}
        </form>
    {% else %}
        <div class="panel panel-warning">
            <div class="panel-heading">
                <h3 class="panel-title">Комментарии</h3>
            </div>
            <div class="panel-body">
                Только авторизованные пользователи могут оставлять комментарии.<br />
            </div>
        </div>
    {% endif %}
{% endblock %}

Пікірлерді бетке жылжыту үшін JavaScript


Жарайды, мұның бәрі жай ғана былық. Егер id = write_comment болса, онда бұл түсініктемеде бірінші деңгей бар және жасырын өріс бос болады және пішін "Пікір жазу" жазуының астына жылжытылады. Әйтпесе, толтырыңыз. жасырын өріске және оны түсініктеме астына қойыңыз , оның астында біз жауап береміз.

function show\_comments\_form(parent\_comment\_id)
{
    if (parent\_comment\_id == 'write\_comment')
    {
        $("#id\_parent\_comment").val('')
    }
    else
    {
        $("#id\_parent\_comment").val(parent\_comment\_id);
    }
    $("#comment\_form").insertAfter("#" + parent\_comment\_id);
}

Django үшін Timeweb хостының VDS-сервері ұсынамын.

Рекомендуем хостинг TIMEWEB
Рекомендуем хостинг TIMEWEB
Стабильный хостинг, на котором располагается социальная сеть EVILEG. Для проектов на Django рекомендуем VDS хостинг.

Ол саған ұнайды ма? Әлеуметтік желілерде бөлісіңіз!

bernar92
  • Жел. 6, 2017, 12:39 Т.Ж.

не совсем понятно как это реализовать куда импортировать view и urls подскажите !

Evgenii Legotckoi
  • Жел. 6, 2017, 3:18 Т.Ж.

views и urls должны присутсвовать в вашем приложении, например сайт в Django состоит из нескольких приложений, которые создаются через команду startapp, по умолчанию там всегда есть директории urls и views .

В моём случае это приложение post.
app_name = 'post'
Evgenii Legotckoi
  • Жел. 6, 2017, 3:22 Т.Ж.

Если говорить про то, куда в итоге подключить urls , то они должны быть подключены в главный файл urls, он должен располагаться там же, где и файл settings.py

bernar92
  • Жел. 6, 2017, 6:19 Т.Ж.

сделал немного по другому

class EArticleView(View):
    template_name = 'knowledge/article.html'
    comment_form = CommentForm

    def get(self, request,  *args, **kwargs):
        article = get_object_or_404(Article, id=self.kwargs['article_id'])
        context = {}
        context.update(csrf(request))
        user = auth.get_user(request)
        context['article'] = article
        # Помещаем в контекст все комментарии, которые относятся к статье
        # попутно сортируя их по пути, ID автоинкрементируемые, поэтому
        # проблем с иерархией комментариев не должно возникать
        context['comments'] = article.comment_set.all().order_by('path')
        context['next'] = article.get_absolute_url()
        # Будем добавлять форму только в том случае, если пользователь авторизован
        if user.is_authenticated:
            context['form'] = self.comment_form

        return render(request, template_name=self.template_name, context=context)

    # Декораторы по которым, только авторизованный пользователь
    # может отправить комментарий и только с помощью POST запроса
    @method_decorator(login_required)
    def post(self, request, *args, **kwargs):
        if request.method == 'POST':

            form = CommentForm(request.POST)
            article = get_object_or_404(Article, id=self.kwargs['article_id'])
            if form.is_valid():
                comment = Comment(
                    path=[],
                    article_id=article,
                    author_id=request.user,
                    content=form.cleaned_data['comment_area']
                )
                comment.save()

                # Django не позволяет увидеть ID комментария по мы не сохраним его,
                # хотя PostgreSQL имеет такие средства в своём арсенале, но пока не будем
                # работать с сырыми SQL запросами, поэтому сформируем path после первого сохранения
                # и пересохраним комментарий
                try:
                    comment.path.extend(Comment.objects.get(id=form.cleaned_data['parent_comment']).path)
                    comment.path.append(comment.id)
                    print('получилось')
                except ObjectDoesNotExist:
                    comment.path.append(comment.id)
                    print('не получилось')
                comment.save()
            return redirect(article.get_absolute_url())
как думаете так тоже хорошо ?

Evgenii Legotckoi
  • Жел. 6, 2017, 6:30 Т.Ж.
Да, так будет даже лучше, я на сайте уже обновил до такого вида код

Вот это уже не нужно
if request.method == 'POST':
Поскольку Вы и так используете метод post, то есть эта проверка избыточна.

Что касается древовидных комментариев, то я от них как видите отказался. Я просто добавляю ID комментария, на который был дан ответ. Древовидные комментарии в итоге оказались не очень удобны для сайта с вставками программного кода.
bernar92
  • Жел. 7, 2017, 4:24 Т.Ж.

есть визуальный пример ?


bernar92
  • Жел. 7, 2017, 4:30 Т.Ж.

так

Evgenii Legotckoi
  • Жел. 7, 2017, 4:47 Т.Ж.

Визуальный пример чего? комментариев?
При ответе на конкретный комментарий рядом с ником отвечающего будет стрелочка и указание ник другого пользователя. Который будет ссылкой на комментарий, на который был дан ответ.

ИМ
  • Ақп. 1, 2018, 1:58 Т.Қ.

Доброго времени суток Евгений. У меня ка то так получилось:

Не подскажете с чем может быть связано?
Evgenii Legotckoi
  • Ақп. 1, 2018, 4:20 Т.Қ.

Добрый день.

Скорее всего поля неправильно были получены. Что-то не так с запросом. То есть в полученном объекте значения values не были корректно забраны. Либо просто неправильно написали названия полей в шаблоне
ИМ
  • Қар. 15, 2018, 4:35 Т.Қ.

Доброго времени суток Евгений. Не подскажете что я делаю не так? Получаю ошибку такого характера:


Reverse for 'add_comment' with arguments '('',)' not found. 1 pattern(s) tried: ['comment\\/(?P<article_id>[0-9]+)\\/$']

Говорит что ошибка в views в этой строке:

return render(request, template_name=self.template_name, context=context) 







Evgenii Legotckoi
  • Қар. 16, 2018, 1:50 Т.Ж.

Добрый день!

шаблон не находит, или шаблон неправильно прописали, или тег шаблона неправильно написан, иных выводов сделать не могу, из того, что вы написали. трейсбек нужно смотреть. Создайте тему на форуме и выложить трейсбек с ошибкой.


progammist
  • Мамыр 20, 2020, 7:15 Т.Ж.

А какие меры можно принять, чтобы обеспечить защиту от спама?
И еще как можно реализовать, чтобы по комменты выводились в админке и их можно было отклонять/одобрять?

Evgenii Legotckoi
  • Мамыр 20, 2020, 4:07 Т.Қ.

Самая первая мера - это комментарии только для зарегистрированных пользователей.
Пока ресурс маленький, то и спамеров почти нет, которые готовы зарегистрироваться на сайте ради спам комментария.
Ну а для регистрации такие ограничения как подтверждение email и recaptcha.
У меня так спамеры активно начали появляться только когда посещаемость перевалила за 1500 в день.

Что касается одобрения и отклонения, то у меня просто есть поле moderation в каждой модели и action для админки, которые помечают контент как спам или модерированный контент. Но если учесть, что обычно спаммер регистрируется и только спамит. То я просто удаляю весь акканут со всем контентом спамера, а ег email заношу в чёрный список, чтобы с этого email регистрация больше не проходила. Для меня это вопрос двух действий и 10 секунд, а для спамера, который вручную этим занимается - это очень накладно.

Ну и чтобы голову не греть лишний раз, то у меня просто есть прокси модели, которые сразу фильтруют в админке только немодерированный контент.

progammist
  • Мамыр 21, 2020, 4:39 Т.Ж.
  • (өңделген)

поле moderation в каждой модели и action для админки, которые помечают контент как спам или модерированный контент.

А action - это функция одобрения/отклонения?

Evgenii Legotckoi
  • Мамыр 22, 2020, 2:43 Т.Ж.

У меня эта часть кода выведена в open source, смотрите здесь

progammist
  • Мамыр 28, 2020, 1:56 Т.Қ.

path = ArrayField(models.IntegerField())

ArrayField подходит только для postresql, а для mysql - выдает ошибку. Как можно переписать эту строку, чтобы на mysql работало?

Evgenii Legotckoi
  • Мамыр 28, 2020, 3:31 Т.Қ.

Я бы вам посоветовал выкинуть mysql на помойку, но вы меня наверное не послушаете.
Скорее или конвертировать последовательность Integer значений в строку и сохранять в обычный CharField или извращаться с ManyToManyField.
Даже не знаю, что из этих двух вариантов будет хуже. Один порожадает лишний оверхед с преобразованиями, а второй оверхед с запросами.
Ни то ни другое не адекватное, но при шлифовке наверное будет работать.

progammist
  • Мамыр 28, 2020, 3:42 Т.Қ.
  • (өңделген)

а в чем явное преимущество postgresql над mysql?)

Evgenii Legotckoi
  • Мамыр 28, 2020, 3:49 Т.Қ.

Он более функциональный и его функционал объективно лучше поддерживается Django.

Из первого, что приходит на ум:

  • Это наличие полей типа Array
  • Поддержка полей для JSON
  • Хорошая поддержка GIS функционала, также есть и батарейки соответствующие

Честно, я так сразу не вспомню, но когда сам задавался вопросом, то столкнулся с тем, что рекомендуют при разработке на Джанго использовать Postgres.

Вообще самая рекомендуемая связка стека - это Django/PostgreSQL/Nginx.

Как бы можно и MySQL, но вы скорее увидите на StackOverflow вопрос о том как сделать в MySQL так, как это делается в PostgreSQL, а не наоборот.
А для меня это уже повод задуматься.

b
  • Наурыз 21, 2022, 5:29 Т.Ж.

Извините, проверка функциональности, визуально здесь не увидел древовидной иерархии в комментах (вроде я тоже не дерево:))
зы спасибо за интересные статьи!

Evgenii Legotckoi
  • Сәуір 17, 2022, 5:36 Т.Қ.

В рамках развития сайта выпилил полностью. Ибо неудобно было поддерживать. Да и всё-таки не нравятся они мне из-за избыточной траты полезного пространства. Не говоря уже о том, что на мобильных платформах это выглядит отвратно.

NSProject
  • Маусым 15, 2022, 6:03 Т.Ж.

Меня интересует как экранировать или преобразовывать теги которые нежелательны при добавлении в бд. Ибо текст комментария выводится

{{ comment.text|safe }}

то есть как html. А без safe это просто набор текста и всё

Evgenii Legotckoi
  • Маусым 15, 2022, 6:50 Т.Ж.
NSProject
  • Маусым 15, 2022, 7:27 Т.Ж.

Плохо я однако пользовался поиском по сайту. Спасибо

Evgenii Legotckoi
  • Маусым 15, 2022, 7:28 Т.Ж.

Поиск не совсем хорошо работает, так что норм

Пікірлер

Тек рұқсаты бар пайдаланушылар ғана пікір қалдыра алады.
Кіріңіз немесе Тіркеліңіз
AD

C++ - Тест 004. Указатели, Массивы и Циклы

  • Нәтиже:50ұпай,
  • Бағалау ұпайлары-4
m
  • molni99
  • Қаз. 26, 2024, 1:37 Т.Ж.

C++ - Тест 004. Указатели, Массивы и Циклы

  • Нәтиже:80ұпай,
  • Бағалау ұпайлары4
m
  • molni99
  • Қаз. 26, 2024, 1:29 Т.Ж.

C++ - Тест 004. Указатели, Массивы и Циклы

  • Нәтиже:20ұпай,
  • Бағалау ұпайлары-10
Соңғы пікірлер
i
innorwallҚар. 11, 2024, 10:12 Т.Қ.
Django - Оқулық 055. Автоматты толтыру өрісі функциясын қалай жазу керек Freckles because of several brand names retin a, atralin buy generic priligy
i
innorwallҚар. 11, 2024, 6:23 Т.Қ.
QML - Сабақ 035. C++ қолданбай QML тілінде сандарды пайдалану priligy cvs 24 Together with antibiotics such as amphotericin B 10, griseofulvin 11 and streptomycin 12, chloramphenicol 9 is in the World Health Organisation s List of Essential Medici…
i
innorwallҚар. 11, 2024, 3:50 Т.Қ.
Qt/C++ - 052-сабақ. Qt аудио ойнатқышын AIMP стилінде теңшеу It decreases stress, supports hormone balance, and regulates and increases blood flow to the reproductive organs buy priligy online safe Promising data were reported in a PDX model re…
i
innorwallҚар. 11, 2024, 2:19 Т.Қ.
Үйінді сұрыптау алгоритмі The role of raloxifene in preventing breast cancer priligy precio
i
innorwallҚар. 11, 2024, 1:55 Т.Қ.
PyQt5 - Оқулық 006. QTableWidget-пен жұмыс buy priligy 60 mg 53 have been reported by Javanovic Santa et al
Енді форумда талқылаңыз
i
innorwallҚар. 11, 2024, 8:56 Т.Қ.
добавить qlineseries в функции buy priligy senior brother Chu He, whom he had known for many years
i
innorwallҚар. 11, 2024, 10:55 Т.Ж.
Всё ещё разбираюсь с кешем. priligy walgreens levitra dulcolax carbs The third ring was found to be made up of ultra relativistic electrons, which are also present in both the outer and inner rings
9
9AnonimҚаз. 25, 2024, 9:10 Т.Ж.
Машина тьюринга // Начальное состояние 0 0, ,<,1 // Переход в состояние 1 при пустом символе 0,0,>,0 // Остаемся в состоянии 0, двигаясь вправо при встрече 0 0,1,>…

Бізді әлеуметтік желілерде бақылаңыз