[Django]レコードの並び替え

Managerクラスの「order_by」メソッドで行う

/hello/views.py

def index(request):
	data = Friend.objects.all().order_by('age')
	params = {
		'title': 'Hello',
		'message': '',
		'data': data,
	}		
	return render(request, 'hello/index.html', params)

/hello/templates/hello/index.html

<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<p>{{message|safe}}</p>
	<table class="table">
		<tr>
			<th>id</th>
			<th>name</th>
			<th>age</th>
			<th>mail</th>
			<th>birth</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>
		</tr>
	{% endfor %}
	</table>
</body>

逆の場合はorder_by().reverse()とする

data = Friend.objects.all().order_by('age').reverse()

### 指定した範囲のレコードを取り出す
/hello/views.py

def find(request):
	if(request.method == 'POST'):
		msg = 'Search Result:'
		form = FindForm(request.POST)
		find = request.POST['find']
		list = find.split()
		data = Friend.objects.all()[int(list[0]):int(list[1])]
	else:
		msg = 'search words...'
		form = FindForm()
		data =Friend.objects.all()
	params = {
		'title': 'Hello',
		'message': msg,
		'form': form,
		'data': data,
	}
	return render(request, 'hello/find.html', params)

/hello/templates/hello/find.html

<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<p>{{message|safe}}</p>
	<form action="{% url 'find' %}" method="post">
		{% csrf_token %}
		{{ form.as_p }}
		<tr>
			<th></th><td><input type="submit" value="click" class="btn btn-primary mt-2"></td>
		</tr>
	</form>
	<table class="table">
		<tr>
			<th>id</th>
			<th>name</th>
			<th>age</th>
			<th>mail</th>
			<th>birthday</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>
		</tr>
	{% endfor %}
	</table>
</body>

### レコードの集計
aggregateメソッドで集計を行わせる
– count: レコード数
– sum: 合計
– avg: 平均
– min: 最小値
– max: 最大値

/hello/views.py

from django.db.models import Count, Sum, Avg, Min, Max

def index(request):
	data = Friend.objects.all()
	re1 = Friend.objects.aggregate(Count('age'))
	re2 = Friend.objects.aggregate(Sum('age'))
	re3 = Friend.objects.aggregate(Avg('age'))
	re4 = Friend.objects.aggregate(Min('age'))
	re5 = Friend.objects.aggregate(Max('age'))
	msg = 'count:' + str(re1['age__count']) + '<br>Sum:' + str(re2['age__sum']) + '<br>Average:' + str(re3['age__avg']) + '<br>Min:' + str(re4['age__min']) + '<br>Max:' + str(re5['age__max'])
	params = {
		'title': 'Hello',
		'message': '',
		'data': data,
	}		
	return render(request, 'hello/index.html', params)

avgの小数点以下がおかしなことになってますが、filterを使えば大抵の事は出来るとのこと。

[Django]filterによる検索

モデルにはobjects属性があり、その中にManagerというクラスのインスタンスが入っている
このManagerにfilter機能がある

/hello/urls.py

urlpatterns = [ 
	path('', views.index, name='index'),
	path('create', views.create, name='create'),
	path('edit/<int:num>', views.edit, name='edit'),
	path('delete/<int:num>', views.delete, name='delete'),
	path('list', FriendList.as_view()),
	path('detail/<int:pk>', FriendDetail.as_view()),
	path('find', view.find, name='find'), # 追加
]

/hello/forms.py

class FindForm(forms.Form):
	find = forms.CharField(label='Find', required=False, widget=forms.TextInput(attrs={'class':'form-control'}))

/hello/templates/hello/find.html

<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<p>{{message|safe}}</p>
	<form action="{% url 'find' %}" method="post">
		{% csrf_token %}
		{{ form.as_p }}
		<tr>
			<th></th><td><input type="submit" value="click" class="btn btn-primary mt-2"></td>
		</tr>
	</form>
	<table class="table">
		<tr>
			<th>id</th>
			<th>name</th>
			<th>mail</th>
		</tr>
	{% for item in data %}
		<tr>
			<td>{{item.id}}</td>
			<td>{{item.name}}({{item.age}})</td>
			<td>{{item.mail}}</td>
		</tr>
	{% endfor %}
	</table>
</body>

