Материал: Разработка архитектуры веб-сервиса закрытой социальной сети с использованием фреймворка Django

Внимание! Если размещение файла нарушает Ваши авторские права, то обязательно сообщите нам

Рисунок 8 - Поисковая форма

При нажатии на ссылку "Добавить в друзья", искомый пользователь заносится в модель Profile в поле friends (ManyToManyField) и у, добавляемого в друзья, пользователя в поле friend_requests модели Profile отмечается тот пользователь, который только что прошел по ссылке "Добавить в друзья" (Рис. 9).

Рисунок 9 - Административная часть. Поле ManyToManyField - friend_requests. В этом поле выделяется синим пользователь, который хочет добавиться в друзья.

На странице у добавленного друга автоматически появится оповещение о пользователе, который его добавил в друзья и две ссылки "Принять друга" и "Отклонить друга".

После этих операций у вас появляется список пользователей, которых вы добавили себе в друзья, с возможностью удалить друга или написать ему сообщение (Рис. 10).

Рисунок 10 - Страница с моими друзьями и возможностью поиска новых.

Теперь рассмотрим функционал обмена сообщениями. При прохождении в меню по ссылки СООБЩЕНИЯ мы попадаем на страницу где показаны лишь чаты и их последние объекты из поля ManyToManyField - messages (Рис. 11).

Рисунок 11 - Страница сообщений. Отображение содержимого модели Chat.

Как мы видим на Рис. 11 отобразилось одно поле, которое содержит логин собеседника, дату последнего сообщения и само сообщение. Если кто то присылает сообщение, то возле ссылки СООБЩЕНИЯ появляется число, указывающее сколько новых сообщений (Рис. 12). Django позволяет самим создавать теги, описывая их действия на языке Python. Реализация этого шаблонного тега представлена в ПРИЛОЖЕНИИ В.

Рисунок 12 - Отображение появления нового сообщения.

В шаблоне вызов шаблонного тега осуществляется следующим образом:

<p>СООБЩЕНИЯ{% if user. id|message_count %} ({{ user. id|message_count}}) {% endif %}</p>

Далее заходим в СООБЩЕНИЯ и нажимаем на чат. Внизу чата видим что новое сообщение отображается синим (Рис.13). При обновлении страницы синий фон заменяется белым, как и у всех остальных сообщений.

Рисунок 13 - Отображение всех сообщений в данном чате.

При отправке нового сообщения, оно также будет становиться синим и у самого отправителя, до тех пор, пока получатель его не прочтет (Рис. 14).

Рисунок 14 - Отправка сообщения собеседнику.

Ну и теперь переходим к последней части сайта - НОВОСТИ.

Страница содержит сортировку по датам, отображение всех новостей и возможность добавлять свои новости (Рис. 15).

Рисунок 15 - Страница НОВОСТИ

Поля "Архивы" генерируются автоматически при добавлении новой новости с другим месяце и годом. При прохождении по одной из таких ссылок на страницу отображаются только те новости, у которых дата публикации обладает таким же месяцем и годом. При переходе по ссылке "Читать полностью >>", мы переходим на страницу, которая отображает всю новость, и внизу появляется поле для ввода комментария (Рис.16). Система отображения новых комментариев такая же как и у сообщений. Пока отправитель новости не прочтет новый комментарий, фон будет оставаться синим.

Рисунок 16 - Просмотр всей новости и комментариев к ней.

При нажатии на ссылку "Отправить сообщение", появляется форма для написания новости (Рис.17). Как мы уже говорили в разделе 3.1, если текст слишком велик, то пользователь может разбить его на две части, так что бы на главной странице новостей была видна только первая часть новости.

Рисунок 17 - Форма для заполнения новости.

Заключение


Результатом данной работы является разработка четырех приложений и построенная модель данных с помощью классов Python, по которой сгенерировалась схема базы данных. Данное приложение реализует все поставленные задачи.

Приложение будет полезно для крупных фирм, которым необходим постоянный и быстрый обмен информации, при этом минимизируя утечку информации за пределы данного сообщества. Написанное приложение можно легко привязать к любому сайту, разработанного с использованием фреймворка Django, который с каждым годом становится все более и более популярным.

Планируется дальнейшее развитие проекта - добавление фото - и видео - галереи, а также, с помощью API Яндекс. translate и API Яндекс. Карты, добавить перевод всего сайта и размещения на изображении местности различных графических объектов.

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

Список использованных источников


