[Django]SNSアプリ1

### アプリケーションの作成
$ python manage.py startapp sns

– settingsにアプリケーション名を追加
/django_app/settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'hello',
    'sns',
]

### アプリケーションの設計
– 機能の洗い出し
– データベース設計
– 各ページの設計

### モデルの作成
– テーブルはUser, Message, Group, Friend, Good
– Message: owner(投稿者), group, content, share_id, good_count, share_count, pub_date
– Group: owner, title
– Friend: owner, user, group
– Good: owner, message
/sns/models.py

from django.db import models
from django.contrib.auth.models import User

# Message
class Message(models.Model):
	owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='message_owner')
	group = models.ForeignKey('Group', on_delete=models.CASCADE)
	content = models.TextField(max_length=1000)
	share_id = models.IntegerField(default=-1)
	good_count = models.IntegerField(default=0)
	pub_date = models.DateTimeField(auto_now_add=True)

	def __str__(self):
		return str(self.content) + ' (' + str(self.owner) + ')'

	class Meta:
		ordering = ('-pub_date',)

# Group
class Group(models.Model):
	owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='group_owner')
	title = models.CharField(max_length=100)

	def __str__(self):
		return '<' + self.title + '(' + str(self.owner) + ')>'

# Friend
class Friend(models.Model):
	owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='friend_owner')
	user = models.ForeignKey(User, on_delete=models.CASCADE)
	group = models.ForeignKey(Group, on_delete=models.CASCADE)

	def __str__(self):
		return str(self.user) + ' (group:"' + str(self.group) + '")'

# Good
class Good(models.Model):
	owner = models.ForeignKey(User, on_delete=models.CASCADE, related_name='good_owner')
	message = models.ForeignKey(Message, on_delete=models.CASCADE)

	def __str__(self):
		return 'good for "' + str(self.message) + '" (by' + str(self.owner) + ')'

– -pub_dateはpub_dateの大きい順

### マイグレーション
$ python manage.py makemigrations sns
$ python manage.py migrate

### admin.py
/sns/admin.py

from django.contrib import admin
from .models import Message, Friend, Group, Good

admin.site.register(Message)
admin.site.register(Friend)
admin.site.register(Group)
admin.site.register(Good)

$ python manage.py runserver 192.168.33.10:8000

– AddUserからpublicユーザの作成
– ユーザ作成
– publicグループを用意

[Django]メッセージの投稿

### Messageモデル
/hello/models.py

class Message(models.Model):
	friend = models.ForeignKey(Friend, on_delete=models.CASCADE)
	title = models.CharField(max_length=100)
	content = models.CharField(max_length=300)
	pub_date = models.DateTimeField(auto_now_add=True)

	def __str__(self):
		return '<Message:id=' + str(self.id) + ', ' + self.title + '(' + str(self.pub_date) + ')>'

	class Meta:
		ordering = ('pub_date',)

– on_delete=models.CASCADEは削除を指定するという意味
– auto_now_add=Trueは自動的に値を設定
– Metaはクラスの基本的設定を記述

### マイグレーション実行
$ python manage.py makemigrations helloMigrations for ‘hello’:
hello/migrations/0002_auto_20200712_2230.py
– Alter field age on friend
– Alter field name on friend
– Create model Message
$ python manage.py migrate

### admin.py
/hello/admin.py

from django.contrib import admin
from .models import Friend, Message

admin.site.register(Friend)
admin.site.register(Message)

http://192.168.33.10:8000/admin/

### Messageページ作成
/hello/urls.py

	path('message/', views.message, name='message'),
	path('message/<int:page>', views.message, name='message'),

/hello/forms.py

from.models import Friend, Message

class MessageForm(forms.ModelForm):
	class Meta:
		model = Message
		fields = ['title', 'content', 'friend']
		widgets = {
			'title': forms.TextInput(attrs={'class':'formcontrol form-control-sm'}),
			'content': forms.Textarea(attrs={'class':'form-control form-control-sm', 'rows':2}),
			'friend': forms.Select(attrs={'class':'form-control form-control-sm'}),
		}

