Пользователь
0,0
рейтинг
15 сентября 2014 в 15:41

Разработка → Django + API Вконтакте: постим записи с вложениями, получение списка групп и записей из песочницы

image


Доброго времени суток, Хабр!

Недавно разрабатывала сайт на python/django и нужна была возможность использования API Вконтакте. А именно:

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

Собственно, для администратора управление этим функционалом осуществляется через админку.

Расскажу по шагам реализацию этого функционала.

На первом этапе администратору нужно создать своё приложение во «Вконтакте», а также получить токен для работы с API. Для получения токена я использую модуль VKAppAuth (ссылка на GitHub, там же пример использования модуля).

Настройки для получения сохраняю в отдельном файле и подключаю его в models.py. В models.py добавляю поля для «Вконтакте» в модель статьи:

    photo_vk = models.ImageField(upload_to=photo_vk_path, verbose_name=u'Прикрепить фото для Вконтакте', max_length = 1000, blank=True)
    file_vk = models.FileField(upload_to=files_vk_path, verbose_name=u'Прикрепить документ для Вконтакте', max_length = 1000, blank=True)
    wall_user_vk = models.BooleanField(verbose_name=u'Отправить на стену пользователя Вконтакте', default=False)
    group_vk = models.ManyToManyField(Vk_groups, verbose_name=u'Отправить в следующие группы Вконтакте', blank=True)
    group_stat = models.BooleanField(verbose_name=u'Публиковать запись от имени группы', default=False)


Поле group_vk отображает все группы «Вконтакте» из таблицы Vk_groups. Код класса Vk_groups в models.py:

class Vk_groups(models.Model):
    title = models.CharField(max_length=1000, verbose_name=u'Название группы')
    gid = models.CharField(max_length=1000, verbose_name=u'ID группы')
    is_closed = models.BooleanField(verbose_name=u'Закрытая группа', default=False)
    is_admin = models.BooleanField(verbose_name=u'Пользователь является администратором', default=False)

    def __str__(self):
        return self.title.encode('utf8')

    class Meta:
        verbose_name = "Группа Вконтакте"
        verbose_name_plural = "Группы Вконтакте"


Также создана модель для записей «Вконтакте». Они будут загружаться с помощью автоматически обновляемого на сервере скрипта, таким образом, в админке будут актуальные записи со страницы администратора и из групп, в которых он состоит.

Поля модели:

class Vk_posts(models.Model):
    group = models.CharField(max_length=1000, verbose_name=u'Страница/группа', blank=True)
    text = HTMLField(verbose_name=u'Текст записи', blank=True)
    date = models.DateTimeField(verbose_name=u'Дата публикации', blank=True)


Для модели статьи я переопределила метод save(), чтобы при сохранении какой-либо статьи происходило обращение к API Вконтакте и производилась отправка записи во «Вконтакте», если поставлены соответствующие галочки.

    def save(self, *args, **kwargs):
        use_vk(self)   
        model = super(Page, self).save(*args, **kwargs)


Перед сохранением вызывается функция use_vk, в которой происходит обращение к API.

#пути для фото и документов
photo_vk_path = 'photo_vk'
files_vk_path = 'files_vk'

def use_vk(self):
    #составление записи
    msg = self.title + '\n' + self.text
    msg = re.sub('<.*?>','',msg)
    #флаг для определения, нужно ли отправлять запись
    post_vk = 0
    #получение списка групп        
    groups = [g for g in self.group_vk.all()]
    
    if len(groups) or self.wall_user_vk:
        post_vk = 1

    if post_vk:
        attachments = []
        #для прикрепления фото
        if self.photo_vk:
            server = vk.photos.getWallUploadServer(uid = my_id)
            path = MEDIA_ROOT + photo_vk_path + '/' + re.split('/', self.photo_vk.url)[-1]
            r = requests.post(server['upload_url'], files={'photo': open(path,"rb")})
            params = {'server': r.json()['server'], 'photo': r.json()['photo'], 'hash': r.json()['hash']}
            wallphoto = vk.photos.saveWallPhoto(**params)
            attachments.append(wallphoto[0]['id'])
        #для прикрепления документа         
        if self.file_vk:
            server = vk.docs.getWallUploadServer()
            path = MEDIA_ROOT + files_vk_path + '/' + re.split('/', self.file_vk.url)[-1]
            r = requests.post(server['upload_url'], files={'file': open(path,"rb")})
            params = {'file': r.json()['file']}
            doc = vk.docs.save(**params)
            attachments.append('doc' + str(my_id) + '_' + str(doc[0]['did']))
                    
        params = {'attachments': ','.join(attachments), 'message': msg}
        #для отправки записи на стену администратора
        if self.wall_user_vk:
            params['owner_id'] = my_id
            vk.wall.post(**params)
        #для отправки записи в выбранные группы           
        if len(groups):
            if self.group_stat:
                params['from_group'] = 1
            for g in groups:
                params['owner_id'] = g.gid
                vk.wall.post(**params)