1.      Форсье Дж., Биссекс П., Чан У. Django. Разработка веб-приложений на Python. - Пер. с англ. - СПб.: Символ-Плюс, 2010. - 456 с., ил.

2.      Лутц М. Программирование на Python, том ӏ - ӏ ӏ, 4-е издание. - Пер. с англ. - СПб.: Символ-Плюс, 2011. - 992с., ил.

3.      Django - http://ru. wikipedia.org/wiki/Django <http://ru.wikipedia.org/wiki/Django>.

.        Object-Relational Mapping - http://ru. wikipedia.org/wiki/ORM.

.        Django 1.5 documentation - https: // docs. djangoproject.com/en/1.5/ <https://docs.djangoproject.com/en/1.5/>.

Приложение А


Файл manage. py

#! /usr/bin/env pythondjango. core. management import execute_managerimp:. find_module ('settings') # Assumed to be in the same directory.

except ImportError:sys. stderr. write ("Error: Can't find the file 'settings. py' in the directory containing %r. It appears you've customized things. \nYou'll have to run django-admin. py, passing it your settings module. \n" % __file__). exit (1)settings__name__ == "__main__":_manager (settings)

Файл setting. py

# - * - coding: utf-8 - *-

# Django settings for SA project.

DEBUG = True_DEBUG = DEBUG= (

# ('Your Name', 'your_email@example.com'),

)= ADMINS

"""

пароль и логин если я опять как обычно забуду

для админки

"""= {

'default': {

'ENGINE': 'django. db. backends. mysql', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'.

'NAME': 'kfnexus_kurs', # Or path to database file if using sqlite3.

'USER': '045320128_kurs', # Not used with sqlite3.

'PASSWORD': '12345', # Not used with sqlite3.

'HOST': '127.0.0.1', # Set to empty string for localhost. Not used with sqlite3.

'PORT': '3306', # Set to empty string for default. Not used with sqlite3.

}

}

"""

№ настройки для локалхсота_HOST = '192.168.137.12'_PORT = '1025'

"""_USE_TLS = True_HOST = 'smtp. jino.ru'_HOST_USER = 'test@epsilon-design.ru'_HOST_PASSWORD = 'test'_PORT = 587_ZONE = 'Europe/Moscow'_CODE = 'ru-ru'_ID = 1_I18N = True_L10N = True_URLCONF = 'SA. urls'

SITE_ROOT = '/home/users1/k/kfnexus/django/kurs2/SA/'_ROOT = '/home/users1/k/kfnexus/domains/kurs2. epsilon-design.ru/' + 'media/'_URL = '/media/'_ROOT = '/home/users1/k/kfnexus/domains/kurs2. epsilon-design.ru/' + 'static/'_URL = '/static/'_MEDIA_PREFIX = '/admin/'_DIRS = ()_FINDERS = (

'django. contrib. staticfiles. finders. FileSystemFinder',

'django. contrib. staticfiles. finders. AppDirectoriesFinder',

)_KEY = '%jh+z=pal8) *6=m3@=-e ( (p66nhy2ej (yc! ^g4n8qgo*z) _$d-'_CONTEXT_PROCESSORS = (

"django. contrib. auth. context_processors. auth",

"django. core. context_processors. request",

"django. core. context_processors. i18n",

'django. contrib. messages. context_processors. messages',

'django. core. context_processors. debug',

'django. core. context_processors. media',

'django. core. context_processors. static',

)_CLASSES = (

'django. middleware. csrf. CsrfViewMiddleware',

'django. middleware.common.commonMiddleware',

'django. contrib. sessions. middleware. SessionMiddleware',

'django. contrib. auth. middleware. AuthenticationMiddleware',

'django. contrib. messages. middleware. MessageMiddleware',

'django. middleware. doc. XViewMiddleware',

'django. middleware. locale. LocaleMiddleware',

)_DIRS = (_ROOT + 'teamplates',

)_APPS = (

'SA. filebrowser',

'django. contrib. auth',

'django. contrib. contenttypes',

'django. contrib. sessions',

'django. contrib. sites',

'django. contrib. messages',

'django. contrib. staticfiles',

'django. contrib. admin',

'django. contrib.comments',

'registration',

# курсовая:

'SA. user_profile',

'SA. rialto',

'SA. blog',

'SA. kurs',

'SA. friends'

)= {

'version': 1,'disable_existing_loggers': False,

'handlers': {

'mail_admins': {

'level': 'ERROR',

'class': 'django. utils. log. AdminEmailHandler'

}

},

'loggers': {

'django. request': {

'handlers': ['mail_admins'],

'level': 'ERROR',

'propagate': True,

},

}

}_noop = lambda s: s_LANGUAGES = (

('ru', gettext_noop ('Russian')),

('en-us', gettext_noop ('US English')),

)= list (PAGE_LANGUAGES)= languages_BACKEND = "locmem: // /? max_entries=5000"_USE_SITE_ID = True_ID = 1

