how to generate a subfactory based on condition of a parent attribute - factory-boy

I have a factory like so:
class PayInFactory(factory.DjangoModelFactory):
class Meta:
model = PayIn
#factory.lazy_attribute
def card(self):
if self.booking_payment and self.booking_payment.payment_type in [bkg_cts.PAYMENT_CARD, bkg_cts.PAYMENT_CARD_2X]:
factory.SubFactory(
CardFactory,
user=self.user,
)
I'm trying to generate a field card only if the booking_payment field has a payment_type value in [bkg_cts.PAYMENT_CARD, bkg_cts.PAYMENT_CARD_2X]
The code goes into that statement but card field is empty after generation.
How can I do that properly ?
Is SubFactory allowed in lazy_attribute ?
I'd like to be able to modify Card field from PayInFactory if possible like so:
>>> PayInFactory(card__user=some_user)
PostGeneration won't do as I need this Card to be available before the call to create. I overrided _create and it may use the card if available.
Thanks !

The solution lies in factory.Maybe:
class PayInFactory(factory.django.DjangoModelFactory):
class Meta:
model = models.PayIn
card = factory.Maybe(
factory.LazyAttribute(
lambda o: o.booking_payment and o.booking_payment.payment_type in ...
),
factory.SubFactory(
CardFactory,
# Fetch 'user' one level up from CardFactory: PayInFactory
user=factory.SelfAttribute('..user'),
),
)
However, I haven't tested whether the extra params get actually passed to the CardFactory, nor what happens when CardFactory is not called — you'll have to check (and maybe open an issue on the project if you get an unexpected behaviour!).

Related

How to get values in onchange with sudo()

I have added one onchange method, in that onchange method I have used sudo() while accessing many2one field.
But with sudo(), I am not able to get record's values with sudo.
So how can I get values of onchange record (<odoo.models.NewId object at 0x7fba62f7b3d8>) with sudo().
Here is sample code :
#api.onchange('product_id')
def onchange_product_id(self):
for record in self:
print(record.product_id)
print(record.sudo().product_id)
Actual result :
product.product(13,)
product.product()
Expected result :
product.product(13,)
product.product(13,)
That's because the recordset doesn't exist outside the current transaction. So your current user can see the contents but other users can't.
The code looks good to me, in fact, if you see path_to_v12/addons/hr_expense/models/hr_expense.py lines 563-567, you'll see a similar code:
#api.onchange('employee_id')
def _onchange_employee_id(self):
self.address_id = self.employee_id.sudo().address_home_id
self.department_id = self.employee_id.department_id
self.user_id = self.employee_id.expense_manager_id or
self.employee_id.parent_id.user_id

How can I give a user the permission to create a field but not to change it?

I wanna a user to create a record but later dun give it the right to change the value of that field. should I do it By overriding create and write methods? is it possible to write such code:
field1: fields.float(string='Field',write=['base.GROUP_ID']),
This may work create a status field this field is a compute field when it's true the field1 will be read only. Because i'm on my phone i'm not going to writr the hole code just try to understand the idea
status = field.Boolean(compute='compute_status')
def compute_status(self):
for rec in self:
# first check of the use belong to the group that have full acces
if self.env.user.has_group('group_id') :
rec.status = False
# then check if the record is saved in databse
# unsaved records There id is instance of NewId it's a dummy class used for this
elif instanceOf(NewId ,rec.id) :
rec.status = False # here all users can fill the field when the record is not created yet but cannot edit
else :
rec.status = True # if record is saved and user is not in group_id make field readonly or invisible as you want
Now create your field and use status property to make it readonly when status field is True .
As you can see my answer is algorithme more than a code sorry for sysntax errors
I think the better way to do this is to create a group to which the user will belong, then set in the ir.model.access a rule, with the rights you want, for that particular group.
Ask if you need more help.
EDIT
You can define a view, that inherit from the original one, but is accessible only for the user group, like:
<field name="groups_id" eval="[(6, 0, [ref(' < your group > ')])]"/>
and there you redefine the field making it readonly. That's it.

