I realize I'm a little bit late to this party, but I'll contribute what I've learned for posterity's sake.
Once upon a time, I found that I needed to customize the category_tree templatetag. As a big believer in the DRY approach to coding, I wanted to do this by copying as little core Oscar code as possible. It turns out that this is doable, and only slightly less elegant that Oscar's approach to importing. (Seriously, thanks to those guys, I think their get_model approach should be more widely adopted.)
As others have observed, Django looks through each of your INSTALLED_APPS in the order they're given to find a module that contains the specified template library. (If you're curious, the source lives at django.template.base.get_templatetags_modules.) This means that you can define e.g. your_app.templatetags.category_tags, and if your_app is listed in INSTALLED_APPS before the call to oscar.get_core_apps, when you ask Django to {% load category_tags %}, Django will load your_app.templatetags.category_tags instead of oscar.templatetags.category_tags.
The next step is to customize the tag logic -- in my case, I needed to modify oscar.templatetags.category_tags.do_category_list so that it would hide certain categories based on which user was logged in. I determined that oscar.templatetags.category_tags.CategoryTreeNode performed the actual task of retrieving a list of Categories, so I subclassed it in my_project.templatetags.category_tags. After that, I had to register oscar.templatetags.category_tags.do_category_list as a tag in my_project.templatetags.category_tags so it would be available for use in my templates.
After I registered category_tree, my templates loaded the customized library and rendered the tag, but I wasn't seeing the changes I had made to CategoryTreeNode. After a little bit of research I determined that this is due to the way Python handles variable scope. The broadest scope a variable can achieve in Python is at the module level. Even though I had declared my_project.templatetags.category_tags.CategoryTreeNode, do_category_list was using oscar.templatetags.category_tags.CategoryTreeNode internally, because they were both part of the oscar.templatetags.category_tags module. Once I overwrote Oscar's CategoryTreeNode with my custom CategoryTreeNode, everything was peachy.
Here's an abridged version of what my_app.templatetags.category_tags looked like when I was all finished:
from django import template
from oscar.templatetags import category_tags
register = template.Library()
# Customize Oscar's CategoryTreeNode
class CategoryTreeNode(category_tags.CategoryTreeNode):
def render(self, context):
"""Do some custom stuff"""
# Overwrite Oscar's CategoryTreeNode with my own custom CategoryTreeNode
category_tags.CategoryTreeNode = CategoryTreeNode
# Register the actual template tag, reusing Oscar's logic.
register.tag(name="category_tree")(category_tags.do_category_list)
I hope someone somewhere finds all this useful. It would be really great if it could be added to Oscar's documentation.