/hello/views.py

from .models import Friend, Message
from .forms import FriendForm, MessageForm

def message(request, page=1):
	if(request.method == 'POST'):
		obj = Message()
		form = MessageForm(request.POST, instance=obj)
		form.save()
	data = Message.objects.all().reverse()
	paginator = Paginator(data, 5)
	params = {
		'title': 'Message',
		'form': MessageForm(),
		'data': paginator.get_page(page),
	}
	return render(request, 'hello/message.html', params)

/hello/templates/hello/message.html

<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<form action="{% url 'message' %}" method="post">
		{% csrf_token %}
		{{ form.as_p }}
		<input type="submit" value="send" class="btn btn-primary">
		<div class="mt-5"></div>
		<table class="table">
			<tr>
				<th class="py-1">title</th>
				<th class="py-1">name</th>
				<th class="py-1">datetime</th>
			</tr>
		{% for item in data %}
			<tr>
				<td class="py-2">{{item.title}}</td>
				<td class="py-2">{{item.friend.name}}</td>
				<td class="py-2">{{item.pub_date}}</td>
			</tr>
		{% endfor %}
		</table>

	<ul class="pagination justify-content-center">
		{% if data.has_previous %}
		<li class="page-item">
			<a class="page-link" href="{% url 'message' %}">
			&laquo; first</a>
		</li>
		<li class="page-item">
			<a class="page-link" href="{% url 'message' %}{{data.previous_page_number}}">
			&laquo; prev</a>
		</li>
		{% else %}
		<li class="page-item">
			<a class="page-link">
			&laquo; first</a>
		</li>
		<li class="page-item">
			<a class="page-link">
			&laquo; prev</a>
		</li>
		{% endif %}
		<li class="page-item">
			<a class="page-link">
			{{data.number}}/{{data.paginator.num_pages}}</a>
		</li>
		{% if data.has_next %}
		<li class="page-item">
			<a class="page-link" href="{% url 'message' %}{{data.next_page_number}}">
			next &raquo;</a>
		</li>
		<li class="page-item">
			<a class="page-link" href="{% url 'message' %}{{data.paginator.num_pages}}">
			last &raquo;</a>
		</li>
		{% else %}
		<li class="page-item">
			<a class="page-link">next &raquo;</a>
		</li>
		<li class="page-item">
			<a class="page-link">last &raquo;</a>
		</li>
		{% endif %}
	</ul>
</body>

/hello/templates/hello/index.html

<table class="table">
			<tr>
				<th>id</th>
				<th>name</th>
				<th>age</th>
				<th>mail</th>
				<th>birthday</th>
				<th>Messages</th>
			</tr>
		{% for item in data %}
			<tr>
				<td>{{item.id}}</td>
				<td>{{item.name}}</td>
				<td>{{item.age}}</td>
				<td>{{item.mail}}</td>
				<td>{{item.birthday}}</td>
				<td><ul>
					{% for ob in item.message_set.all %}
						<li>{{ob.title}}</li>
					{% endfor %}
				</ul>
				</td>
			</tr>
		{% endfor %}
		</table>

[Django]クエリパラメータを使用する

### クエリパラメータを記述する
クエリパラメータとはアドレスの後につけて記述するパラメータの事
e.g. http://hoge.com/hello/index?xxx=yyyy&zzz=aaa… など

from django.shortcuts import render
from django.http import HttpResponse

def index(request):
	msg = request.GET['msg']
	return HttpResponse('you typed : "' + msg +'".')

– request.GET[‘&{param}’]でGETパラメータを取り出す。
– リクエストはHttpRequestクラスを使用し、レスポンスはHttpResponseクラスを使用する

### クエリパラメータがない時
MultiValueDictKeyErrorになるので、views.pyでmsgがない時の処理を追加する

/hello/views.py

from django.shortcuts import render
from django.http import HttpResponse

def index(request):
	if 'msg' in request.GET:
		msg = request.GET['msg']
		result = 'you typed : "' + msg +'".'
	else:
		result = 'please send msg parameter!'
	return HttpResponse(result)