Odoo: get type of field by name

in odoo you can get value of field by it's str name:
exm:
name = getattr(self, 'name')
what i want now is to know the type of field name is it :
fields.Char, fields.Many2one, fields.Many2many .....
so what i need is something like this
gettype(self, 'user_id')
is there a way to now what is the type of field in odoo?
You can search from ir.model.fields model.
ir_model_obj=self.env['ir.model.fields']
ir_model_field=ir_model_obj.search([('model','=',model),('name','=',field)])
field_type=ir_model_field.ttype
if field_type=='many2one':
print "do operation"
This may help you.
Odoo provides this information in the _fields attribute, I think It's better because every thing happens In the Python side no need for contacting the database, especially In my case my model have more than 30 fields :
for name, field in self._fields.iteritems():
if not isinstance(field, (fields.Many2one, fields.Many2many, fields.One2many)):
# logic go here
If you you want to verify just one fields:
if not isinstance(self._fields[field_name], (fields.Many2one, ...)): # do something

django "use_natural_foreign_keys=True" issue

I currently use the well documented "use_natural_foreign_keys=True" to return the relevant field data required instead of the id:
all_orders = Orders.objects.all()
resp = serializers.serialize('json', all_orders, use_natural_foreign_keys=True)
What I don't know how to do is return both the id AND the field data required as typically returned by the "use of use_natural_foreign_keys=True".
Anyone know of a quick fix to return both?
Many thanks, Alan.
define a "natural_key" method in your model class, whose id and field_name you like to get. e.g
def natural_key(self):
return (self.id, self.field_name)

Testing dynamic attributes with cucumber, undefined method

We have a Style model with dynamic attributes, which can be saved by filling one field with the attribute key and the next field with the value.
A typical params hash looks like this:
{"utf8"=>"✓", "style"=>{"collection_id"=>"48", "program_id"=>"989", "number"=>"454632", "name"=>"t67f", "category_id"=>"19", "field_KEY"=>"VALUE"}, "commit"=>"save", "id"=>"4521"}
This works as intended when clicking it through, and the "field_KEY" => "VALUE" pair creates a new dynamic attribute with a getter(field_KEY) and setter(field_KEY=) method.
The Problem is: If the process is simulated with cucumber, something calls the getters for all keys in the hash before the attributes are set, including field_KEY.
Normal attributes will return nil for a new record, but since the getter for field_KEY has not yet been created, this results in an
`UndefinedMethodError: undefined method 'field_KEY'`.
Now my question: would you rather track down the caller of the field_KEY getter and mess around with cucumber, or should I try to simulate a fake method, something like:
def check_method(method_name)
if method_name =~ /^field_/
nil
else
... # let the Error be raised
end
Better ideas or solutions are more than welcome
Thanks
The Problem was:
The call to field_KEY came from pickle, because I included the step
And the style's "field_KEY" should be "VALUE"
which looks like this:
Then(/^#{capture_model}'s (\w+) (should(?: not)?) be #{capture_value}$/) do |name, attribute, expectation, expected|
actual_value = model(name).send(attribute)
expectation = expectation.gsub(' ', '_')
case expected
when 'nil', 'true', 'false'
actual_value.send(expectation, send("be_#{expected}"))
when /^[+-]?[0-9_]+(\.\d+)?$/
actual_value.send(expectation, eql(expected.to_f))
else
actual_value.to_s.send(expectation, eql(eval(expected)))
end
end
I still don't know why the dynamic_attribute getter had not been created up to this point.
What I ended up doing:
In my opinion (also, it solved the problem ;)), cucumber tests should be black-box tests, thats why I chose to change the steps and now I use
And the "key1" field should contain "KEY"
which checks if the field has been filled with the correct value after the page reloads.