/hello/views.py

def find(request):
	if(request.method == 'POST'):
		form = FindForm(request.POST)
		find = request.POST['find']
		data = Friend.objects.filter(name=find)
		msg = 'Result: ' + str(data.count())
	else:
		msg = 'search words...'
		form = FindForm()
		data =Friend.objects.all()
	params = {
		'title': 'Hello',
		'message': msg,
		'form': form,
		'data': data,
	}
	return render(request, 'hello/find.html', params)

Friend.objects.filter(name=find)で指定している。

### 曖昧検索
– 値を含む検索: __contains=value
– 値で始まるもの: __startswith=value
– 値で終わるもの: __endswith=value

if(request.method == 'POST'):
		form = FindForm(request.POST)
		find = request.POST['find']
		data = Friend.objects.filter(name__contains=find)
		msg = 'Result: ' + str(data.count())

– 大文字小文字を区別しない: __iexact=value
– 曖昧検索: __icontains=value, __istartswith=value, __iendswith=value

### 数値の比較
– 等しい: =value
– 大きい: __gt=value
– 以上: __gte=value
– 小さい: __lt=value
– 以下: __lte=value

if(request.method == 'POST'):
		form = FindForm(request.POST)
		find = request.POST['find']
		data = Friend.objects.filter(age__lte=int(find))
		msg = 'Result: ' + str(data.count())

●●以上●●以下
split()は改行やスペースで分割する

	if(request.method == 'POST'):
		form = FindForm(request.POST)
		find = request.POST['find']
		val = find.split()
		data = Friend.objects.filter(age__gte=val[0], age__lte=val[1])
		msg = 'Result: ' + str(data.count())

### AとBどちらも検索
変数 = <<モデル>>.object.filter(Q(条件) | Q(条件))

from django.db.models import Q
// 
data = Friend.objects.filter(Q(name__contains=find)|Q(mail__contains=find))

### リストを使って検索

	if(request.method == 'POST'):
		form = FindForm(request.POST)
		find = request.POST['find']
		list = find.split()
		data = Friend.objects.filter(name__in=list)
		msg = 'Result: ' + str(data.count())

検索条件はアプリケーションの要件や機能によって変わってきますね。

[Django]ジェネリックビュー

ジェネリックビューは指定したモデルから全レコードを取り出したり、特定のIDのものだけ取り出す基本的機能を持ったビュー
ListViewは全レコードを取り出すためのジェネリックビュー
DetailViewは特定のレコードを取り出すためのジェネリックビュー

### ジェネリックビューで表示
/hello/views.py

from django.views.generic import ListView
from django.views.generic import DetailView

class FriendList(ListView):
	model = Friend

class FriendDetail(DetailView):
	model = Friend

/hello/urls.py

from .views import FriendList
from .views import FriendDetail

urlpatterns = [ 
	path('', views.index, name='index'),
	path('create', views.create, name='create'),
	path('edit/<int:num>', views.edit, name='edit'),
	path('delete/<int:num>', views.delete, name='delete'),
	path('list', FriendList.as_view()),
	path('detail/<int:pk>', FriendDetail.as_view()),
]

/hello/templates/hello/friend_list.html

<body class="container">
	<h1 class="display-4 text-primary">Friends List</h1>
	<table class="table">
		<tr>
			<th>id</th>
			<th>name</th>
			<th></th>
		</tr>
	{% for item in object_list %}
		<tr>
			<td>{{item.id}}</td>
			<td>{{item.name}}</td>
			<td><a href="/hello/detail/{{item.id}}">detail</a></td>
		</tr>
	{% endfor %}
	</table>
</body>

/hello/templates/hello/friend_detail.html

<body class="container">
	<h1 class="display-4 text-primary">Friends Detail</h1>
	<table class="table">
		<tr>
			<th>id</th>
			<th>{{object.id}}</th>
		</tr>
		<tr>
			<th>name</th>
			<th>{{object.name}}</th>
		</tr>
		<tr>
			<th>mail</th>
			<th>{{object.mail}}</th>
		</tr>
		<tr>
			<th>gender</th>
			<th>{{object.gender}}</th>
		</tr>
		<tr>
			<th>age</th>
			<th>{{object.age}}</th>
		</tr>
	</table>
</body>

なるほど

