РАЗРАБОТКА АРХИТЕКТУРЫ ЗАКРЫТОЙ КОРПОРАТИВНОЙ СЕТИ С ИСПОЛЬЗОВАНИЕМ ФРЕЙМВОРКА DJANGO
МИНИСТЕРСТВО ОБРАЗОВАНИЯ И НАУКИ РОССИЙСКОЙ ФЕДЕРАЦИИ
Федеральное государственное бюджетное образовательное учреждение
высшего профессионального образования
«КУБАНСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ»
(ФГБОУ ВПО «КубГУ»)
Кафедра математического моделирования
ДОПУСТИТЬ К ЗАЩИТЕ В ГАК
Заведующий кафедрой
академик РАН, д-р физ.-мат наук
В.А.Бабешко
(подпись) (инициалы, фамилия)
2014 г.
ВЫПУСКНАЯ КВАЛИФИКАЦИОННАЯ (ДИПЛОМНАЯ)
РАБОТА
РАЗРАБОТКА АРХИТЕКТУРЫ ЗАКРЫТОЙ КОРПОРАТИВНОЙ СЕТИ С ИСПОЛЬЗОВАНИЕМ ФРЕЙМВОРКА DJANGO
Работу выполнил В.Д. Михно
(подпись, дата) (инициалы, фамилия)
Факультет компьютерных технологий и прикладной математики
Специальность 010501 Прикладная математика и информатика
Научный руководитель,
доцент, канд. физ.-мат наук С.Е.Рубцов
(подпись, дата) (инициалы, фамилия)
Нормоконтролер
канд. физ.-мат наук, доцент М.С.Капустин
(подпись, дата) (инициалы, фамилия)
Краснодар 2014
РЕФЕРАТ
Дипломная работа 56 с., 11 рис., 12 источников, 3 приложения.
DJANGO, MODEL-VIEW-CONTROLLER, CLASS-BASE-VIEW, МОДЕЛЬНЫЕ ФОРМЫ, МИГРАЦИИ.
Работа посвящена разработке веб-сайта с помощью свободно распространяемого фреймворка Django.
Цель работы создание корпоративной сети и ее административной части для организации ККМОО «Молодежная лига развития национальных культур Кубани».
Задачи работы: с помощью свободно распространяемого фреймворка Django для веб-приложений, на языке Python, разработать веб-приложение, для фирмы ККМОО «Молодежная лига развития национальных культур Кубани», реализующее четвертый и пятый разделы технического задания; реализовать главную страница пользователя, возможность загружать документы себе на страницу и скачивать со страниц других пользователей; реализовать личную ленту новостей и страницу новостей всех пользователей, с возможностью поиска новости по словам в тексте и заголовке, фильтрация по тегам и по датам; переписать уже реализованный функционал сайта и дописать новый, используя Class-Base-View (CBV-способ описания view с помощью классов); подключить библиотеку south, для осуществления динамического изменения структуры базы данных; подключить библиотеки grapelli, для придания административной части гибкости и дружественного интерфейса; подключить библиотеку filebrowser, для множественной загрузки файлов; реализовать структуру базы данных в модельной части проекта; реализовать функциональную часть проекта(VIEW); реализовать интерфейс пользователя с помощью html и css.
Для реализации проекта используется язык Python и СУБД PostreSQL.
СОДЕРЖАНИЕ
ВВЕДЕНИЕ 4
1 Анализ корпоративных сетей. 5
1.1 ASmallWorld 5
1.2 Decayenne 6
1.3 Evrika 7
2 Структура web-приложения 8
3 База данных и административная часть 10
4 Class-Base-View 19
5 Логическая часть приложения и отображение данных на страницах 21
ЗАКЛЮЧЕНИЕ 26
СПИСОК ИСПОЛЬЗУЕМОЙ ЛИТЕРАТУРЫ 27
ПРИЛОЖЕНИЕ А 28
ПРИЛОЖЕНИЕ Б 29
ПРИЛОЖЕНИЕ В 56
ВВЕДЕНИЕ
Корпоративная сеть это сеть передачи данных, работающая под единым управлением и предназначенная для удовлетворения собственных производственных потребностей компании и организации. Корпоративная информация это информация, разглашение или несанкционированное изменение которой может привести к огромным финансовым потерям.
Поэтому корпоративная сеть закрытая структура с достаточно высокой степенью защиты, доступ к которой извне запрещен вообще или строго ограничен, а доступ к информации внутри нее разграничен с использованием административных и технических методов.
Для обеспечения защиты данных в корпоративных сетях могут использоваться различные организационно технические методы (выделение ответственных специалистов, применение списков контроля доступа, использование VPN и т.п.). Их совокупность называется комплексной системой защиты информации.
Некоторые работодатели запрещают пользоваться обычными социальными сетями в рабочее время не только ради экономии времени, но и чтобы воспрепятствовать утечке информации.
В данной дипломной работе представлена разработка веб приложения, обеспечивающего ограничение доступа к пользовательскому интерфейсу и административной части, социальные взаимоотношения между сотрудниками и ограниченный обмен документами. Что позволяет не только свести риск потери конфиденциальной информации к минимуму, но и возможность ограничение доступа к социальным сетям, компенсируя это наличием собственной корпоративной сети.
1 Анализ корпоративных сетей
Кроме общедоступных «В Контакте», «Facebook», «Одноклассники», существуют корпоративные сети, членство в которых доступно только для избранных или для людей с определенной профессией, ограниченные в доступе и возможностях обмена информации. Рассмотрим некоторые из них.
1.1 ASmallWorld
Рисунок 1 Корпоративная сеть ASmallWorld
Одна из самых первых эксклюзивных европейских социальных сетей, принимающая только «людей из мира высокого искусства, которые определяются по трем параметрам». Вы должны быть приглашены кем-то из существующих членов данной сети, но даже в этом случае нет никакой гарантии, что вас примут. Руководит сетью 25 летний швейцарец Патрик ЛиотардВойт (Patrick Liotard-Vogt).
1.2 Decayenne
Одна из первых корпоративных сетей с элитными участниками была основана в 2001 году тремя немецкими бизнесменами. Членство обычно выдается по приглашению, хотя вы можете обратиться напрямую к создателям с просьбой о вступлении.
Создатели сети описывают ее как «оазис вдохновения и развлечений» для «космополитичного, независимого, значимого, толерантного, либерального нонконформиста».
Рисунок 2 Корпоративная сеть Decayenne
1.3 Evrika
Evrika корпоративная сеть для российских врачей. Принадлежит медиа группе "MedInform Healthcare Communications". Все страницы сайта, кроме главной, не индексируются поисковиками и доступны только зарегистрированным пользователям. Для получения доступа нужно быть действующим врачом, способным подтвердить свои профессиональные знания и факт работы в медицинском учреждении.
Имеется как социальная составляющая: профили пользователей, обмен контактами и сообщениями, группы, комментарии; так и внутренний информационный портал: медицинские новости, перепечатки научных статей из российских и зарубежных журналов, оригинальные публикации медиков экспертного уровня.
По нашему мнению, сайт нацелен на доходы от продвижения лекарств, медоборудования и других изделий для здравоохранения в профессиональную среду. Это подтверждает его интеграция с ресурсом "МедМнение". Последний в интересах фармацевтических компаний проводит опросы среди врачей и платит деньги за участие в них.
2 Структура webприложения
Компоненты данного приложения основополагающие части социальной сети: отправка сообщений, поиск друзей, главная страница пользователя, блог, обмен файлами между пользователями.
Ранее уже был реализован функционал для отправки сообщений, поиска, добавления и удаления друзей и частично главная страница пользователя. В данной работе полностью реализована главная страница пользователя, осуществлен доступ к другим страницам пользователей, возможность загружать документы себе на страницу и скачивать со страниц других пользователей, личная лента новостей и страница новостей всех пользователей, с возможностью поиска новости по словам в тексте и заголовке, фильтрация по тегам и по датам.
Весь функционал сайта разбит на 3 приложения profile, blog, message. Первое осуществляет отображение и редактирование личной информации, ленту своих новостей, создание новой новости, добавление документов для скачивания другим пользователям, поиск, добавление и удаление других пользователей. Второе отображение новостей для всех пользователей, с возможностью комментирования. И последнее третье, отвечает за обмен сообщениями между пользователями.
Каждое приложение состоит из двух частей:
Общедоступная часть, которая дает людям возможность управлять своим профилем.
Административная часть, которая позволит администратору редактировать базу данных сайта.
Эти две части реализованы, следуя шаблону Модель-Представление-Контроллер (Model-View-Controller, MVC). Примем, что MVC определяет способ разработки программного обеспечения, при котором код для определения и доступа к данным (модель, файл models.py) отделен от логики приложения (управление, файл views.py), которая в свою очередь отделена от интерфейса пользователя (представление, файл html) так, что модификация одного из компонентов оказывает минимальное воздействие на остальные.
Основой проекта будут manage.py, __init__.py, settings.py, urls.py, wsgi.py.
manage.py скрипт, который позволяет вам взаимодействовать с проектом Django.
__init__.py пустой файл, который указывает Python, что текущий каталог является пакетом Python.
settings.py настройки/конфигурация проекта.
urls.py конфигурация URL-ов для вашего проекта Django. Это “содержимое” всех Django-сайтов.
wsgi.py точки входа для WSGI-совместимый веб-серверов.
Содержимое этих файлов представлено в приложении А.
В данной работе помимо модуля django-registration, будем использовать модуль south, для осуществления миграции базы данных, grapelli, для придания административной части красивого вида, filebrowser, для множественной загрузки файлов.
3 База данных и административная часть
В качестве СУБД выбрана PostgreSQL, в связи с наличием некоторых особенностей в PostgreSQL, которые часто используются внешние ключи, триггеры и представления. Они позволяют скрывать сложность базы данных от приложения, таким образом избегая создания сложных команд SQL. Подключение к базе данных осуществляется в папке settings (в предыдущей работе все настройки находились в одном файле settings.py).
DATABASE = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'diplom',
'USER': 'postgres',
'PASSWORD':'postgres',
'HOST': '127.0.0.1',
'PORT': '5432',
}
}
Эта настройка ведется с учетом того, что в PostgreSQL мы уже создали пустую базу данных с названием diplom.
Рассмотрим модельную часть приложения, отвечающую за главную страницу пользователя.
Класс Profile описывает структуру таблицы profile, которая ссылается на таблицу User, (сгенерированную при подключении приложения django.contrib.auth), с помощью поля user (объект класса ForeignKey, атрибут-указатель которого будет модель User).
user = models.ForeignKey(User, related_name='profile', verbose_name = ('User'), blank = True, null=True)
Так же в этой модели описаны два поля friends и friesnd_requests, которые являются объектами класса ManyToManyField(отношение многие-ко-многим) и ссылаются на свою же модель (“self”), для определения, какие объекты Profile находятся в друзьях и кто хочется стать другом.
friends = models.ManyToManyField("self", blank=True, null=True, symmetrical = False, related_name='friends_targets')
friend_requests = models.ManyToManyField("self", blank=True, null=True, symmetrical = False, related_name='friend_requests_targets' )
Поле party необходимо для администратора, что бы подтверждать, что пользователь является сотрудником фирмы, обычный пользователь видеть его не будет. Если модуль django-registration, обеспечивает проверку на регистрацию существующего человека, то поле party будет рычагом администратора, для добавления в социальную сеть только сотрудников фирмы. Только после того как администратор свяжется с зарегистрированным пользователем и убедится, что тот является участником, он активирует его аккаунт через административную часть, и пользователь сможет полноценно пользоваться сайтом.
party = models.BooleanField(u'Является участником сообщества', blank=True, default = False)
Поле для фотографии пользователя изменено в этой работе на FileBrowserField. Этот класс предоставляет нам возможность загружать сразу несколько файлов, выбирать какого размера они будут и многое другое (рисунок 3).
Рисунок 3 Административная часть модели Profile
Рисунок 3 Административная часть модели Profile
Остальные поля описывают данные пользователя: имя, фамилию, отчество и другие контактные данные.
Для того что бы хранить документы пользователя, которые он будет загружать для скачивания другим пользователям, создаем дополнительную модель FileProfile (рисунок 4).
Рисунок 4 Административная часть модели FileProfile
Эта модель содержит поле для документа file (FileBrowserField), в которое будет сохраняться файл при добавлении его пользователем. Для того что бы поле загружало конкретно документы необходимо установить параметр format строковым аргументом document. Тогда поле сможет сохранять форматы '.pdf','.doc','.rtf','.txt','.xls','.csv'.
file = FileBrowseField(u"Файл", directory='uploads/files/' , max_length=100, format = 'document', null=True, blank=True)
Поле user(ForeignKey), которое будет автоматически, при сохранении объекта модели, заполняться объектом модели Profile текущего пользователя.
user = models.ForeignKey(Profile, related_name='profile', verbose_name=('User'), blank=True, null=True)
Так же добавим два дополнительных поля title и date соответственно CharField и DateTimeField.
title = models.CharField(u"Заголовок", max_length=64, blank=True, null=True)
date = models.DateTimeField(verbose_name=u'Дата загрузки файла',blank=True, null=True, default = datetime.datetime.now)
После сохранения файла models.py в приложении profile, надо что бы в базе данных сгенерировалась описанные нами модели. В предыдущей работе я использовала стандартную команду django syncdb, которая может с нуля создать таблицу, но не производить с ней в дальнейшем никаких изменений. Т. е. если я изменяла какие-либо поля мне приходилось удалять всю таблицу, зависимые таблицы, и все их данные для того что бы просто добавить поля. Иногда вручную, в PostgreSQL создавала или редактировала поля, но при добавления поля-связи (ManyToManyField, ForeignKey и т. д.) конечно же возникали трудности. Для того что бы избежать всего этого устанавливаем и подключаем модуль south, который осуществляет редактирование таблиц в базе данных с помощью команд schemamigration и migrate. Если мы первый раз делаем миграцию, то необходимо выполнять команду schemamigration с ключём initial:
>python manage.py schemamigration profile initial
После этой команды в приложении создастся папка migrations, в которой сформируется файл c расширением .py, при запуске которого командой migrate, выполнятся питоновские скрипты и произойдут изменения в нашей таблице:
>python manage.py migrate profile
В дальнейшем при редактировании полей в этом приложении будем использовать те же команды, но уже с ключом auto.
При создании остальных таблиц на основе моделей будем также использовать south.
Теперь перейдем к приложению blog, которое содержит модели Blog, Tags и Comment, описывающие соответствующие таблицы в базе данных. Модель Blog (рисунок 6) ссылается на модель Tags (рисунок 5) с помощью поля tags, которое является экземпляром класса ManyToManyField. Это означает, что каждому объекту модели Blog будет соответствовать множество объектов модели Tags.
tags = models.ManyToManyField(Tags, verbose_name=u'Теги', blank=True)
Рисунок 5 Административная часть модели Tags
Рисунок 6 Административная часть модели Blog
Модель Comment (рисунок 7) будет ссылаться на модель Blog с помощью связи ForeignKey.
Рисунок 7 Административная часть модели Comment
Это необходимо для того что бы определять, какие комментарии относятся к конкретной новости. При создании этих трёх моделей также нам необходимы поля:
Blog: zagol, author (обьект класса CharField) заголовок новости и имя автора, image (объект класса FileBrowseField) обложка новости, date_of_publication (DateTimeField) дата публикации (по умолчанию устанавливается в текущую дату создания), и text (TextField) текст новости;
Tags: name (CharField) название тега;
Comment: author_name (CharField) имя автора комментария, text (TextField) текст комментария, pub_date (DateTimeField) дата публикации комментария, admin_comment (BooleanField) устанавливается в True администратором, если он одобряет комментарий;
Для реализации раздела СООБЩЕНИЯ создаем еще одно приложение messages. Файл models.py для этого приложения будет содержать модели Message и Chat, которые будут позволять обмениваться сообщениями между друзьями социальной сети.
Модель Message содержит два поля sender и recipient (ForeignKey), которые ссылаются на модель User, и определяют, кто из пользователей отправляет сообщение, а кто получает (рисунок 8).
Рисунок 8 Административная часть модели Message
Модель Chat служит для объединения в один объект всех объектов из модели Message, у которых получатель или отправитель соответствует полям person1 и person2, которые в свою очередь ссылаются на модель User (рисунок 9).
Рисунок 9 Административная часть модели Сhat
Поле messages экземпляр класса ManyToManyField и ссылается на модель Message. Т. е. один объект Chat будет ссылаться на несколько объектов модели Message.
Так же эти две модели будут содержать поля date дата создания сообщения (DateTimeField), title заголовок сообщения (CharField), message текст сообщения (TextField), reader булевское поле для установления, прочитано ли сообщение получателем или нет (BooleanField).
Все модели представлены в приложении Б в разделах соответствующих файлам models.py.
4. Class-Base-View
Весь функционал сайта был переписан, используя Class-Base-View(CBV), способ описания view с помощью классов, в связи с наличием часто используемого функционала. Чтобы не приходилось писать однообразный код, подобные view описаны прямо в коде фреймворка.
В данном проекте в качестве родительских классов используются стандартные классы ListView, DetailView, TemplateView, View, CreateView и MonthArchiveView.
Класс TemplateView позволяет отображать один шаблон, который мы указываем в атрибуте template_name. Что бы изменить передаваемый в запросе словарь с переменными, необходимо переопределить метод get_context_data и в нем добавлять в словарь context все необходимые нам аргументы.
Класс ListView обеспечивает вывод объектов в виде списка. В качестве основных аргументов используются model для указания, из какой модели мы будем получать объекты, template_name для явного указания какой в какой шаблон будем обрабатывать. Хотя если явно не указывать этот атрибут, Django “вычислит” его из названия объекта. В данном случае, таким “вычисленным” шаблоном будет "APP/MODEL_list.html" часть “APP” берется из имени приложения, определяющего модель, а часть “MODEL” это просто название модели в нижнем регистре.
Класс DetailView позволяет получать один объект, id которого будет соответствовать аргументу pk, который будет передаваться в url. Так же основным аргументом является model, которому мы будем указывать нашу модель, из которой нужно получить объект.
Класс View умеет вызывать свои фунции get(), post() и т.п. в зависимости от типа запроса, передавая request.
Класс CreateView осуществляет создание нового объекта. Основной параметр который нам необходимо указать так же является model. При генерации формы, если нам нужно что бы сохранялись какие-нибудь конкретные поля или наоборот исключить ненужные, используются атрибуты fields и exclude, которым передаются списки полей.
MonthArchiveView класс, который позволяет фильтровать только те объекты, у которых поле, отвечающее за дату создания, соответствует месяцу и году, которые передаются в url. Для этого используются атрибуты date_field и queryset.
5 Логическая часть приложения и отображение данных на страницах
Теперь приступим к реализации функционала наших страниц.
Главная страница сайта будет представлять собой страницу профиля пользователя, где будут размещены его контактные данные, новостную ленту текущего пользователя, с возможностью добавлять новые новости, и форму для выкладывания документов и список для их скачивания (рисунок 10).
Для этого создадим класс ProfileView, который будет наследоваться от класса CreateView. Но в связи с тем, что на странице будет выполняться множество действий, необходимо поменять поведение view, которые описывают родительский класс. Метод get_context_data будет срабатывать при GET запросе, и выводить две формы (для создания новости и загрузки документа). Так же в каждой view мы будем устанавливать проверку:
if (self.request.user.is_authenticated()) and (Profile.objects.get(pk= self.request.user.id ).party == True) and (not 'pk' in self.kwargs)
Этот фрагмент определят, зарегистрирован ли текущий пользователь, активирован ли он администратором и находится ли на своей странице или на странице другого пользователя.
Рисунок 10 Главная страница пользователя
Формы для создания новости BlogForm и для загрузки файла FileForm опишем в той же директории в файле forms.py.
Первую форму укажем как стандартный атрибут родительского класса form_class. И при срабатывании POST запроса, эта форма будет обрабатываться стандартными методами CreateView. Что бы не прописывать много лишних полей в форме BlogForm, мы будем использовать класс ModelForm, который укажет что форма будет наследоваться от модели Blog, с помощью атрибута model, и атрибуту exclude укажем какие поля мы не хотим изменять.
class BlogForm(forms.ModelForm):
class Meta:
model = Blog
exclude = ('number','author','date_of_publication', 'image','tags')
thumbnail = forms.FileField(max_length=200, label="Обложка новости", required=False)
Т. к. нам нужно что бы пользователь не видел технические поля (такие как author, date_of_publication, number, поле tags представляет собой ссылку на объекты (ManyToManyField), поле image FileBrowserField не предоставляет функционал загрузки файла на станице сайта) то их мы будем обрабатывать и заполнять вручную во время проверки на валидность формы.
Что же касается второй формы, FileForm, то мы ее будем просто передавать в словаре context вместе с другими нужными нам переменными, и обработка будет происходить с помощью другого класса UploadView, который будет наследоваться от стандартного класса View, обеспечивающий метод post, который мы переопределим.
class FileForm(forms.ModelForm):
class Meta:
model = FileProfile
exclude = ('user', 'date', 'file')
doc = forms.FileField(max_length=200, label="Файл", required=False)
Хочу заметить, что при загрузке картинки новости или документа для скачивания, в формах использовалось дополнительно поле FileField. Это связано с тем, что функционал поля FielBrowserField не позволяет загружать файл на странице сайта, в отличие от стандартных Django-полей ImageField и FileField, которое описано в форме. Поэтому при выводе на шаблон (файл *.html), мы скрываем поле моделей Blog и FileProfile, а вместо них указываем дополнительное поле FileField. И уже при переопределении метода, мы из POST запроса получаем объект файла, сохраняем его по нужному нам пути и с помощью метода FileObject, который импортируется из библиотеки filebrowser, преобразуем его в объект класса FileBrowserField.
Весь код данных страниц представлен в приложении Б.
Приложение, реализующее раздел БЛОГ, описывается в папке blog нашего проекта. Основная страница этого раздела представляет собой ленту новостей, с возможностью просматривать каждую новость полностью, формой поиска по словам в тексте, заголовке и тегам новости, а также боковое меню, для фильтрации новостей по архивам (рисунок 11).
Рисунок 11 Главная страница новостей
Функционал главной страницы новостей осуществляется с помощью класса ListView, которому передаем в качестве атрибута model модель Blog, что бы вывести все объекты данной модели.
class BlogList(ListView):
model = Blog
context_object_name = "blog"
def get_context_data(self, **kwargs):
context = super(BlogList, self).get_context_data(**kwargs)
if self.request.user.is_authenticated() and Profile.objects.get(pk=self.request.user.id).party==True:
date_obj = []
for obj in Blog.objects.dates('created', 'month'):
date_obj.append(obj)
context['archives'] = date_obj
context['active_main_menu']='main_blog'
else:
self.template_name = "profile/error.html"
context['ErrorText'] = u"Вы не авторизированны"
return context
Для просмотра каждой новости создаем класс BlogDetail, а в качестве родителя класса будем указывать CreateView, что бы была возможность создавать комментарии. Атрибуту model задаем как объект модели Blog, указывая на основе какой модели, мы будем строить форму, а в атрибуте exclude укажем какие поля, мы не будем отображать на шаблоне.
model = Comment
exclude = ['article', 'author_name', 'pub_date', 'admin_comment']
И теперь, что бы при сохранении объекта наши исключенные поля тоже приобрели нужные нам значения, переопределим метод form_valid:
def form_valid(self, form):
instance = form.save(commit=False)
instance.article = Blog.objects.get(pk=self.kwargs['pk'])
instance.author_name = self.request.user.username
instance.pub_date = datetime.datetime.now()
instance.save()
return redirect(self.get_absolute_url())
def get_absolute_url(self):
return reverse("blog_detail", kwargs={"pk": self.kwargs['pk']})
Для того что бы наши объекты модели Blog фильтровались по датам указанным в боковом меню, используем класс MonthArchiveView. Этот класс обладает атрибутом date_field, принимающего название поле типа DateTimeField в виде строки, по которому будем фильтровать объекты. При этом в url будут предаваться два параметра month, year, которые соответствуют месяцу и году указанным в ссылке.
ЗАКЛЮЧЕНИЕ
В дипломной работе с помощью Class-Base-View разработано приложение, являющееся корпоративной сетью для организации ККМОО «Молодежная лига развития национальных культур Кубани», сгенерирована база данных в PostgeSQL на основе полей описанных в моделях проекта, c помощью модуля south. Приложение осуществляет обмен сообщениями, поиск , добавление и удаления других пользователей в друзья, новостной блог, с возможностью комментирования каждой статьи и личную страницу пользователя, содержащую личную ленту новостей, список документов для скачивания и контактную информацию пользователя.
Приложение будет полезно для крупных фирм, которым необходим постоянный и быстрый обмен информации, при этом минимизируя утечку информации за пределы данного сообщества. Разработанный проект представляет собой Сайт-под-ключ, так как обладает оптимизированной и при этом функциональной административной частью, которой может управлять обычный компьютерный пользователь, что позволяет владельцу сайта не нанимать постоянного администратора.
Планируется дальнейшее развитие проекта добавление фото- и видео- галереи, а также, с помощью API Яндекс.translate и API Яндекс.Карты, добавить перевод всего сайта и размещения на изображении местности различных графических объектов.
Следовательно, приложение не утратит актуальности и будет в дальнейшем гораздо полезнее.
Весь проект написан на языке Python.
СПИСОК ИСПОЛЬЗУЕМОЙ ЛИТЕРАТУРЫ
1 Django 1.5 documentation [Электронный ресурс] // Django 1.5 documentation. URL: https://docs.djangoproject.com/en/1.5/. (Дата обращения: 1.09.2013)
2 Форсье, Дж. Django. Разработка веб-приложений на Python./ Дж. Форсье, П. Биссекс, У. Чан, СПб.: Символ-Плюс, 2010. 456 с.
3 Лутц М. Программирование на Python, том - , 4-е издание/ М. Лутц СПб.: Символ-Плюс, 2011. 922с.
4 Бизли, Д. Python. Подробный справочник, 4-е издание/ Д. Бизли М.: Символ-Плюс, 2010. 326 с.
5 Object-Relational Mapping! [Электронный ресурс] // ORM. URL: http://ru.wikipedia.org/wiki/ORM. (Дата обращения: 12.03.2012).
6 Django [Электронный ресурс] // Django. URL: http://ru.wikipedia.org/wiki/Django. (Дата обращения: 2.09.2013)
7 Buter-Brod [Электронный ресурс] // Представляем корпоративную сеть asmallworld.net для богатых и знаменитых. URL: http://buter-brot.ru/Moda/prdedstavlyaem-soczialnuyu-set-asmallworldnet-dlya-bogatyx-i-znamenityx.html/. (Дата обращения: 4.04.2014).
8 ASmallWorld. Корпоративная сеть [Электронный ресурс] // URL: https://www.asmallworld.com/. (Дата обращения: 4.30.2014).
9 Adamant [Электронный ресурс] // Оператор системных решений. URL: http://www.adamant.ua/projects/corporate-network/. (Дата обращения: 4.04.2014).
ПРИЛОЖЕНИЕ A
Файл manage.py
#!/usr/bin/env python
from django.core.management import execute_manager
import imp
try:
imp.find_module('settings') # Assumed to be in the same directory.
except ImportError:
import sys
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__)
sys.exit(1)
import settings
if __name__ == "__main__":
execute_manager(settings)
ПРИЛОЖЕНИЕ Б
Папка settings:
Файл __init.py__ (содержит все настройки Django):
# -*- encoding: utf-8 -*-
import os, sys
from django.contrib import messages
from django.utils.translation import ugettext_lazy as _
import os.path
DEBUG = True
PROJECT_ROOT = os.path.realpath(os.path.join(os.path.dirname(__file__), "../.."))
rel = lambda p: os.path.join(PROJECT_ROOT, p)
TEMPLATE_DEBUG = DEBUG
MANAGERS = ADMINS
EMAIL_BACKEND = 'django.core.mail.backends.filebased.EmailBackend'
EMAIL_FILE_PATH = os.path.join(PROJECT_ROOT, '_emails')
EMAIL_HOST = 'localhost'
EMAIL_PORT = '8000'
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
EMAIL_USE_TLS = True
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',#mysql',
'NAME': 'diplom',
'USER': 'postgres',
'PASSWORD': 'postgres',
'HOST': '127.0.0.1',
'PORT': '5432',
}
}
ALLOWED_HOSTS = []
TIME_ZONE = 'Europe/Moscow'
LANGUAGE_CODE = 'ru-ru'
SITE_ID = 1
USE_I18N = True
USE_L10N = True
USE_TZ = True
LOCALE_PATHS = (os.path.join(PROJECT_ROOT, 'locale'),)
MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'private/media/')
MEDIA_URL = '/media/'
STATIC_ROOT = os.path.join(PROJECT_ROOT, 'public/static/')
STATIC_URL = '/static/'
ADMIN_MEDIA_PREFIX = '/static/admin/'
STATICFILES_DIRS = (
os.path.join(PROJECT_ROOT, 'diplom/static/'),
)
DEFAULT_HOST = 'www.example.com'
STATICFILES_FINDERS = (
'django.contrib.staticfiles.finders.FileSystemFinder',
'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)
SECRET_KEY = '_z(8gb-k+c^y5^1r3b02^km64ve0)bja%soa3@a23d5@*geodf'
TEMPLATE_DEBUG = DEBUG
TEMPLATE_CONTEXT_PROCESSORS = (
'django.core.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.core.context_processors.debug',
'django.core.context_processors.i18n',
'django.core.context_processors.media',
'django.core.context_processors.static',
'django.core.context_processors.tz',
'django.contrib.messages.context_processors.messages',
)
TEMPLATE_LOADERS = (
'django.template.loaders.filesystem.Loader',
'django.template.loaders.app_directories.Loader',
'django.template.loaders.eggs.Loader',
)
TEMPLATE_DIRS = (
os.path.join(PROJECT_ROOT, 'templates/'),
)
"""Middleware related settings."""
MIDDLEWARE_CLASSES = (
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.locale.LocaleMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
)
AUTHENTICATION_BACKENDS = (
'django.contrib.auth.backends.ModelBa
EMAIL_HOST = 'localhost'
EMAIL_PORT = 1025
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
EMAIL_USE_TLS = False
DEFAULT_FROM_EMAIL = 'info@google.ru'
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse'
}
},
'handlers': {
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler'
}
},
'loggers': {
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': True,
},
}
}
AUTH_PROFILE_MODULE = 'profile.Profile'ckend', # this is default
'guardian.backends.ObjectPermissionBackend',
)
ANONYMOUS_USER_ID = -1
ROOT_URLCONF = 'diplom.urls'
MESSAGE_TAGS = {
messages.DEBUG: 'debug',
messages.INFO: 'alert-info',
messages.SUCCESS: 'alert-success',
messages.WARNING: 'alert-warning',
messages.ERROR: 'alert-error',
}
FIXTURE_DIRS = (
os.path.join(PROJECT_ROOT, 'diplom/fixtures'),
)
WSGI_APPLICATION = 'diplom.wsgi.application'
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.sites',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.flatpages',
'django.contrib.redirects',
'filebrowser',
'grappelli.dashboard',
'grappelli',
'guardian',
'django.contrib.admin',
'django.contrib.admindocs',
'south',
'registration',
'diplom',
'profile',
'blog',
'messages',
)
from installed_apps.grappelli import *
from installed_apps.logs import *
from installed_apps.filebrowser import *
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.JSONSerializer'
DEFAULT_FROM_EMAIL = 'info@google.ru'
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'filters': {
'require_debug_false': {
'()': 'django.utils.log.RequireDebugFalse'
}
},
'handlers': {
'mail_admins': {
'level': 'ERROR',
'filters': ['require_debug_false'],
'class': 'django.utils.log.AdminEmailHandler'
}
},
'loggers': {
'django.request': {
'handlers': ['mail_admins'],
'level': 'ERROR',
'propagate': True,
},
}
}
AUTH_PROFILE_MODULE = 'profile.Profile'
Файл installed_apps/filebrowser.py (содержит все настройки для Filebrowser):
# -*- encoding: utf-8 -*-
from django.utils.translation import ugettext_lazy as _
FILEBROWSER_DIRECTORY = 'uploads/'
FILEBROWSER_CONVERT_FILENAME = True
FILEBROWSER_NORMALIZE_FILENAME = True
FILEBROWSER_VERSIONS = {
'admin_thumbnail': {'verbose_name': _('Admin Thumbnail'), 'width': 60, 'height': 60, 'opts': 'crop'},
'thumbnail': {'verbose_name': _('Thumbnail (1 col)'), 'width': 60, 'height': 60, 'opts': 'crop'},
'news_logo': {'verbose_name': (u'Логотип новости'), 'width': 100, 'height': 75, 'opts': 'crop'},
'services_thumbnail': {'verbose_name': (u'Миниатюры для услуг'), 'width': 120, 'height': 90, 'opts': 'crop'},
'our_face': {'verbose_name': (u'Фотографии наших сотрудников'), 'width': 216, 'height': 278, 'opts': 'crop'},
'profile_photo': {'verbose_name': (u'Фотография пользователя'), 'width': 205, 'height': 250, 'opts': 'crop'},
'admin_list_blog':{'verbose_name':u'Изображение для списока блогов(655*215)', 'width': 655, 'height': 215, 'opts': 'crop'},
}
FILEBROWSER_ADMIN_VERSIONS = ['thumbnail', 'news_logo', 'services_thumbnail', 'our_face', 'profile_photo',
'admin_list_blog'] #'small', 'medium', 'big', 'large',
FILEBROWSER_ADMIN_THUMBNAIL = 'admin_thumbnail'
FILEBROWSER_MAX_UPLOAD_SIZE = 60485760
FILEBROWSER_IMAGE_MAXBLOCK = 1024*1024
FILEBROWSER_EXTENSIONS = {
'Folder': [''],
'Image': ['.jpg','.jpeg','.gif','.png','.tif','.tiff'],
'Document': ['.pdf','.doc','.rtf','.txt','.xls','.csv'],
'video': ['.mov','.wmv','.mpeg','.mpg','.avi','.rm'],
'audio': ['.mp3','.mp4','.wav','.aiff','.midi','.m4p']
}
FILEBROWSER_SELECT_FORMATS = {
'file': ['Folder','Image','Document','video','audio'],
'image': ['Image'],
'document': ['Document'],
'media': ['video','audio'],
}
Файл diplom/urls.py (содержит все url настроек и сслыки на urls.py других приложений):
from django.conf.urls import patterns, include, url
from django.conf import settings
from django.contrib import admin
from filebrowser.sites import site
from .forms import RegistrationFormProfile
from profile.views import ProfileSettingView, UploadView
import registration
import os
admin.autodiscover()
urlpatterns = patterns('',
(r'^accounts/', include('registration.urls')),
url(r'^grappelli/', include('grappelli.urls')),
(r'^accounts/', include('registration.urls')),
(r'^accounts/profile/$', 'profile.views.profile_return'),
(r'^accounts/edit/profile/$', ProfileSettingView.as_view()),
url(r'^admin/filebrowser/', include(site.urls)),
url(r'^admin/', include(admin.site.urls)),
url(r'^media/(?P<path>.*)$', 'django.views.static.serve', {'document_root': settings.MEDIA_ROOT}),
url(r'^admin/doc/', include('django.contrib.admindocs.urls')),
(r'^upload_file/$', UploadView.as_view()),
url(r'^profile/', include('profile.urls')),
url(r'^message/', include('messages.urls')),
url(r'^blog/', include('blog.urls')),
)
Приложение PROFILE:
Файл profile/urls.py (содержит все url-ы приложения profile):
from django.conf.urls import patterns, include, url
from views import ProfileView
urlpatterns = patterns('',
url(r'^$', ProfileView.as_view(), name="profile"),
url(r'^friend/(?P<pk>\d+)/$', ProfileView.as_view(), name="profile_friend"),
url(r'^friends/all/$', 'profile.views.search_person'),
url(r'^friends/add/(?P<id>[^/]+)/$', 'profile.views.add_person'),
url(r'^friends/del/(?P<id>[^/]+)/$', 'profile.views.del_person'),
url(r'^friends/accept/(?P<objid>[^/]+)/$', 'profile.views.accept_friend'),
url(r'^friends/reject/(?P<objid>[^/]+)/$', 'profile.views.reject_friend'),
)
Файл profile/models.py (содержит модели Profile и FileProfile приложения profile):
# -*- coding: utf-8 -*-
from django.db import models
from django.contrib.auth.models import User
from diplom.settings import *
from filebrowser.fields import FileBrowseField
from django.core.urlresolvers import reverse
import datetime
class Profile(models.Model):
user = models.ForeignKey(User, related_name='profile', verbose_name=('User'), blank=True, null=True)
thumbnail = FileBrowseField(u"Иконка", max_length=200, directory='uploads/', format='image', null=True, blank=True)
requisites = models.TextField(u'Дополнительные сведения', blank=True)
login = models.CharField(u'Логин',max_length=150,blank=True, null=True)
last_name = models.CharField(u'Фамилия',max_length=150,blank=True)
first_name = models.CharField(u'Имя',max_length=150,blank=True)
midlle_name = models.CharField(u'Отчество',max_length=150,blank=True)
friends = models.ManyToManyField("self", blank=True, null=True, symmetrical = False, related_name='friends_targets')
friend_requests = models.ManyToManyField("self", blank=True, null=True, symmetrical = False, related_name='friend_requests_targets' )
icq = models.CharField(u'ICQ', max_length=150, blank=True)
skype = models.CharField(u'Skype', max_length=150, blank = True)
telephone = models.CharField(u'Телефон', max_length=150, blank = True)
party = models.BooleanField(u'Является участником сообщества', blank=True, default=False)
def __unicode__(self):
return u'%s' % (self.user)
def get_absolute_url(self):
return reverse("profile", kwargs={})
def get_files(self):
return FileProfile.objects.filter(user = self)
class Meta:
verbose_name = u"Пользователь"
verbose_name_plural = u"Пользователи"
class FileProfile(models.Model):
user = models.ForeignKey(Profile, related_name='profile', verbose_name=('User'), blank=True, null=True)
title = models.CharField(u"Заголовок", max_length=64, blank=True, null=True)
date = models.DateTimeField(verbose_name=u'Дата загрузки файла',blank=True, null=True, default = datetime.datetime.now)
file = FileBrowseField(u"Файл", directory='uploads/files/' , max_length=100, format = 'document', null=True, blank=True)
def __unicode__(self):
return u'%s' % (self.user)
class Meta:
verbose_name = u"Файл пользователя"
verbose_name_plural = u"Файлы пользователей"
Файл profile/admins.py (содержит подключение и настройки модели Profile, FileProfile для административной части):
from django.contrib import admin
from models import Profile, FileProfile
class ProfileContentAdmin(admin.TabularInline):
model = FileProfile
fields = ('title','file',)
extra = 1
class FileProfileAdmin(admin.ModelAdmin):
list_display = ('user', 'title', 'file', 'date')
ordering = ('user', 'date')
list_filter = ('user', 'date')
class ProfileAdmin(admin.ModelAdmin):
list_display = ('user', 'last_name', 'first_name', 'midlle_name', 'party')
ordering = ('user',)
list_filter = ('user',)
inlines = [ProfileContentAdmin]
admin.site.register(Profile,ProfileAdmin)
admin.site.register(FileProfile, FileProfileAdmin)
Файл profile/forms.py (содержит модельные формы ProfileForm, BlogForm, FileForm приложения profile):
# -*- encoding: utf-8 -*-
from django import forms
from models import Profile, FileProfile
from blog.models import Blog
class ProfileForm(forms.ModelForm):
class Meta:
model = Profile
exclude = ('user', 'thumbnail', 'login', 'friends', 'friend_requests', 'party')
image = forms.FileField(max_length=200, label="Иконка", required=False)
new_password = forms.CharField(max_length=150, required=False,label='Новый пароль')
confirm_password = forms.CharField(max_length=150, required=False,label='Подтвердить пароль')
class BlogForm(forms.ModelForm):
class Meta:
model = Blog
exclude = ('number','author','date_of_publication', 'image','tags')
thumbnail = forms.FileField(max_length=200, label="Обложка новости", required=False)
class FileForm(forms.ModelForm):
class Meta:
model = FileProfile
exclude = ('user', 'date', 'file')
doc = forms.FileField(max_length=200, label="Файл", required=False)
Файл profile/views.py (содержит View описывающие функционал главной страницы пользователя и страницы поиска других пользователей):
# -*- coding: utf-8 -*-
from django.views.generic import ListView, DetailView, TemplateView, View
from models import Profile
from messages.models import Chat, Message
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import render_to_response
from django.contrib.auth.models import User
from forms import ProfileForm, BlogForm, FileForm
from django.views.generic.edit import FormView, CreateView, UpdateView
from django.template import RequestContext
from django import forms
from django.db.models import Q
from django.http import HttpResponse, HttpResponseRedirect
import datetime
from django.shortcuts import render_to_response, redirect
from blog.models import Blog, Tags
from django.core.urlresolvers import reverse
from django.core.files.storage import default_storage
from django.core.files.base import ContentFile
from filebrowser.base import FileObject
def profile_return(request):
return render_to_response('profile/return.html', {}, context_instance=RequestContext(request))
class UploadView(View):
def post(self, request, *args, **kwargs):
form = FileForm(request.POST, request.FILES)
if form.is_valid:
instance = form.save(commit=False)
path = default_storage.save('uploads/files/file.pdf', ContentFile(self.request.FILES['doc'].read()))
instance.file = FileObject(path)
instance.user = Profile.objects.get(user = request.user)
instance.save()
return redirect(self.get_absolute_url())
def get_absolute_url(self):
return reverse("profile", kwargs={})
class ProfileView(CreateView):
template_name = "profile/profile.html"
form_class = BlogForm
def create_object_profile(self, pk):
vector = User.objects.get(pk = pk)
object = Profile()
object.user_id = vector.id
object.first_name = vector.first_name
object.last_name = vector.last_name
object.save()
return object
def get_context_data(self, **kwargs):
context = super(ProfileView, self).get_context_data(**kwargs)
if (self.request.user.is_authenticated()) and (Profile.objects.get(pk=self.request.user.id).party==True) and (not 'pk' in self.kwargs):
person_obj = self.request.user
if Profile.objects.filter(pk=person_obj.id).count()==0:
self.create_object_profile(person_obj.id)
person_pk = person_obj.id
elif self.request.user.is_authenticated() and 'pk' in self.kwargs and self.kwargs['pk']:
person_pk = self.kwargs['pk']
if Profile.objects.filter(pk=person_pk).count()==0:
self.create_object_profile(person_pk)
person_obj = User.objects.get(pk = person_pk)
else:
self.template_name = "profile/error.html"
context['ErrorText'] = u"Вы не авторизированны"
context['blog'] = Blog.objects.filter(author=person_obj.username).order_by('-number',)
context['person'] = Profile.objects.get(pk = person_pk)
context['form_file'] = FileForm
return context
def form_valid(self, form):
arr_obj_tags=[]
all_tags = self.request.POST['tag'].split(', ' or ',')
for elem in all_tags:
max_num_tags = Tags.objects.all().order_by('-number')[0]
obj_tag = Tags(number = max_num_tags.number+1, name = elem)
obj_tag.save()
arr_obj_tags.append(obj_tag)
instance = form.save(commit=False)
blogs = Blog.objects.filter(author=self.request.user.username).order_by('-number')
if blogs.count() > 0:
instance.number = blogs[0].number+1
else:
instance.number = 1
instance.author = self.request.user.username
instance.date_of_publication = datetime.datetime.now()
path = default_storage.save('uploads/mar.jpg', ContentFile(self.request.FILES['thumbnail'].read()))
obj_image = FileObject(path).version_generate('admin_list_blog')
instance.image = obj_image
instance.save()
for elem in arr_obj_tags:
instance.tags.add(elem)
return redirect(self.get_absolute_url())
def get_absolute_url(self):
return reverse("profile", kwargs={})
class ProfileSettingView(TemplateView, View):
template_name = "user_profile/user_settings.html"
http_method_names = ['get', 'post']
def post(self, request, *args, **kwargs):
object = Profile.objects.get(user = request.user)
form = ProfileForm(request.POST, request.FILES, instance = object) # A form bound to the POST data
if form.is_valid():
form.save(commit = False)
path = default_storage.save('uploads/mar.jpg', ContentFile(request.FILES['image'].read()))
obj_image = FileObject(path).version_generate('profile_photo')
object.thumbnail = obj_image
object.save()
# Новый пароль и Подверждение пароля
new_password = request.POST['new_password']
confirm_password = request.POST['confirm_password']
# Выбираем обьект User для изменеия пароля
index = User.objects.get(pk = request.user.id)
index.set_password(new_password)
if new_password == '':
mes = 'Поле (новый пароль) пусто:'
else:
if confirm_password == new_password:
mes = 'Пароли совпадают!'
index.save()
else:
mes = 'Пароли не совпадают!'
form.save()
return redirect(reverse("profile", kwargs={}))
def get_context_data(self, **kwargs):
context = super(ProfileSettingView, self).get_context_data(**kwargs)
if self.request.user.is_authenticated() and Profile.objects.get(pk=self.request.user.id).party==True:
object = Profile.objects.get(user = self.request.user)
context['object'] = object
user_id = self.request.user
context['form'] = ProfileForm(instance = object)
if not Profile.objects.filter(pk = user_id.id):
person = Profile()
person.pk = user_id.id
person.user = user_id
person.save()
return context
else:
template_name = "profile/error.html"
context['ErrorText'] = u"Вы не авторизированны"
return context
@csrf_exempt
def search_person(request):
ERROR = None
if request.user.is_authenticated() and Profile.objects.get(pk=request.user.id).party==True:
class ContactForm(forms.Form):
last_name = forms.CharField(max_length=150, required=False)
first_name = forms.CharField(max_length=150, required=False)
user = forms.CharField(max_length=150, required=False)
midlle_name = forms.CharField(max_length=150, required=False)
current_user = request.user.get_profile
if request.method == 'POST':
form = ContactForm(request.POST)
params = []
if form.is_valid():
last_name = form.cleaned_data['last_name']
first_name = form.cleaned_data['first_name']
midlle_name = form.cleaned_data['midlle_name']
user = form.cleaned_data['user']
kargs = {'last_name__icontains':last_name}
params.append(apply(Q, (), kargs))
params.append(Q(first_name__icontains = first_name))
params.append(Q(midlle_name__icontains = midlle_name))
params.append(Q(user__username__icontains = user))
search_result = Profile.objects.filter(*params)
context = {'form': form, 'search_result': search_result, 'User':current_user}
else:
form = ContactForm()
context = {'form':form, 'User':current_user}
return render_to_response('friends/search_person.html', context, context_instance=RequestContext(request))
else:
return render_to_response("friends/error.html", {'ErrorText': u"Вы не авторизированны"})
def add_person(request, id):
obj = Profile.objects.get(user = id)
user = Profile.objects.get(user = request.user.get_profile)
user.friends.add(obj)
obj.friend_requests.add(user)
return HttpResponseRedirect('/profile/friends/all/')
def del_person(request, id):
obj = Profile.objects.get(user = id)
user = Profile.objects.get(user = request.user.get_profile)
user.friends.remove(obj)
return HttpResponseRedirect('/profile/friends/all/')
def accept_friend(request, objid):
obj = Profile.objects.get(user= request.user.get_profile)
obj_friend = Profile.objects.get(user = objid)
obj.friends.add(obj_friend)
obj.friend_requests.remove(obj_friend)
return HttpResponseRedirect('/profile/friends/all/')
def reject_friend(request, objid):
obj = Profile.objects.get(user = request.user.get_profile)
obj_friend = Profile.objects.get(user = objid)
obj.friend_requests.remove(obj_friend)
return HttpResponseRedirect('/profile/friends/all/')
@csrf_exempt
def my_friends(request):
ERROR = None
if request.user.is_authenticated() and Profile.objects.get(pk=request.user.id).party==True:
friends = User.objects.all()
if request.method == 'POST':
pass
context = {'friends': friends}
return render_to_response('friends/choice_friend.html', context, context_instance=RequestContext(request))
else:
# если не залогинен
# хорошо бы сделать из этой проверки декоратор, чтоб всюду её не тоскать за собой
return render_to_response("friends/error.html", {'ErrorText': u"Вы не авторизированны"})
Приложение BLOG:
Файл urls.py для приложения blog:
from django.conf.urls import patterns, include, url
from .views import BlogList, ArticleMonthArchiveView, BlogDetail, FilterTagsView, BlogSearchView
urlpatterns = patterns('',
url(r'^list/$', BlogList.as_view(), name='blog_list'),
url(r'^(?P<year>\d{4})/(?P<month>\d+)/$',
ArticleMonthArchiveView.as_view(month_format='%m'),
name="archive_month_numeric"),
url(r'^detail/(?P<pk>\d+)/$', BlogDetail.as_view(), name='blog_detail'),
url(r'^filter/tag/(?P<pk>\d+)/', FilterTagsView.as_view(),name='filter_tags'),
url(r'^search/$', BlogSearchView.as_view(), name='blog_search'),
)
Файл models.py для приложения blog:
# -*- coding: utf-8 -*-
from django.db import models
from django.contrib.auth.models import User
from filebrowser.fields import FileBrowseField
from model_utils.fields import SplitField
from model_utils.models import TimeStampedModel
import datetime
from django.db.models import permalink
from django.core.urlresolvers import reverse
class Tags(models.Model):
number = models.IntegerField(u'Порядковый номер', blank=True, null=True)
name = models.CharField(u'Название тега', max_length=500)
def __unicode__(self):
return u'%s' % (self.name)
class Meta:
verbose_name = u"Тег"
verbose_name_plural = u"Теги"
class Blog(models.Model):
number = models.IntegerField(u"Порядкоый номер", blank=True, null=True)
zagol = models.CharField(u"Заголовок", max_length=64, blank=True, null=True)
author = models.CharField(u"Автор", max_length=200, blank=True)
image = FileBrowseField(u"Фото", max_length=200, directory='images/blog/', format='image', null=True, blank=True)
date_of_publication = models.DateTimeField(verbose_name=u'Дата публикации новости',blank=True, null=True, default = datetime.datetime.now)
text = SplitField(u'Текст', blank=True)
tags = models.ManyToManyField(Tags, verbose_name=u'Теги', blank=True)
def __unicode__(self):
return u'%s' % (self.zagol,)
class Meta:
verbose_name = u"Блог"
verbose_name_plural = u"Блог"
class Comment(models.Model):
blog=models.ForeignKey(Blog, verbose_name=u'От какого блога', related_name='link_blog', blank=True,null=True)
author_name=models.CharField(u"Автор", max_length=120, blank=True)
text=models.TextField(u'Комментарий',blank=True)
pub_date=models.DateTimeField(u'Дата публикации', default = datetime.datetime.now, blank=True)
admin_comment=models.BooleanField(u'Допущена администратором', default=False, blank=True)
def __unicode__(self):
return self.author_name
def count_comment(self):
return Comment.objects.filter(article=self.article).count()
class Meta:
verbose_name = u"Комментарий"
verbose_name_plural = u"Комментарии"
ordering=[ 'pub_date', ]
Файл admin.py для приложения blog:
# -*- encoding: utf-8 -*-
from django.contrib import admin
from .models import Blog, Comment, Tags
class BlogAdmin(admin.ModelAdmin):
list_display = ('number', 'zagol',)
list_display_links = ('number', 'zagol',)
list_filter = ()
readonly_fields = ()
ordering = ('number',)
date_hierarchy='date_of_publication'
class TagsAdmin(admin.ModelAdmin):
list_display = ('number', 'name',)
list_display_links = ('number', 'name',)
list_filter = ()
readonly_fields = ()
ordering = ('name',)
class CommentAdmin(admin.ModelAdmin):
list_display=('pub_date', 'author_name', )
search_fields=('text', )
list_filter=('author_name', )
date_hierarchy='pub_date'
admin.site.register(Blog, BlogAdmin)
admin.site.register(Tags, TagsAdmin)
admin.site.register(Comment, CommentAdmin)
Файл forms.py для приложения blog:
# -*- coding: utf-8 -*-
from django import forms
from registration.forms import RegistrationFormUniqueEmail
from django.core.mail import send_mail
from .models import Blog
class RegistrationFormProfile(RegistrationFormUniqueEmail):
requisites = forms.CharField(label = 'Работа')
class BlogForm(forms.Form):
name = forms.CharField(max_length=150)
email = forms.EmailField()
message = forms.CharField(widget=forms.Textarea)
def send_email(self, subject, message, from_email, recipient_list=['viktori2000@qip.ru'], fail_silently=False):
return send_mail(subject, message, from_email, recipient_list, fail_silently)
Файл views.py для приложения blog:
# -*- coding: utf-8 -*-
from django.views.generic import ListView, DetailView, TemplateView
from .models import Blog, Tags, Comment
from django.contrib.auth.models import User
from django.views.generic.edit import FormView, CreateView, UpdateView
from django.forms import ModelForm
from django import forms
from django.db.models import Q
from django.contrib.auth.models import User
import datetime
from .forms import BlogForm
from django.views.generic.edit import CreateView
from django.views.generic.dates import MonthArchiveView
from profile.models import Profile
from django.shortcuts import redirect
from django.core.urlresolvers import reverse
class CreateNews(CreateView):
model = Blog
fields = ['number','zagol','author','image','date_of_publication','text','tags']
def get_context_data(self, **kwargs):
if self.request.user.is_authenticated() and Profile.objects.get(pk=self.request.user.id).party==True:
context = super(CreateNews, self).get_context_data(**kwargs)
context['object'] = Profile.objects.get(pk = self.request.user.id)
return context
class BlogList(ListView):
model = Blog
context_object_name = "blog"
def get_context_data(self, **kwargs):
context = super(BlogList, self).get_context_data(**kwargs)
if self.request.user.is_authenticated() and Profile.objects.get(pk=self.request.user.id).party==True:
date_obj = []
for obj in Blog.objects.dates('created', 'month'):
date_obj.append(obj)
context['archives'] = date_obj
context['active_main_menu']='main_blog'
else:
self.template_name = "profile/error.html"
context['ErrorText'] = u"Вы не авторизированны"
return context
class FilterTagsView(DetailView):
model = Blog
template_name = "blog/blog_list.html"
queryset = Blog.objects.all()
def get_context_data(self, **kwargs):
context = super(FilterTagsView, self).get_context_data(**kwargs)
if self.request.user.is_authenticated() and Profile.objects.get(pk=self.request.user.id).party==True:
date_obj = []
for obj in Blog.objects.dates('created', 'month'):
date_obj.append(obj)
object = super(FilterTagsView, self).get_object()
context['blog'] = Blog.objects.filter(tags__pk = object.pk)
context['archives'] = date_obj
context['active_main_menu']='main_blog'
return context
class BlogSearchView(ListView):
template_name = 'blog/blog_list.html'
model = Blog
def get_context_data(self, **kwargs):
context = super(BlogSearchView, self).get_context_data(**kwargs)
if self.request.user.is_authenticated() and Profile.objects.get(pk=self.request.user.id).party==True:
date_obj = []
for obj in Blog.objects.dates('created', 'month'):
date_obj.append(obj)
context['archives'] = date_obj
context['active_main_menu']='main_blog'
search_word = self.request.GET['search_box'].lower()
arr = []
arr.append(Blog.objects.filter(Q(zagol__icontains = search_word)))
context['blog']=Blog.objects.filter(Q(zagol__icontains = search_word)|Q(author__icontains = search_word)|Q(text__icontains = search_word))
return context
class ArticleMonthArchiveView(MonthArchiveView):
queryset = Blog.objects.all()
date_field = "created"
make_object_list = True
allow_future = True
def get_context_data(self, **kwargs):
if self.request.user.is_authenticated() and Profile.objects.get(pk=self.request.user.id).party==True:
context = super(ArticleMonthArchiveView, self).get_context_data(**kwargs)
date_obj = []
for obj in Blog.objects.dates('created', 'month'):
date_obj.append(obj)
context['archives'] = date_obj
context['active_main_menu']='main_blog'
return context
class BlogDetail(CreateView):
template_name = "blog/blog_detail.html"
model = Comment
exclude = ['article', 'author_name', 'pub_date', 'admin_comment']
def get_context_data(self, **kwargs):
if self.request.user.is_authenticated() and Profile.objects.get(pk=self.request.user.id).party==True:
context = super(BlogDetail, self).get_context_data(**kwargs)
date_obj = []
for obj in Blog.objects.dates('created', 'month'):
date_obj.append(obj)
context['blog'] = Blog.objects.get(pk = self.kwargs['pk'])
context['archives'] = date_obj
context['active_main_menu'] = 'main_blog'
context['comments'] = Comment.objects.filter(article = context['blog'], admin_comment=True)
return context
def form_valid(self, form):
instance = form.save(commit=False)
instance.article = Blog.objects.get(pk=self.kwargs['pk'])
instance.author_name = self.request.user.username
instance.pub_date = datetime.datetime.now()
instance.save()
return redirect(self.get_absolute_url())
def get_absolute_url(self):
return reverse("blog_detail", kwargs={"pk": self.kwargs['pk']})
Приложение MESSAGE:
Файл urls.py для приложения message:
from django.conf.urls import patterns, include, url
urlpatterns = patterns('',
url(r'^$', 'messages.views.all_message'),
url(r'^sendmessage/(?P<id>[^/]+)/$', 'messages.views.send_message'),
)
Файл admin.py для приложения message:
from django.contrib import admin
from .models import Message, Chat
class MessageAdmin(admin.ModelAdmin):
list_display = ('recipient', 'sender', 'date', 'title')
ordering = ('recipient', 'sender')
list_filter = ('recipient', 'sender')
admin.site.register(Message,MessageAdmin)
class ChatAdmin(admin.ModelAdmin):
list_display = ('person1', 'person2')
ordering = ('person1', 'person2')
list_filter = ('person1', 'person2')
admin.site.register(Chat,ChatAdmin)
Файл views.py для приложения message:
# -*- coding: utf-8 -*-
from .models import Message, Chat
from profile.models import Profile
from django.views.decorators.csrf import csrf_exempt
from django.template import RequestContext # нужно чтобы передавать реквест в контекст
from django.forms import ModelForm
from django.http import HttpResponse, HttpResponseRedirect
from django import forms
from django.db.models import Q
from django.shortcuts import render_to_response, redirect
from django.contrib.auth.models import User
import datetime
import time
@csrf_exempt
def all_message(request):
ERROR = None
if request.user.is_authenticated() and Profile.objects.get(pk=request.user.id).party==True:
i_am = User.objects.get(pk = request.user.id)
temp4 = Q(person2 = i_am)
temp5 = Q(person1 = i_am)
all_friend = Chat.objects.filter(temp4 | temp5)
i_am = User.objects.get(pk = request.user.id)
messages = Message.objects.filter(recipient = i_am).filter(reader = False).count()
image = all_friend
context = {'all_friend':all_friend, 'messages': messages, 'image':image}
return render_to_response('friends/all_message.html', context, context_instance=RequestContext(request))
else:
return render_to_response("friends/error.html", {'ErrorText': u"Вы не авторизированны"})
@csrf_exempt
def send_message(request, id):
class MessageForm(forms.Form):
title = forms.CharField(max_length=350, required=False)
message = forms.CharField(max_length=350, required=False, widget=forms.Textarea)
friend = User.objects.get(pk = id)
i_am = User.objects.get(pk = request.user.id)
temp1 = Q(recipient = friend) & Q(sender = i_am)
temp2 = Q(recipient = i_am) & Q(sender = friend)
temp3 = temp1 | temp2
temp4 = Q(person2 = i_am) & Q(person1 = friend)
temp5 = Q(person1 = i_am) & Q(person2 = friend)
if len(Chat.objects.filter(temp4 | temp5)) == 0:
chat = Chat()
chat.person1 = i_am
chat.person2 = friend
chat.save()
else:
pass
if request.method == 'POST':
all_message = Message.objects.filter(temp3).order_by('date')
chat_obj = Chat.objects.get(temp4 | temp5)
form = MessageForm(request.POST)
if form.is_valid():
title = form.cleaned_data['title']
message = form.cleaned_data['message']
send_message = Message()
send_message.recipient = friend
send_message.sender = i_am
send_message.date = datetime.datetime.now()
send_message.title = title
send_message.message = message
send_message.reader = False
send_message.save()
chat_obj.messages.add(send_message)
chat_obj.save()
return redirect('/message/sendmessage/'+ str(id) + '/')
else:
all_message = Message.objects.filter(temp3).order_by('date')[0::1]
temp4 = Q(person2 = i_am) & Q(person1 = friend)
temp5 = Q(person1 = i_am) & Q(person2 = friend)
person_type = Chat.objects.filter(temp4 | temp5)
form = MessageForm()
send = Message.objects.filter(temp2)
for elem in send:
if not elem.reader:
elem.reader=True
elem.save()
context = {'form':form, 'friend': friend, 'all_message': all_message}
return render_to_response('friends/send_message.html', context, context_instance=RequestContext(request))
ПРИЛОЖЕНИЕ В
ККМОО «Молодежная лига развития национальных культур Кубани»
350000, г. Краснодар, ул. Красноармейская 53.
ИНН: 2310023918
КПП: 231001001
Счет № 407038108000000000019
Банк: АО «Юг-Инвест банк», г. Краснодар
БИК 040349966
Счет №: 30101810600000000966
Код по ОКОНХ 98400
Код по ОКПО 39744165
Приложение № 1 к договору № ________ от «__» ______ 2012 г.
Техническое задание для создания программной и визуальной части сайта:
1.Разработка дизайна для графической части сайта.
2.Разработка логотипа в векторном формате.
3.Создание программного обеспечения на языке программирования Python, в среде Django.
4.Создание социального раздела для общения между участниками «Клубов интернациональной дружбы».
5.Размещение файлов в сети интернет для отображения текстовой и графической информации в сети интернет, с возможностью вывода информации из базы данных и редактирования содержимого базы данных
6.Создание раздела для вновь созданных и создаваемых «Клубов интернациональной дружбы», в который можно будет добавлять текстовую, графическую и видео информацию, в том числе на английском языке.
Правообладатель __________________
Приобретатель _______________
РАЗРАБОТКА АРХИТЕКТУРЫ ЗАКРЫТОЙ КОРПОРАТИВНОЙ СЕТИ С ИСПОЛЬЗОВАНИЕМ ФРЕЙМВОРКА DJANGO