'plugins': "autolink,lists,spellchecker,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions, iespell, inlinepopups, insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template",

'theme': "advanced",

'theme_advanced_buttons1': "save,newdocument,|,bold, italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,styleselect,formatselect,fontselect,fontsizeselect",

'theme_advanced_buttons2': "cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numlist,|,outdent, indent,blockquote,|,undo,redo,|,link,unlink,anchor, image,cleanup,help,code,|, insertdate, inserttime,preview,|,forecolor,backcolor",

'theme_advanced_buttons3': "tablecontrols,|,hr,removeformat,visualaid,|,sub,sup,|,charmap,emotions, iespell,media,advhr,|,print,|,ltr,rtl,|,fullscreen",

'theme_advanced_buttons4': "insertlayer,moveforward,movebackward,absolute,|,styleprops,spellchecker,|,cite,abbr,acronym,del, ins,attribs,|,visualchars,nonbreaking,template,blockquote,pagebreak,|, insertfile, insertimage",

'theme_advanced_toolbar_location': "top",

'theme_advanced_toolbar_align': "left",

'theme_advanced_statusbar_location': "bottom",

'theme_advanced_resizing': True,

'custom_undo_redo_levels': 10,"relative_urls": False,

}_FILEBROWSER = True_SPELLCHECKER = True_COMPRESSOR = False

# заставляем базу данных этого хостинга (sweb.ru) работать с правильной кодировкой

"""django. db import connection, transaction= connection. cursor (). execute ("SET NAMES 'utf8' COLLATE 'utf8_general_ci'").commit_unless_managed ()

"""

#-- - Настройки для джанго рестрации - ---

ACCOUNT_ACTIVATION_DAYS = 7 # One-week activation window; you may, of course, use a different value.

AUTH_PROFILE_MODULE = 'user_profile. Message'_PROFILE_MODULE = 'user_profile. Profile'

Файл url. py

# - * - coding: utf-8 - *-django. conf. urls. defaults import patterns, include, urldjango. conf import settingsdjango. contrib import admindjango. contrib. staticfiles. urls import staticfiles_urlpatternsSA. user_profile. models import ProfileSA. user_profile. signals import *SA. user_profile. forms import RegistrationFormProfileSA. user_profile. signals import *. autodiscover ()= patterns ('',(r'^accounts/register/$', 'registration. views. register', {'backend': 'registration. backends. default. DefaultBackend', 'form_class': RegistrationFormProfile,},name='registration_register'),

(r'^accounts/', include ('registration. backends. default. urls')),

# новый файлбраузер без гарппели(r'^admin/', include (admin. site. urls)),(r'^media/ (? P<path>. *) $', 'django. views. static. serve', {'document_root': settings. MEDIA_ROOT, }),(r'^static/ (? P<path>. *) $', 'django. views. static. serve', {'document_root': settings. STATIC_ROOT, }),

(r'^person/user_settings/$', 'SA. kurs. views. user_settings'),

(r'^$', 'SA. kurs. views. my_profile'),

(r'^friends/$', 'SA. friends. views. search_person'),

(r'^friends/add/ (? P<id> [^/] +) /$', 'SA. friends. views. add_person'),

(r'^friends/del/ (? P<id> [^/] +) /$', 'SA. friends. views. del_person'),

(r'^friends/accept/ (? P<objid> [^/] +) /$', 'SA. friends. views. accept_friend'),

(r'^friends/reject/ (? P<objid> [^/] +) /$', 'SA. friends. views. reject_friend'),

(r'^all_message/$', 'SA. friends. views. all_message'),

(r'^myfriends/$', 'SA. friends. views. my_friends'),

(r'^myfriends/sendmessage/ (? P<id> [^/] +) /$', 'SA. friends. views. send_message'),

(r'^blog/new_news/$', 'SA. blog. views. new_news'),

(r'^blog/blog_all_text/ (? P<id> [^/] +) /$', 'SA. blog. views. blog_all_text'),

(r'^blog/$', 'SA. blog. views. blog_views'),

(r'^blog/ (? P<slug> [^/] +) /$', 'SA. blog. views. blog_sort'),

