I have two models:
class Product(models.Model):
name = models.CharField(max_length=255)
class ProductPhoto(models.Model):
product = models.ForeignKey('Product', related_name='photos')
is_live = models.IntegerField(choices=LIVE_CHOICES, default=1)
live = LiveManager()
class LiveManager(Manager):
def get_query_set(self):
return super(LiveManager, self).get_query_set().filter(is_live=1)
I am trying to get live photos from product detail templates.
Tried,
{% for photo in product.photos.live %}
which doesn't work and looked at docs and couldn't find examples. Is it possible to call the foreign key's manager from a template? Should I make a function in Product model that returns product photo queryset?
Thank you.
Well, the way you're using it is wrong, anyways. You'd just be passing the manager into the for loop, not a queryset that could be iterated over. However, photos
is itself a "related manager", not the actual ProductPhoto
model, and related managers are based off the first listed manager or objects
(the default manager).
Since, you define live
, but do not also define objects
, you don't actually have an objects
manager on this model, i.e. the this will fail: ProductPhoto.objects.all()
. Remember, if you define a custom manager on your model, Django will no longer automatically add one named objects
.
The good news is that because live
is the default manager now, you can use it just like:
{% for photo in product.photos.all %}
And, you'll only get "live" objects. The bad news is that this will break a lot of other things that depend on the default manager being the full collection of objects (admin for example). You're essentially hiding the block of "non-live" objects.
What you should have is:
class ProductPhoto(models.Model):
product = models.ForeignKey('Product', related_name='photos')
is_live = models.IntegerField(choices=LIVE_CHOICES, default=1)
objects = models.Manager()
live = LiveManager()
Notice that objects
is defined manually and it's first, meaning it will remain the default manager. However, that then no longer allows you to use your live
manager in the template. Generally, for something like this, it's best to just use a single manager and add a method to it to return "live" objects:
class ProductPhotoQuerySet(models.query.QuerySet):
def live(self):
return self.filter(is_live=1)
class ProductPhotoManager(models.Manager):
use_for_related_fields = True
def get_query_set(self):
return ProductPhotoQuerySet(self.model)
def live(self, *args, **kwargs):
return self.get_query_set().live(*args, **kwargs)
Here, we're actually subclassing both QuerySet and Manager. This will allow you to chain live
anywhere instead of just at the front. For example, if you just had a custom manager without a custom queryset, you would only be able to do ProductPhoto.objects.live().filter(...)
and not ProductPhoto.objects.filter(...).live()
.
So, you then add that to your model as objects
(taking the place of the default one Django provides):
class ProductPhoto(models.Model):
product = models.ForeignKey('Product', related_name='photos')
is_live = models.IntegerField(choices=LIVE_CHOICES, default=1)
objects = ProductPhotoManager()
And, finally, you'll be able to use it in your template:
{% for photo in product.photos.live %}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With