追記
index.htmlの
{{tweet.text}}を
{{tweet.text | linebreaks | urlize}}
にしたほうが幸せになれそうです。
参考:【Django】model:テキストフィールド表示時に自動でp・br・a要素を付ける | OFFICE54
<終わり>
本日の進捗
リプの表示はできるようになったが、リプをどうやって実装しようかなぁ・・・。 pic.twitter.com/FCN8iaIAvr
— elve (@elve_hatena) 2021年7月23日
フォルダ構成
├─SNS │ └─mysite │ ├─mysite │ │ └─__pycache__ │ └─polls │ ├─migrations │ │ └─__pycache__ │ ├─static │ ├─templates │ │ ├─polls │ │ └─user │ └─__pycache__
SNS\mysite\mysite\urls.py
from django.contrib import admin from django.urls import include, path urlpatterns = [ path('polls/', include('polls.urls')), path('admin/', admin.site.urls), ]
SNS\mysite\mysite\settings.py
import os #大事 #中略 INSTALLED_APPS = [ 'polls.apps.PollsConfig', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ] #中略 TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', #'DIRS': [], 'DIRS': [os.path.join(BASE_DIR, 'templates')], # 追記 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] #中略 LANGUAGE_CODE = 'ja' TIME_ZONE = 'Asia/Tokyo' #中略 # https://kokiblog.com/2019/09/12/django_css/ # https://blog.fantom.co.jp/2021/01/23/name-os-is-not-defined/ STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static"), ]
SNS\mysite\polls\views.py
from django.shortcuts import render import requests from polls.models import USERS,TWEETS from polls.forms import UserForm import datetime from django.views.generic import FormView import json def get_username(my_user_id): try: user = USERS.objects.get(user_id = my_user_id) username=user.name userdescription=user.description except USERS.DoesNotExist: username = my_user_id userdescription="" return {'username':username,'userdescription':userdescription} class UserFormView(FormView): template_name = 'user/user.html' form_class = UserForm success_url = 'http://localhost:8000/polls/' 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")} print(prm) # JSON変換 params = json.dumps(prm) # POST送信 res = sess.put(url, data=params, headers=headers) return super().form_valid(form) def get_object(): url = "https://versatileapi.herokuapp.com/api/user/all" users = requests.get(url).json() USERS.objects.all().delete() for user in users: 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']) url = "https://versatileapi.herokuapp.com/api/text/all?$orderby=_created_at desc"#全部取得すると重いので・・・ tweets = requests.get(url).json() TWEETS.objects.all().delete() for tweet in tweets: Uid = tweet['_user_id'] my_user = get_username(Uid) if 'in_reply_to_user_id' in tweet: re_user = get_username(tweet['in_reply_to_user_id']) else: re_user={'username':"",'userdescription':""} TWEETS.objects.create( id = tweet['id'], created_at = datetime.datetime.fromisoformat(tweet['_created_at']) , updated_at = datetime.datetime.fromisoformat(tweet['_updated_at']) , user_id = Uid, 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 = tweet['in_reply_to_user_id'] if 'in_reply_to_user_id' in tweet else "", in_reply_to_user_name=re_user['username'], text = tweet['text'], user_name = my_user['username'], user_description= my_user['userdescription']) output = {'tweets': TWEETS.objects.all()} return output def index(request): if request.method=='GET': o = get_object() return render(request, 'polls/index.html',o) 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'} # 送信データ prm = {"text": request.POST['text']} # JSON変換 params = json.dumps(prm) # POST送信 res = sess.post(url, data=params, headers=headers) o = get_object() return render(request, 'polls/index.html',o)
SNS\mysite\polls\models.py
from django.db import models # Create your models here. class USERS(models.Model): id = models.CharField(primary_key=True,max_length=40) created_at = models.DateTimeField() updated_at = models.DateTimeField() user_id = models.CharField(max_length=40) description = models.TextField(max_length=300) name = models.CharField(max_length=40) class TWEETS(models.Model): id = models.CharField(primary_key=True,max_length=40) created_at = models.DateTimeField() updated_at = models.DateTimeField() user_id= models.CharField(max_length=40) in_reply_to_text_id= models.CharField(max_length=40) in_reply_to_user_id= models.CharField(max_length=40) in_reply_to_user_name=models.CharField(max_length=30,null=True) text= models.TextField(max_length=200) user_name = models.CharField(max_length=30,null=True) user_description = models.TextField(max_length=300,null=True)
SNS\mysite\polls\forms.py
要ファイル作成
from django import forms #https://note.com/mihami383/n/n29630c65abf6 #https://qiita.com/box16/items/23f78e1014f6dc8f849e class UserForm(forms.Form): name=forms.CharField(label ="名前", max_length=30); description=forms.CharField(label ="自己紹介", widget=forms.Textarea, max_length=300);
SNS\mysite\polls\urls.py
from django.urls import path from .views import index, UserFormView urlpatterns = [ path('', index, name='index'), path('user/', UserFormView.as_view(), name='user'), ]
SNS\mysite\polls\templates
要フォルダ作成
中にpollsとuserってフォルダも作る
SNS\mysite\polls\templates\polls\index.html
<!DOCTYPE html> <!--https://kokiblog.com/2019/09/12/django_css/--> <!--https://www.nishishi.com/javascript-tips/input-counter.html--> {% load static %} <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta charset="UTF-8"> <title>index.html</title> <link rel="stylesheet" href="{% static 'style1.css' %}"> <script type="text/javascript"> function ShowLength( str ) { document.getElementById("inputlength").innerHTML = str.length + "文字"; } </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>つぶやく</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> </fieldset> <input type="submit" value="囁き 祈り 詠唱 念じよ!"> </form> {% for tweet in tweets %} <div class="box27" id="{{tweet.id}}"> <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 %} <p>{{tweet.text}}</p> <p class="add-time">{{tweet.created_at}}</p> </div> {% endfor %} </body> </html>
SNS\mysite\polls\templates\user\user.html
<!DOCTYPE html> {% load static %} <html> <head> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta charset="UTF-8"> <title>user.html</title> <link rel="stylesheet" href="{% static 'style1.css' %}"> <script type="text/javascript"> function ShowLength1( str ) { document.getElementById("inputlength1").innerHTML = str.length + "文字"; } function ShowLength2( str ) { document.getElementById("inputlength2").innerHTML = str.length + "文字"; } </script> </head> <body> <h1>例のSNS</h1> <div class="box27" id="link"> <p><a href="../">つぶやく</a></p> </div> <form action="" method="post" class="box27"> {% csrf_token %} <fieldset> <legend><h1>ユーザー登録</h1></legend> {% if error_message %}<p><strong>{{ error_message }}</strong></p>{% endif %} <label id="inputlength1">名前 30字まで</label><br> <input type="text" id="add-user" name="name" cols="60" maxlength="30" onkeyup="ShowLength1(value);"> <label id="inputlength2">自己紹介 300字まで</label><br> <textarea id="add-user" name="description" rows="5" cols="60" maxlength="300" onkeyup="ShowLength2(value);"></textarea> </fieldset> <input type="submit" value="囁き 祈り 詠唱 念じよ!"> </form> </body> </html>
SNS\mysite\polls\static
要フォルダ作成
SNS\mysite\polls\static\style1.css
/*https://kokiblog.com/2019/09/12/django_css/*/ /*https://saruwakakun.com/html-css/reference/box*/ body { background-color: #B2EBF2; font-family: sans-serif; } h1{ width: 80%; margin: 0 auto; padding: 0.5em 1em; max-width: 500px; } .box27 { width: 80%; position: relative; margin: 2em auto; padding: 0.5em 1em; border: solid 3px #62c1ce; max-width: 500px; } .box27 .box-title { position: absolute; display: inline-block; top: -27px; left: -3px; padding: 0 9px; height: 25px; line-height: 25px; font-size: 17px; background: #62c1ce; color: #ffffff; font-weight: bold; border-radius: 5px 5px 0 0; } .box27 p { margin: 0; padding: 0; } .add-time { text-align : right; } /*https://www.jungleocean.com/programming/190308tooltip-css*/ .tooltip1{ position: absolute; display: inline-block; top: -27px; left: -3px; padding: 0 9px; height: 25px; line-height: 25px; font-size: 17px; background: #62c1ce; color: #ffffff; font-weight: bold; border-radius: 5px 5px 0 0; cursor: pointer; display: inline-block; } .tooltip1 p{ margin:0; padding:0; } .description1 { display: none; position: absolute; padding: 10px; font-size: 12px; line-height: 1.6em; color: #fff; border-radius: 5px; background: #000; width: 200px; } .description1:before { content: ""; position: absolute; top: 0%; right: 95%; border: 15px solid transparent; border-top: 15px solid #000; margin-left: -15px; transform: rotateZ(90deg); } .tooltip1:hover .description1{ display: inline-block; top: 0px; left: 80px; }
実行手順
VisualStudioCodeのターミナルから下記コマンド実行
C:\Users\user\Documents\SNS\mysite> python manage.py makemigrations polls C:\Users\user\Documents\SNS\mysite> python manage.py migrate C:\Users\user\Documents\SNS\mysite> python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...System check identified no issues (0 silenced).
July 23, 2021 - 21:20:12
Django version 3.2.5, using settings 'mysite.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.
みたいなメッセージが表示されたらローカルサーバー立ち上がってるのでブラウザで
http://localhost:8000/polls/
にアクセス。重い。
終了するときはターミナルでCtrl+cするとサーバーが終了します。
・・・うん、ごめん、何もわかってない・・・
まだリプライを送ることはできない。
参考
Django 逆引きチートシート (Template編) - Qiita
DjangoでDBに保存したデータをTemplateで表示するあれこれについて · PengNote - 勉強した事や行った場所の感想を書くブログ
はじめての Django アプリ作成、その 4 | Django ドキュメント | Django
DjangoでModelからTemplatesにデータを渡す方法と考え方 | エンジニアが送る穴倉生活のすゝめ
djangoのフォームを扱うビュー3種類 – igreks開発日記
Django モデル(Model)の一覧表示画面を作成する
TemplateViewを使ってみよう!IndexView編 - Qiita
Python Django チュートリアル(3) - Qiita
Django#3(フォームの値を受け取る) - Qiita
Django - [Django] ビュー関数で、「if request.method == 'GET'」を書いたあとに処理を書く意味|teratail
Python - Django FormViewを使っているがsubmitボタンを押してもsuccess_urlに飛ばない|teratail
python — Djangoテンプレートのクエリセットオブジェクトにアクセスする
DjangoでGETリクエストで値を送信して、View内で処理して画面上に表示する方法 - Tech Links
他色々なサイトを参考にさせていただきました。