スナックelve 本店

バツイチ40代女の日記です

改良あれこれ

snack.elve.club
いやーもう、誰もどうでもいいだろうけど自分的によく頑張ったのでw
index.htmlとview.pyをいじったので、どやどやさせてくれ!!

結果

こんな感じになりました!*1
f:id:elve:20210725090003p:plain

環境

Windows10
Python 3.9.6
Django 3.2.5

VisualStudioCode: 1.58.2

フォルダ名とか変更

├─SNSTools
│  └─__pycache__
└─tool1
    ├─fixtures
    ├─migrations
    │  └─__pycache__
    ├─static
    ├─templates
    │  └─tool1
    └─__pycache__

index.html

<!DOCTYPE html>
{% load static %}
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <meta charset="UTF-8">
  <title>index.html</title>
  <!-- https://kokiblog.com/2019/09/12/django_css/ -->
  <link rel="stylesheet" href="{% static 'style1.css' %}">
  
  <script type="text/javascript">
    //文字数カウント https://www.nishishi.com/javascript-tips/input-counter.html
    function ShowLength( str ) {
      document.getElementById("inputlength").innerHTML = str.length + "文字";
    }
    //CLICK要素のID取得 https://uxmilk.jp/11596
    function getId(ele){
    document.getElementById("in_reply_to_text_id").value=ele.id;
    location.hash = "pagetop"
    }
  </script>
</head>
<body>
  <h1>例のSNS</h1>

  <div class="box27" id="link">
    <p><a href="./user/">ユーザー情報更新</a></p>
  </div>
  
  <!-- 投稿フォーム -->
  <form action="" method="post" class="box27">
  {% csrf_token %}
  <fieldset>
    <legend><h1><a name="pagetop">つぶやく</a></h1></legend>
    {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %}
    <label id="inputlength">280文字までよ</label><br>
    <textarea id="add-tweet" name="text" rows="5" cols="60" maxlength="280" onkeyup="ShowLength(value);"></textarea><br>
    <label id="label">リプライ先(投稿のID)</label><br>
    <textarea id="in_reply_to_text_id" name="in_reply_to_text_id" row="1" cols="60" max_length="40"></textarea>
  </fieldset>
  <input type="submit" value="囁き 祈り 詠唱 念じよ!">
  </form>

  <div class="box27" id="reload">
    <p><a href="http://localhost:8000/tool1/">再読み込み</a></p>
  </div>
  
  {% for tweet in tweets %}
  <!--投稿数分ループ-->
  <div class="box27" id="{{tweet.id}}" onclick="getId(this);">
    <!--投稿者自己紹介-->
    <div class="tooltip1">
      <p>{{tweet.user_name}}</p>
      <div class="description1">{{tweet.user_description}} </div>
    </div>
    <!--投稿がリプライならそこへのリンク 投稿者名-->
    {% if tweet.in_reply_to_text_id %}<p><a href="#{{tweet.in_reply_to_text_id}}">Re:{{tweet.in_reply_to_user_name}}</a></p>{% endif %}
    <!--投稿本文 改行あり URLリンクあり https://office54.net/python/django/model-textfield-add#:~:text=p%E8%A6%81%E7%B4%A0%E3%81%AF%E3%81%A4%E3%81%91%E3%81%AA%E3%81%84,%E3%81%AF%E3%80%81linebreaksbr%E3%82%92%E4%BD%BF%E3%81%84%E3%81%BE%E3%81%99%E3%80%82-->    
    <p>{{tweet.text | linebreaks | urlize}}</p>
    <!--投稿日-->
    <p class="add-time">{{tweet.created_at}}</p>
  </div>
  {% endfor %}
</body>
</html>

view.py

from django.shortcuts import render
import requests
from tool1.models import USERS,TWEETS
from tool1.forms import UserForm
import datetime
from django.views.generic import FormView 
import json
from django.core.exceptions import ObjectDoesNotExist
from django.core import management
import os
import sys
#投稿IDからユーザー情報取得
def get_user_by_tweet_id(tweet_id):
    try:
        tweet = TWEETS.objects.get(pk = tweet_id)
        user_name=tweet.user_name
        user_id=tweet.user_id
    except:
        #来ないはず・・・
        user_name = ""
        user_id=""
    return {'user_name':user_name,'user_id':user_id}

#ユーザーIDから名前と自己紹介文取得
def get_user_name(my_user_id):
    try:
        user = USERS.objects.get(user_id = my_user_id)
        user_name=user.name
        user_description=user.description
    except USERS.DoesNotExist:
        #新規ユーザー追加
        url = "https://versatileapi.herokuapp.com/api/user/" + my_user_id
        try:
            users = requests.get(url).json()
            for user in users:
                add_user(user)
            user_name=user.name
            user_description=user.description
            #保存
            my_save()
        except:
            #新規で名無しさん
            user_name = my_user_id
            user_description=""
    return {'user_name':user_name,'user_description':user_description}
