Odoo: How to create many records in Transient.Model? - python

This code only creates one record. What is wrong?
class PartnerTagCreate(models.TransientModel):
""" Choose tags to be added to partner."""
_name = 'partner.tags.create'
_description = __doc__
market_id = fields.Many2one('partner.tags', string='Market Tag')
application_id = fields.Many2one('partner.tags', string='Application Tag')
partner_id = fields.Integer()
#api.multi
def create_contact_tag(self):
for record in self.env['sale.order.line'].browse(self._context.get('active_ids', [])):
vals = {}
vals['partner_id'] = record.order_partner_id
self.write(vals)
return True
I need this function to create one record for each order_partner_id I selected before opening the wizard...
How to achieve that?
Here my new code (function) ...
def create_contact_tag(self):
sale_order_line_ids = self.env['sale.order.line'].browse(self._context.get('active_ids', []))
for partner in sale_order_line_ids:
values = {}
values['partner_id'] = partner.order_partner_id
self.create(values)
return {}
This creates one record for marketing_id and/or application_id and dedicated records for each partner_id in the record.

You use the 'create' method to create new records; this is the same for TransientModel as for the persistent Model.
So, replace
self.write(vals)
by
self.create(vals)
and you should be fine.

Related

Django update_or_create with potentially null idenitfier

I have a function in Django that accepts a post request. If that post request includes an id, I want to update that object. If the post request sends a blank/null id, I'd like to create a new object and have Django set the ID (primary key) of the model.
Here is what I have:
def save_user_roster_to_db():
name = request.data['name']
id = request.data['id'] # this could be null/blank or be an ID
try:
user_roster = SavedUserRoster.objects.update_or_create(
id=id,
defaults={
"name": name,
}
)
return user_roster.pk
except:
raise Exception
However, this seems to try to assign the blank/null value as the ID when creating a new object. How can I tell Django to set its own ID (which is the primary key) if it's creating a new object?
You can't use update_or_create with the id field. Because in the current situation, id can have a value of None, and the Django model can't create the object with an id of None.
So I think you can try like the following.
def save_user_roster_to_db():
name = request.data['name']
id = request.data['id'] # this could be null/blank or be an ID
try:
if id:
user_roster = SavedUserRoster.objects.get(pk = id).update(name = name)
else:
user_roster = SavedUserRoster.objects.create(name = name)
return user_roster.pk
except:
raise Exception
Traditionally id is auto generated and always unique. In this case replacing None id will create exception when you create record for first time.
There are two possible options.
OPTION 1:
Create another unique_identified i.e username or email.
def save_user_roster_to_db():
name = request.data['name']
unique_identified = request.data['unique_identified'] # this could be null/blank or be an ID
user_roster, is_created = SavedUserRoster.objects.update_or_create(
unique_identified=unique_identified,
defaults={
"name": name,
}
)
return user_roster.pk
OPTION 2:
For Create:
Get the last id from database and add 1 with last id, so that It will be the next id value, It will avoid the None exception.
For Update:
It will update the existence record against id
def save_user_roster_to_db():
name = request.data['name']
id = request.data['id'] # this could be null/blank or be an ID
if id is None:
id = int(MyUserModel.objects.all().last().id)+1
user_roster, is_created = SavedUserRoster.objects.update_or_create(
id=id,
defaults={
"name": name,
}
)
return user_roster.pk

Add computed field to custom filter as stored field in ODOO

