スナックelve 本店

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

エンジニア・プログラマにしか使えないSNSとやらを一介の事務派遣が覗いてみようじゃぁないか!2

追記

index.htmlの
{{tweet.text}}を
{{tweet.text | linebreaks | urlize}}
にしたほうが幸せになれそうです。
参考:【Django】model:テキストフィールド表示時に自動でp・br・a要素を付ける | OFFICE54
<終わり>
f:id:elve:20210723205107p:plain

本日の進捗

フォルダ構成

├─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するとサーバーが終了します。


・・・うん、ごめん、何もわかってない・・・
まだリプライを送ることはできない。