#30355: Specifying custom manager doesn't work with prefetch
-------------------------------+----------------------------------------
     Reporter:  Kyle Mulka     |                    Owner:  Akshat verma
         Type:  Bug            |                   Status:  assigned
    Component:  Documentation  |                  Version:  dev
     Severity:  Normal         |               Resolution:
     Keywords:                 |             Triage Stage:  Accepted
    Has patch:  1              |      Needs documentation:  1
  Needs tests:  1              |  Patch needs improvement:  1
Easy pickings:  1              |                    UI/UX:  1
-------------------------------+----------------------------------------
Changes (by Akshat verma):

 * cc: Akshat verma (added)
 * needs_better_patch:  0 => 1
 * needs_tests:  0 => 1
 * easy:  0 => 1
 * has_patch:  0 => 1
 * ui_ux:  0 => 1


Old description:

> When using prefetch and specifying a custom manager to use for a reverse
> relation, Django doesn't filter correctly. Here's an example:
>
> {{{
> class Business(models.Model):
>     name = models.CharField(max_length=255)
>
>     def approved_reviews(self):
>         return self.review_set(manager='approved_reviews').all()
>

> class ApprovedReviewsManager(models.Manager):
>     def get_queryset(self):
>         return super().get_queryset().filter(status=Review.APPROVED)
>

> class Review(models.Model):
>     NEW = 1
>     APPROVED = 2
>     STATUS_CHOICES = (
>         (NEW, 'New'),
>         (APPROVED, 'Approved'),
>     )
>     business = models.ForeignKey(Business)
>     text = models.CharField(max_length=255)
>     status = models.IntegerField(choices=STATUS_CHOICES, default=NEW)
>
>     objects = models.Manager()
>     approved_reviews = ApprovedReviewsManager()
>

> class ApprovedReviewsTest(TestCase):
>     def test_with_prefetch(self):
>         business = Business()
>         business.save()
>
>         review = Review()
>         review.business = business
>         review.save()
>
>         businesses =
> Business.objects.prefetch_related('review_set').all()
>
>         business = businesses[0]
>         approved_reviews =
> business.review_set(manager='approved_reviews').all()
>
>         self.assertEqual(len(approved_reviews), 0)
> }}}
>
> The full test project is available here:
> https://github.com/mulka/django_prefetch_manager_bug/blob/master/review_site/tests.py

New description:

 When using prefetch and specifying a custom manager to use for a reverse
 relation, Django doesn't filter correctly. Here's an example:

 {{{
 class Business(models.Model):
     name = models.CharField(max_length=255)

     def approved_reviews(self):
         return self.review_set(manager='approved_reviews').all()


 class ApprovedReviewsManager(models.Manager):
     def get_queryset(self):
         return super().get_queryset().filter(status=Review.APPROVED)


 class Review(models.Model):
     NEW = 1
     APPROVED = 2
     STATUS_CHOICES = (
         (NEW, 'New'),
         (APPROVED, 'Approved'),
     )
     business = models.ForeignKey(Business)
     text = models.CharField(max_length=255)
     status = models.IntegerField(choices=STATUS_CHOICES, default=NEW)

     objects = models.Manager()
     approved_reviews = ApprovedReviewsManager()


 class ApprovedReviewsTest(TestCase):
     def test_with_prefetch(self):
         business = Business()
         business.save()

         review = Review()
         review.business = business
         review.save()

         businesses = Business.objects.prefetch_related('review_set').all()

         business = businesses[0]
         approved_reviews =
 business.review_set(manager='approved_reviews').all()

         self.assertEqual(len(approved_reviews), 0)
 }}}

 The full test project is available here:
 
https://github.com/mulka/django_prefetch_manager_bug/blob/master/review_site/tests.py


 #This implementation: by Akshat verma

 class ToppingManager(models.Manager):
     def filter_vegetarian(self):
         return self.filter(is_vegetarian=True)

 #Looks non-standard. docs look like they do a safer method of modifying
 the super-class method for this sort of lazy-eval stuff. #If I rewrite
 your method in that style, it would look like:

 class ToppingManager(models.Manager):
     def filter_vegetarian(self):
         return super(ToppingManager,
 self).get_queryset().filter(is_vegetarian=True)

 #You wouldn't strictly need the super() here, but safer to use it because
 you should know that you want to start with the #models.Manager
 get_queryset method.

--

Comment:

 #This implementation: by Akshat verma

 class ToppingManager(models.Manager):
     def filter_vegetarian(self):
         return self.filter(is_vegetarian=True)

 #Looks non-standard. docs look like they do a safer method of modifying
 the super-class method for this sort of lazy-eval stuff. #If I rewrite
 your method in that style, it would look like:

 class ToppingManager(models.Manager):
     def filter_vegetarian(self):
         return super(ToppingManager,
 self).get_queryset().filter(is_vegetarian=True)

 #You wouldn't strictly need the super() here, but safer to use it because
 you should know that you want to start with the #models.Manager
 get_queryset method.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/30355#comment:10>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/010701874695a99f-8f512c93-3107-4dbe-a9f8-abb7dc8ea9c9-000000%40eu-central-1.amazonses.com.

Reply via email to