customer_count = fields.Char(string='Customer Count', compute='cust_count')
class ResBuilding(models.Model):
_name = "res.partner.building"
_description = "Customer BUilding"
_order = "id desc"
region_id = fields.Many2one('res.state.city', string='Region', required=True, ondelete='cascade')
city_id = fields.Many2one('city.covered.area', string='Area', required=True, ondelete='cascade')
name = fields.Char(string='Name', required=True, translate=True, copy=False)
image = fields.Binary(string="Building image")
latitude = fields.Char(string='Latitude')
customer_count = fields.Char(string='Customer Count', compute='cust_count', store=True)
longitude = fields.Char(string='Longitude')
active = fields.Boolean(string='Active', default=True, track_visibility='onchange')
partner_ids = fields.One2many('res.partner', 'building_id', string='Customer List', readonly=True)
#api.multi
#api.depends('partner_ids')
def cust_count(self):
for record in self:
count = self.env['res.partner'].search_count([('building_id', '=', record.id)])
record.customer_count = count
#api.multi
def name_get(self):
result = []
for route in self:
name = route.city_id.name + '-' + route.name
result.append((route.id, name))
return result
#api.multi
def write(self, vals):
res = super(ResBuilding, self).write(vals)
print(vals, self.id)
if vals.get('city_id'):
customers = self.env['res.partner'].search([('building_id', '=', self.id)])
for c in customers:
c.living_area = vals.get('city_id')
return res
#api.multi
#api.depends('latitude', 'longitude')
def on_change_location(self):
for li in self:
if li.latitude and li.longitude:
self.env.cr.execute("""update res_partner set location_latitude=""" + str(li.latitude) + """,
location_longitude=""" + str(li.longitude) + """where building_id=""" + str(
li.id))
return True
#api.multi
def write(self, vals):
res = super(ResBuilding, self).write(vals)
self.on_change_region_id()
return res
#api.multi
#api.depends('region_id')
def on_change_region_id(self):
for li in self:
if li.region_id:
self.env.cr.execute(
"""update res_partner set city_id=""" + str(li.region_id.id) + """where building_id=""" + str(
li.id))
return True
I want find the customer count in a specific building based on building id. and want The coustomer_count field to be added to the add custom filter
by this code ,i am getting count correctly . But the field customer_count does not appear in custom filter
when i add store=True, the field is coming in custom filter but the count is coming as 0.
Your code is not correct, even with storing the field.
customer_count = fields.Char(
string='Customer Count', compute='cust_count', store=True)
#api.multi
#api.depends()
def cust_count(self):
for record in self:
count = self.env['res.partner'].search_count([('building_id', '=', record.id)])
record.customer_count = count
Always use for each loops in compute methods, because in case of multi relational fields using your building model or just by presenting this computed field in a list of your building model will lead to a multi recordset behind self.
But that's not all. There should be a possibility to trigger the recomputation of the field and if easy to do using depends. Right now i don't see any easy possibility, because i don't know all your relations and workflows. Without storing the field you probably don't need that, but it would work there too.
So what to do to trigger a recomputation? Just work up from the other site of the relation: res.partner. Override it's write, create and unlink method to trigger the recomputation "manually".
class ResPartner(models.Model):
_inherit = "res.partner"
#api.multi
def write(self, values):
old_buildings = self.mapped('building_id')
res = super(ResPartner, self).write(values)
if 'building_id' in values:
new_buildings = self.mapped('building_id')
trigger_buildings = old_buildins | new_buildings
trigger_buildings.cust_count()
return res
#api.model
def create(self, values):
partner = super(ResPartner, self).create(values)
partner.building_id.cust_count()
return partner
#api.multi
def unlink(self):
buildings = self.mapped('building_id')
res = super(ResPartner, self).unlink()
buildings.cust_count()
return res
Another way is to use a one2many field on your building model in relation to res.partner and fill depends with it, like depends('partner_ids'). But in my experience one2many fields in such and lot of other situations tend to lead to bad performance.

How to Handle When Request Returns None

