Skip to content

Latest commit

 

History

History
157 lines (104 loc) · 5.43 KB

advanced_usage.rst

File metadata and controls

157 lines (104 loc) · 5.43 KB

Advanced Usage

Using a ListBrick

Now that you have run through the basics <usage>, let's tackle some more advanced topic.

Building up from our previous example, let's say that our designers team decided that the list of videos and news must change. Specifically, the news have a new layout and they have to be grouped in list of five elements each.

In this case, we are going to use the :pyListBrick <djangobricks.models.ListBrick> class.

So let's replace our declaration of NewsBricks:

from djangobricks.models import ListBrick

class NewsBrick(ListBrick):
    template_name = 'bricks/list/news.html

The VideoBrick doesn't need to be changed.

By default :pyget_bricks_for_queryset <djangobricks.models.ListBrick.get_bricks_for_queryset> returns a list of bricks containing 5 elements each. Unless you need some more complicated behaviour, you can simply change the number of elements by setting the :pychunk_size <djangobricks.models.ListBrick.chunk_size> attribute accordingly.

Now, as the brick does not contain a single element, is not clear what a :pyCriterion <djangobricks.models.Criterion> should return when applied to it. The value of the first element? The average? That is really up to you.

In this case, let's say that we want to change CRITERION_PUB_DATE to return the max value of the list (that is, the brick will be sorted based on the newest news it contains) and CRITERION_COMMENT_COUNT to return the average number of comments.

To do that, we simply change the criteria declaration by adding a callback function that accepts a list and return a value:

CRITERION_PUB_DATE = Criterion('pub_date', max, default=datetime.datetime.now)
CRITERION_COMMENT_COUNT = Criterion('thread__comment_count',
                                    lambda x:sum(x)/len(x), default=0)

We don't need to change anything else.

Filtering a wall

Our designers are relentless. They want the user to be able to filter some content from the wall. In our simple example, we can say that they want to hide the videos from the wall.

In this case, we can probably just build a second wall and returns it in our view depending on user choice, but we are going to do it the Bricks way.

The :pyBaseWall <djangobricks.models.BaseWall> class provides a simple :pyfilter <djangobricks.models.BaseWall.filter> method that accepts a list of callables that accepts a brick instance and returns a boolean, and a boolean operator. Each brick is then filtered against the list of callables: all of them if the operator is AND (the default value) or any of them if the operator is OR.

In our case, we need a single callback that should return True if the brick contains some news (remember: we want to hide the videos!) and False otherwise.

Something like this would to the trick:

def is_news_brick(brick):
    return brick.__class__.__name__ == 'NewsBrick'

And in our view:

...
filtered_last_content_wall = last_content_wall.filter(is_news_brick)
...

The advantage of this approach is speed. The creation of a wall can be an expensive operation. Caching a wall and filtering the cached result can be faster then building a new wall from scratch, especially if you have a more complicated setup with a lot of filters.

Handling heterogeneous models

Now let's say that we need to add another model to our wall, defined below:

from django.db import models

class PhotoGallery(models.Model):
    title = models.CharField()
    images = models.ManyToManyField(Photo)
    public_from_date = models.DateTimeField()

As you can see, this model doesn't have a pub_date field like News and Video. How can we use the CRITERION_PUB_DATE over this model?

Remember that is up to the brick to return a value for a given criterion of its content. So let's write a brick class for our new model:

from djangobricks.models import SingleBrick

class PhotoGalleryBrick(SingleBrick):
    template_name = 'bricks/single/photo_gallery.html'

    def get_value_for_criterion(self, criterion):
        if criterion.attrname == 'pub_date':
            return self.item.public_from_date
        return super(PhotoGalleryBrick, self).get_value_for_criterion(criterion)

And that's it! Unless you are sure to cover each possible criterion, it's a good practice to return the value from super at least, as shown above.

Adding context to the template

By default, :pySingleBrick <djangobricks.models.SingleBrick> will pass the object to the context with an object key.

The :pyListBrick <djangobricks.models.ListBrick> context contains an object_list key instead.

If you want to add extra context to render the template, you can either override the :pyget_context <djangobricks.models.BaseBrick.get_context> method as show below:

class NewsBrick(SingleBrick):

    def get_context(self, **kwargs)
        context = super(NewsBrick, self).get_context(**kwargs)
        context['color'] = 'red'
        return context

or you can add them using directly the templatetag

{% render_brick brick color='red' %}