Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Developing custom QuerySet methods

Developing custom QuerySet methods

Avatar for Gennady Chibisov

Gennady Chibisov

November 20, 2012

Other Decks in Programming

Transcript

  1. Instance  methods   class Event(models.Model): ! name = models.CharField(max_length=255)! slug

    = models.SlugField(unique=True)! date_start = models.DateTimeField()! ! def __unicode__(self):! return self.name ! ! @property! def is_published(self):! # uses current instance data! return (self.date_start <=! datetime.datetime.now())! ! ! >>> yac2012 = Event.objects.get(slug='yac2012')! >>> yac2012.__unicode__()! 'Yet another Conference 2012'! >>> yac2012.is_published! True!  
  2. Class  methods   class Event(models.Model): ! name = models.CharField(max_length=255)! slug

    = models.SlugField(unique=True)! date_start = models.DateTimeField()! ! @classmethod! def get_fields_for_csv(cls):! return ['name', 'slug']! ! ! >>> Event.get_fields_for_csv()! ['name', 'slug']!  
  3. Manager  methods   class RegionManager(models.Manager):! def sync_with_geobase(self):! for region in

    regions_from_geobase():! try:! region_instance = self.model.objects.get(geobase_id=region.id)! # can modify any instance! region_instance.name_ru = region.name! region_instance.name_en = region.ename! region_instance.save()! except self.model.DoesNotExist:! # can create new instances! region_instance = self.model.objects.create(! geobase_id=region, ! name_ru=region.name, ! name_en=region.ename! )! # can remove instance! if region.is_deprecated:! region_instance.delete()! ! ! >>> Region.objects.sync_with_geobase()!
  4. Manager  methods   # app/management/commands/sync_with_geobase.py! class Command(BaseCommand):! help = u"""Syncs

    regions with geobase"""! ! def handle(self, *args, **options):! Region.objects.sync_with_geobase()! ! ! $ ./manage.py sync_with_geobase! !
  5. Readability  L   events = Events.objects.all()! # filter for current

    site type! if settings.IS_INTERNAL_SITE:! events = events.filter(! Q(is_published_external=True) | ! Q(is_published_internal=True)! )! else:! events = events.filter(! is_published_for_external=True! )!
  6. Readability  L   yac2012 = Events.objects.get(slug='yac2012')! ! # is for

    current site type?! if settings.IS_INTERNAL_SITE:! is_for = (yac2012.is_published_for_internal ! and yac2012.is_published_for_external)! else:! is_for = yac2012.is_published_for_external!
  7. Reusability  J   class EventDetailView(DetailView): ! queryset = (Event.objects! .filter_for_current_site_type())!

    ! ! class EventListView(ListView):! queryset = (Event.objects! .filter_for_current_site_type())!
  8. First  manager   # apps/events/managers.py ! from datetime.datetime import now!

    from django.db.models.query import QuerySet! from django.db import models! ! ! class EventQuerySet(QuerySet):! def filter_by_published(self):! return self.filter(date_start__lte=now())! ! ! class EventManager(models.Manager):! def get_query_set(self):! return EventQuerySet(self.model, ! using=self._db)!
  9. First  manager   # apps/events/models.py! from django.db import models! from

    apps.events.managers import EventManager! ! ! class Event(models.Model):! name = models.CharField(max_length=255)! slug = models.SlugField(unique=True)! date_start = models.DateTimeField()! ! objects = EventManager()!
  10. Oops…   >>> Event.objects.filter_by_published()! ! Traceback (most recent call last):!

    File "<console>", line 1, in <module>! AttributeError: 'EventManager' object has no attribute 'filter_by_published'!
  11. Add  proxy  method   class EventManager(models.Manager):! def get_query_set(self):! return EventQuerySet(self.model,

    ! using=self._db)! ! def filter_by_published(self):! return (self.get_query_set()! .filter_by_published())!
  12. Or  try  to  proxy  all  not  found   class EventManager(models.Manager):!

    def get_query_set(self):! return EventQuerySet(self.model, ! using=self._db)! ! def __getattr__(self, name):! if name.startswith('_'):! raise AttributeError(name)! else:! return getattr(self.get_query_set(), ! name)!
  13. apps.events.models   class EventType(models.Model):! """! Example: Yet Another Conference! """!

    name = models.CharField(max_length=255)! slug = models.SlugField(unique=True)! is_moderated = models.BooleanField(default=False)!
  14. apps.events.models   class Event(models.Model):! """! Example: Yac 2012! """! name

    = models.CharField(max_length=255)! slug = models.SlugField(unique=True)! type = models.ForeignKey(EventType)! date_start = models.DateTimeField()!
  15. /events/yac/   •  Yac  2010   •  Yac  2011  

    •  Yac  2012  (too  early)  
  16. Event  QuerySet  method   import datetime! from datetime.datetime import now!

    ! class EventQuerySet(QuerySet):! def filter_by_it_time_to_publish(self):! d = now() + datetime.timedelta(days=2)! return self.filter(date_start__lte=d)!
  17. /events/yac/   # /templates/events/eventtype_detail.html! ! {% for e in object.event_set!

    .filter_by_it_time_to_publish %}! <p>{{ e }}</p>! {% endfor %}!
  18. /events/yac/2012/   Show  if   •  It  is  Tme  to

     publish   •  Yac  EventType  is  published  
  19. /events/yac/2012/   class EventDetailView(DetailView):! queryset = (Event.objects! .filter_by_it_time_to_publish())! ! def

    get_queryset(self):! q = super(EventDetailView, ! self).get_queryset()! return q.filter(type__is_moderated=True)!
  20. /events/yac/2012/   class EventDetailView(DetailView):! queryset = (Event.objects! .filter_by_it_time_to_publish())! ! def

    get_queryset(self):! q = super(EventDetailView, ! self).get_queryset()! pks = (EventType.objects! .filter_by_published()! .values_list('pk', flat=True))!
  21. /events/yac/2012/   class EventDetailView(DetailView):! queryset = (Event.objects! .filter_by_it_time_to_publish())! ! def

    get_queryset(self):! q = super(EventDetailView, ! self).get_queryset()! pks = (EventType.objects! .filter_by_published()! .values_list('pk', flat=True))! return q.filter(type__pk__in=pks)!
  22. class EventTypeQuerySet(QuerySet):! def filter_by_published(self):! return self.filter(Q(is_moderated=True))! ! ! class EventQuerySet(QuerySet):!

    ...! def filter_by_with_published_type(self):! return self.filter(Q(type__is_moderated=True))!
  23. class EventTypeQuerySet(QuerySet):! def filter_by_published(self):! return self.filter(Q(is_moderated=True))! ! ! class EventQuerySet(QuerySet):!

    ...! def filter_by_with_published_type(self):! return self.filter(Q(type__is_moderated=True))! Diff  
  24. class EventTypeConditions(object):! @classmethod! def is_published(cls, prefix=None):! if prefix:! field =

    '%s__is_moderated' % prefix! else:! field = 'is_moderated'! ! return Q(is_moderated=True)! Field  name  with  prefix  
  25. class EventTypeConditions(object):! @classmethod! def is_published(cls, prefix=None):! if prefix:! field =

    '%s__is_moderated' % prefix! else:! field = 'is_moderated'! ! return Q(**{field: True})! Use  prefixed  field  
  26. class EventConditions(object):! @classmethod! def is_with_published_type(cls):! event_prefix = 'type' ! return

    EventTypeConditions.is_published(! prefix=event_prefix! ) ! Create  Event  condiTons  
  27. class EventConditions(object):! @classmethod! def is_with_published_type(cls, prefix=None): ! event_prefix = 'type'

    ! return EventTypeConditions.is_published(! prefix=event_prefix! )! Prefix  as  param  
  28. class EventConditions(object):! @classmethod! def is_with_published_type(cls, prefix=None):! event_prefix = 'type' !

    if prefix:! prefix = '%s__%s' % prefix, event_prefix! else:! prefix = event_prefix! ! return EventTypeConditions.is_published(! prefix=event_prefix! )! Add  prefix  
  29. class EventConditions(object):! @classmethod! def is_with_published_type(cls, prefix=None): event_prefix = 'type'! if

    prefix:! prefix = '%s__%s' % prefix, event_prefix! else:! prefix = event_prefix! ! return EventTypeConditions.is_published(! prefix=prefix! )! Use  prefix  
  30. Lets  refactor   class EventQuerySet(QuerySet):! ...! def filter_by_it_time_to_publish(self):! d =

    now() + datetime.timedelta(days=2)! return self.filter(date_start__lte=d)!
  31. class EventConditions(object):! @classmethod! def is_with_published_type(cls, prefix=None):! ...! ! @classmethod! def

    is_it_time_to_publish(cls, prefix=None): ! if prefix:! field = '%s__date_start__lte' % prefix! else:! field = 'date_start__lte'! ! d = now() + datetime.timedelta(days=2) ! return Q(**{field: d})!
  32. class EventConditions(object):! @classmethod! def is_with_published_type(cls, prefix=None):! ...! ! @classmethod! def

    is_it_time_to_publish(cls, prefix=None): ! ...! ! @classmethod! def is_published(cls, prefix=None):! return (! cls.is_with_published_type(prefix) & ! cls.is_it_time_to_publish(prefix)! )! !
  33. How  to  reuse  condiTons?   class  Event(models.Model):      

       ...          @property          def  is_with_published_type(self):                  return  EventConditions.is_with_published_type(model_instance=self)            @property          def  is_it_time_to_publish(self):                  return  EventConditions.is_it_time_to_publish(model_instance=self)            @property          def  is_published(self):                  return  EventConditions.is_published(model_instance=self)!
  34. True  and  False   =   True  &  False  

        How  to  reuse  condiTons?  
  35. True  or  False   =   True  |  False  

        How  to  reuse  condiTons?  
  36. Use  makers   class TestEventIsPublished(TestConditionBase):! def test_1(self):! makers.event.make_with_published_type(self.instance)! makers.event.make_it_time_to_publish(self.instance)! !

    self.assertConditionTrue(msg=msg)! ! def test_2(self):! makers.event.make_with_published_type(self.instance)! makers.event.make_not_it_time_to_publish(self.instance)! ! self.assertConditionFalse(msg=msg)! ! def test_3(self):! makers.event.make_it_time_to_publish(self.instance)! makers.event.make_not_with_published_type(self.instance)! ! self.assertConditionFalse(msg=msg)!
  37. class DistributionSendedConditions(object):! @classmethod! def is_ready_to_start_with_in_progress_status(cls, prefix=None):! k = {! 'prefix':

    prefix! }! ! return (! (cls.is_with_status(status='in_progress', **k)) &! ((cls.is_last_message_sended_more_than_5_minutes_ago(**k)) |! (cls.is_not_has_messages(**k) & ! cls.is_started_sending_more_than_5_minutes_ago(**k)))! )! More  complex  condiTons