小村のポートフォリオサイト開発(10) DjangoRestFramework はてなブログの記事のデータ格納(2)



目次

  1. はてな記事をentryに書き込み
  2. 出たエラーとその対策



記録

はてな記事をentryに書き込み

  • 前回、はてなブログAPIで取得してたデータを、格納する形に変換してたんだよね

  • 引き続きもくもくとコーディングして下記になりました!

  • はてなブログのIDが一致するものがあれば更新をかけて、なければ新規作成します

# coding: utf-8

import os
import requests
import xmltodict

from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.response import Response

from ..models import Entry
from ..serializer import EntryAllSerializer, EntryCreateAndUpdateSerializer


class EntryViewSet(viewsets.ModelViewSet):
    queryset = Entry.objects.all()
    serializer_class = EntryAllSerializer

    @action(detail=False, methods=['post'])
    def capture(self, request):
        url = self.getHatenaApiUrl('entry')
        auth = self.getHatenaApiAuth()
        hatena_list = requests.get(url, auth=auth)
        dictData = xmltodict.parse(hatena_list.text, encoding='utf-8')
        entries = dictData['feed']['entry']
        for entry in entries:
            if not isinstance(entry, dict):
                continue

            # hatena_entry_id取得
            hatena_entry_id = entry['id'][
                entry['id'].rfind('-') + 1:] if 'id' in entry else ''

            # category取得
            if 'category' in entry:
                if isinstance(entry['category'], list):
                    category = entry['category'][0]['@term']
                else:
                    category = entry['category']['@term']
            else:
                category = ''

            # title取得
            title = entry['title'] if 'title' in entry else ''

            # summary取得
            summary = entry['summary']['#text'] if 'summary' in entry else ''

            # content_md取得
            content_md = entry['content']['#text'] if 'content' in entry else ''

            # content_html取得
            content_html = entry['hatena:formatted-content']['#text'] if 'hatena:formatted-content' in entry else ''

            # draft取得
            draft = entry['app:control']['app:draft'] if 'app:control' in entry else ''

            # published_at取得
            published_at = entry['published'] if 'published' in entry else None

            # edited_at取得
            edited_at = entry['app:edited'] if 'app:edited' in entry else None

            # updated_at取得
            updated_at = entry['updated'] if 'updated' in entry else None

            # 更新用パラメータ
            param = {
                'hatena_entry_id': hatena_entry_id,
                'category': category,
                'title': title,
                'summary': summary,
                'content_md': content_md,
                'content_html': content_html,
                'draft': draft,
                'published_at': published_at,
                'edited_at': edited_at,
                'updated_at': updated_at,
            }

            entry = Entry.objects.filter(
                hatena_entry_id=hatena_entry_id).first()

            if not entry:
                # 新規作成
                serializer = EntryCreateAndUpdateSerializer(data=param)
            else:
                # 更新
                serializer = EntryCreateAndUpdateSerializer(entry, data=param)

            if serializer.is_valid():
                serializer.save()
                print('valid-OK')
            else:
                print('valid-NG')

        return Response(entries)

    def getHatenaApiUrl(self, action):
        HATENA_API_URL_HEADER = 'https://blog.hatena.ne.jp'
        HATENA_API_USER = os.environ.get('HATENA_API_USER')
        HATENA_API_BLOG = os.environ.get('HATENA_API_BLOG')
        HATENA_API_URL_FUTTER = 'atom'

        url = [
            HATENA_API_URL_HEADER,
            HATENA_API_USER,
            HATENA_API_BLOG,
            HATENA_API_URL_FUTTER,
            action
        ]
        return os.path.join(*url)

    def getHatenaApiAuth(self):
        HATENA_API_USER = os.environ.get('HATENA_API_USER')
        HATENA_API_KEY = os.environ.get('HATENA_API_KEY')

        return (HATENA_API_USER, HATENA_API_KEY)
  • serializerは下記
# coding: utf-8

from rest_framework import serializers

from ..models import Entry


class EntryAllSerializer(serializers.ModelSerializer):
    class Meta:
        model = Entry
        fields = '__all__'


class EntryCreateAndUpdateSerializer(serializers.ModelSerializer):
    class Meta:
        model = Entry
        fields = (
            'entry_id',
            'hatena_entry_id',
            'title',
            'summary',
            'content_md',
            'content_html',
            'draft',
            'published_at',
            'edited_at',
            'updated_at',
        )



f:id:kom314_prog:20210821151236p:plain
  • entryモデルに10件のレコードが登録されることが確認できました。

  • 再度実行して更新されることも確認。ひゃっほ!



出たエラーとその対策

  • やっていく中でエラーとの格闘が勃発したのでそのメモ


TypeError: 'in <string>' requires string as left operand, not collections.OrderedDict

  • 下記のコードが原因で発生。辞書型のentrycategoryというkeyがあるかを確認しようとしている

  • しむらー!ぎゃくぎゃく!!!

            if entry in 'category':
  • 正解は下記
            if 'category' in entry:



おわりに

  • APIの第一歩がようやく踏み出せた感ありますね!

  • この先やっていくこととして

    • はてな記事全件取得するまで回す
    • content_mdを正しく取得
    • 記事の削除処理
    • リファクタリング
    • デプロイ
    • 毎日0:00に自動キック
  • あたりの作業がまっております。がんばるぞい!

  • ではでは。ちゃお~~~!