#ザックリ作ってみる
bottleのときもそうでしたが、Twitterクライアントを作るところからやるとDjangoの基本的理解が深まるんじゃないかなー。というところで、投稿・リプライ・ホームタイムラインくらいが1画面で見れるようなものを作ります。
このようなイメージに仕上がります。
1.APIのリミット表示
2.タイムラインを1つ表示
3.タイムラインのユーザー名または画像クリックでリプライ準備
4.テキストツイートが出来る
といった仕様になります。
同じような条件下で試す場合は前の記事を参考にどうぞ。
http://qiita.com/Gen6/items/735245423b65698428be
#中身
##.py
from requests_oauthlib import OAuth1Session
import time, calendar
import datetime
import json
import re
import os
import requests
import sys, codecs
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
from django.http.response import HttpResponse
from django.shortcuts import render
def index(request):
msg = request.GET.get('words')
C_KEY = '********************'
C_SECRET = '********************'
A_KEY = '********************'
A_SECRET = '********************'
url = 'https://api.twitter.com/1.1/statuses/update.json'
params = {'status': msg,'lang': 'ja'}
tw = OAuth1Session(C_KEY,C_SECRET,A_KEY,A_SECRET)
req = tw.post(url, params = params)
url = 'https://api.twitter.com/1.1/statuses/home_timeline.json'
params = {'count': 1}
req = tw.get(url, params = params)
if req.status_code == 200:
timeline = json.loads(req.text)
limit = req.headers['x-rate-limit-remaining']
for tweet in timeline:
Text = (tweet['text'])
User = (tweet['user']['screen_name'])
Name = (tweet['user']['name'])
Img = (tweet['user']['profile_image_url'])
Created_at = YmdHMS(tweet['created_at'])
Message = {
'Words': msg,
'timeline': timeline,
'API_limit': limit,
'Text': Text,
'User': User,
'Name': Name,
'Img': Img,
'Created_at': Created_at,
}
return render(request, 'index.html', Message)
else:
Error = {
'Error_message': 'API制限中',
}
return render(request, 'index.html', Error)
def YmdHMS(created_at):
time_utc = time.strptime(created_at, '%a %b %d %H:%M:%S +0000 %Y')
unix_time = calendar.timegm(time_utc)
time_local = time.localtime(unix_time)
return int(time.strftime('%Y%m%d%H%M%S', time_local))
*最後の関数は以下でも通ります。
def YmdHMS(created_at):
time_utc = time.strptime(created_at, '%a %b %d %H:%M:%S +0000 %Y')
unix_time = calendar.timegm(time_utc)
time_local = time.localtime(unix_time)
return str(time.strftime('%Y'+'年'+'%m'+'月'+'%d'+'日'+'%H'+'時'+'%M'+'分', time_local))
##HTMLとCSS
<!DOCTYPE html>
<html lang="la">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="../static/css/bootstrap.min.css" rel="stylesheet">
<link href="../static/css/custom.css" rel="stylesheet">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="../static/js/bootstrap.min.js"></script>
<title></title>
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
URLべた書きなのがダサいですね。
{% extends "base.html" %}
{% block body %}
<div class="container">
<div class="row">
<div class="col-md-6">
<form action="" method="get" class="form-group">
<label><input type="text" size="60" name="words" class="form-control" placeholder="ツイート"></label>
<input type="submit" class="btn btn-primary" value="送信">
<input type="button" class="btn btn-warning" value="リロード" onclick="location.reload();" />
</form>
{% if Words %}
<p>「{{ Words }}」 とツイートしました。</p>
{% endif %}
<p class="error-message">{{ Error_message }}</p>
<p><span class="status">API-Limit:{{ API_limit }}</p>
</div>
<script type="text/javascript">
$(function() {
$(".reply").click(function() {
var newText = '@{{ User }} ';
$(':text[name="words"]').val(newText);
});
});
</script>
<div class="col-md-6">
{% if API_limit %}
<dl>
<dt><span class="reply"><img class="users-img" src="{{ Img }}">{{ User }}/{{ Name }}</span><span class="d-time">{{ Created_at }}</span></dt>
<dd>{{ Text }}</dd>
</dl>
<ul id="output"></ul>
{% endif %}
</div>
</div>
</div>
{% endblock %}
.container {
width: 100%;
}
.row {
margin: 40px auto;
}
.status {
background: #E2264D;
border-radius: 4px;
color: #fff;
padding: 4px;
width: 100px;
text-align: center;
margin-right: 10px;
}
.reply {
cursor: pointer;
font-weight: bold;
}
.btn-primary {
margin-right: 2px;
}
.d-time {
margin-left: 10px;
border: 1px solid #d8d8d8;
border-radius: 4px;
padding: 4px;
font-weight: normal;
}
ul li {
list-style: none;
}
.users-img {
margin-right: 10px;
border-radius: 4px;
}
dt {
margin-bottom: 10px;
}
.error-message {
color: #ff3300;
}
データベースに保存したい場合は次の記事もご覧いただけると幸いです。
http://qiita.com/Gen6/items/907d869cdf1d588a4751
上記のようなイメージで作成します。
##settings.pyを確認する
107行目あたりを書いておきます。
LANGUAGE_CODE = 'ja'
TIME_ZONE = 'Asia/Tokyo'
USE_I18N = True
USE_L10N = True
USE_TZ = True
STATIC_URL = '/static/'
STATICFILES_DIRS = (
os.path.join(BASE_DIR, "static"),
)
##views.pyを書き足す
投稿・メンション・検索を実装するパターンになります。
一つのviewsファイルに書いているのでメンテナンス性は低いですが参考までにどうぞ。
KEYを外部に置いたり、もっとシンプルに書けるわけですがわかりやすく一纏めとしています。
コードをすっきりさせたりするには色々やってみてください。
検索結果はデータベースに保存するパターンとなります。
from requests_oauthlib import OAuth1Session
import time, calendar
import datetime
import json
import re
import os
import requests
import sys, codecs
sys.stdout = codecs.getwriter('utf-8')(sys.stdout)
from django.http.response import HttpResponse
from django.shortcuts import render
from myapp.models import Supermodel
def index(request):
msg = request.GET.get('words')
C_KEY = '********************'
C_SECRET = '********************'
A_KEY = '********************'
A_SECRET = '********************'
url = 'https://api.twitter.com/1.1/statuses/update.json'
params = {'status': msg,'lang': 'ja'}
tw = OAuth1Session(C_KEY,C_SECRET,A_KEY,A_SECRET)
req = tw.post(url, params = params)
url = 'https://api.twitter.com/1.1/statuses/home_timeline.json'
params = {'count': 1}
req = tw.get(url, params = params)
if req.status_code == 200:
timeline = json.loads(req.text)
limit = req.headers['x-rate-limit-remaining']
for tweet in timeline:
Text = (tweet['text'])
User = (tweet['user']['screen_name'])
Name = (tweet['user']['name'])
Img = (tweet['user']['profile_image_url'])
Created_at = YmdHMS(tweet['created_at'])
Message = {
'Words': msg,
'timeline': timeline,
'API_limit': limit,
'Text': Text,
'User': User,
'Name': Name,
'Img': Img,
'Created_at': Created_at,
}
return render(request, 'index.html', Message)
else:
Error = {
'Error_message': 'API制限中',
}
return render(request, 'index.html', Error)
def search(request):
search_words = 'hogehoge'
search_words = request.GET.get('words')
C_KEY = '********************'
C_SECRET = '********************'
A_KEY = '********************'
A_SECRET = '********************'
tw = OAuth1Session(C_KEY,C_SECRET,A_KEY,A_SECRET)
url = 'https://api.twitter.com/1.1/search/tweets.json?'
params = {
'q': (search_words, 'utf-8'),
'lang': 'ja',
'count': '1'
}
req = tw.get(url, params = params)
if req.status_code == 200:
timeline = json.loads(req.text)
limit = req.headers['x-rate-limit-remaining']
for tweet in timeline['statuses']:
Text = (tweet['text'])
User = (tweet['user']['screen_name'])
Name = (tweet['user']['name'])
Img = (tweet['user']['profile_image_url'])
Created_at = YmdHMS(tweet['created_at'])
data = Supermodel()
data.user_id = User
data.user_name = Name
data.user_img = Img
data.user_text = Text
data.user_created_at = Created_at
data.save()
Message = {
'Words': search_words,
'timeline': timeline,
'API_limit': limit,
'Text': Text,
'User': User,
'Name': Name,
'Img': Img,
'Created_at': Created_at,
}
return render(request, 'search.html', Message)
else:
Error = {
'Error_message': 'API制限中',
}
return render(request, 'search.html', Error)
def mentions(request):
msg = request.GET.get('words')
C_KEY = '********************'
C_SECRET = '********************'
A_KEY = '********************'
A_SECRET = '********************'
url = 'https://api.twitter.com/1.1/statuses/update.json'
params = {'status': msg,'lang': 'ja'}
tw = OAuth1Session(C_KEY,C_SECRET,A_KEY,A_SECRET)
req = tw.post(url, params = params)
url = 'https://api.twitter.com/1.1/statuses/mentions_timeline.json'
params = {'count': 1}
req = tw.get(url, params = params)
if req.status_code == 200:
timeline = json.loads(req.text)
limit = req.headers['x-rate-limit-remaining']
for tweet in timeline:
Text = (tweet['text'])
User = (tweet['user']['screen_name'])
Name = (tweet['user']['name'])
Img = (tweet['user']['profile_image_url'])
Created_at = YmdHMS(tweet['created_at'])
Message = {
'Words': msg,
'timeline': timeline,
'API_limit': limit,
'Text': Text,
'User': User,
'Name': Name,
'Img': Img,
'Created_at': Created_at,
}
return render(request, 'mentions.html', Message)
else:
Error = {
'Error_message': 'API制限中',
}
return render(request, 'mentions.html', Error)
def YmdHMS(created_at):
time_utc = time.strptime(created_at, '%a %b %d %H:%M:%S +0000 %Y')
unix_time = calendar.timegm(time_utc)
time_local = time.localtime(unix_time)
return int(time.strftime('%Y%m%d%H%M%S', time_local))
データベースに保存する動作は以下のコードになってます。
シンプルでわかりやすく何をやっているのか把握が簡単だと思います。
data = Supermodel()
data.user_id = User
data.user_name = Name
data.user_img = Img
data.user_text = Text
data.user_created_at = Created_at
data.save()
##各種HTMLファイルを書く
bootstrapを使用しています。
<!DOCTYPE html>
<html lang="la">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link href="../static/css/bootstrap.min.css" rel="stylesheet">
<link href="../static/css/custom.css" rel="stylesheet">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="../static/js/bootstrap.min.js"></script>
<title></title>
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
{% extends "base.html" %}
{% block body %}
<div class="container">
<nav class="nav">
<ul>
<li><a href="{% url 'index' %}">POST</a></li>
<li><a href="{% url 'search' %}">SEARCH</a></li>
<li><a href="{% url 'mentions' %}">MENTIONS</a></li>
</ul>
</nav>
<div class="row">
<div class="col-md-6">
<form action="" method="get" class="form-group">
<label><input type="text" size="60" name="words" class="form-control" placeholder="ツイート"></label>
<input type="submit" class="btn btn-primary" value="送信">
<input type="button" class="btn btn-warning" value="リロード" onclick="location.reload();" />
</form>
{% if Words %}
<p>「{{ Words }}」 とツイートしました。</p>
{% endif %}
<p class="error-message">{{ Error_message }}</p>
<p><span class="status">API-Limit:{{ API_limit }}</p>
</div>
<script type="text/javascript">
$(function() {
$(".reply").click(function() {
var newText = '@{{ User }} ';
$(':text[name="words"]').val(newText);
});
});
</script>
<div class="col-md-6">
{% if API_limit %}
<dl>
<dt><span class="reply"><img class="users-img" src="{{ Img }}">{{ User }}/{{ Name }}</span><span class="d-time">{{ Created_at }}</span></dt>
<dd>{{ Text }}</dd>
</dl>
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% extends "base.html" %}
{% block body %}
<div class="container">
<nav class="nav">
<ul>
<li><a href="{% url 'index' %}">POST</a></li>
<li><a href="{% url 'search' %}">SEARCH</a></li>
<li><a href="{% url 'mentions' %}">MENTIONS</a></li>
</ul>
</nav>
<div class="row">
<div class="col-md-6">
<form action="" method="get" class="form-group">
<label><input type="text" size="60" name="words" class="form-control" placeholder="検索"></label>
<input type="submit" class="btn btn-primary" value="送信">
<input type="button" class="btn btn-warning" value="リロード" onclick="location.reload();" />
</form>
{% if Words %}
<p>「{{ Words }}」 を検索しました。</p>
{% endif %}
<p class="error-message">{{ Error_message }}</p>
<p><span class="status">API-Limit:{{ API_limit }}</p>
</div>
<script type="text/javascript">
$(function() {
$(".reply").click(function() {
var newText = '@{{ User }} ';
$(':text[name="words"]').val(newText);
});
});
</script>
<div class="col-md-6">
{% if API_limit %}
<dl>
<dt><span class="reply"><img class="users-img" src="{{ Img }}">{{ User }}/{{ Name }}</span><span class="d-time">{{ Created_at }}</span></dt>
<dd>{{ Text }}</dd>
</dl>
{% endif %}
</div>
</div>
</div>
{% endblock %}
{% extends "base.html" %}
{% block body %}
<div class="container">
<nav class="nav">
<ul>
<li><a href="{% url 'index' %}">POST</a></li>
<li><a href="{% url 'search' %}">SEARCH</a></li>
<li><a href="{% url 'mentions' %}">MENTIONS</a></li>
</ul>
</nav>
<div class="row">
<div class="col-md-6">
<form action="" method="get" class="form-group">
<label><input type="text" size="60" name="words" class="form-control" placeholder="ツイート"></label>
<input type="submit" class="btn btn-primary" value="送信">
<input type="button" class="btn btn-warning" value="リロード" onclick="location.reload();" />
</form>
{% if Words %}
<p>「{{ Words }}」 とツイートしました。</p>
{% endif %}
<p class="error-message">{{ Error_message }}</p>
<p><span class="status">API-Limit:{{ API_limit }}</p>
</div>
<script type="text/javascript">
$(function() {
$(".reply").click(function() {
var newText = '@{{ User }} ';
$(':text[name="words"]').val(newText);
});
});
</script>
<div class="col-md-6">
{% if API_limit %}
<dl>
<dt><span class="reply"><img class="users-img" src="{{ Img }}">{{ User }}/{{ Name }}</span><span class="d-time">{{ Created_at }}</span></dt>
<dd>{{ Text }}</dd>
</dl>
{% endif %}
</div>
</div>
</div>
{% endblock %}
##CSS
.container {
width: 100%;
}
.nav {
background: rgba(0,0,0,0.7);
color: #fff;
width: 100%;
margin: 0 auto;
}
.nav ul li {
list-style: none;
margin: 10px auto;
}
.nav ul {
margin-left: -20px;
}
.nav li {
float: left;
margin-right: 10px!important;
}
.nav li a:link, .nav li a:visited {
color: #fff!important;
}
.row {
margin: 40px auto;
}
.status {
background: #E2264D;
border-radius: 4px;
color: #fff;
padding: 4px;
width: 100px;
text-align: center;
margin-right: 10px;
}
.reply {
cursor: pointer;
font-weight: bold;
}
.btn-primary {
margin-right: 2px;
}
.d-time {
margin-left: 10px;
border: 1px solid #d8d8d8;
border-radius: 4px;
padding: 4px;
font-weight: normal;
}
ul li {
list-style: none;
}
.users-img {
margin-right: 10px;
border-radius: 4px;
}
dt {
margin-bottom: 10px;
}
.error-message {
color: #ff3300;
}
##urls.pyを書く
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name='index'),
url(r'^search/', views.search, name='search'),
url(r'^mentions/', views.mentions, name='mentions'),
]
##models.pyとadmin.pyを書く
ツイート検索結果をデータベースに保存しない場合は以下は必要なしとなりますが、views.pyに記述しているので念のため。SQLITEを使用しますので、基本settings.pyはデフォルトのままとします。
from django.db import models
class Supermodel(models.Model):
user_name = models.CharField(max_length=140)
user_id = models.CharField(max_length=140)
user_img = models.CharField(max_length=140)
user_text = models.TextField(null=True)
user_created_at = models.CharField(max_length=140)
def __str__(self):
return self.user_name
from django.contrib import admin
from myapp.models import Supermodel
class SupermodelAdmin(admin.ModelAdmin):
list_display = ('id','user_id','user_name','user_img','user_text','user_created_at')
admin.site.register(Supermodel,SupermodelAdmin)
##最終的な構成
こういう感じになります。
あとはmigrateしてrunserverでどうぞ。