Нужно отметить, что загрузчик для фото и документов выдавал некорректный путь к файлам в папках по вызову его метода url (не исключаю, что это именно у меня), поэтому я сама составляла путь до картинок и документов (переменная path).

Подробнее об этапах загрузки фото или документа к записи:

a) Отправка запроса на получение адреса сервера Вконтакте, куда можно загрузить фото или документ;
b) Получение адреса сервера Вконтакте;
c) Формирование post-запроса по адресу сервера с загрузкой на него документа или фото;
d) При успешной загрузке получение ответа с идентификатором загруженного документа или фото;
e) Формирование списка атрибутов для размещения записи во Вконтакте, в числе которых идентификатор фото или документа.

После прикрепления фото/документа используется завершающий метод API vk.wall.post(**params), отправляющий запись на стену администратора/группы запись.

Для получения списка групп и сохранения новых записей из групп и со стены администратора используется скрипт, который автоматически обновляется на сервере в заданное время. Данный скрипт получает настройки django, импортирует к себе необходимые модели и файл получения токена, а затем через API получает группы администратора, записи со стены/из групп и обновляет таблицы базы данных:

import sys
import time
sys.path = ['C:/site/umc/'] + sys.path
from django.core.management import setup_environ
import settings
setup_environ(settings)

from www.models import Vk_groups, Vk_posts
from umc.vk_response import *

count_posts = 15

def get_posts(owner_id, g_name):
    """Функция получения записей со стены администратора и групп и добавление новых записей в таблицу Vk_posts"""
    params = {'owner_id': owner_id, 'count': count_posts}
    answer = vk.wall.get(**params)
    for i in range(count_posts):
        params = {
            'group': g_name,
            'text': answer[i+1]['text'],
            'date': time.strftime("%Y-%m-%d %H:%M:%S+06:00", time.localtime(answer[i+1]['date']))
        }
        try:
            Vk_posts.objects.get_or_create(**params)
        except:
            params['text'] = u'Невозможно отобразить текст статьи'
            Vk_posts.objects.get_or_create(**params)
            

#добавление новых групп в таблицу Vk_groups
params = {'owner_id': my_id, 'extended': 1, 'filter': 'events, groups, admin'}
answer = vk.groups.get(**params)
for i in range(answer[0]):
    Vk_groups.objects.get_or_create(title = answer[i+1]['name'], gid = '-' + str(answer[i+1]['gid']), is_admin = answer[i+1]['is_admin'], is_closed = answer[i+1]['is_closed'])

#добавление новых записей из групп в таблицу Vk_posts
groups = Vk_groups.objects.all()
for g in groups:
    get_posts(g.gid, g.title)

#добавление новых записей со стены пользователя в таблицу Vk_posts    
user = vk.users.get(uid = my_id)
get_posts(my_id, user[0]['first_name']+ ' ' + user[0]['last_name'])


Собственно, теперь можно попробовать отправить записи из админки на стену или любую группу (если права доступа позволяют размещать записи на стене группы), и проверить работу.

Надеюсь, статья была полезной.
Ссылка на проект: github.com/julia-bikova/DjangoVkPosting