def add_user(user):
    USERS.objects.create(
        id = user['id'],
        created_at = datetime.datetime.fromisoformat(user['_created_at']) ,
        updated_at = datetime.datetime.fromisoformat(user['_updated_at']) ,
        user_id = user['_user_id'],
        description = user['description'],
        name = user['name'])

#ファイルを読み込んで登録
def read_object():
    #ユーザーデータ空にする
    USERS.objects.all().delete()
    #投稿情報空にする
    TWEETS.objects.all().delete()
    #ファイル読み込み
    management.call_command('loaddata', 'data.json')
    USERS.save
    TWEETS.save

    output = {'tweets': TWEETS.objects.order_by('created_at').reverse().all()}
    return output

def add_tweet(tweet):
    #投稿を追加
    my_user = get_user_name(tweet['_user_id'])
    
    if 'in_reply_to_user_id' in tweet:
        re_user_id=tweet['in_reply_to_user_id']
        re_user_name = get_user_name(tweet['in_reply_to_user_id'])['user_name']
    elif 'in_reply_to_text_id' in tweet:
        re_user=get_user_by_tweet_id(tweet['in_reply_to_text_id'])
        re_user_id = re_user['user_id']
        re_user_name = re_user['user_name']
    else:
        re_user_id=''
        re_user_name=''
    TWEETS.objects.create(
        id = tweet['id'],
        created_at = datetime.datetime.fromisoformat(tweet['_created_at']) ,
        updated_at = datetime.datetime.fromisoformat(tweet['_updated_at']) ,
        user_id = tweet['_user_id'],
        in_reply_to_text_id = tweet['in_reply_to_text_id'] if 'in_reply_to_text_id' in tweet  else "",
        in_reply_to_user_id = re_user_id,
        in_reply_to_user_name=re_user_name,
        text = tweet['text'],
        user_name = my_user['user_name'],
        user_description= my_user['user_description'])

def set_reply():
    tweets = TWEETS.objects.all()
    for tweet in tweets:
        if tweet.in_reply_to_text_id != "":
            re_user=get_user_by_tweet_id(tweet.in_reply_to_text_id)
            tweet.in_reply_to_user_id = re_user['user_id']
            tweet.in_reply_to_user_name = re_user['user_name']
            print(re_user)
        if tweet.in_reply_to_user_id != "":
            re_user=get_user_name(tweet.in_reply_to_user_id)
            tweet.in_reply_to_user_name = re_user['user_name']
            print(re_user)
    TWEETS.save


