Pengelola

class Manager[sumber]

Sebuah manager adalah antarmuka melalui mana tindakan permintaan basisdata disediakan pada model Django. Setidaknya satu Manager ada untuk setiap model dalam sebuah apliaksi Django.

Cara kelas-kelas Manager bekerja didokumentasikan dalam Membuat query; dokumen ini secara khusus menyentuh pada pilihan model yang menyesuaikan perilaku Manager.

Nama pengelola

Secara awalan, Django menambah sebuah Manager dengan nama objects pada setiap kelas model Django. Bagaimanapun, jika anda ingin menggunakan objects sebagai sebuah nama bidang, atau jika anda ingin menggunakan sebuah nama selain dari objects untuk Manager, anda dapat menamai kembali itu pada berdasarkan per-model. Untuk menamai kembali Manager untuk kelas diberikan, tentukan sebuah atribut kelas dari jenis models.Manager() pada model itu. Sebagai contoh:

from django.db import models

class Person(models.Model):
    #...
    people = models.Manager()

Menggunakan model contoh ini, Person.objects akanmembangkitkan sebuah pengecualian AttributeError, tetapi Person.people.all() akan menyediakan daftar dari semua obyek Person.

Pengelolaan penyesuaian

Anda dapat menggunakan sebuah penyesuaian Manager dalam model tertentu dengan memperpanjang kelas Manager dasar dan menginstansiasi penyesuaian Manager anda dalam model anda.

Ada dua alasan anda mungkin ingin menyesuaikan sebuah Manager: untuk menambah metode Manager tambahan, dan/atau merubah QuerySet awal kembalian Manager.

Menambahkan metode pengelolaan tambahan

Menambahkan metode Manager tambahan adalah cara disukai untuk menambah fungsionalitas "table-level" pada model anda. (Untuk fungsionalitas "row-level" -- yaitu yang bertindak pada sebuah instance tunggal dari sebuah obyek model -- gunakan Model methods, bukan penyesuaian metode Manager.)

Sebuah metode Manager penyesuaian dapat mengembalikan apapun anda inginkan. Itu tidak harus mengembalikan sebuah QuerySet.

Sebagai contoh, Manager penyesuaian ini menawarkan sebuah metode with_counts(), yang mengembalikan sebuah daftar dari semua obyek OpinionPoll, setiap dengan sebuah atribut num_responses tambahan yaitu hasil dari sebuah pengumpulan permintaan:

from django.db import models

class PollManager(models.Manager):
    def with_counts(self):
        from django.db import connection
        with connection.cursor() as cursor:
            cursor.execute("""
                SELECT p.id, p.question, p.poll_date, COUNT(*)
                FROM polls_opinionpoll p, polls_response r
                WHERE p.id = r.poll_id
                GROUP BY p.id, p.question, p.poll_date
                ORDER BY p.poll_date DESC""")
            result_list = []
            for row in cursor.fetchall():
                p = self.model(id=row[0], question=row[1], poll_date=row[2])
                p.num_responses = row[3]
                result_list.append(p)
        return result_list

class OpinionPoll(models.Model):
    question = models.CharField(max_length=200)
    poll_date = models.DateField()
    objects = PollManager()

class Response(models.Model):
    poll = models.ForeignKey(OpinionPoll, on_delete=models.CASCADE)
    person_name = models.CharField(max_length=50)
    response = models.TextField()

Dengan contoh ini, anda akan menggunakan OpinionPoll.objects.with_counts() untuk mengembalikan daftar itu dari obyek OpinionPoll dengan atribut num_responses.

Hal lain untuk dicatat tentang contoh ini adalah bahwa metode Manager dapat mengakses self.model untuk mendapatkan kelas model dimana mereka dilampirkan.

Merubah QuerySet awal pengelolaan

Dasar Manager QuerySet mengembalikan semua obyek-obyek di sistem. Sebagai contoh, menggunakan model ini:

from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

