Djangoで他のオブジェクトをそのまま保存したかった

経緯

スカンディナヴィア半島2(Twitter Web クライアント)を作るためにDjangoでTweepyからとってきたオブジェクトをそのまま保存したほうがいいと思った
tweetpy でとれたobjectをDjangoで保存したい→pickleしよう - Togetter

結論

無理だった。理由は2つ

  • そもそもデータ量がデカすぎて現実的じゃない
  • なぜかModelsに格納できないし、取り出せない

メモを箇条書きしたのでどうぞ

pickleについて

  • オブジェクトはポインタを持っていたりして複雑
  • だからシリアライズする必要がある
  • シリアライズ
    • データの直列化、バイト化
    • pickle.dumps(obj)
  • シリアライズ
  • データ量が大きい
  • 無駄に大きい
    • データ保存に難がある
    • 検索やなにかをするときに手間がかかる

試算

  • 1ユーザーが1日10000ツイート取得するのを1日分持つ
    • 1オブジェクトが2575B程度
      • zlib使っても1200B程度。まだでかい
    • 25MB
      • 100ユーザーで25000MB, 2.5TB
  • 現実的じゃない

結論

必要なデータだけモデルにマッピングしたほうが現実的

簡単なpickleの使い方

import tweepy
#seeはdirするよりわかりやすい便利パッケージ
from see import see
#パブリックTimeLineの取得
tm = tweepy.api.public_timeline()
print tm[0]
# <tweepy.models.Status at 0x2ccf5d0>
import cPickle as pickle
#シリアライズ
dump = pickle.dumps(tm[0])
#ちゃんとシリアライズされました。これが2475文字程度ある
print dump
 #'ccopy_reg\n_reconstructor\np0\n(ctweepy.models\nStatus\np1\nc__builtin__\nobject\np2\nNtp3\nRp4\n(dp5\nS\'favorited\'\np6\nI00\nsS\'contributors\'\np7\nNsS\'truncated\'\np8\nI00\nsS\'text\'\np9\nS"Today\'s Full Moon in your sign heightens your emotional sensit... More for Virgo http://t.co/hrG5PcAW"\np10\nsS\'source_url\'\np11\nS\'http://www.twittascope.com\'\np12\nsS\'in_reply_to_status_id\'\np13\nNsS\'user\'\np14\ng0\n(ctweepy.models\nUser\np15\ng2\nNtp16\nRp17\n(dp18\nS\'follow_request_sent\'\np19\nNsS\'profile_use_background_image\'\np20\nI01\nsS\'contributors_enabled\'\np21\nI00\nsS\'id\'\np22\nI278282775\nsS\'verified\'\np23\nI00\nsS\'profile_image_url_https\'\np24\nS\'https://si0.twimg.com/profile_images/1864096060/Snapshot_20120225_3_normal.JPG\'\np25\nsS\'profile_sidebar_fill_color\'\np26\nS\'E6F6F9\'\np27\nsS\'is_translator\'\np28\nI00\nsS\'profile_text_color\'\np29\nS\'333333\'\np30\nsS\'followers_count\'\np31\nI320\nsS\'profile_sidebar_border_color\'\np32\nS\'DBE9ED\'\np33\nsS\'location\'\np34\nS\'Bittersweet, Texas\'\np35\nsS\'default_profile_image\'\np36\nI00\nsS\'listed_count\'\np37\nI3\nsS\'utc_offset\'\np38\nI-28800\nsS\'statuses_count\'\np39\nI12091\nsS\'description\'\np40\nS" Everything sounds better when it\'s written out, but it\'s up to you to believe what you see. Remember seeing is believing. Follow me or Unfollow me!"\np41\nsS\'friends_count\'\np42\nI326\nsS\'profile_link_color\'\np43\nS\'db1154\'\np44\nsS\'profile_image_url\'\np45\nS\'http://a0.twimg.com/profile_images/1864096060/Snapshot_20120225_3_normal.JPG\'\np46\nsS\'notifications\'\np47\nNsS\'show_all_inline_media\'\np48\nI01\nsS\'geo_enabled\'\np49\nI01\nsS\'profile_background_color\'\np50\nS\'DBE9ED\'\np51\nsS\'id_str\'\np52\nS\'278282775\'\np53\nsS\'profile_background_image_url\'\np54\nS\'http://a0.twimg.com/profile_background_images/434604785/Snapshot_20120224_8.JPG\'\np55\nsS\'screen_name\'\np56\nS\'doLL_housE_lova\'\np57\nsS\'lang\'\np58\nS\'en\'\np59\nsS\'profile_background_tile\'\np60\nI01\nsS\'favourites_count\'\np61\nI30\nsS\'name\'\np62\nS\'FunSize_Tweeter :)\'\np63\nsS\'url\'\np64\nS\'http://www.facebook.com/dannielynn\'\np65\nsS\'created_at\'\np66\ncdatetime\ndatetime\np67\n(S\'\\x07\\xdb\\x04\\x07\\x00\\x031\\x00\\x00\\x00\'\np68\ntp69\nRp70\nsS\'profile_background_image_url_https\'\np71\nS\'https://si0.twimg.com/profile_background_images/434604785/Snapshot_20120224_8.JPG\'\np72\nsS\'time_zone\'\np73\nS\'Pacific Time (US & Canada)\'\np74\nsS\'protected\'\np75\nI00\nsS\'default_profile\'\np76\nI00\nsS\'following\'\np77\nI00\nsbsS\'geo\'\np78\nNsg22\nI177751129516544000\nsS\'possibly_sensitive\'\np79\nI00\nsS\'author\'\np80\ng17\nsg66\ng67\n(S\'\\x07\\xdc\\x03\\x08\\r*\\x1a\\x00\\x00\\x00\'\np81\ntp82\nRp83\nsS\'retweeted\'\np84\nI00\nsS\'coordinates\'\np85\nNsS\'in_reply_to_user_id_str\'\np86\nNsS\'source\'\np87\nS\'Twittascope\'\np88\nsS\'in_reply_to_status_id_str\'\np89\nNsS\'in_reply_to_screen_name\'\np90\nNsS\'in_reply_to_user_id\'\np91\nNsS\'place\'\np92\nNsS\'retweet_count\'\np93\nI0\nsg52\nS\'177751129516544000\'\np94\nsb.'