I have a list of IDs which corresponds to a set of records (opportunities) in a database. I then pass this list as a parameter in a RESTful API request where I am filtering the results (tickets) by ID. For each match, the query returns JSON data pertaining to the individual record. However, I want to handle when the query does not find a match. I would like to assign some value for this case such as the string "None", because not every opportunity has a ticket. How can I make sure there exists some value in presales_tickets for every ID in opportunity_list? Could I provide a default value in the request for this case?
views.py
opportunities = cwObj.get_opportunities()
temp = []
opportunity_list = []
cw_presales_engineers = []
for opportunity in opportunities:
temp.append(str(opportunity['id']))
opportunity_list = ','.join(temp)
presales_tickets = cwObj.get_tickets_by_opportunity(opportunity_list)
for opportunity in opportunities:
try:
if opportunity['id'] == presales_tickets[0]['opportunity']['id']:
try:
for presales_ticket in presales_tickets:
cw_engineer = presales_ticket['owner']['name']
cw_presales_engineers.append(cw_engineer)
except:
pass
else:
cw_engineer = 'None'
cw_presales_engineers.append(cw_engineer)
except AttributeError:
cw_engineer = ''
cw_presales_engineers.append(cw_engineer)
So, lets say you have a Ticket model and Opportunity model. Connected via a foreign key.
class Opportunity(models.Model):
... some fields here ...
class Ticket(models.Model):
opportunity = models.ForeignKey(Opportunity)
and in your view, you get a list of opportunity ids
def some_view(request):
ids = request.GET['ids']
It sounds, like what you want is to fetch all the tickets for the supplied opportunities and add some default processing for the opportunities that do not have tickets. If that is the case, why not do something like
def some_view(request):
ids = request.GET['ids']
tickets = Ticket.objects.filter(opportunity__id__in=ids)
results = []
for ticket in tickets:
result = ... do your thing here ...
results.append(result)
# now handle missing opportunities
good_ids = tickets.values_list('opportunity__id', flat=True).distinct()
for id in ids:
if id not in good_ids:
result = ... do your default processing ...
results.append(result)
Is that what you are trying to do?

How to correctly Update Odoo / Openerp website context?

I trying to adapt the module https://www.odoo.com/apps/modules/9.0/website_sale_product_brand/ to have a select box on the shop page, and filter by brand and category, and not to have to go to a diferent page and select the brand.
In that module they update context with the brand_id so the sale_product_domain function could append to the domain. In the module, it filter it as a charm, but in my code not....
Any guest?
When I debug self.env.context in the sale_product_domain function not brand if append, but in the website_sale_product_brand yes, with the exactly same code
controller.py
class WebsiteSale(website_sale):
#http.route(['/shop',
'/shop/page/<int:page>',
'/shop/category/<model("product.public.category"):category>',
'/shop/category/<model("product.public.category"):category>/page/<int:page>',
],type='http',auth='public',website=True)
def shop(self, page=0, category=None, search='', brand=None, **post):
# Update context to modify sale_product_domain function from website model
if brand:
context = dict(request.env.context)
context.setdefault('brand', int(brand))
request.env.context = context
result = super(WebsiteSale, self).shop(page=page, category=category,
brand=brand, search=search,
**post)
#append brand to keep so links mantain brand filter
keep = QueryURL('/shop',
brand=brand,
category=category and int(category),
search=search,)
#attrib=attrib_list TODO
#Update result
result.qcontext['keep'] = keep
result.qcontext['brands'] = http.request.env['product.brand'].search([]) #use to populate template select box
result.qcontext['sel_brand_id'] = brand #use to select the selected brand on brand select box
return result
models.py
class WebSite(models.Model):
_inherit = 'website'
#api.multi
def sale_product_domain(self):
domain = super(WebSite, self).sale_product_domain()
print self.env.context
if 'brand' in self.env.context:
domain.append(
('product_brand_id', '=', self.env.context['brand']))
return domain
ctx = dict (request.context)
ctx.update ({'bin_size': True})
request.context = ctx
That's it!

How do I order by date when using ReferenceProperty?

I have a simple one-to-many structure like this:
class User(db.Model):
userEmail = db.StringProperty()
class Comment(db.Model):
user = db.ReferenceProperty(User, collection_name="comments")
comment = db.StringProperty()
date = db.DateTimeProperty()
I fetch a user from by his email:
q = User.all() # prepare User table for querying
q.filter("userEmail =", "az#example.com") # apply filter, email lookup
results = q.fetch(1) # execute the query, apply limit 1
the_user = results[0] # the results is a list of objects, grab the first one
this_users_comments = the_user.comments # get the user's comments
How can I order the user's comments by date, and limit it to 10 comments?
You will want to use the key keyword argument of the built-in sorted function, and use the "date" property as the key:
import operator
sorted_comments = sorted(this_users_comments, key=operator.attrgetter("date"))
# The comments will probably be sorted with earlier comments at the front of the list
# If you want ten most recent, also add the following line:
# sorted_comments.reverse()
ten_comments = sorted_comments[:10]
That query fetches the user. You need to do another query for the comments:
this_users_comments.order('date').limit(10)
for comment in this_users_comments:
...

Categories