...pernyataan Book.objects.all() akan mengembalikan semua buku-buku di basisdata.

Anda dapat menimpa QuerySet dasar Manager dengan menimpa metode Manager.get_queryset(). get_queryset() harus mengembalikan sebuah QuerySet dengan sifat-sifat anda butuhkan.

Sebagai contoh, model berikut mempunyai dua Manager -- satu yang mengembalikan semua obyek, dan satu yang mengembalikan hanya buku-buku oleh Roald Dahl:

# First, define the Manager subclass.
class DahlBookManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(author='Roald Dahl')

# Then hook it into the Book model explicitly.
class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=50)

    objects = models.Manager() # The default manager.
    dahl_objects = DahlBookManager() # The Dahl-specific manager.

Dengan model contoh ini, Book.objects.all() akan mengembalikan semua buku-buku di basisdata, tetapi Book.dahl_objects.all() akan hanya mengembalikan satu yang ditulis oleh Roald Dahl.

Tentu saja, karena get_queryset() mengembalikan sebuah obyek QuerySet, anda dapat menggunakan filter(), exclude() dan semua dari metode QuerySet lain pada itu. Jadi pernyataan ini adalah semua sah:

Book.dahl_objects.all()
Book.dahl_objects.filter(title='Matilda')
Book.dahl_objects.count()

Contoh ini juga menunjuk teknik menarik lainnya: menggunakan banyak pengelola pada model sama. Anda dapat melampirkan sebanyak instance Manager() pada sebuah model sesuka anda. Ini adalah cara mudah untuk menentukan "filters" umum untuk model anda.

Sebagai contoh:

class AuthorManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(role='A')

class EditorManager(models.Manager):
    def get_queryset(self):
        return super().get_queryset().filter(role='E')

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    role = models.CharField(max_length=1, choices=(('A', _('Author')), ('E', _('Editor'))))
    people = models.Manager()
    authors = AuthorManager()
    editors = EditorManager()

