ユーニックス総合研究所

  • home
  • archives
  • django-exclude

Djangoのexcludeで指定のレコードを除外する

  • 作成日: 2021-01-12
  • 更新日: 2023-12-24
  • カテゴリ: Django

Djangoのexclude

PythonのWebフレームワークであるDjango(ジャンゴ)では、QuerySetというクラスを使ってモデルのレコードを自由に取得することが出来ます。
filter()all()などの代表的なメソッドがQuerySetのメソッドです。

そのメソッドの中にexclude(エクスクルード)という指定のレコードを除外するメソッドがあります。
このメソッドを使うことでレコードの取得時に特定のデータを除外することが可能になります。

この記事ではこのexclude()について、具体的に↓を見ていきます。

  • exclude()の構造
  • exclude()でレコードを除外する
  • exclude()のソースコードの解析

exclude()の構造

exclude()django.db.models.queryモジュール内のQuerySetクラスのメソッドです。
exclude()は↓のような構造になっています。

exclude(self, *args, **kwargs)  

exclude()は可変長引数とキーワード引数を取り、返り値として新しく生成したQuerySetを返します。
引数にはモデルのフィールドをキーワードで指定することが出来ます。

exclude()でレコードを除外する

今回は↓のようなモデルを作成している前提で解説します。

from django.db import models  


class Person(models.Model):  
    name = models.CharField(max_length=64, help_text='名前')  
    age = models.IntegerField(help_text='年齢')  

Personモデルは人物を表すモデルです。人物名を表すnameフィールドと、年齢を表すageフィールドを持ちます。
データベースには↓のようなコードでオブジェクトを作成しています。

Person.objects.create(name='Taro', age=20)  
Person.objects.create(name='Hanako', age=18)  
Person.objects.create(name='Kenta', age=25)  
Person.objects.create(name='Tama', age=4)  

全部で4件のレコードですね。
このデータベースに対して、特定のレコードを除外して結果を取得したい場合を考えます。
例えばnameが「Kenta」のレコードのみを結果から除外したいとします。
その場合、コードは↓のようになります。

Person.objects.exclude(name='Kenta')  

↑のようにexclude()メソッドのキーワード引数にモデルのフィールド名と値を設定します。
↑の場合、nameフィールドが「Kenta」のレコードが結果から除外されます。
コードの返り値を↓のように出力します。

persons = Person.objects.exclude(name='Kenta')  
for person in persons:  
    print(person.name, person.age)  

↑のコードの出力結果は↓のようになります。

Taro 20  
Hanako 18  
Tama 4  

↑の結果を見ると、取得したレコード内に「Kenta」のレコードがありません。
除外されているのがわかります。

containsを使った除外

フィールド名に__containsをつけることで、そのフィールドの値に対して大文字小文字を区別しない部分一致を行うことが可能です。
例えば↓のコードで初期化したデータベースから、nameに「Ta」が含まれるレコードを除外したいとします。

Person.objects.create(name='Taro', age=20)  
Person.objects.create(name='Hanako', age=18)  
Person.objects.create(name='Kenta', age=25)  
Person.objects.create(name='Tama', age=4)  

その場合、コードは↓のようになります。

Person.objects.exclude(name__contains='Ta')  

結果を↓のように出力します。

persons = Person.objects.exclude(name__contains='Ta')  
for person in persons:  
     print(person.name, person.age)  

データベースがSqlite3の場合は、↑のコードの出力は↓のようになります。

Hanako 18  

↑のようにSqlite3をデータベースにしている場合、containsは大文字小文字を区別しません。
MySQLやMariaDBなどの場合は大文字小文字が区別されます。
MySQLやMariaDBなどで大文字小文字を区別しないようにしたい場合は__icontainsを使います。

exclude()のソースコードの解析

exclude()はDjangoではどのように実装されているのでしょうか?
↓のコードを見てみます。

内容的にはself._filter_or_exclude(True, args, kwargs)を呼び出しています。
これはfilter()メソッド内でも使っていて、共通のメソッドです。
_filter_or_exclude()は最終的に_filter_or_exclude_inplace()を呼び出しています。
このメソッド内ではQオブジェクトを使ってフィルタリングを行っています。
つまりexclude()は最終的にQオブジェクトでフィルタリングされることがわかります。

Qオブジェクトは_query.add_q()に渡されます。
この_queryオブジェクトはdjango.db.models.sql.query内のQueryクラスのオブジェクトです。
そのためadd_q()メソッドはこのQueryクラスのメソッドです。
add_q()メソッド内では引数のQオブジェクトを使ったSQL文の構築を行います。

Queryクラスで構築されたSQL文が最終的にデータベースに発行されるということみたいです。

おわりに

今回はDjangoのQuerySetのメソッドであるexclude()を見て見ました。
特定のレコードを除外したい場合はこのexclude()を使いましょう。

🦝 < 除外してフィルタリングしよう