#デシリアライズ
load = pickle.loads(dump)
#ちゃんとオブジェクトに復元されてる
print load
#<tweepy.models.Status at 0x2ccf5d0>
see(load)
# hash()                    repr()                    str()
#   .author                   .contributors             .coordinates
#    .created_at               .destroy()                .favorite()
#    .favorited                .geo                      .id
#   .id_str                   .in_reply_to_screen_name  .in_reply_to_status_id
#  .in_reply_to_status_id_str                          .in_reply_to_user_id
#    .in_reply_to_user_id_str  .parse()                  .parse_list()
#    .place                    .possibly_sensitive       .retweet()
#    .retweet_count            .retweeted                .retweets()
#    .source                   .source_url               .text
#    .truncated                .user
#もちろんプロパティにもアクセスできる
print load.text
# "Today's Full Moon in your sign heightens your emotional sensit... More for Virgo http://t.co/hrG5PcAW"

ただDjangoのmodles.CharFiledや modles.TextFieldではつかえなかった

DBになぜか保存されるし取り出せるけど、中途半端にしか入らないし、なんかいろいろエラーがでる。原理的にはおかしいと思うんだけど、なぜだろう……

追記

@xxxx DBに保存できてないってのは、エラー内容を見てみないと正確なことは言えないけど、max_lengthの指定が小さいとかunicode/strの変換で失敗してるとかじゃないかな。

max_lengthの指定は3000とか5000とか10000とか試したのでそこで文字列が入りきらないということはない感じでした。ただ、たしかに文字列の変換あたりでコケてる感じはしていています。あとはmodels.CharField()とTextFiledってmodels.Model を継承しているクラスだから models.Fieldを継承すればいいのか?とか検証の余地はあります。
まあおれはどちらにしろ意義がなくなったから疲れたのだよ……

thx!!

@xxxx ++