[Django]CRUDのDelete

レコードのモデルインスタンスを取得してdeleteメソッドを実行する

urlspaternsの追記
/hello/urls.py

urlpatterns = [ 
	path('', views.index, name='index'),
	path('create', views.create, name='create'),
	path('edit/<int:num>', views.edit, name='edit'),
	path('delete/<int:num>', views.delete, name='delete'),
]

index.htmlの修正
/hello/templates/hello/index.html

{% for item in data %}
		<tr>
			<td>{{item}}</td>
			<td><a href="{% url 'edit' item.id %}">Edit</a></td>
			<td><a href="{% url 'delete' item.id %}">Delete</a></td>
		</tr>
	{% endfor %}

delete.htmlの作成
/hello/templates/hello/delete.html

<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<p>※以下のレコードを削除します。</p>
	<table class="table">
		<tr>
			<th>ID</th>
			<td>{{obj.id}}</td>
		</tr>
		<tr>
			<th>Name</th>
			<td>{{obj.name}}</td>
		</tr>
		<tr>
			<th>Gender</th>
			<td>
				{% if obj.gender == False %} male {% endif %}
				{% if obj.gender == True %} female {% endif %}
			</td>
		</tr>
		<tr>
			<th>Email</th>
			<td>{{obj.mail}}</td>
		</tr>
		<tr>
			<th>Age</th>
			<td>{{obj.age}}</td>
		</tr>
		<tr>
			<th>Birth</th>
			<td>{{obj.birthday}}</td>
		</tr>
		<form action="{% url 'delete' id %}" method="post">
		{% csrf_token %}
		<tr><th></th><td>
			<input type="submit" value="click" class="btn btn-primary">
		</td></tr>
	</form>
	</table>
</body>

delete関数を作る
/hello/views.py

def delete(request, num):
	friend = Friend.objects.get(id=num)
	if(request.method == 'POST'):
		friend.delete()
		return redirect(to='/hello')
	params = {
		'title': 'Hello',
		'id': num,
		'obj': friend,
	} 
	return render(request, 'hello/delete.html', params)

CRUDの基本は抑えました。

[Django]CRUDのUpdate

/hello/urls.py

urlpatterns = [ 
	path('', views.index, name='index'),
	path('create', views.create, name='create'),
	path('edit/<int:num>', views.edit, name='edit'),
]

/hello/templates/hello/create.html

<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<table class="table">
		<tr>
			<th>data</th>
		</tr>
	{% for item in data %}
		<tr>
			<td>{{item}}</td>
			<td><a href="{% url 'edit' item.id %}">Edit</a></td>
		</tr>
	{% endfor %}
	</table>
</body>

– url ‘edit’ item.idで/edit/1というようにアクセスできるようになる

edit.htmlを作る
/hello/templates/hello/edit.html

<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<form action="{% url 'edit' id %}" method="post">
		{% csrf_token %}
		<table class="table">
		{{ form.as_table }}
		<tr>
			<td></td>
			<td><input type="submit" value="click" class="btn btn-primary mt-2"></td>
		</tr>
	</table>
	</form>
</body>

edit関数を作る
/hello/views.py

# edit
def edit(request, num):
	obj = Friend.objects.get(id=num)
	if(request.method == 'POST'):
		friend = FriendForm(request.POST, instance=obj)
		friend.save()
		return redirect(to='/hello')
	params = {
		'title': 'Hello',
		'id': num,
		'form': FriendForm(instance=obj),
	}
	return render(request, 'hello/edit.html', params)

引っ張る時は、’form’: FriendForm(instance=obj),として、updateの時はFriendForm(request.POST, instance=obj)とするのか。なお、createの時はobj = Friend()

[Django]ModelFormを使う

ModelFormを使うことでよりスムーズにレコードの保存を行うことができる

/hello/forms.py

from django import forms
from.models import Friend

class FriendForm(forms.ModelForm):
	class Meta:
		model = Friend
		fields = ['name','mail','gender','age','birthday']

/hello/views.py

def create(request):
	if(request.method == 'POST'):
		obj = Friend()
		friend = FriendForm(request.POST, instance=obj)
		friend.save()
		return redirect(to='/hello')
	params = {
		'title': 'Hello',
		'form': HelloForm(),
	}
	return render(request, 'hello/create.html', params)

