First, Odoo Portal Search Customization gives users a better way to find tasks in your Odoo 17 portal. Next, our tutorial will guide you step by step to add dynamic filter options in the customer portal search bar. Then, you will see how to extend the _get_my_tasks_searchbar_filters method to include project‑ and stage‑based filters. After that, you will integrate your custom filters into the portal route. Finally, you will modify the QWeb template to render new filters and test your work.
Prerequisites for Portal Search Customization
First, you need a running Odoo 17 instance with portal access enabled.
Next, you need to install the official portal and project modules via the Apps menu.
Then, you need to confirm that your user holds portal rights to access the customer portal.
After that, you need to prepare your system with a code editor like VS Code or PyCharm.
Moreover, you need to confirm that you can restart Odoo services when you apply code changes.
Furthermore, you need basic skills in Python, XML, and QWeb templates to follow this guide.
Also, you need access to the Odoo logs to help you debug any errors in real time.
Additionally, you need an active plan to test your Odoo Portal Search Customization throughout the development process.
Finally, you need knowledge of Git or another version control system to track your module updates.
Module Setup and File Structure
First, you create a new folder under your Odoo add‑ons path named portal_search_custom.
Next, you add these files and folders inside portal_search_custom:
portal_search_custom/
├── __init__.py
├── __manifest__.py
├── controllers/
│ └── portal_task_search.py
└── views/
└── portal_templates.xml
Then, you edit __manifest__.py to declare your module and its dependencies:
{
'name': 'Portal Search Custom',
'version': '1.0',
'summary': 'Customize search bar filters in Odoo 17 portal tasks',
'category': 'Website',
'author': 'Your Name',
'website': 'https://www.odoo.com',
'license': 'LGPL-3',
'depends': ['portal', 'project'],
'data': ['views/portal_templates.xml'],
'installable': True,
}
After that, you leave __init__.py empty or import your controller:
from . import controllers
Moreover, you create controllers/portal_task_search.py to hold your custom code.
Finally, you add views/portal_templates.xml to inherit and update the portal task template.
Controller: Extending _get_my_tasks_searchbar_filters
Understanding the Base Method
First, you inherit Odoo’s CustomerPortal class from addons/portal/controllers/portal.py.
Next, you override the _get_my_tasks_searchbar_filters method to build new filter options.
Then, you call request.env to fetch projects and tasks for the dropdown.
After that, you use read_group with AND to group tasks by project not yet followed by the user.
Moreover, you add a final option “All Stages” by reading stage records.
Finally, you return the updated searchbar_filters dictionary for use in your route.
Step-by-Step Code Explanation
First, here is the full code for controllers/portal_task_search.py:
from odoo import _
from odoo.addons.portal.controllers.portal import CustomerPortal
from odoo.http import request
from odoo.osv.expression import AND
from collections import OrderedDict
class CustomProjectCustomerPortal(CustomerPortal):
def _get_my_tasks_searchbar_filters(self, project_domain=None, task_domain=None):
# initial: only “All”
searchbar_filters = {
'all': {
'label': _('All'),
'domain': [('project_id', '!=', False)],
},
}
# 1) add projects that the portal user can access
projects = request.env['project.project'].search(project_domain or [])
for project in projects:
searchbar_filters[str(project.id)] = {
'label': project.name,
'domain': [('project_id', '=', project.id)],
}
# 2) add projects with tasks that the user does not follow
project_groups = request.env['project.task'].read_group(
AND([[('project_id', 'not in', projects.ids)], task_domain or []]),
['project_id'],
['project_id']
)
for group in project_groups:
proj_id = group['project_id'][0] if group['project_id'] else False
proj_name = group['project_id'][1] if group['project_id'] else _('Others')
searchbar_filters[str(proj_id)] = {
'label': proj_name,
'domain': [('project_id', '=', proj_id)],
}
# 3) add “All Stages” option
stage_ids = request.env['project.task.type'].search([], limit=1)
searchbar_filters['all_stages'] = {
'label': _('All Stages'),
'domain': [('stage_id', 'in', stage_ids.ids)],
}
# return the complete filter dict
return searchbar_filters
@http.route([
'/my/tasks',
'/my/tasks/page/<int:page>'
], type='http', auth='user', website=True)
def portal_my_tasks(self, page=1, date_begin=None, date_end=None,
sortby=None, filterby=None,
search=None, search_in=None, **kwargs):
# default filter
if not filterby:
filterby = 'all'
# build filters
searchbar_filters = self._get_my_tasks_searchbar_filters()
domain = searchbar_filters.get(filterby, searchbar_filters['all'])['domain']
# add text search if needed
if search and search_in:
domain += [(search_in, 'ilike', search)]
# fetch tasks with ordering
tasks = request.env['project.task'].search(
domain, order='date_deadline desc'
)
# prepare values for QWeb
values = {
'tasks': tasks,
'searchbar_filters': OrderedDict(searchbar_filters),
'filterby': filterby,
'search': search or '',
'search_in': search_in or 'all',
}
# render the portal template
return request.render('portal_search_custom.portal_my_tasks', values)
Then, you save this file and include it in your module.
Moreover, you ensure you import collections.OrderedDict to preserve filter order.
Finally, you restart your Odoo server and install the Portal Search Custom module.
Integrating Filters into the Portal Route
First, you handle URL routes with @http.route for /my/tasks.
Next, you read URL parameters like filterby, search, and search_in.
Then, you pick the matching domain from your custom filters.
After that, you append a text‑search domain if the user enters a keyword.
Additionally, you call .search() on project.task to fetch tasks.
Moreover, you render your view with the values dictionary.
Finally, you let Odoo deliver the updated page to the portal user.
Customizing QWeb Template for Search Bar Filters
Template Inheritance and Search Bar
First, you open or create views/portal_templates.xml.
Next, you inherit the original portal template portal.portal_my_tasks.
Then, you replace the search bar block with your own HTML.
After that, you loop over searchbar_filters to render the dropdown.
Moreover, you insert a text input for keyword search.
Finally, you add a submit button to let users apply filters.
Sample QWeb Code
<odoo>
<template id="portal_my_tasks" inherit_id="portal.portal_my_tasks">
<xpath expr="//div[@id='search_bar']" position="replace">
<div id="search_bar">
<form method="get" action="/my/tasks">
<!-- Filter dropdown -->
<select name="filterby">
<t t-foreach="searchbar_filters.items()" t-as="f">
<option t-att-value="f[0]" t-att-selected="filterby==f[0]">
<t t-esc="f[1]['label']"/>
</option>
</t>
</select>
<!-- Text search input -->
<input
type="text"
name="search"
t-att-value="search"
placeholder="Search tasks..."
/>
<!-- Search-in field (optional) -->
<select name="search_in">
<option value="name" t-att-selected="search_in=='name'">Name</option>
<option value="description" t-att-selected="search_in=='description'">Description</option>
</select>
<!-- Apply button -->
<button type="submit">Apply</button>
</form>
</div>
</xpath>
</template>
</odoo>
Then, you save the template file and update your module.
Finally, you refresh your portal page to view the new search bar filters.
Testing and Troubleshooting Portal Search
First, you clear your browser cache to avoid stale templates.
Next, you navigate to My Tasks in the portal and test each filter option.
Then, you type keywords in the search box to confirm text search works.
After that, you check the Odoo log for errors if the page fails to load.
Moreover, you verify filterby values match keys in your Python code.
Additionally, you confirm search_in fields map to valid task fields.
Finally, you adjust domains if you get unexpected results or empty lists.
Common Issues and Fixes
- Error “KeyError: filterby”: Ensure your default key
'all'exists insearchbar_filters. - No tasks displayed: Verify that your domain list is not empty and matches real records.
- Template not updating: Confirm your
inherit_idmatches the base template and reload assets.
Advanced Portal Search Customization Tips
Adding Sort and Pagination
First, you add a _get_task_sortings method to define sort options.
Next, you pass sortby and searchbar_sortings to your QWeb values.
Then, you add a sort dropdown in your template.
After that, you call .search(domain, order=order_clause, limit=20, offset=(page-1)*20).
Finally, you include a pager in your QWeb to navigate pages.
Using Full‑Text Search
First, you install the postgrefts module or enable PostgreSQL full‑text indexing.
Next, you use search([('name', '=ilike', search)], limit=...) for faster results.
Then, you compare performance with simple ilike queries.
Finally, you choose the best strategy for your data size.
Securing Portal Routes
First, switch auth='public' to auth='user' if you restrict access.
Next, check proper access rights on project.task and project.project.
Finally, add CSRF tokens to forms if you switch to POST requests.
Further Reading and Resources
First, you can consult the Odoo 17 Portal Documentation for more details.
Next, you can review the CustomerPortal base code on GitHub.
Finally, you can join the Odoo Forum to ask questions and share your experiences.
Conclusion
First, you learned how to implement Odoo Portal Search Customization in Odoo 17 Customer Portal.
Next, you saw how to extend _get_my_tasks_searchbar_filters to add project and stage filters.
Then, you integrated custom filters into the portal route and updated your QWeb template.
Finally, you tested your changes and explored advanced tips for sorting, pagination, and security.
Consequently, you now have full control over task search options in your Odoo 17 portal.
Discover more from teguhteja.id
Subscribe to get the latest posts sent to your email.


Pingback: Odoo 18 Portal Integration - teguhteja.id