(r'^blog/ (? P<year> [^/] +) / (? P<month> [^/] +) /$', 'SA. blog. views. blog_sort_date'),

)

Файл wsgi. py.os, sys_env = os. path. expanduser ('~/virtualenv/MyEnv')_this = os. path. join (virtual_env, 'bin/activate_this. py')(activate_this, dict (__file__=activate_this)). path. insert (0, os. path. join (os. path. expanduser ('~'), 'django/kurs2')). environ ['DJANGO_SETTINGS_MODULE'] = 'SA. settings'django. core. handlers. wsgi= django. core. handlers. wsgi. WSGIHandler ()

Приложение В


Главный шаблон всего сайта supreme.html.

<! DOCTYPE html PUBLIC "- // W3C // DTD HTML 4.01 // EN"

"http://www.w3.org/TR/html4/strict. dtd">

{% load user_profile %}

<html xmlns="http://www.w3.org/1999/xhtml" lang="en">

<head>

<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />

<title>{% block title %}{% endblock %} Chat+++</title>

<link type="text/css" href="/media/yaml/core/base. css" rel="stylesheet">

<link type="text/css" href="/media/css/styles. css" rel="stylesheet">

<script type="text/javascript" src="/media/lib/jquery-1.7.1 min. js"></script>

<script type="text/javascript" src="/media/lib/balrog. js"></script>

<meta name="telderi" content="cba1d7c2e286711d42d0a79572178226" />

</head>

<body>

<div>

</div>

<div>

<div>

<a href="/"><div>

{% if user. is_authenticated %}

<div>

<ul>

<li><a>

<li><a>

<li><a href="{% url auth_password_change %}">Сменить пароль</a></li>

</ul>

</div>

{% else %}

<div>

<form method="post" action="/accounts/login/">

<ul>

{% csrf_token %}

<li>

<input type="text">

</li>

<li>

<input type="text">

</li>

<li>

<input>

</li>

<li>

<input>

</li>

</ul>

</form>

</div>

{% endif %}

</div>

<div>

{% if user. is_authenticated %}

<div>

<ul>

<li><a href="/"><img src="/media/images/search. png"/><p>МОЯ СТРАНИЦА</p></a></li>

<li><a href="/blog/"><img src="/media/images/notes. png"/><p>НОВОСТИ</p></a></li>

<li><a href="/all_message/"><img src="/media/images/mail. png"/><p>СООБЩЕНИЯ {% if user. id|messages_count %} ({{ user. id|messages_count }}) {% endif %}</p></a>

</li>

<li><a href="/friends/"><img src="/media/images/profile. png"/><p>ДРУЗЬЯ</p></a></li>

</ul>

</div>

{% else %}

<div>

<img src="/media/images/sitesimg. png"/>

<h2>Зарегестрируйтесь прежде чем пользоваться сайтом</h2>

</div>

{% endif %}

</div>

<div>

{% block title_site_info %}

{% endblock %}

<div>

МЕСТО КУДА БУДЕТ ВЫВЕДЕН КОНТЕНТ ВСПЛЫВАЮЩИХ ОКОН

{% block main %}

{% endblock %}

</div>

<div>

<div>

{% block right %}

{% endblock %}

</div>

</div>

</div>

</div>

</body>

</html>

Файл models. py приложения user_profile.

# - * - coding: utf-8 - *-django. db import modelsdjango. contrib. auth. models import UserSA. user_profile. fields import AutoOneToOneFielddatetimeImagedatetimejsonmpttos. pathSA. settings import *Profile (models. Model):= AutoOneToOneField (User, related_name='profile', verbose_name= ('User'), primary_key=True)= models. ImageField (verbose_name=u"Иконка", upload_to=u"photos/%Y/%m/%d", help_text=u'Загрузите картинку', blank=True)= models. TextField (verbose_name=u'Дополнительные сведения', blank=True)= models. CharField (max_length=150,verbose_name=u'Логин',blank=True)_name = models. CharField (max_length=150,verbose_name=u'Фамилия',blank=True)_name = models. CharField (max_length=150,verbose_name=u'Имя',blank=True)_name = models. CharField (max_length=150,verbose_name=u'Отчество',blank=True)= models. ManyToManyField ("self", blank=True, symmetrical = False, related_name='friends_targets')_requests = models. ManyToManyField ("self", blank=True, symmetrical = False, related_name='friend_requests_targets')= models. CharField (max_length=150, verbose_name = u'ICQ', blank=True)= models. CharField (max_length=150, verbose_name=u'Skype', blank = True)= models. CharField (max_length=150, verbose_name=u'Телефон', blank = True)__unicode__ (self):str (self. user)get_thumbnail_url (self, width, height):