– obj = Friend()でインスタンスを作成し、FriendForm(request.POST, instance=obj)インスタンスを作成する

/hello/templates/hello/create.html

<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<form action="{% url 'create' %}" method="post">
		{% csrf_token %}
		<table class="table">
		{{ form.as_table }}
		<tr>
			<td></td>
			<td><input type="submit" value="click" class="btn btn-primary mt-2"></td>
		</tr>
	</table>
	</form>
</body>

先が長いな。

[Django]CRUDのCreate

/hello/forms.py

from django import forms

class HelloForm(forms.Form):
	name = forms.CharField(label='Name', widget=forms.TextInput(attrs={'class':'form-control'}))
	mail = forms.EmailField(label='Email', widget=forms.EmailInput(attrs={'class':'form-control'}))
	gender = forms.BooleanField(label='Gender', widget=forms.CheckboxInput(attrs={'class':'form-check'}))
	age = forms.IntegerField(label='Age', widget=forms.NumberInput(attrs={'class':'form-control'}))
	birthday = forms.DateField(label='Birth', widget=forms.DateInput(attrs={'class':'form-control'}))

### create.htmlの作成
/hello/templates/hello/create.html

{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>{{title}}</title>
	<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossoorigin="anonymous">
</head>
<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<form action="{% url 'create' %}" method="post">
		{% csrf_token %}
		{{ form.as_p }}
		<input type="submit" value="click" class="btn btn-primary mt-2">
	</form>
</body>
</html>

/hello/templates/hello/index.html

<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<table class="table">
		<tr>
			<th>data</th>
		</tr>
	{% for item in data %}
		<tr>
			<td>{{item}}</td>
		</tr>
	{% endfor %}
	</table>
</body>

/hello/views.py

from django.shortcuts import render
from django.http import HttpResponse
from django.shortcuts import redirect
from .models import Friend
from .forms import HelloForm

def index(request):
	data = Friend.objects.all()
	params = {
		'title': 'Hello',
		'data': data,
	}		
	return render(request, 'hello/index.html', params)

# create model
def create(request):
	params = {
		'title': 'Hello',
		'form': HelloForm(),
	}
	if(request.method == 'POST'):
		name = request.POST['name']
		mail = request.POST['mail']
		gender = 'gender' in request.POST
		age = int(request.POST['age'])
		birth = request.POST['birthday']
		friend = Friend(name=name, mail=mail, gender=gender, age=age, birthday=birth)
		friend.save()
		return redirect(to='/hello')
	return render(request, 'hello/create.html', params)

– models.pyで定義しているFriendインスタンスを作成して、インスタンスを保存する
– return redirectでリダイレクト

/hello/urls.py

from django.urls import path
from . import views

urlpatterns = [ 
	path('', views.index, name='index'),
	path('create', views.create, name='create'),
]

流れとしては、create.htmlからcreate.htmlにpostして、modelのインスタンスを使ってsave()してリダイレクトさせる
リダイレクト先で、if request.method == ‘POST’としても同じだろう。

[Django]モデルのカスタマイズ

### モデルのリストを調査
/hello/views.py

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

def index(request):
	params['data'] = Friend.objects.all()
	params = {
		'title': 'Hello',
		'data': data,
	}		
	return render(request, 'hello/index.html', params)

/hello/templates/hello/index.html

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

def index(request):
	data = Friend.objects.all()
	params = {
		'title': 'Hello',
		'data': data,
	}		
	return render(request, 'hello/index.html', params)

– .objects.all()で取り出していたのはQuerySetというインスタンスだということがわかる
– QuerySetには__str__も含む

### valuesメソッド
– .objects.values()で辞書でデータを取り出すことができる
/hello/views.py

def index(request):
	data = Friend.objects.values()
	params = {
		'title': 'Hello',
		'data': data,
	}		
	return render(request, 'hello/index.html', params)

– 特定の項目を指定

def index(request):
	data = Friend.objects.values('id', 'name')
	params = {
		'title': 'Hello',
		'data': data,
	}		
	return render(request, 'hello/index.html', params)

– リストとして取り出す

data = Friend.objects.all().values_list('id', 'name','age')

– first最初、last最後、countレコード数

def index(request):
	num = Friend.objects.all().count()
	first = Friend.objects.all().first()
	last = Friend.objects.all().last()
	data = [num, first, last]
	params = {
		'title': 'Hello',
		'data': data,
	}		
	return render(request, 'hello/index.html', params)

– QuerySetのカスタマイズ

from django.shortcuts import render
from django.http import HttpResponse
from .models import Friend
from django.db.models import QuerySet

def __new_str__(self):
	result = ''
	for item in self:
		result += '<tr>'
		for k in item:
			result += '<td>' + str(k) + '=' + str(item[k]) + '</td>'
		result += '</tr>'
	return result

QuerySet.__str__ = __new_str__

def index(request):
	data = Friend.objects.all().values('id', 'name', 'age')
	params = {
		'title': 'Hello',
		'data': data,
	}		
	return render(request, 'hello/index.html', params)
<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<table class="table">
		{{data|safe}}
	</table>
</body>

reverseなどもviews.pyで整形すれば良いのですね、少しイメージが湧いてきました。

[Django]指定IDのレコードを取得

### 特定のIDのみ取り出す
/hello/forms.py

from django import forms

class HelloForm(forms.Form):
	id = forms.IntegerField(label='ID')

/hello/templates/hello/index.html

<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<p class="h5 mt-4">{{message|safe}}</p>
	<form action="{% url 'index' %}" method="post">
		{% csrf_token %}
		{{ form}}
		<input type="submit" value="click">
	</form>
	<hr>
	<table class="table">
		<tr>
			<th>ID</th>
			<th>NAME</th>
			<th>GENDER</th>
			<th>MAIL</th>
			<th>AGE</th>
			<th>BIRTHDAY</th>
		</tr>
	{% for item in data %}
		<tr>
			<td>{{item.id}}</td>
			<td>{{item.name}}</td>
			<td>{% if item.gender == False %} male{% endif%}
				{% if item.gender == True %} female{% endif%}</td>
			<td>{{item.mail}}</td>
			<td>{{item.age}}</td>
			<td>{{item.birthday}}</td>
		</tr>
	{% endfor %}
	</table>
</body>

– Friend.objects.get(id=num)でレコードを一つ取り出す
– Managerクラスが取り出している
/hello/views.py

from django.shortcuts import render
from django.http import HttpResponse
from .models import Friend
from .forms import HelloForm

def index(request):
	params = {
		'title': 'Hello',
		'message': 'all friends.',
		'form': HelloForm(),
		'data': [],
	}
	if(request.method == 'POST'):
		num=request.POST['id']
		item=Friend.objects.get(id=num)
		params['data'] = [item]
		params['form'] = HelloForm(request.POST)
	else:
		params['data'] = Friend.objects.all()
	return render(request, 'hello/index.html', params)

views.pyの関数がMVCのコントローラみたいな役割をしてますね。

[Django]レコードの取得

### レコードの表示
– data = Friend.objects.all() で取り出す
/hello/views.py

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

def index(request):
	data = Friend.objects.all()
	params = {
		'title': 'Hello',
		'message': 'all friends.',
		'data': data,
	}
	return render(request, 'hello/index.html', params)

– {% for item in data %}{% endfor %}で繰り返し処理
– {% if %}{% endif %}で条件分岐
/hello/templates/hello/index.html

<body class="container">
	<h1 class="display-4 text-primary">{{title}}</h1>
	<p class="h5 mt-4">{{message|safe}}</p>
	<table class="table">
		<tr>
			<th>ID</th>
			<th>NAME</th>
			<th>GENDER</th>
			<th>MAIL</th>
			<th>AGE</th>
			<th>BIRTHDAY</th>
		</tr>
	{% for item in data %}
		<tr>
			<td>{{item.id}}</td>
			<td>{{item.name}}</td>
			<td>{% if item.gender == False %} male{% endif%}
				{% if item.gender == True %} female{% endif%}</td>
			<td>{{item.mail}}</td>
			<td>{{item.age}}</td>
			<td>{{item.birthday}}</td>
		</tr>
	{% endfor %}
	</table>
</body>

/hello/urls.py

from django.urls import path
from . import views

urlpatterns = [ 
	path('', views.index, name='index'),
]

おおお、いいですね。MVCを触ってる感が出てきました。