– GETプロパティに設定されている値はQueryDictというクラスのインスタンス

### クエリパラメーターをスラッシュ(“/”)に変更する
/hello/urls.py

urlpatterns = [ 
	path('<int:id>/<nickname>', views.index, name='index'),
]

/hello/views.py

def index(request, id, nickname):
	result = 'your id: ' + str(id) + ', name: "' \
		+ nickname + '".'
	return HttpResponse(result)

– 文末のバックスラッシュ(“\”)は見かけの改行

[Django]アプリケーション内にurls.py新規作成

プロジェクト内のurls.pyではなく、アプリケーション内にurls.pyを新規作成しルーティングを書いていくのが一般的

### urls.pyの新規作成
helloフォルダ内にurls.pyを作成する

from django.urls import path
from . import views

urlpatterns = [   # path 関数で値を記述していく
	path('', views.index, name='index'), # 第一引数がアドレス、第二引数がviewsの実行関数、第三引数が実行
]

### django/urls.pyの新規作成
hello内のurls.pyを読み込むように修正

from django.contrib import admin
from django.urls import path, include # includeは引数に指定したモジュールを読み込む

urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello/', include('hello.urls')), # path(アドレス、呼び出す処理)
]

表示が変わらないことを確認します
djangoにはプロジェクトとアプリケーションという概念があるようです

[Django]viewの作成方法

### アプリケーションを作成
$ python manage.py startapp hello

– migrationフォルダ: データベース関連の機能
– __initi__.py : アプリケーションの初期化処理を行うスクリプトファイル
– admin.py : 管理者ツールのため
– apps.py : アプリケーション本体の処理
– models.py : モデルに関する処理を記述
– tests.py : プログラムのテストに関するもの
– views.py : 画面表示

### views.py
/hello/view.py

from django.shortcuts import render
from django.http import HttpResponse # HttpResponseクラスをimport

def index(request): # requestはHttpResponseクラスのインスタンス
	return HttpResponse("Hello Django!!")

# Create your views here.

urlpatternsに登録した情報を元にどのアドレスにアクセスしたらどの処理が呼び出されるか決まる

### urls.py
/django_app/urls.py

from django.contrib import admin
from django.urls import path
import hello.views as hello

urlpatterns = [
    path('admin/', admin.site.urls),
    path('hello/', hello.index), # path(アドレス、呼び出す処理)
]

$ python manage.py runserver 192.168.33.10:8000

第一印象としては、Laravelは直感的ですが、Djangoはより関数的な書き方のように感じます。

Djangoプロジェクトの中身とサーバ起動

### プロジェクトフォルダ
– __initi__.py : 初期化処理を行うスクリプトファイル
– asgi.py : ASGIという非同期Webアプリケーションの為のプログラム
– settings.py : プロジェクトの設定情報
– urls.py : URLを管理するファイル
– wsgi.py : 一般的なWebアプリケーションプログラム
– manage.py : 機能に関するファイル

### サーバー起動
$ python manage.py runserver
-> このサイトにアクセスできません

ん? vagrantだから、http://127.0.0.1:8000/ は通ってない?

公式tutorial01/を見ると、ipとポートを指定できるみたい。
再度実行
$ python manage.py runserver 192.168.33.10:8000

あれ?
DisallowedHost at / … が表示される。
settings.pyのALLOWED_HOSTS = []を変更する

ALLOWED_HOSTS = ['192.168.33.10']

$ python manage.py runserver 192.168.33.10:8000

OK^^

Djangoを始めよう

$ python -V
Python 3.8.0
$ sudo apt install python-pip
$ pip install Django==3.0.4
Traceback (most recent call last):
File “/usr/bin/pip”, line 9, in
from pip import main
ModuleNotFoundError: No module named ‘pip’

ん?
$ wget https://bootstrap.pypa.io/get-pip.py
$ python get-pip.py

再度インストール
$ pip install Django==3.0.4

### make Django project
$ mkdir django
$ cd django
$ django-admin startproject django_app

おおおおおお、ファイル少ないな
テンション上がってきました