Цель лекции: реализовать возможность чтения твитов других пользователей, используя специальную кнопку "Читать"; рассмотреть воз-можность прекратить чтение твитов выбранного пользователя; создать модель пользователя для чтения твитов пользователями; создание страницы входа и выхода из системы.
Ключевые термины: твит, django, python, модель, шаблон, пользователь, читатель, данные, html, class, url база, файл, счетчик, профиль.
Наши пользователи уже могут находить новые твиты, обозревая хэш-теги и пользовательские страницы. давайте предоставим способ читать других пользователей и возможность видеть на своих индивидуальных страницах агрегированные твиты от всех пользователей, которых они читают.
Мы так же создадим страницу, где пользователи могут просматривать список самых популярных пользователей по числу читателей. Эта особенность важна для нашего приложения, потому что она меняет основную страницу с простой страницы приветствия до часто обновляемого списка пользователей, где пользователи могут найти самых читаемых пользователей и их интересные твиты.
Стратегия для внедрения этой особенности состоит в следующем:
Так давайте начнем.
Во-первых, мы добавим счетчик ретвитов для каждого твита и отслеживание всех твитов, которые отметил пользователь. Для реализации этого, мы используем модель данных UserModel.
Когда пользователь читает другого пользователя, нам нужно хранить следующую информацию в базу данных:
Это нужно, чтобы предотвратить двойное добавление для чтения на одного и того же пользователя.
Для этой цели, Мы создадим новую модель данных, назовем ее UserFollowers. Откроем файл user_profile/model.py и добавим в него следующий класс:
class UserFollower(models.Model):
user = models.ForeignKey(User, unique=True)
date = models.DateTimeField(auto_now_add=True)
count = models.IntegerField(default=1)
followers= models.ManyToManyField(User,related_name='followers')
def __unicode__(self):
return self.user.username
Эта модель данных выполняет очень важные функции, мы рассмотрим ее поля шаг за шагом.
Поле user - это внешний ключ, который отсылает к пользователю, который читает. Мы хотим, чтобы оно было уникальным, чтобы на одного и тот же пользователя не могло быть больше одного читателя.
Поле date имеет тип models.DateTimeField. Как можно догадаться по имени поля, оно нужно для хранения значения даты/времени. Аргумент auto_now_add указывает Django на то, чтобы автоматически устанавливалась текущая дата/время в это поле, когда объект этих данных будет создан в первый раз.
Поле count имеет тип models.IntegerField. Это поле хранит целочисленное значение. Используя параметр default=1, мы указываем Django на то, чтобы установить 1 в это поле, когда объект этих данных будет создан в первый раз.
Параметр followers типа models.ManyToManyField содержит список пользователей, которых читает данный пользователь.
Здесь параметр related_name=`followers` должен быть вторым парметром. И пользователь и читатель относятся к классу user, которые, если обратиться к по относительному имени, выдаст ошибку, такую как получающий доступ к полю user сталкивается с относительным m2m полем User.userfollowers_set.
После введения кода модели данных в файл user_profile/model.py, выполните следующую команду, чтобы создать его корреспондирующие таблицы в базе данных:
python manage.py syncdb
С этой командой мы сможем сохранить всю информацию, которая нам нужна для поддержки читателей.
Далее, мы создадим представление, в котором пользователи читают других пользователей, нажимая кнопку "Читать" по имени профиля.
Изменим страницу профиля пользователя, ведь если пользователь, который посетил вашу страницу вас не читает, у него должна быть активна кнопка чтения. Если же он вас читает, то у него должна быть активна кнопка отмены чтения.
Давайте отредактируем текущий профиль пользователя, profile.html
Добавим напротив имени пользователя иконку пользователя, Мы можем использовать глиф-иконки Bootstrap. Это набор иконок, поставляемых по умолчанию с Bootstrap.
<p class="navbar-text navbar-right">
<span class="glyphicon glyphicon-user"></span>
{{ user.username }}
</p>
{% endblock %}
Мы также сделаем дизайн нового текстового поля публикации твита на странице профиля. Обновленный файл profile.html будет выглядеть так:
{% extends "base.html" %}
{% block navbar %}
<p class="navbar-text navbar-left">
<span class="glyphicon glyphicon-user">
</span> {{ profile.username } </p>
{% endblock %}
{% block content %}
<div class="row clearfix">
<div class="col-md-6 col-md-offset-3 column">
<form id="search-form" action="post/" method="POST">{% csrf_token %}
<div class="input-group">
{{ form.text.errors }}
{{ form.text }}
{{ form.country.as_hidden }}
<span class="input-group-btn">
<button class="btn btn-default" type="submit">Post</button>
</span>
</div>
<!-- /input-group -->
</form>
</div>
<h1> </h1>
<div class="col-md-12 column">
{% for tweet in tweets %}
<div class="well">
<span>{{ tweet.text }}</span>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
Обновите файл forms.py для отрисовки новой формы:
class TweetForm(forms.Form):
text = forms.CharField(widget=forms.Textarea(attrs={'rows': 1, 'cols': 85, 'class':'form-control post-tweet', 'placeholder': 'Опубликовать новый твит'}), max_length=160)
country = forms.CharField(widget=forms.HiddenInput())
Обновленный элемент интерфейса будет выглядеть примерно так:
Для добавления возможности подписаться на пользователя, необходимо сначала создать другого пользователя. Мы осуществим это тем же самым способом, что и раньше, то есть используя интерфейс администратора Django.
Еще одна важная вещь, которую мы не можем оставить без внимания - это логин пользователя и регистрация. Будущие функции не могут использоваться без этого. Мы осуществим сначала функцию входа, а затем мы будем двигаться к функциональности чтения.
Для осуществления входа пользователя в систему, мы должны добавить URL-адреса по умолчанию для входа и регистрации. Мы будем добавлять следующие шаблоны URL-адресов в файл urls.py:
url(r'^login/$', 'django.contrib.auth.views.login'), url(r'^logout/$', 'django.contrib.auth.views.logout'),
Теперь наш файл urls.py выглядит следующим образом:
from django.conf.urls import patterns, include, url
from django.contrib import admin
from tweet.views import Index, Profile, PostTweet, HashTagCloud, Search, SearchHashTag, HashTagJson
admin.autodiscover()
urlpatterns = patterns('',
url(r'^$', Index.as_view()),
url(r'^user/(\w+)/$', Profile.as_view()),
url(r'^admin/', include(admin.site.urls)),
url(r'^user/(\w+)/post/$', PostTweet.as_view()),
url(r'^hashTag/(\w+)/$', HashTagCloud.as_view()),
url(r'^search/$', Search.as_view()),
url(r'^search/hashTag$',SearchHashTag.as_view())),
url(r'^hashtag.json$', HashTagJson.as_view()),
url(r'^login/$', 'django.contrib.auth.views.login'),
url(r'^logout/$', 'django.contrib.auth.views.logout'),
)
У представлений входа в систему и выхода из нее имена шаблонов по умолчанию, registration/login.html и registration/login_out.html соответственно. Поскольку эти представления специфичны для пользователя и не очень специфичны для повторного использования нашего приложения, мы создадим новый каталог шаблона/регистрации внутри проекта mytweets, используя следующую команду:
mkdir –p mytweets/templates/registration
Затем создадим простые страницы входа в систему и выхода из нее. Оформим следующий фрагмент кода в файл login.html.
{% extends "base.html" %}
{% block content %}
{% if form.errors %}
<p> Ваши логин и пароль не совпадают. Попробуйте еще. </p>
{% endif %}
<form method="post" action="{% url 'django.contrib.auth.views.login' %}">
{% csrf_token %}
<table>
<tr>
<td>{{ form.username.label_tag }}</td>
<td>{{ form.username }}</td>
</tr>
<tr>
<td>{{ form.password.label_tag }}</td>
<td>{{ form.password }}</td>
</tr>
</table>
<input type="submit" value="login"/>
<input type="hidden" name="next" value="{{ next }}"/>
</form>
{% endblock %}
Оформим следующий фрагмент кода в файл logout.html:
{% extends "base.html" %}
{% block content %}
Вы вышли из системы!
{% endblock %}
Мы просто активируем систему аутентификации Django по умолчанию. Так как это базовая аутентификация, то требуется предопределить URL для правильного перенаправления. Например мы уже знаем, что что /login принимает пользователя от html-страницы /registration/login. Аналогично, после того, как пользователь прошел проверку подлинности, перенаправляются на URL accounts/profile. В нашем проекте у нас есть пользовательские URL для каждого пользователя. Мы обновим эти записи в файле settings.py:
LOGIN_REDIRECT_URL = '/profile' LOGIN_URL = 'django.contrib.auth.views.login'
Для сохранения простоты, мы просто создаем представление, которое будет забирать пользователя, прошедшего аутентификацию из профиля и перенаправляющего этого пользователя на страницу его профиля. Главным образом, мы создаем параметр имени пользователя после его аутентификации. Иными словами, /profile | /profile/<username> генерируется в отдельное представление класса. Для этого нам нужно создать нижеследующую URL-запись:
url(r'^profile/$', UserRedirect.as_view()),
А перенаправляющий класс Profile с методом get() будет выглядеть как:
class UserRedirect(View):
def get(self, request):
return HttpResponseRedirect('/user/'+request.user.username)
Вот так. Теперь каждый пользователь, входящий в систему пользователь будет перенаправляться на его страницу профиля.
Теперь возвращаясь к исходной задаче, когда пользователь посещает профиль другого пользователя, у него должна быть возможность чтения профиля пользователя; это означает, что читатель будет получать обновления о всех опубликованных твитах на своей домашней странице.
После того, как пользователь начал читать, у него появляется возможность перестать читать, а если пользователь посетил свой собственный профиль, он не должен увидеть никаких вариантов.
Обновленный код для профиля пользователя выглядит следующим образом:
{% extends "base.html" %}
{% load staticfiles %}
{% block navbar %}
<p class="navbar-text navbar-left">
<span class="glyphicon glyphicon-user"> </span> {{ profile.username }}'s Profile Page
{% if profile.username != user.username %}
<span class="btn btn-xs btn-default follow-btn" title="Click to follow {{ profile.username }}" value="{{ following }}" username="{{ profile.username }}">
<span class="glyphicon glyphicon-plus"></span><span class="follow-text">
{{ following|yesno:"Unfollow,Follow" }}
</span></span>
{% endif %}
</p>
<p class="navbar-text navbar-right">
<span class="glyphicon glyphicon-user"></span> {{ user.username }}
</p>
{% endblock %}
{% block content %}
<div class="row clearfix">
<div class="col-md-6 col-md-offset-3 column">
<form id="search-form" action="post/" method="POST">{% csrf_token %}
<div class="input-group">
{{ form.text.errors }}
{{ form.text }}
{{ form.country.as_hidden }}
<span class="input-group-btn">
<button class="btn btn-default" type="submit">Post</button>
</span>
</div>
<!-- /input-group -->
</form>
</div>
<h1> </h1>
<div class="col-md-12 column">
{% for tweet in tweets %}
<div class="well">
<span>{{ tweet.text }}</span>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
Следующий код проверяет, просматривает ли пользователь свой собственный профиль; Если это так, кнопка чтения не отображается. Он также проверяет находится ли пользователь вошедший в систему в профиле пользователя, который тот читает; если это так, будет показана кнопка прекращения чтения, а если нет, будет показана кнопка чтения.
{% if profile.username != user.username %}
<span class="btn btn-xs btn-default follow-btn" title="Click to follow {{ profile.username }}" value="{{ following }}" username="{{ profile.username }}">
<span class="glyphicon glyphicon-plus"></span><span class="follow-text">
{{ following|yesno:"Unfollow,Follow" }}
</span></span>
{% endif %}
Для отрисовки обновленного содержимого, обновим класс Profile() соответственно:
class Profile(LoginRequiredMixin, View):
"""User Profile page reachable from /user/<username> URL"""
def get(self, request, username):
params = dict()
userProfile = User.objects.get(username=username)
userFollower = UserFollower.objects.get(user=userProfile)
if userFollower.followers.filter(username=request.user.username).exists():
params["following"] = True
else:
params["following"] = False
except:
userFollower = []
form = TweetForm(initial={'country': 'Global'})
search_form = SearchForm()
tweets = Tweet.objects.filter(user=userProfile).order_by('-created_date')
params["tweets"] = tweets
params["profile"] = userProfile
params["form"] = form
params["search"] = search_form
return render(request, 'profile.html', params)
Следующий код проверяет, является ли пользователь, вошедший в систему, читателем пользователя, страницу которого он посетил:
if userFollower.followers.filter (username=request.user.username).exists()
Давайте создадим метод post() для профиля, чтобы добавлять и удалять читателей, основано на параметрах:
def post(self, request, username):
follow = request.POST['follow']
user = User.objects.get(username= request.user.username)
userProfile = User.objects.get(username=username)
userFollower,status=UserFollower.objects.get_or_create(user=userProfile)
userFollower.count += 1
userFollower.save()
if follow == 'true':
#follow user
userFollower.followers.add(user)
else:
#unfollow user
userFollower.followers.remove(user)
return HttpResponse(json.dumps(""),
content_type="application/json")
Это простая функция, которая проверяет параметры, чтобы добавлять или удалять пользователей из списка читателей.
Часть кнопки подписки в файле profile.html должны быть обновлены с именами классов, так, что мы можем переключать функциональность событий JavaScript, как показано ниже:
<p class="navbar-text navbar-left">
<span class="glyphicon glyphicon-user"> </span> {{ profile.username }}'s Profile Page
{% if profile.username != user.username %}
<span class="btn btn-xs btn-default follow-btn" title="Click
to follow {{ profile.username }}" value="{{ following }}"
username="{{ profile.username }}">
<span class="glyphicon glyphicon-plus"></span><span class="follow-text">
{{ following|yesno:"Unfollow,Follow" }}
</span></span>
{% endif %}
</p>
Наконец, создадим файл Javascript c именем profile.js, который вызывает метод post () в зависимости от нажатой кнопки "Читать"/ "Перестать читать":
$(".follow-btn").click(function () {
var username = $(this).attr('username');
var follow = $(this).attr('value') != "True";
$.ajax({
type: "POST",
url: "/user/"+username+"/",
data: { username: username , follow : follow },
success: function () {
window.location.reload();
},
error: function () {
alert("ERROR !!");
}
})
});
Не забудьте добавить этот файл JavaScript в файл profile.html в конец страницы, как показано следующим кодом:
{% block js %}
<script src="{% static 'js/profile.js' %}"></script>
{% endblock %}
После того, как мы реализовали функцию для читателей, мы можем двигаться дальше с новым дизайном страницы где мы отобразим список пользователей, на которые наиболее часто подписываются. Логика этой страницы могут быть использованы для разработки страницы, на которой отображается наибольшее количество комментариев.
Основные компоненты для дизайна этой страницы будут:
Добавьте следующее содержимое в файл users.html:
{% extends "base.html" %}
{% load staticfiles %}
{% block navbar %}
<p class="navbar-text navbar-right">
<span class="glyphicon glyphicon-user"x/span>
{{ user.username }}
</p>
{% endblock %}
{% block content %}
<div class="row clearfix">
<div class="col-md-12 column">
{% for userFollower in UserFollowers %}
<div class="well">
<span class="username">{{ userFollower.user.username }}</span>
<span class="count text-muted"> ({{ userFollower.count }} followers)</span>
</div>
{% endfor %}
</div>
</div>
{% endblock %}
И следующий класс в контроллер:
class MostFollowedUsers(View):
def get(self, request):
userFollowers = UserFollower.objects.order_by('-count')[:2]
params = dict()
params['userFollowers'] = userFollowers
return render(request, 'users.html', params)
Следующая строка сортирует пользователей по количеству читателей:
userFollowers = UserFollower.objects.order_by('-count1)
Нам нужно обновить URL-представление, как показано ниже:
url(r’^mostFollowed/$', MostFollowedUsers.as_view()),
Вот и все! Мы сделали страницу со списком пользователей по числу читателей. Если подсчет затягивается, можно ограничить список с помощью базового синтаксиса списка Python:
userFollowers = UserFollower.objects.order_by('-count1) [: 10]
Будет выведен топ-10 пользователей.
Контрольные вопросы
Упражнения
Упражнение 1.
Поменяйте иконку пользователя
Упражнение 2.
Доработайте систему комментирования
Упражнение 3.
Добавьте возможность отображения общего количества твитов для конкретного пользователя
Упражнение 4.
Доработайте страницу входа пользователя с тем, чтобы была возможность проверки
Список тем курсовых работ
Краткие итоги
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.