0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Django】008. Managerクラスによるレコードの取得

Posted at

前回までで、モデル/テーブルの作成、管理画面でのテーブルのダミーデータ作成を行いました。

前回記事:【Django】007. 管理ツール+管理画面でのデータベースCRUD

今回はDjangoのアプリケーションからManagerクラスを利用してデータベースを利用していきます。(今回はレコードの取得)

Managerクラスとは?

公式ドキュメントにて以下のように記載されています。

マネージャ (Manager) とは、Django のモデルに対するデータベースクエリの操作を提供するインターフェイスです。Django アプリケーション内の1つのモデルに対して、Manager は最低でも1つは存在します。

Manager クラスの詳細については、クエリを作成する に書かれています。ここでは、特に、Manager の動作をカスタマイズするモデルのオプションについて説明しています。

通常はデータベースを操作するのにSQLを使用することが多いと思いますが、Managerクラスを使用することでSQLを避けてデータベースの操作が行えます。

さらに、Djangoのモデルに対し自動でManagerが作成されるため特別な準備は不要!(というように読み取れます)

今回使用する前回までに作成したFriendモデルを再掲しておきます。

class Friend(models.Model):
    name = models.CharField(max_length=100)
    mail = models.EmailField(max_length=200)
    age = models.IntegerField(default=0)
    birthday = models.DateField()
    
    def __str__(self):
        return '<Friend:id=' + str(self.id) + ', ' + \
            self.name + '(' + str(self.age) + ')>'

レコードの表示

helloというアプリケーションを作成したという設定で、helloアプリケーションのviews.pyに以下を追加します。

views.py
from django.shortcuts import render
from .models import Friend

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

Friendテーブルのレコードをすべて取得する方法になります。

data = Friend.objects.all()

の部分で取得を行っています。

ここで登場したobjectsというのがManagerクラスのインスタンスとしてモデルクラスに設定されているものになります。

ManagerクラスのインスタンスであるFriend.objectsを通してallというメソッドを呼び出しレコードをモデルのインスタンス(の集合)として取得するという流れになります。

取得した内容の表示

テンプレート(ここではshow_record.htmlとしました)を以下のようにします。

show_record.html
{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">

</head>
<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>
</html>

views.pyにてパラメータとして渡したdata(取得したレコードの集合)がイテラブルなオブジェクトとして渡されfor分で取得するという内容です。

アクセス数と以下のように表示され、ちゃんと取得できていることが確認できます。

image.png

特定のレコードを取り出す

allメソッドで全てのレコードを取得する方法はわかりましたが、毎回すべてが必要になるわけではありません。

特定の条件に一致するレコードを取得してみます。

今回はブラウザ上でidを指定して一致するものを取り出すということにします。

Formの準備

まず、id入力用のフォームとして以下を準備します。

forms.py
from django import forms

class FriendForm(forms.Form):
    id = forms.IntegerField(label="ID")

テンプレートの変更

先ほどのshow_record.htmlへ以下のようにformを追加します。

{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">

</head>
<body class="container">
    <h1 class="display-4 text-primary">{{ title }}</h1>
    <p class="h5 mt-4">{{ message | safe }}</p>
    <form action="{% url 'show_record' %}" method="post">
        {% csrf_token %}
        {{ form }}
        <input type="submit" value="click">
    </form>
    
    <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>
</html>

ビュー関数の変更

ビュー関数をpostリクエスト時の動作を追加します。
※これまでの処理をpost以外の時(実質ほぼget)の処理として残す

from django.shortcuts import render
from .forms import FriendForm
from .models import Friend

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

Managerクラスのgetというメソッドを使用しています。
引数でid=idとして条件を指定しています。

動作確認

アクセスすると以下のようになります。

image.png

試しに2としてクリックすると以下のようにちゃんと抽出されていることが確認できました。

image.png

allメソッドで取得されたモデル/レコードの集合を少し詳しく見る

テンプレートを以下に変更してみます。

{% load static %}
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>{{ title }}</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-9ndCyUaIbzAi2FUVXJi0CjmCapSmO7SnpJef0486qhLnuZ2cdeRhO02iuK6FUUVM" crossorigin="anonymous">

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

すると以下のような表示になります。

image.png

allメソッドで取得したレコードの集合はQuerySetというクラスのインスタンスであったことがわかります。

<Friend:id=1, taro(20)>等の表記はモデルクラスに定義した__str__メソッドの内容なので、一つ一つの要素はモデルクラスのインスタンスだとわかります。

QuerySetには様々なメソッドが用意されています。
例:

メソッド名 詳細
values モデルの中から値だけを取り出す
values_list 値をリストとして取り出す

valuesメソッド

ビュー関数を以下のように変更します。

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

アクセスすると以下のようにモデルクラスとしてではなく、dictとして取得できていることが確認できます。

image.png

valuesメソッドで特定項目を取り出す

ビュー関数を以下のように変更します。

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

values関数の引数にて取得したい項目名を指定しています。

アクセスすると以下のように指定した項目のみ取得できていることが確認できます。

image.png

values_listメソッド

ビュー関数を以下のように変更します。

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

すると以下のようになります。
リストと言いつつタプルで返ってきていることがわかります。(細かいことは気にしない…。)

image.png

QuerySetのその他のメソッド

ここでは以下3つを使ってみます。

メソッド名 詳細
first allなどで得られたレコードのうち最初のものだけを返す
last allなどで得られたレコードのうち最後のものだけを返す
count allなどで得られたレコード数を返す
def show_record(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/show_record.html', params)

表示は以下のようになります。

image.png

上から順にcountによる要素数、firstによるレコード、lastによるレコードが確認できました。

ここで見てきたようにallメソッドとメソッドチェーンでvalues / values_list / first / last / countメソッド等をつなぐことで幅広い処理を行うことが可能になります。

QuerySetの表示をカスタマイズする

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

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

QuerySet.__str__ = __new_str__

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

Pythonの言語仕様を悪用するパターンです。
もともと用意されているクラスでも__str__メソッドを上書きすることができ、それにより好きなように表示させることができます。

確認のためテンプレートを以下に変更します。(bodyのみ記載)

<body class="container">
    <h1 class="display-4 text-primary">{{ title }}</h1>
    <table class="table">
        {{ data | safe }}
    </table>
</body>

アクセスすると以下のように表示されます。

image.png

__new_str__メソッドで指定した通りの記載になっていることが確認できました。

既存の特殊メソッドを書き換えるのはなんとなくアンチパターンな気もしますが、使い方次第では良いのかもしれませんね。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?