#保存してる最終投稿から今まで取得
def add_object():
    #一番新しい投稿の投稿時間
    enddt= TWEETS.objects.order_by('created_at').reverse().first().created_at
    #https://note.nkmk.me/python-datetime-pytz-timezone/
    endstr = enddt.astimezone(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%S-00")
    #よくわからんが多分コレ https://docs.microsoft.com/ja-jp/azure/search/search-query-odata-comparison-operators
    filterstr="&$filter=_created_at ge '" + endstr + "'"
    url = "https://versatileapi.herokuapp.com/api/text/all?$orderby=_created_at desc" + filterstr

    #json取得
    tweets = requests.get(url).json()

    for tweet in tweets:
        try:
            e = TWEETS.objects.get(id=tweet['id'])
            #処理終了
            break
        except ObjectDoesNotExist:
            #投稿を追加
            add_tweet(tweet)
    
    #保存
    my_save()

    #ここで並び替えないとindexに反映されない
    output = {'tweets': TWEETS.objects.order_by('created_at').reverse().all()}
    return output

def new_object():

    #ユーザーデータ空にする
    USERS.objects.all().delete()

    #ユーザー全件取得
    url = "https://versatileapi.herokuapp.com/api/user/all?$orderby=_created_at asc"
    users = requests.get(url).json()
    #登録
    for user in users:
        add_user(user)
    
    #投稿情報空にする
    TWEETS.objects.all().delete()
    #投稿全件ソートして取得
    url = "https://versatileapi.herokuapp.com/api/text/all?$orderby=_created_at asc"
    tweets = requests.get(url).json()
    
    for tweet in tweets:
        add_tweet(tweet)
    #保存
    #set_reply()#強引にリプライ情報を付けた
    my_save()

    output = {'tweets': TWEETS.objects.order_by('created_at').reverse().all()}
    return output

def my_save():
    #保存
    sysout = sys.stdout
    sys.stdout = open('./tool1/fixtures/data.json', 'w', encoding='UTF-8')
    management.call_command('dumpdata', 'tool1')
    sys.stdout = sysout
    print("保存終了")

def index(request):
    #https://techacademy.jp/magazine/18994
    if os.path.isfile("./tool1/fixtures/data.json"):
        print("read")
        output = read_object()
    else:
        print("new")
        output = new_object()


    if request.method =='GET':
        #表示
        output = add_object()
        return render(request, 'tool1/index.html',output)

    if request.method =='POST':
        #投稿
        #https://hombre-nuevo.com/python/python0075/#h3_7
        sess = requests.session()
        url = "https://versatileapi.herokuapp.com/api/text/"
        # ヘッダ
        headers = {'Authorization': 'HelloWorld'}
        if request.POST['in_reply_to_text_id'] != "":
            user_info = get_user_by_tweet_id(request.POST['in_reply_to_text_id'])
            # 送信データ
            prm = {"text": request.POST['text'],"in_reply_to_text_id":request.POST['in_reply_to_text_id'],"in_reply_to_user_id":user_info['user_id']}
        else:
            # 送信データ
            prm = {"text": request.POST['text']}

        # JSON変換
        params = json.dumps(prm)
        # POST送信
        res = sess.post(url, data=params, headers=headers)
        print(res)
        output = add_object()
        return render(request, 'tool1/index.html',output)

#ユーザー情報更新画面 登録はPOSTにすればいけると思う
class UserFormView(FormView):
    template_name = 'tool1/user.html'
    form_class = UserForm
    success_url = 'http://localhost:8000/tool1/'
    def form_valid(self, form):
        #https://hombre-nuevo.com/python/python0075/#h3_7
        sess = requests.session()
        url = "https://versatileapi.herokuapp.com/api/user/create_user"
        # ヘッダ
        headers = {'Authorization': 'HelloWorld'}
        # 送信データ
        prm = {"name": form.data.get("name"),"description":form.data.get("description")}
        # JSON変換
        params = json.dumps(prm)
        # POST送信
        res = sess.put(url, data=params, headers=headers)
        
        return super().form_valid(form)

参考

Djangoのカスタムコマンドをコード内から呼ぶ | Akashic Records
python - Djangoモデル.getは失敗しますが、.filterと.allは機能します-オブジェクトがデータベースに存在します - ITツールウェブ
Cliborの使い方 | 千草ウェブ
Fixtureの使い方 初期データ追加を自動化しよう! - Djangoの学習ができるチュートリアルサイトDjangoBrothers
django-admin and manage.py | Django documentation | Django
Model instance reference | Django documentation | Django
Djangoのクエリセットから値を取得する方法 - 知的好奇心
Python エラー'cp932' codec can't encode character - スタック・オーバーフロー
Djangoで開発中、データベースへ初期データを入力する【loaddata】 - 自動化無しに生活無し
Django SQL データベース INSERT UPDATE 追加更新方法 save() create() add() update()
Djangoの初期データをfixtureで作成する - Qiita
django データ投入 dumpdata loaddata - Qiita
Djangoよくあるエラー集 - Qiita
Django データベース操作 についてのまとめ - Qiita
[Django] マスタデータの作り方 - Qiita
【初心者向け】Djangoのモデル操作でobjectsが必要な場合・不要な場合を理解する - Qiita
Djangoでモデルオブジェクトの変更前のデータをチェックする - Qiita
PythonのWebアプリケーション(Django)を初心者にもわかりやすく解説(5)【Django shellでDB操作入門編】 - Qiita
json-sns-viewer
python - Django Models .get fails but .filter and .all works - object exists in database - Stack Overflow
django - How to use call_command with dumpdata command to save json to file - Stack Overflow
Programmatically using Django's loaddata - Stack Overflow
Pythonでwith構文を使う方法【初心者向け】現役エンジニアが解説 | TechAcademyマガジン
Django - 【Django】views.pyにおいて、filterで絞り込んだクエリセットの合計値を算出したい|teratail
Python - Django オブジェクトから特定のIDの特定のフィールドの値を抽出したい|teratail
Python - Django administrationでnot definedエラーが出てしまいます。|teratail
Python - django seed データを入れたい|teratail
Django - Djangoでモデルクラスの名前を参照してくれない|teratail
Django ORM(クエリセット) · HonKit
「loaddata」を使用してdjangoデータベースにデータをロードする際の問題
python — Djangoでオブジェクトの値を更新するにはどうすればよいですか?
Django dumpdata loaddata で、データを移行する | Monotalk
Django escape の動作について | Monotalk
SQLで並び替え!ORDER BYを基礎から応用まで学ぼう! | 侍エンジニアブログ
DjangoのModel.objects.filter()の使い方【QuerySet】 - なるぽのブログ

*1:シングルコーテーションを投稿するとエラーになる。