"""Возвращает URL уменьшенной копии фотографии""":not os. path. exists (self. thumbnail. path): return None_url = None= str (self. thumbnail)_path_to_file = "%s/%s" % (MEDIA_ROOT, photo)= self. thumbnail. path [self. thumbnail. path. rfind ("/") +1:]_to = ""_to_thumbnail_dir = "%s/thumbnails%s/%sx%s" % (MEDIA_ROOT, upload_to, str (width), str (height))not os. path. exists (path_to_thumbnail_dir): os. makedirs (path_to_thumbnail_dir)_to_thumbnail = "%s/%s" % (path_to_thumbnail_dir, filename)not os. path. exists (path_to_thumbnail)::= Image. open (self. thumbnail. path). thumbnail ( (width,height), Image. ANTIALIAS). save (path_to_thumbnail, image. format, quality=95):None"%sthumbnails/%s%sx%s/%s" % (MEDIA_URL, upload_to, str (width), str (height), filename):''get_thumbnail (self):self. get_thumbnail_url (100, "*")get_big (self):self. get_thumbnail_url (600, "*")get_min_thumbnail (self):self. get_thumbnail_url (45, "*")Message (models. Model):= models. ForeignKey (User, verbose_name=u'Получатель', related_name='recipients')= models. ForeignKey (User, verbose_name=u'Отправитель', related_name='senders')= models. DateTimeField (verbose_name=u'Дата отправки сообщения',blank=True, null=True, default = datetime. datetime. now)= models. CharField (max_length=350,verbose_name=u'Заголовок сообщения',blank=True)= models. TextField (blank=True,verbose_name=u'Сообщение')= models. BooleanField (blank=True,verbose_name=u'Сообщение отправлено')Chat (models. Model):= models. ForeignKey (User, verbose_name=u'Первый участник чата', related_name='persons1')= models. ForeignKey (User, verbose_name=u'Второй участник чата', related_name='persons2')= models. ManyToManyField (Message, verbose_name=u'Все их сообщения', blank=True, related_name='all_message')last_message (self):self. messages. latest ('date')

Файл views. py для главной страницы и страницы настроек личной информации.

# - * - coding: utf-8 - *-SA. user_profile. models import Profiledjango. views. decorators. csrf import csrf_exemptdjango. shortcuts import render_to_response, redirectdjango. template import RequestContext # нужно чтобы передавать реквест в контекст

from django. forms import ModelFormdjango. http import HttpResponse, HttpResponseRedirectdjango import formsdjango. db. models import Qdjango. contrib. auth. models import Userdatetimetime

@csrf_exemptmy_profile (request):

# выводим все данные= Nonerequest. user. is_authenticated ():= User. objects. get (pk = request. user. id)Profile. objects. get (pk=request. user. id):= Profile. objects. get (pk = request. user. id):= Profile (). user_id = vector. id. first_name = vector. first_name. last_name = vector. last_name. save ()= {'person': object}render_to_response ("kurs/my_profile.html", context, context_instance=RequestContext (request)):

# хорошо бы сделать из этой проверки декоратор, чтоб всюду её не тоскать за собойrender_to_response ("kurs/error.html", {'ErrorText': u"Вы не авторизированны"}, context_instance=RequestContext (request))

@csrf_exemptuser_settings (request):= Nonerequest. user. is_authenticated ():ProfileForm (ModelForm):Meta:= Profile= ('user', 'friends', 'friend_requests')_password = forms. CharField (max_length=150, required=False,label='Новый пароль')_password = forms. CharField (max_length=150, required=False,label='Подтвердить пароль')_id = request. user. idrequest. method == 'POST':= Profile. objects. get (pk = user_id)= ProfileForm (request. POST, request. FILES, instance = object)form. is_valid ():_password = form. cleaned_data ['new_password']_password = form. cleaned_data ['confirm_password']= User. objects. get (pk = request. user. id). set_password (new_password)new_password == '':= 'Поле (новый пароль) пусто: ':confirm_password == new_password:= 'Пароли совпадают! '. save ():= 'Пароли не совпадают! '. save ()= {'form': form, 'error': str, 'object': object}render_to_response ("kurs/user_settings.html", context, context_instance=RequestContext (request)):= ProfileForm ()= Profile. objects. get (pk = user_id)= ProfileForm (instance = object)= {'form': form, 'object': object}render_to_response ('kurs/user_settings.html', context, context_instance=RequestContext (request)):