Contoh ini mengizinkan anda meminta Person.authors.all()`, Person.editors.all(), dan Person.people.all(), menghasilkan hasil dapat diramalkan.

Pengelola awal

Model._default_manager

Jika anda menggunakan obyek Manager penyesuaian, ambil catatan bahwa Manager Django pertama hadapi (dalam urutan dimana mereka ditentukan dalam model) mempunyai sebuah keadaan khusus. Django menafsirkan Manager pertama ditentukan dalam sebuah kelas sebagai Manager "default", dan beberapa bagian dari Django (termasuk dumpdata) akan menggunakan yang Manager khusus untuk model itu. Sebagai sebuah hasil, itu adalah ide bagus untuk hati-hati dalam pilihan anda dari pengelola awalan untuk menghindari keadan dimana menimpa hasil get_queryset() dalam sebuah ketidakmampuan mengambil obyek-obyek anda sukai bekerja dengannya.

Anda dapat menentukan sebuah penyesuaian pengelolaan awalan menggunakan Meta.default_manager_name.

Jika anda sedang menulis beberapa kode yang harus menangani sebuah model tidak dikenal, sebagai contoh, dalam sebuah aplikasi pihak-ketiga yang menerapkan sebuah tampilan umum, gunakan pengelola ini (atau _base_manager) daripada beranggapan model mempunyai sebuah pengelola objects.

Pengelolaan dasar

Model._base_manager

Jangan menyaring hasil apapun dalam jenis ini dari pengelola subkelas

Pengelola ini digunakan untuk mengakses obyek yang terkait pada dari beberapa model lain. Dalam keadaan tersebut, Django harus dapat melihat semua obyek untuk model itu sedang diambil, sehingga apapun yang mengacu dapat diambil.

Jika anda menimpa metode get_queryset() dan menyaring setiap baris, Django akan mengembalikan hasil salah. Jangan lakukan itu, Sebuah pengelola yang menyaring hasil dalam get_queryset() tidak sesuai untuk digunakan sebagai pengelola dasar.

Memanggil metode QuerySet penyesuaian dari pengelola

Selagi kebanyakan metode dari QuerySet standar dapat diakses langsung dari Manager, ini hanya kasus untuk metode tambahan ditentukan pada sebuah penyesuaian QuerySet jika anda juga menerapkan mereka pada Manager:

class PersonQuerySet(models.QuerySet):
    def authors(self):
        return self.filter(role='A')

    def editors(self):
        return self.filter(role='E')

class PersonManager(models.Manager):
    def get_queryset(self):
        return PersonQuerySet(self.model, using=self._db)

    def authors(self):
        return self.get_queryset().authors()

    def editors(self):
        return self.get_queryset().editors()

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    role = models.CharField(max_length=1, choices=(('A', _('Author')), ('E', _('Editor'))))
    people = PersonManager()

Contoh ini mengizinkan anda memanggil kedua authors() dan editors() secara langsung dari pengelola Person.people.

Membuat pengelola dengan cara QuerySet

Sebagai pengganti dari pendekatan diatas yang membutuhkan metode penggandaan pada kedua QuerySet dan Manager, QuerySet.as_manager() dapat digunakan untuk membuat sebuah instance dari Manager dengan salinan dari sebuah metode QuerySet penyesuaian:

class Person(models.Model):
    ...
    people = PersonQuerySet.as_manager()

Instance Manager dibuat oleh QuerySet.as_manager() akan secara virtual mirip pada PersonManager dari contoh sebelumnya.

Tidak setiap metode QuerySet masuk akal pada tingkat Manager; sebagai contoh kami sengaja mencegah metode QuerySet.delete() dari menjadi disalin ke atas kelas Manager.

Cara disalin berdasarkan aturan berikut:

  • Cara umum disalin secara awal.
  • Metode pribadi (mulai dengan sebuah garis bawah) tidak disalin secara awalan.
  • Metode dengan atribut queryset_only disetel menjadi False adalah selalu disalin.
  • Metode dengan atribut queryset_only disetel menjadi True adalah tidak pernah disalin.

Sebagai contoh:

class CustomQuerySet(models.QuerySet):
    # Available on both Manager and QuerySet.
    def public_method(self):
        return

    # Available only on QuerySet.
    def _private_method(self):
        return

    # Available only on QuerySet.
    def opted_out_public_method(self):
        return
    opted_out_public_method.queryset_only = True

    # Available on both Manager and QuerySet.
    def _opted_in_private_method(self):
        return
    _opted_in_private_method.queryset_only = False

from_queryset()

classmethod from_queryset(queryset_class)

Untuk penggunaan lanjutan anda mungkin ingin kedua penyesuaian Manager dan penyesuaian QuerySet. Anda dapat melakukan itu dengan memanggil Manager.from_queryset() yang mengembalikan sebuah subkelas dari dasar Manager anda dengan salinan dari metode QuerySet penyesuaian:

class BaseManager(models.Manager):
    def manager_only_method(self):
        return

class CustomQuerySet(models.QuerySet):
    def manager_and_queryset_method(self):
        return

class MyModel(models.Model):
    objects = BaseManager.from_queryset(CustomQuerySet)()

Anda mungkin juga menyimpan kelas dibangkitkan kedalam sebuah variabel:

CustomManager = BaseManager.from_queryset(CustomQuerySet)

class MyModel(models.Model):
    objects = CustomManager()

Pengelolaan penyesuaian dan warisan model

Ini adalah bagaimana Django menangani pengelolaan penyesuaian dan model inheritance:

  1. Pengelola dari kelas-kelas dasar selalu diwarisi oleh anak kelas, menggunakan urutan resolusi nama biasa Python (nama pada kelas anan menimpa semua lainnya; kemudian datang nama pada kelas induk pertama, dan seterusnya).
  2. Jika tidak ada pengelola dinyatakan pada model dan/atau induknya, Django secara otomatis membuat pengelola objects.
  3. Pengelola awalan pada sebuah kelas adalah antara satu dipilih dengan Meta.default_manager_name, atau pengelola pertama dinyatakan pada model, atau pengelola awalan dari model induk pertama.

Aturan-aturan ini menyediakan keluwesan yang dibutuhkan jika anda ingin memasang sebuah kumpulan dari penyesuaian pengelola pada sekelompok model, melalui sebuah kelas dasar, tetapi masih menyesuaikan pengelola awalan. Sebagai contoh, misalkan anda mempunyai kelas dasar ini:

class AbstractBase(models.Model):
    # ...
    objects = CustomManager()

    class Meta:
        abstract = True

Jika anda menggunakan ini langsung di sebuah subkelas, objects akan berupa pengelola awalan jika anda menyatakan tidak ada pengelola di kelas dasar:

class ChildA(AbstractBase):
    # ...
    # This class has CustomManager as the default manager.
    pass

Jika anda ingin mewarisi dari AbstractBase, tetapi menyediakan pengelola awalan berbeda, anda dapat menyediakan pengelola awalan pada kelas anak:

class ChildB(AbstractBase):
    # ...
    # An explicit default manager.
    default_manager = OtherManager()

Disini, default_manager adalah awalan. Pengelola objects masih tersedia, sejak itu diwarisi. Itu hanya tidak digunakan sebagai awalan.

Akhirnya untuk contoh ini, misalkan anda ingin menambah pengelola tambahan pada kelas anak, tetapi masih menggunakan awalan dari AbstractBase. Anda tidak dapat menambah pengelola baru langsung dalam kelas anak, seperti itu akan menimpa awalan dan anda akan juga jelas menyertakan semua pengelola dari kelas dasar abstrak. Pemecahannya adalah menaruh pengelola tambahan dalam kelas dasar lain dan memperkenalkan itu kedalam hirarki warisan setelah awalan:

class ExtraManager(models.Model):
    extra_manager = OtherManager()

    class Meta:
        abstract = True

class ChildC(AbstractBase, ExtraManager):
    # ...
    # Default manager is CustomManager, but OtherManager is
    # also available via the "extra_manager" attribute.
    pass

Catat bahwa selagi anda dapat menentukan pengelolaan penyesuaian pada model abstrak, anda tidak dapat meminta metode apapun menggunakan model abstrak. Yaitu:

ClassA.objects.do_something()

legal, tetapi:

AbstractBase.objects.do_something()

akan memunculkan sebuah pengecualian. Ini karena pengelola bermaksud untuk mengenkapsulasi logika untuk mengelola kumpulan dari obyek. Sejak anda tidak dapat mempunyai sebuah kumpulan dari obyek-obyek abstrak, itu tidak masuk akal untuk mengelola mereka. Jika anda mempunyai fungsionalitas yang berlaku pada model abstrak, anda harus menaruh fungsionalitas itu dalam sebuah staticmethod atau classmethod pada model abstrak.

Perhatian penerapan

Fitur-fitur apapun anda tambahkan ke Manager penyesuaian anda, itu harus mungkin untuk membuat salinan dangkal dari sebuah instance Manager; yaitu, kode berikut harus bekerja:

>>> import copy
>>> manager = MyManager()
>>> my_copy = copy.copy(manager)

Django membuat salinan dangkal dari obyek-obyek pengelola selama permintaan tertentu; jika Manager anda tidak dapat disalin, permintaan itu akan gagal.

Ini tidak akan menjadi sebuah masalah untuk kebanyakan pengelola penyesuaian. Jika anda hanya menambahkan metode sederhana ke Manager anda, itu tidak mungkin bahwa anda akan secara tidak sengaja membuat instance-instance dari tidak dapat disalin Manager anda. Bagaimanapun, jika anda menimpa __getattr__ atau beberapa metode pribadi lain dari obyek Manager anda yang mengendalikan keadaan obyek, anda harus memastikan bahwa anda tidak mempengaruhi kemampuan dari Manager anda untuk disalin.