Автор оригинала: Arun Ravindran.
Вы читаете пост из серии руководств, состоящей из четырех частей.
- Часть 1
- Часть 2
- Часть 3
- Часть 4
Да, все хорошее когда-нибудь заканчивается. Становится еще лучше, когда хороший финал. Steel Rumors – это проект, который помогал новичкам в Django перейти на следующий уровень с базовых руководств. В нем были элементы, которые были бы полезны для большинства практических сайтов, таких как регистрация пользователей и создание просмотров CRUD.
Честно говоря, я сам не люблю большинство видеоуроков, так как на их просмотр нужно много времени. Однако, если они приходят с полной расшифровкой, я могу бегло просмотреть текст и решить, есть ли в нем советы, которые стоит посмотреть на видео. Иногда стоит посмотреть на дополнительный комментарий, объясняющий контекст.
Итак, я решил создать Steel Rumors как нечто, что всем, включая меня, понравилось бы смотреть. Но оказывается, сделать стенограмму намного сложнее, чем записать быстрое видео. На самом деле, иногда это становится однообразным (я очень сочувствую тем, кто работает в Medical Transcription!).
Видео также трудно записывать в более длинном и сложном проекте, таком как этот. Я не исправляю ошибки или опечатки, которые делаю, поскольку их отладка представляет собой ценную возможность обучения для новичков. Но объединение всех деталей воедино при сохранении преемственности может стать невероятно сложным.
Также в большинстве руководств будет показано, как использовать самый популярный пакет или самый простой способ реализации функции. Я сознательно избегал этого, возможно, вдохновленный тем, что усердно изучил Python . Было бы нормально изобрести колесо с первого раза, потому что это поможет вам понять, как колеса работают на всю жизнь. Итак, несмотря на то, что многие комментарии говорят мне, что использовать X проще, чем Y, я придерживался альтернативы, которая помогает вам лучше всего учиться.
В этом руководстве мы рассмотрим некоторые интересные области, например, как заставить формы Django работать с AJAX и как работает простой алгоритм ранжирования. Как всегда, вы можете посмотреть видео, прочитать пошаговое описание ниже или следовать обоим.
Я бы рекомендовал просмотреть все предыдущие части перед просмотром этого видео.
Вы узнали много нового из этой серии видео? Тогда вам следует подписаться на мою готовящуюся к выпуску книгу «Создание сайта социальных новостей на Django» . В нем объясняется в стиле учиться у друга , как создаются веб-сайты, и постепенно рассматриваются сложные темы, такие как тестирование, безопасность, миграция баз данных и отладка.
Пошаговая инструкция
Это расшифровка видео. В части 3 мы создали сайт социальных новостей, где пользователи могут публиковать и комментировать слухи о “Человеке из стали” , но не может голосовать.
План части 4 скринкаста таков:
- Голосование с помощью FormView
- Голосование через AJAX
- Миксины
- Показать статус голосования
- Алгоритм ранжирования
- Фоновые задачи
Голосование с помощью FormView
Мы добавим кнопку голосования (со знаком плюс) к каждому заголовку. Нажатие на нее переключает статус пользователя “проголосовал” для ссылки, т.е. проголосовал или не голосовал. Самый безопасный способ реализовать это – использовать ModelForm
для нашей модели Vote
.
Добавьте новую форму в links/forms.py
:
from .models import Vote
...
class VoteForm(forms.ModelForm):
class Meta:
model Vote
Мы будем использовать другое общее представление под названием FormView
для обработки части представления этой формы. Добавьте эти строки в links/views.py
from django.shortcuts import redirect
from django.shortcuts import get_object_or_404
from django.views.generic.edit import FormView
from .forms import VoteForm
from .models import Vote
...
class VoteFormView(FormView):
form_class VoteForm
def form_valid(self, form):
link get_object_or_404(Link, pkform.data["link"])
user self.request.user
prev_votes Vote.objects.filter(voteruser, linklink)
has_voted (prev_votes.count() > 0)
if not has_voted:
# add vote
Vote.objects.create(voteruser, linklink)
print("voted")
else:
# delete vote
prev_votes[0].delete()
print("unvoted")
return redirect("home")
def form_invalid(self, form):
print("invalid")
return redirect("home")
Эти операторы печати скоро будут удалены, и они определенно не рекомендуются для производственного сайта.
Отредактируйте шаблон домашней страницы, чтобы добавить форму голосования для каждого заголовка. Добавьте строки со знаком ‘+’ (убрав знак ‘+’) в steelrumors/templates/links/link_list.html
:
{% for link in object_list %}
+
Добавьте это представление в steelrumours/urls.py
:
from links.views import VoteFormView
url(r'^vote/$', auth(VoteFormView.as_view()), name"vote"),
Обновите браузер, чтобы увидеть кнопки «+» на каждом заголовке. Вы также можете проголосовать за них. Но узнать статус голосования можно только с консоли.
Голосование с помощью AJAX
Вы уже скопировали статическую папку из набора вкусностей в предыдущей части. Но если вы этого не сделали, выполните этот шаг.
Создайте папку с именем ‘js’ в steelrumors/static
наших файлах javascript. Скопируйте в эту папку jquery и vote.js из пакета вкусностей.
mkdir steelrumors/static/js
cp /tmp/sr-goodies-master/static/js/* ~/proj/steelrumors/steelrumors/static/js/
Добавьте эти строки в steelrumors/templates/base.html
внутри блока
:
Steel Rumors
+
+
В views.py
удалите весь класс VoteFormView
и замените этими тремя классами. Мы используем миксин для реализации ответа JSON на наши запросы AJAX:
import json
from django.http import HttpResponse
...
class JSONFormMixin(object):
def create_response(self, vdictdict(), valid_formTrue):
response HttpResponse(json.dumps(vdict), content_type'application/json')
response.status 200 if valid_form else 500
return response
class VoteFormBaseView(FormView):
form_class VoteForm
def create_response(self, vdictdict(), valid_formTrue):
response HttpResponse(json.dumps(vdict))
response.status 200 if valid_form else 500
return response
def form_valid(self, form):
link get_object_or_404(Link, pkform.data["link"])
user self.request.user
prev_votes Vote.objects.filter(voteruser, linklink)
has_voted (len(prev_votes) > 0)
ret {"success": 1}
if not has_voted:
# add vote
v Vote.objects.create(voteruser, linklink)
ret["voteobj"] v.id
else:
# delete vote
prev_votes[0].delete()
ret["unvoted"] 1
return self.create_response(ret, True)
def form_invalid(self, form):
ret {"success": 0, "form_errors": form.errors }
return self.create_response(ret, False)
class VoteFormView(JSONFormMixin, VoteFormBaseView):
pass
Отображение состояния “Проголосовано”
Нам нужно какое-то указание, чтобы узнать, проголосовали за заголовок или нет. Для этого мы можем передать идентификаторы всех ссылок, за которые проголосовал вошедший в систему пользователь. Это можно передать как переменную контекста, т. Е. voted
.
Добавьте это в класс LinkListView
в links/views.py
:
class LinkListView(ListView):
...
def get_context_data(self, **kwargs):
context super(LinkListView, self).get_context_data(**kwargs)
if self.request.user.is_authenticated():
voted Vote.objects.filter(voterself.request.user)
links_in_page [link.id for link in context["object_list"]]
voted voted.filter(link_id__inlinks_in_page)
voted voted.values_list('link_id', flatTrue)
context["voted"] voted
return context
Снова измените шаблон домашней страницы. Добавьте строки со знаком ‘+’ (убрав знак ‘+’) в steelrumors/templates/links/link_list.html
:
{{ user.pk }}" />
+ {% if not user.is_authenticated %}
+
+ {% elif link.pk not in voted %}
+ {% else %}
+
+ {% endif %}
Теперь кнопка меняется в зависимости от проголосованного состояния заголовка. Попробуйте это в своем браузере с разными учетными записями пользователей.
Расчет рейтинга
Мы собираемся изменить порядок сортировки ссылок с наивысшей оценки на самую высокую. Добавьте новую функцию в models.py
для вычисления рейтинга:
from django.utils.timezone import now
...
class Link(models.Model):
...
def set_rank(self):
# Based on HN ranking algo at http://amix.dk/blog/post/19574
SECS_IN_HOUR float(60*60)
GRAVITY 1.2
delta now() - self.submitted_on
item_hour_age delta.total_seconds() // SECS_IN_HOUR
votes self.votes - 1
self.rank_score votes / pow((item_hour_age+2), GRAVITY)
self.save()
В том же файле измените критерии сортировки в классе LinkVoteCountManager
. Измененная строка отмечена знаком «+».
class LinkVoteCountManager(models.Manager):
def get_query_set(self):
return super(LinkVoteCountManager, self).get_query_set().annotate(
+ votesCount('vote')).order_by('-rank_score', '-votes')
Рейтинг вакансий
Подсчет баллов для всех ссылок, как правило, является периодической задачей, которая должна выполняться в фоновом режиме. Создайте в корне проекта файл rerank.py со следующим содержимым:
#!/usr/bin/env python
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "steelrumors.settings")
from links.models import Link
def rank_all():
for link in Link.with_votes.all():
link.set_rank()
import time
def show_all():
print "\n".join("%10s %0.2f" % (l.title, l.rank_score,
) for l in Link.with_votes.all())
print "----\n\n\n"
if __name__"__main__":
while 1:
print "---"
rank_all()
show_all()
time.sleep(5)
Это выполняется каждые 5 секунд на переднем плане.
Превратите это в фоновую работу
(nohup python -u rerank.py&)
tail -f nohup.out
Обратите внимание, что это очень упрощенная реализация фонового задания. Чтобы получить более надежное решение, ознакомьтесь с Celery .
Просмотр новостей
Приятно наблюдать, как рейтинги ссылок растут и падают. Это почти так же весело, как наблюдать за аквариумом, за исключением цифр. Но функция ранжирования set_rank
в models.py
имеет разрешение в час. Это делает его довольно скучным.
Чтобы увидеть более резкое изменение рейтингов, измените константу SECS_IN_HOUR
на небольшое значение, например 5.0. Теперь отправьте новую ссылку и смотрите, как очки падают, как камень!
Заключительные комментарии
Steel Rumors – далеко не полный клон Hacker News. Но он поддерживает голосование, отправку ссылок и регистрацию пользователей. Фактически, на данный момент это вполне пригодно.
Посмотрите сами демонстрацию Steel Rumors .
Надеюсь, вам понравилась эта серия руководств так же, как и мне, когда я их делал. Если вы где-то застряли, сначала проверьте исходный код github для справки. Ваши комментарии продолжаются!
Ресурсы
- Полный исходный код на Github