Всем приятной работы с Django!
@If-so-girl1
карма
4,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (24)

  • +1
    Какое-то все это… нерациональное и не параллельное. Интересно, сервер не захлебнется постить в 20-30 групп?
    Я правильно понима, что вы сначала постите записи, а потом совершаете обход и собирает ID-постов? Зачем?
    • 0
      Я понимаю, что наработки сыроватые, т.к. программист python ещё junior=)С опытом всё придёт)
      Нет, постинг записей и собирание постов — разные процессы. Любую статью или новость можно запостить Вконтакте — это одна функция. Параллельно с этим на сервере работает скрипт, который просто через определённое время обновляет отдельные таблицы в базе данных — таблицу для групп и таблицу для постов.
      • 0
        Наврятли это джуниор, это какой-нибудь стартер.
        >>Параллельно с этим на сервере работает скрипт, который просто через определённое время обновляет отдельные таблицы в базе данных — таблицу для групп и таблицу для постов.
        Вот это не совсем понятно что вы имели в виду.
        • 0
          Для отправки поста Во Вконтакте нужен список групп, куда можно запостить запись. Этот список периодически обновляется с помощью скрипта, который получает список групп администратора, и если появились новые, добавляет их в базу данных(в таблицу). Точно так же и со всеми последними постами из групп и со стены админа — проверяется, если ли новые, и если есть — добавляются в базу данных.
          • 0
            Странное решение. Обычно делаетка так — спрашиваем человека В КАКИЕ группы И ЧТО публиковать.

            Простой поймите — крутость системы определяется не тем, сколько процессов совершаются автоматически, а сколько процессов пользователь может кастомизировать под себя.
            • 0
              Вы видимо просто не понимаете. С помощью скрипта обновляется список групп админа, это отдельная таблица в базе данных.
              В админке у статей/новостей есть поля — отправить на стену админа вконтакте, есть поле manytomanyfield — список этих самых групп. То есть админ САМ выбирает, в КАКИЕ группы ему отправить КОНКРЕТНУЮ запись.
  • +3
    У вас при каждом сохранении статьи, будет создаваться запись вконтакте.

    Зачем переопределять метод save(), если есть сигналы?

    За try:except без указания типа исключения — нужно надавать по рукам.

    if len(groups) — то же самое, что и if groups

    • 0
      Нет, запись вконтакте создаётся только тогда, когда в админке администратор указал, что эту запись нужно отправить на стену вконтакте/ в группу. Для этого есть флаг post_vk.
      Про сигналы не знала, обязательно изучу.
      Спасибо за рекомендации и советы, буду знать и дальше развивать знания по python=)
      • 0
        Попробуйте несколько раз сохранить статью с флагом wal_post_vk равным True ;)
        • 0
          Ну логично запись постится Вконтакте)
          • 0
            Причем не один раз. Так и надо?
            • 0
              При каждом сохранении записи, если её надо отправить вконтакт, то она постится ровно один раз раз на стену админа и/или выбранной группы
              • 0
                В коде этого нет.
                • 0
                  В функции use_vk, где и происходит обращение к API, в конце два условия как раз на проверку — отправлять запись на стену админу и отправлять ли запись в выбранные группы. Там метод vk.wall.post(**params) — как раз и отправляет запись, если нужно. Т.е. если выполняется условие, что запись нужно отправить на стену — выполняется этот метод API, один раз (метод вызывается один раз).
                  • 0
                    Я все это прекрасно вижу. Похоже вы не понимаете суть проблемы.

                    Сценарий:
                    1) пользователь добавляет статью, установил wall_post_vk в True, нажал сохранить. Статья отправилась вконтакт.
                    2) пользователь заметил ошибку в заголовке, исправил ее, нажал сохранить
                    3) заметил еще одну ошибку, исправил, нажал сохранить

                    Вопрос — сколько будет статей опубликовано?
                    • 0
                      Хм, ну этого я не отрицаю, действительно, пока стоит wall_post_vk в True, статья будет поститься при каждом сохранении.
                      Просто это уже дальнейшее углубление в функциональность, такое и правда надо доработать… Только опять же, а если администратор именно так и хочет, что исправить заголовок и заново опубликовать запись вконтакте?.. Пока при быстром рассмотрении появляется идея удалять предыдущий пост и публиковать новый.
  • 0
    А почему не воспользовались готовыми наработками? например у github.com/ramusus есть полно репозиториев по теме.
    Может вам пригодится github.com/ramusus/django-vkontakte-wall
    • 0
      Это была работа по самостоятельной разработке, как раз для лучшего понимания) + диплом
  • +1
    Помимо советов выше, я бы добавил еще это. Вместо
    from umc.vk_response import *
    Использовать это
    from umc import vk_response ... vk_response.some_method()
    Так более понятно, откуда тот или иной метод, который используется в коде.
    И да. Почитайте про сигналы.
    И вообще не понятно, зачем вот эта строчка
    groups = [g for g in self.group_vk.all()]
    • 0
      строка
      groups = [g for g in self.group_vk.all()]
      

      получает весь список групп, которые выбрал админ, куда надо отправить запись. Затем запись постится в каждую группу из этого списка.
      • 0
        Я полагаю, что вопрос был конкретно про решение с:
        [g for g in self.group_vk.all()]
        
      • 0
        ОК. Тогда почитайте еще про models.ForeinKey(Model)
        и про models.ManyToManyField(Model)
        Тогда можно будет избавится от этой какахи) и получать делать так
        for group in self.group_vk.all()
        • 0
          Спасибо! Учусь)
          • 0
            Ну и за одно про reverse foreign key.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.