Soru Django'da, querysets'e bir yöntem ekleyebilir misiniz?


Django'da, model sınıfım varsa, ör.

from django.db import models

class Transaction(models.Model):
    ...

Daha sonra modele metot eklemek istiyorum, ör. makul karmaşık filtreler, özel bir model yöneticisi ekleyebilirim, ör.

class TransactionManager(models.Manager):

    def reasonably_complex_filter(self):
        return self.get_query_set().filter(...)


class Transaction(models.Model):
    objects = TransactionManager()

Ve sonra yapabilirim:

>>> Transaction.objects.reasonably_complex_filter()

Modelden bir sorgu kümesinin sonuna zincirlenebilecek özel bir yöntem ekleyebileceğim herhangi bir yol var mı?

Özel yöntemi, bunu yapabileceğim şekilde ekleyin:

>>> Transaction.objects.filter(...).reasonably_complex_filter()

25
2018-01-02 01:10


Menşei




Cevaplar:


Yöntemleri eklemek gerekir QuerySet sonunda sonuçlanacaksın. Bu yüzden, bir QuerySet Bu işlevselliği istediğiniz her yerde tanımladığınız yöntemlere sahip alt sınıf.

Bunu nasıl yapacağınızı ve neden istediğinizi açıklayan bu öğreticiyi buldum:

http://adam.gomaa.us/blog/2009/feb/16/subclassing-django-querysets/index.html


5
2018-01-02 01:19



Doğru, yakaladım. Bu blogda önerilen yöntemin güvenilir bir şekilde çalışıp çalışmadığını biliyor musunuz? Yorumlar, __getattr__ yaklaşımın bazı sorunları var. - Paul D. Waite
Bu kodu kendim uygulamaya koymadım, ama gördüm __getattr__ Django projelerinde benzer şekilde güvenilir bir şekilde kullanılır. - Marcus Whybrow
Makale çok eskidir, ancak konu şu ki; QuerySet bazı moda nesneler. - Marcus Whybrow
Var daha yeni makale biraz daha kolay grok buluyorum. Yorumlarda bir düzeltme olduğunu unutmayın, kod olmadan 1.2 sözde kırılır. - Dan Abramov
Bağlantı öldü - Wallace Maxters


Django 1.7'den itibaren, yetenek sorgu kümesini yönetici olarak kullanmak eklendi:

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

    def editors(self):
        return self.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 = PersonQuerySet.as_manager()

Aşağıdakilere yol açıyor:

Person.people.authors(last_name='Dahl')

Ek olarak, ekleme yeteneği özel aramalar ayrıca eklendi.


31
2017-10-30 06:36





Bu, Django 1.3'te çalıştığı bilinen eksiksiz bir çözümdür. Zach Smith ve Ben.

class Entry(models.Model):
    objects = EntryManager() # don't forget this

    is_public = models.BooleanField()
    owner = models.ForeignKey(User)


class EntryManager(models.Manager):
    '''Use this class to define methods just on Entry.objects.'''
    def get_query_set(self):
        return EntryQuerySet(self.model)

    def __getattr__(self, name, *args):
        if name.startswith("_"): 
            raise AttributeError
        return getattr(self.get_query_set(), name, *args) 

    def get_stats(self):
        '''A sample custom Manager method.'''
        return { 'public_count': self.get_query_set().public().count() }


class EntryQuerySet(models.query.QuerySet):
    '''Use this class to define methods on queryset itself.'''
    def public(self):
        return self.filter(is_public=True)

    def by(self, owner):
        return self.filter(owner=owner)


stats = Entry.objects.get_stats()    
my_entries = Entry.objects.by(request.user).public()

Not:  get_query_set() yöntem şimdi Django 1.6'da kullanımdan kaldırıldı; get_queryset() Bu durumda yerine kullanılmalıdır.


14
2017-10-31 23:40





Değiştirebilirsin get_query_set() İstediğiniz yöntemleri ekleyerek, özel bir QuerySet döndürme yöntemi. Senin durumunda, şunları kullanırsın:

class TransactionManager(models.Manager):
    def get_query_set(self):
        return TransactionQuerySet(self.model)

class TransactionQuerySet(models.query.QuerySet):
    def reasonably_complex_filter(self):
        return self.filter(...)

TransactionQuerySet alt sınıflarına örneklerini gördüm Transaction model veya ilgili Managerama bu tamamen size kalmış.

Düzenle: Ben gerçeği gözden kaçırmış gibi görünüyor objects ilk referanslar TransactionManager ve bu nedenle Transaction.objects.reasonably_complex_filter() benim uygulamamda mümkün değil. Bu üç şekilde düzeltilebilir:

  • Uygulamak reasonably_complex_filter hem Yönetici hem de QuerySet'te;
  • kullanım Transaction.objects.all().reasonably_complex_filter() bu tek filtre gerektiğinde;
  • Her iki yöntemde de yöntemi uygulayacak bir çözüm için Marcus Whybrow'un cevabına bakınız. QuerySet ve Manager kod çoğaltması olmadan.

Kodun çoğaltılmasına karşı şiddetle tavsiye etmeme rağmen (ki bunun üstesinden gelmek için global bir metot kullanabilmenize rağmen), hangi seçeneğin en arzu edilir uygulama olduğuna bağlı. Yine de, bu tür uygulamaları bir kez yapmanız gerekiyorsa ya da yalnızca karmaşık filtreyi başka bir filtreyle birlikte kullanmayı düşünüyorsanız, son seçenek fazla masraflı olabilir.


4
2018-01-02 01:21





Aslında başka bir yöntemle gittim. Sadece zincire ihtiyacım olduğu ortaya çıktı. filter özel yöntemimin sonuna çağırır, bu yüzden isteğe bağlı anahtar kelime argümanlarını alma yöntemimi değiştirdim ve filter() makul karmaşık sorgumun sonunu çağır:

class TransactionManager(models.Manager):

    def reasonably_complex_filter(self, **kwargs):
        return self.get_query_set().filter(...).filter(**kwargs)

Amaçlarım için iyi çalışıyor gibi görünüyor ve alt sınıftan biraz daha basit QuerySet.


0
2018-01-08 00:58





Her iki özel yönetim yöntemine ve özel QuerySet yöntemlerine ihtiyacınız varsa from_queryset.

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)()

https://docs.djangoproject.com/en/2.1/topics/db/managers/#from-queryset


0
2017-08-06 09:40