ओडू पायथन विकास: शुरुआती और पेशेवरों के लिए संपूर्ण मार्गदर्शिका
2025 पायथन डेवलपर सर्वेक्षण के अनुसार, 47% से अधिक एंटरप्राइज़ पायथन डेवलपर्स कम से कम एक ईआरपी फ्रेमवर्क के साथ काम करते हैं, और ओडू ओपन-सोर्स ईआरपी के बीच 68% बाजार हिस्सेदारी के साथ उस सेगमेंट पर हावी है। चाहे आप अपना पहला मॉड्यूल बना रहे हों या मल्टी-कंपनी परिनियोजन की योजना बना रहे हों, ओडू की पायथन परत को समझना आपके द्वारा विकसित किया जाने वाला सबसे प्रभावशाली कौशल है।
यह मार्गदर्शिका सतह-स्तरीय अवलोकनों से कहीं आगे जाती है। हम ओडू के पायथन बैकएंड की हर परत से गुजरेंगे - ओआरएम के मेटाक्लास जादू से लेकर उन्नत नियंत्रक पैटर्न तक, उत्पादन मुद्दों को डीबग करने से लेकर लेखन परीक्षण तक जो वास्तव में प्रतिगमन को पकड़ते हैं।
मुख्य बातें
- ओडू का ओआरएम एक पूर्ण ऑब्जेक्ट-रिलेशनल मैपर है मेटाक्लास पर बनाया गया है जो एसक्यूएल स्कीमा को स्वचालित रूप से उत्पन्न करता है, कैशिंग का प्रबंधन करता है, और मल्टी-कंपनी सुरक्षा को संभालता है - इसे गहराई से समझना ओडू के साथ काम करने और उससे लड़ने के बीच का अंतर है।
- पांच फ़ील्ड श्रेणियां प्रत्येक डेटा मॉडलिंग की आवश्यकता को कवर करती हैं: बुनियादी (चार, पूर्णांक, फ़्लोट, बूलियन, टेक्स्ट, एचटीएमएल, दिनांक, दिनांक समय, बाइनरी, चयन), रिलेशनल (कई2एक, एक2कई, कई2कई), परिकलित, संबंधित और मौद्रिक क्षेत्र।
- तीन इनहेरिटेंस मैकेनिज्म आपको ओडू के किसी भी हिस्से को बिना फोर्क किए विस्तारित करने देते हैं: क्लास इनहेरिटेंस (_इनहेरिट), प्रोटोटाइप इनहेरिटेंस (_इनहेरिट + _नाम), और डेलिगेशन इनहेरिटेंस (_इनहेरिट्स)।
- कंट्रोलर JSON-RPC और HTTP एंडपॉइंट पैटर्न, प्रमाणीकरण मोड और CORS कॉन्फ़िगरेशन के साथ डेकोरेटर्स (@http.route) का उपयोग करके HTTP को संभालते हैं।
- TransactionCase और HttpCase के साथ स्वचालित परीक्षण उत्पादन तक पहुंचने से पहले प्रतिगमन को पकड़ लेता है - व्यावसायिक तर्क पर 80%+ कवरेज का लक्ष्य।
1. अपना विकास परिवेश स्थापित करना
कोड लिखने से पहले, आपको एक उचित विकास वातावरण की आवश्यकता होती है। यहां 2026 के लिए अनुशंसित सेटअप है:
# Install system dependencies (Ubuntu/Debian)
# sudo apt install python3.12 python3.12-venv python3.12-dev
# sudo apt install postgresql-17 libpq-dev libxml2-dev libxslt1-dev
# Clone Odoo source (enterprise requires license)
# git clone https://github.com/odoo/odoo.git --branch 18.0 --depth 1
# git clone https://github.com/odoo/enterprise.git --branch 18.0 --depth 1
# Create virtual environment
# python3.12 -m venv odoo-venv
# source odoo-venv/bin/activate
# pip install -r odoo/requirements.txt
# Create database and start Odoo
# createdb mydb
# python odoo/odoo-bin -d mydb --addons-path=odoo/addons,enterprise,custom_addons
अपने मॉड्यूल के लिए custom_addons निर्देशिका बनाएं। प्रत्येक मॉड्यूल एक विशिष्ट संरचना के साथ अपनी उपनिर्देशिका में रहता है जिसकी ओडू अपेक्षा करता है।
2. मॉड्यूल आर्किटेक्चर डीप डाइव
प्रत्येक Odoo मॉड्यूल एक कन्वेंशन-ओवर-कॉन्फ़िगरेशन संरचना का अनुसरण करता है। प्रत्येक घटक की भूमिका को समझना आवश्यक है:
my_module/
├── __init__.py # Python package initializer
├── __manifest__.py # Module metadata and dependencies
├── models/
│ ├── __init__.py
│ ├── sale_order.py # Business logic models
│ └── res_partner.py # Partner extensions
├── views/
│ ├── sale_order_views.xml # Form, tree, search views
│ └── menu.xml # Menu items and actions
├── controllers/
│ ├── __init__.py
│ └── main.py # HTTP controllers
├── security/
│ ├── ir.model.access.csv # ACL rules
│ └── security.xml # Record rules and groups
├── data/
│ ├── data.xml # Default data
│ └── demo.xml # Demo data (dev only)
├── wizards/
│ ├── __init__.py
│ └── mass_update.py # TransientModel wizards
├── report/
│ ├── report_invoice.xml # QWeb report templates
│ └── report.py # Report parsers
├── static/
│ ├── description/
│ │ └── icon.png # Module icon (128x128)
│ └── src/
│ ├── js/ # OWL components
│ └── xml/ # OWL templates
└── tests/
├── __init__.py
└── test_sale_order.py # Automated tests
प्रकट फ़ाइल
__manifest__.py आपके मॉड्यूल का हृदय है। हर क्षेत्र मायने रखता है:
# __manifest__.py
{
'name': 'Sales Commission Engine',
'version': '18.0.1.0.0', # Odoo version.major.minor.patch
'category': 'Sales/Commission',
'summary': 'Automated commission calculations for sales teams',
'description': """
Long description with RST formatting.
Supports multi-tier commission plans.
""",
'author': 'ECOSIRE Private Limited',
'website': 'https://ecosire.com',
'license': 'LGPL-3',
'depends': ['sale', 'account', 'hr'], # Required modules
'data': [
'security/ir.model.access.csv',
'views/commission_plan_views.xml',
'views/menu.xml',
'data/commission_data.xml',
],
'demo': ['data/demo.xml'],
'installable': True,
'application': True, # Shows in Apps menu
'auto_install': False,
'external_dependencies': {
'python': ['pandas'], # pip packages
},
}
संस्करण क्रमांकन परंपरा: {odoo_version}.{major}.{minor}.{patch} - पहला नंबर हमेशा ओडू श्रृंखला से मेल खाता है।
3. ओआरएम एपीआई: संपूर्ण संदर्भ
Odoo का ORM अधिकांश डेवलपर्स के अनुमान से कहीं अधिक शक्तिशाली है। यह स्कीमा जेनरेशन, कैशिंग, एक्सेस कंट्रोल और मल्टी-कंपनी फ़िल्टरिंग को स्वचालित रूप से संभालता है।
मॉडल प्रकार
from odoo import models, fields, api
# Persistent model — stored in database
class CommissionPlan(models.Model):
_name = 'commission.plan'
_description = 'Commission Plan'
_order = 'sequence, name'
_rec_name = 'name'
name = fields.Char(string='Plan Name', required=True, index=True)
active = fields.Boolean(default=True)
sequence = fields.Integer(default=10)
company_id = fields.Many2one('res.company', default=lambda self: self.env.company)
# Transient model — auto-cleaned after ~1 hour
class CommissionWizard(models.TransientModel):
_name = 'commission.calculate.wizard'
_description = 'Calculate Commissions'
date_from = fields.Date(required=True)
date_to = fields.Date(required=True)
# Abstract model — no database table, only for inheritance
class CommissionMixin(models.AbstractModel):
_name = 'commission.mixin'
_description = 'Commission Mixin'
commission_rate = fields.Float(string='Commission %')
फ़ील्ड प्रकार गहरा गोता
class SaleCommission(models.Model):
_name = 'sale.commission'
_description = 'Sale Commission Record'
# Basic fields
name = fields.Char(string='Reference', required=True, copy=False,
default=lambda self: self.env['ir.sequence'].next_by_code('sale.commission'))
amount = fields.Float(string='Amount', digits=(16, 2))
percentage = fields.Float(string='Rate %', digits=(5, 2))
is_paid = fields.Boolean(string='Paid', default=False)
notes = fields.Text(string='Internal Notes')
description = fields.Html(string='Description', sanitize=True)
date = fields.Date(string='Commission Date', default=fields.Date.today)
create_datetime = fields.Datetime(string='Created', default=fields.Datetime.now)
document = fields.Binary(string='Attachment')
document_name = fields.Char(string='File Name')
# Selection field
state = fields.Selection([
('draft', 'Draft'),
('confirmed', 'Confirmed'),
('paid', 'Paid'),
('cancelled', 'Cancelled'),
], string='Status', default='draft', tracking=True)
# Relational fields
salesperson_id = fields.Many2one('res.users', string='Salesperson',
required=True, ondelete='restrict')
order_id = fields.Many2one('sale.order', string='Sale Order')
line_ids = fields.One2many('sale.commission.line', 'commission_id', string='Lines')
tag_ids = fields.Many2many('sale.commission.tag', string='Tags')
# Computed field with store and search
total_amount = fields.Monetary(string='Total', compute='_compute_total',
store=True, currency_field='currency_id')
currency_id = fields.Many2one('res.currency', related='company_id.currency_id')
@api.depends('line_ids.amount')
def _compute_total(self):
for record in self:
record.total_amount = sum(record.line_ids.mapped('amount'))
# Related field (shortcut for computed)
salesperson_email = fields.Char(related='salesperson_id.email', store=True)
सीआरयूडी संचालन
# CREATE — returns new recordset
commission = self.env['sale.commission'].create({
'salesperson_id': self.env.user.id,
'order_id': order.id,
'state': 'draft',
'line_ids': [
(0, 0, {'product_id': product.id, 'amount': 150.00}), # Create
],
})
# Batch create (much faster than loop)
vals_list = [{'name': f'COM-{i}', 'amount': i * 10} for i in range(100)]
records = self.env['sale.commission'].create(vals_list)
# READ — browse by IDs
commission = self.env['sale.commission'].browse(42)
commissions = self.env['sale.commission'].browse([42, 43, 44])
# SEARCH — returns recordset matching domain
pending = self.env['sale.commission'].search([
('state', '=', 'confirmed'),
('date', '>=', '2026-01-01'),
('salesperson_id.department_id.name', '=', 'Sales'),
], order='date desc', limit=50)
# search_count — just the count
count = self.env['sale.commission'].search_count([('state', '=', 'draft')])
# search_read — search + read in one query (optimal for API calls)
data = self.env['sale.commission'].search_read(
[('state', '=', 'confirmed')],
fields=['name', 'amount', 'salesperson_id'],
limit=20, offset=0, order='amount desc'
)
# UPDATE
commission.write({'state': 'paid', 'notes': 'Paid on March 2026'})
# Batch update (all records get same values)
pending.write({'state': 'confirmed'})
# DELETE
old_drafts.unlink()
# SPECIAL: (0, 0, vals) create, (1, id, vals) update, (2, id, 0) delete
# (3, id, 0) unlink M2M, (4, id, 0) link M2M, (5, 0, 0) clear M2M
# (6, 0, [ids]) replace M2M
डोमेन एक्सप्रेशन
डोमेन ओडू की क्वेरी भाषा हैं। उन्हें मास्टर करें:
# Basic operators: =, !=, >, >=, <, <=, like, ilike, in, not in
# Special operators: =like (SQL LIKE), =ilike (case-insensitive LIKE)
# child_of, parent_of — for hierarchical models
domain = [
'|', # OR (next two leaves)
('state', '=', 'confirmed'),
'&', # AND (next two leaves)
('state', '=', 'draft'),
('amount', '>', 1000),
('salesperson_id', '!=', False), # Implicit AND with above
('date', '>=', '2026-01-01'),
('tag_ids.name', 'in', ['priority', 'vip']), # Dot notation for relations
]
# Negation
domain = [('state', 'not in', ['cancelled', 'paid'])]
# NULL checks
domain = [('notes', '!=', False)] # Has notes
domain = [('notes', '=', False)] # No notes
4. वंशानुक्रम पैटर्न
Odoo तीन इनहेरिटेंस तंत्र प्रदान करता है जो आपको किसी भी मॉड्यूल को उसके स्रोत कोड को संशोधित किए बिना विस्तारित करने देता है। क्लास इनहेरिटेंस मौजूदा मॉडल में फ़ील्ड और तरीके जोड़ता है। प्रोटोटाइप इनहेरिटेंस अलग-अलग डेटाबेस तालिकाओं के साथ मौजूदा मॉडलों के आधार पर नए मॉडल बनाता है। डेलिगेशन इनहेरिटेंस मॉडल को संरचना के माध्यम से जोड़ता है, दोनों तालिकाओं में स्वचालित रूप से रिकॉर्ड बनाता है।
वर्ग वंशानुक्रम (सबसे सामान्य)
class SaleOrderInherit(models.Model):
_inherit = 'sale.order' # Extend existing model
# Add new fields
commission_plan_id = fields.Many2one('commission.plan', string='Commission Plan')
commission_amount = fields.Monetary(compute='_compute_commission')
@api.depends('amount_total', 'commission_plan_id.rate')
def _compute_commission(self):
for order in self:
rate = order.commission_plan_id.rate or 0
order.commission_amount = order.amount_total * rate / 100
# Override existing method
def action_confirm(self):
res = super().action_confirm() # Always call super()
for order in self:
if order.commission_plan_id:
order._create_commission_record()
return res
def _create_commission_record(self):
self.ensure_one()
self.env['sale.commission'].create({
'order_id': self.id,
'salesperson_id': self.user_id.id,
'amount': self.commission_amount,
})
प्रोटोटाइप वंशानुक्रम
class ProjectCommission(models.Model):
_name = 'project.commission'
_inherit = 'sale.commission' # Copy structure to new model
_description = 'Project-based Commission'
# Has all fields from sale.commission PLUS its own table
project_id = fields.Many2one('project.project', required=True)
milestone_id = fields.Many2one('project.milestone')
प्रतिनिधिमंडल विरासत
class CommissionEmployee(models.Model):
_name = 'commission.employee'
_inherits = {'hr.employee': 'employee_id'} # Delegation
_description = 'Commission-eligible Employee'
employee_id = fields.Many2one('hr.employee', required=True, ondelete='cascade')
commission_plan_id = fields.Many2one('commission.plan')
lifetime_earnings = fields.Monetary()
# Can access employee fields directly: record.name, record.department_id
5. बिजनेस लॉजिक पैटर्न
बाधाएँ
from odoo.exceptions import ValidationError
class CommissionPlan(models.Model):
_name = 'commission.plan'
rate = fields.Float(required=True)
name = fields.Char(required=True)
# Python constraint
@api.constrains('rate')
def _check_rate(self):
for plan in self:
if not 0 <= plan.rate <= 100:
raise ValidationError(
f"Commission rate must be between 0 and 100. Got: {plan.rate}"
)
# SQL constraint (faster, database-level)
_sql_constraints = [
('name_unique', 'UNIQUE(name, company_id)',
'Commission plan name must be unique per company.'),
('rate_positive', 'CHECK(rate >= 0)',
'Commission rate must be positive.'),
]
बदले के तरीके
@api.onchange('commission_plan_id')
def _onchange_plan(self):
"""Triggered in UI when field changes. Sets defaults and warnings."""
if self.commission_plan_id:
self.percentage = self.commission_plan_id.rate
if self.commission_plan_id.rate > 30:
return {
'warning': {
'title': 'High Commission Rate',
'message': 'This plan has a rate above 30%. Please confirm with management.',
}
}
स्टेट मशीन के साथ वर्कफ़्लो
class SaleCommission(models.Model):
_name = 'sale.commission'
state = fields.Selection([
('draft', 'Draft'),
('confirmed', 'Confirmed'),
('approved', 'Approved'),
('paid', 'Paid'),
('cancelled', 'Cancelled'),
], default='draft', tracking=True)
def action_confirm(self):
for rec in self.filtered(lambda r: r.state == 'draft'):
rec.state = 'confirmed'
rec.message_post(body="Commission confirmed.")
def action_approve(self):
self.filtered(lambda r: r.state == 'confirmed').write({
'state': 'approved',
'approved_by': self.env.user.id,
'approved_date': fields.Datetime.now(),
})
def action_pay(self):
for rec in self.filtered(lambda r: r.state == 'approved'):
rec._process_payment()
rec.state = 'paid'
def action_cancel(self):
allowed = self.filtered(lambda r: r.state in ('draft', 'confirmed'))
allowed.write({'state': 'cancelled'})
6. नियंत्रक और HTTP समापन बिंदु
from odoo import http
from odoo.http import request, Response
import json
class CommissionController(http.Controller):
# JSON-RPC endpoint (used by Odoo's internal JS framework)
@http.route('/commission/calculate', type='json', auth='user', methods=['POST'])
def calculate_commission(self, order_id, **kwargs):
order = request.env['sale.order'].browse(order_id)
if not order.exists():
return {'error': 'Order not found'}
commission = order.commission_amount
return {'order_id': order_id, 'commission': commission}
# HTTP endpoint (REST-style, for external integrations)
@http.route('/api/v1/commissions', type='http', auth='api_key',
methods=['GET'], csrf=False)
def list_commissions(self, **kwargs):
domain = [('state', '!=', 'cancelled')]
if kwargs.get('salesperson_id'):
domain.append(('salesperson_id', '=', int(kwargs['salesperson_id'])))
commissions = request.env['sale.commission'].search_read(
domain,
fields=['name', 'amount', 'state', 'date'],
limit=int(kwargs.get('limit', 50)),
offset=int(kwargs.get('offset', 0)),
)
return Response(
json.dumps({'data': commissions, 'count': len(commissions)}),
content_type='application/json',
status=200,
)
# File download endpoint
@http.route('/commission/report/<int:commission_id>', type='http', auth='user')
def download_report(self, commission_id, **kwargs):
commission = request.env['sale.commission'].browse(commission_id)
pdf = request.env['ir.actions.report']._render_qweb_pdf(
'my_module.commission_report', commission.ids
)
return request.make_response(
pdf[0],
headers=[
('Content-Type', 'application/pdf'),
('Content-Disposition', f'attachment; filename=commission_{commission_id}.pdf'),
]
)
प्रमाणीकरण मोड
| मोड | विवरण | केस का प्रयोग करें |
|---|---|---|
auth='user' | लॉगिन सत्र की आवश्यकता है | आंतरिक पृष्ठ, उपयोगकर्ता क्रियाएँ |
auth='public' | सत्र वैकल्पिक, गुमनाम हो सकता है | वेबसाइट पेज |
auth='none' | कोई सत्र नहीं, कोई पर्यावरण नहीं | स्वास्थ्य जांच, स्थैतिक समापन बिंदु |
auth='api_key' | हेडर में एपीआई कुंजी | बाहरी REST एकीकरण |
7. सुरक्षा: एसीएल और रिकॉर्ड नियम
प्रवेश नियंत्रण सूचियाँ (ir.model.access.csv)
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_commission_user,commission.user,model_sale_commission,sales_team.group_sale_salesman,1,0,0,0
access_commission_manager,commission.manager,model_sale_commission,sales_team.group_sale_manager,1,1,1,0
access_commission_admin,commission.admin,model_sale_commission,base.group_system,1,1,1,1
रिकॉर्ड नियम (सुरक्षा.एक्सएमएल)
<record id="commission_own_rule" model="ir.rule">
<field name="name">Salesperson sees own commissions</field>
<field name="model_id" ref="model_sale_commission"/>
<field name="groups" eval="[(4, ref('sales_team.group_sale_salesman'))]"/>
<field name="domain_force">[('salesperson_id', '=', user.id)]</field>
</record>
<record id="commission_company_rule" model="ir.rule">
<field name="name">Company-level commission isolation</field>
<field name="model_id" ref="model_sale_commission"/>
<field name="global" eval="True"/>
<field name="domain_force">[('company_id', 'in', company_ids)]</field>
</record>
8. डिबगिंग तकनीक
ओडू शैल का उपयोग करना
# Start interactive shell with full ORM access
python odoo-bin shell -d mydb --addons-path=addons,custom_addons
# In shell:
>>> orders = env['sale.order'].search([('state', '=', 'sale')], limit=5)
>>> for o in orders:
... print(f"{o.name}: {o.amount_total} - {o.partner_id.name}")
>>> env.cr.commit() # Commit changes made in shell
लॉगिंग
import logging
_logger = logging.getLogger(__name__)
class SaleCommission(models.Model):
_name = 'sale.commission'
def action_confirm(self):
_logger.info("Confirming commission %s for user %s", self.name, self.env.user.name)
try:
self._validate_commission()
except Exception as e:
_logger.exception("Failed to confirm commission %s: %s", self.name, e)
raise
प्रदर्शन प्रोफ़ाइलिंग
# Enable SQL logging in odoo.conf:
# log_level = debug_sql
# Or use the profiler decorator
from odoo.tools.profiler import profile
@profile('/tmp/commission_profile')
def action_bulk_calculate(self):
# This will generate a profile dump you can analyze
for order in self.env['sale.order'].search([]):
order._calculate_commission()
सामान्य प्रदर्शन हानियाँ:
- लूप्स (एन+1 क्वेरी) में रिलेशनल फ़ील्ड तक पहुंच - इसके बजाय
read()याmapped()का उपयोग करें - लूप के अंदर
search()को कॉल करना - अपनी खोजों को बैच करें - बार-बार फ़िल्टर किए गए परिकलित फ़ील्ड पर
store=Trueका उपयोग नहीं करना sudo()को भूलने से प्रत्येक ऑपरेशन पर अतिरिक्त एक्सेस-राइट जांच होती है
9. स्वचालित परीक्षण
from odoo.tests.common import TransactionCase, HttpCase, tagged
from odoo.exceptions import ValidationError
@tagged('post_install', '-at_install')
class TestCommission(TransactionCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.partner = cls.env['res.partner'].create({'name': 'Test Customer'})
cls.user = cls.env['res.users'].create({
'name': 'Test Salesperson',
'login': 'test_sales',
'groups_id': [(4, cls.env.ref('sales_team.group_sale_salesman').id)],
})
cls.plan = cls.env['commission.plan'].create({
'name': 'Standard 10%',
'rate': 10.0,
})
def test_commission_calculation(self):
"""Commission amount should be 10% of order total."""
order = self.env['sale.order'].create({
'partner_id': self.partner.id,
'user_id': self.user.id,
'commission_plan_id': self.plan.id,
})
# Add order line...
self.assertAlmostEqual(order.commission_amount, 100.0, places=2)
def test_rate_constraint(self):
"""Rates above 100 should raise ValidationError."""
with self.assertRaises(ValidationError):
self.env['commission.plan'].create({
'name': 'Invalid Plan',
'rate': 150.0,
})
def test_state_machine(self):
"""Commission should follow draft -> confirmed -> paid flow."""
commission = self.env['sale.commission'].create({
'salesperson_id': self.user.id,
'amount': 500,
})
self.assertEqual(commission.state, 'draft')
commission.action_confirm()
self.assertEqual(commission.state, 'confirmed')
# HTTP/Tour tests
@tagged('post_install', '-at_install')
class TestCommissionTour(HttpCase):
def test_commission_dashboard(self):
"""Test that the commission dashboard loads correctly."""
self.start_tour('/web#action=commission.action_dashboard',
'commission_dashboard_tour', login='admin')
इसके साथ परीक्षण चलाएँ:
# Run all tests for a module
python odoo-bin -d testdb -i my_module --test-enable --stop-after-init
# Run specific test class
python odoo-bin -d testdb --test-tags /my_module:TestCommission --stop-after-init
# Run with coverage
coverage run odoo-bin -d testdb -i my_module --test-enable --stop-after-init
coverage report --include="custom_addons/my_module/*"
10. उन्नत पैटर्न
निर्धारित क्रियाएँ (क्रॉन जॉब्स)
<record id="ir_cron_monthly_commission" model="ir.cron">
<field name="name">Calculate Monthly Commissions</field>
<field name="model_id" ref="model_sale_commission"/>
<field name="state">code</field>
<field name="code">model._cron_calculate_monthly()</field>
<field name="interval_number">1</field>
<field name="interval_type">months</field>
<field name="numbercall">-1</field>
</record>
मल्टी-कंपनी पैटर्न
class CommissionPlan(models.Model):
_name = 'commission.plan'
company_id = fields.Many2one('res.company', required=True,
default=lambda self: self.env.company)
# Use sudo() carefully — only when crossing company boundaries is intended
def get_global_plans(self):
return self.sudo().search([('global_plan', '=', True)])
पुन: प्रयोज्य तर्क के लिए मिक्सिन पैटर्न
class CommissionMixin(models.AbstractModel):
_name = 'commission.mixin'
_description = 'Adds commission tracking to any model'
commission_ids = fields.One2many('sale.commission', 'res_id')
commission_total = fields.Monetary(compute='_compute_commission_total', store=True)
@api.depends('commission_ids.amount')
def _compute_commission_total(self):
for record in self:
record.commission_total = sum(record.commission_ids.mapped('amount'))
# Apply to any model via class inheritance
class SaleOrder(models.Model):
_inherit = ['sale.order', 'commission.mixin']
वास्तविक दुनिया का उदाहरण: पूर्ण कमीशन मॉड्यूल
पूर्ण, उत्पादन-तैयार कमीशन मॉड्यूल उदाहरण के लिए, [ECOSIRE के ओपन-सोर्स ओडू मॉड्यूल] (/products) पर जाएं जहां हम इस गाइड में शामिल सभी सर्वोत्तम प्रथाओं का पालन करते हुए कनेक्टर, उपयोगिताओं और बिजनेस मॉड्यूल प्रकाशित करते हैं।
हमारी Odoo डेवलपमेंट टीम ने इन सटीक पैटर्न का उपयोग करके 60 से अधिक Odoo मॉड्यूल बनाए हैं। यदि आपको कस्टम ओडू पायथन विकास की आवश्यकता है - एकल फ़ील्ड जोड़ से लेकर पूर्ण ईआरपी कार्यान्वयन तक - निःशुल्क परामर्श के लिए हमारी टीम से संपर्क करें।
अक्सर पूछे जाने वाले प्रश्न
ओडू 18 को किस पायथन संस्करण की आवश्यकता है?
Odoo 18 के लिए Python 3.10 या उच्चतर की आवश्यकता है, Python 3.12 अनुशंसित संस्करण है। Odoo 17 Python 3.10+ को सपोर्ट करता है। अपने Odoo संस्करण के लिए आवश्यक सटीक निर्भरताओं के लिए हमेशा आधिकारिक require.txt की जांच करें।
मैं ओडू ओआरएम क्वेरीज़ को कैसे डिबग करूं?
अपनी odoo.conf फ़ाइल में log_level को debug_sql पर सेट करके SQL लॉगिंग सक्षम करें, या कमांड लाइन पर --log-sql पास करें। आप कोड में प्रश्नों का पता लगाने के लिए self.env.cr.sql_log का भी उपयोग कर सकते हैं। प्रदर्शन प्रोफ़ाइलिंग के लिए, odoo.tools.profiler से अंतर्निहित @profile डेकोरेटर का उपयोग करें।
क्या मुझे मॉडलों का विस्तार करने के लिए _inherit या _inherits का उपयोग करना चाहिए?
95% समय _inherit (क्लास इनहेरिटेंस) का उपयोग करें - यह मौजूदा मॉडल की तालिका में फ़ील्ड और विधियां जोड़ता है। _inherits (प्रतिनिधिमंडल) का उपयोग केवल तभी करें जब आपको एक अलग मॉडल की आवश्यकता हो जो पारदर्शी रूप से किसी अन्य मॉडल के फ़ील्ड तक पहुंच सके, जैसे एक विशेष कर्मचारी प्रकार बनाना जो सभी hr.employee फ़ील्ड को इनहेरिट करता हो।
मैं मल्टी-कंपनी डेटा अलगाव को कैसे प्रबंधित करूं?
अपने मॉडल में self.env.company लौटाने वाले डिफ़ॉल्ट लैम्ब्डा के साथ एक कंपनी_आईडी मैनी2वन फ़ील्ड जोड़ें। फिर डोमेन [('कंपनी_आईडी', 'इन', कंपनी_आईडी)] के साथ एक वैश्विक ir.rule बनाएं। यह सुनिश्चित करता है कि उपयोगकर्ता केवल उन्हीं कंपनियों के रिकॉर्ड देखें जिन तक उनकी पहुंच है, जो मानक ओडू मल्टी-कंपनी पैटर्न है।
@api.depends और @api.onchange के बीच क्या अंतर है?
@api.डेटाबेस में निर्दिष्ट फ़ील्ड बदलने पर ट्रिगर निर्भर करता है और संग्रहीत गणना फ़ील्ड के लिए उपयोग किया जाता है - यह यूआई और प्रोग्रामेटिक परिवर्तनों दोनों के लिए काम करता है। @api.onchange केवल यूआई में ट्रिगर होता है जब कोई उपयोगकर्ता किसी फ़ील्ड को संशोधित करता है और डिफ़ॉल्ट सेट करने या चेतावनियां दिखाने के लिए उपयोग किया जाता है। डेटा अखंडता के लिए @api.depends और UX सुधारों के लिए @api.onchange का उपयोग करें।
मैं अपने ओडू मॉड्यूल का प्रभावी ढंग से परीक्षण कैसे करूं?
यूनिट परीक्षणों के लिए TransactionCase का उपयोग करें जो व्यावसायिक तर्क का परीक्षण करते हैं - प्रत्येक परीक्षण एक लेन-देन में चलता है जिसे वापस ले लिया जाता है। नियंत्रकों और वेब टूर के परीक्षण के लिए HttpCase का उपयोग करें। मॉड्यूल इंस्टालेशन के बाद चलाने के लिए @tagged('post_install', '-at_install') के साथ टैग परीक्षण। व्यावसायिक तर्क विधियों पर कम से कम 80% कोड कवरेज का लक्ष्य रखें।
अगले चरण
उत्पादन-ग्रेड ओडू मॉड्यूल के निर्माण के लिए तकनीकी कौशल और डोमेन विशेषज्ञता दोनों की आवश्यकता होती है। सरल वर्ग वंशानुक्रम से प्रारंभ करें, फिर पूर्ण कस्टम मॉड्यूल की ओर प्रगति करें। आगे पढ़ने के लिए:
- ओडू कस्टम मॉड्यूल डेवलपमेंट गाइड - मॉड्यूल विकास का उच्च-स्तरीय अवलोकन
- ओडू रेस्ट एपीआई ट्यूटोरियल - ओडू के साथ बाहरी एकीकरण
- सर्वश्रेष्ठ ईआरपी सॉफ्टवेयर 2026 - बाजार में ओडू की स्थिति को समझना
विशेषज्ञ ओडू डेवलपर्स की आवश्यकता है? ECOSIRE की Odoo डेवलपमेंट टीम ने 40 देशों में व्यवसायों के लिए 200+ कस्टम मॉड्यूल वितरित किए हैं। कमीशन इंजन से लेकर पूर्ण बाज़ार एकीकरण तक, हमारी टीम इस गाइड में हर पैटर्न का पालन करते हुए उत्पादन-तैयार कोड लिखती है। मुफ़्त परामर्श प्राप्त करें।
लेखक
ECOSIRE TeamTechnical Writing
The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.
संबंधित लेख
blog.posts.ai-powered-customer-segmentation-guide.title
blog.posts.ai-powered-customer-segmentation-guide.description
blog.posts.ai-supply-chain-optimization-2026.title
blog.posts.ai-supply-chain-optimization-2026.description
blog.posts.b2b-ecommerce-strategy-wholesale-guide.title
blog.posts.b2b-ecommerce-strategy-wholesale-guide.description