class SomeOrder(models.Model):
_inherit = 'some.order'
#api.multi
def write(self, vals):
result = super(SomeOrder, self).write(vals)
for rec in self:
rec.update_other_order()
return result
#api.model
def update_pos_quatation(self):
# my logic.write
class OtherOrder(models.Model):
_inherit = "other.order"
#api.multi
def write(self, vals):
result = super(OtherOrder, self).write(vals)
if 'amount_paid' in vals:
self.update_Some_order()
return result
#api.model
def update_some_order(self):
# my logic.write
i have method that at the end writes vals to sales order but i overridden sales write method for another method to update sales order. The problem is that i get “maximum recursion depth exceeded” because update_sales_order triggers something method. how can i avoid this recursion error? probably i need to add some context to write method
So basically both methods call each other with write.
You just need a termination or break condition here.
For example, if you call another write logic if a special field value was changed, which won't change the field value again:
#api.multi
def write(self, vals):
res = super(MyModel, self).write(vals)
if 'my_trigger_field_name' in vals:
self.my_other_logic(vals)
#api.multi
def my_other_logic(self, vals):
my_trigger_field_value = vals.get('my_trigger_field_name')
if my_trigger_field_value == 1:
self.write({'another_field': 2})
Or if you have to change the values, which had to be written, just use to context to stop a recursion:
#api.multi
def write(self, vals):
res = super(MyModel, self).write(vals)
if 'stop_write_recursion' not in self.env.context:
self.my_other_logic(vals)
#api.multi
def my_other_logic(self, vals):
# get values to write
self.with_context(stop_write_recursion=1).write(other_values)
Related
I have a model with a nullable boolean field that I'd like to have serialized in a way that converts null in the output to false.
My model:
class UserPreferences(models.Model):
receive_push_notifications = models.BooleanField(
null=True, blank=True,
help_text=("Receive push notifications))
I'm trying to do it with a custom field like so:
class StrictlyBooleanField(serializers.Field):
def to_representation(self, value):
# Force None to False
return bool(value)
def to_internal_value(self, data):
return bool(data)
class UserPreferencesSerializer(serializers.ModelSerializer):
class Meta(object):
model = UserPreferences
fields = ('receive_push_notifications',)
receive_push_notifications = StrictlyBooleanField()
but this isn't working, I'm still seeing null in my API responses.
I think I must be missing something simple in wiring it up because I don't even get an error if I replace my to_representation with:
def to_representation(self, value):
raise
DRF doesn't seem to be calling my method at all... What am I missing here?
Explanation
After looking into rest framework's Serializer's to_representation method, you will find that it iterates through all of the fields and calls field.get_attribute method for each field. If the value returned from that method is None it skips calling field.to_representation entirely and set None as the field value.
# Serializer's to_representation method
def to_representation(self, instance):
"""
Object instance -> Dict of primitive datatypes.
"""
ret = OrderedDict()
fields = self._readable_fields
for field in fields:
try:
attribute = field.get_attribute(instance)
except SkipField:
continue
# We skip `to_representation` for `None` values so that fields do
# not have to explicitly deal with that case.
#
# For related fields with `use_pk_only_optimization` we need to
# resolve the pk value.
check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
if check_for_none is None:
ret[field.field_name] = None
else:
ret[field.field_name] = field.to_representation(attribute)
return ret
Solution
Override field.get_attribute by calling super().get_attribute and return False if the value is None
class StrictlyBooleanField(serializers.Field):
def get_attribute(self, instance):
attribute = super().get_attribute(instance)
return bool(attribute)
def to_representation(self, value):
return value
def to_internal_value(self, data):
return bool(data)
You can just write a simple function inside your serializer
class UserPreferencesSerializer(serializers.ModelSerializer):
yourField = serializers.SerializerMethodField(read_only=True)
class Meta(object):
model = UserPreferences
fields = ['receive_push_notifications', 'yourField']
def get_yourField(self, obj):
if obj.receive_push_notifications == null:
return False
else:
return True
(Crossposting note: I have asked this already at the Ruby Forum one week ago, but did not get any response yet).
Here is a (very) simplified, working version of what I have so far:
# A class S with two methods, one which requires one parameter, and
# one without parameters.
class S
def initialize(s); #ms = s; end
def s_method1(i); puts "s_method1 #{i} #{#ms}"; end
def s_method2; puts "s_method2 #{#ms}"; end
end
# A class T which uses S, and "associates" itself to
# one of the both methods in S, depending on how it is
# initialized.
class T
def initialize(s, choice=nil)
#s = S.new(s)
# If choice is true, associate to the one-parameter-method, otherwise
# to the parameterless method.
#pobj = choice ? lambda { #s.s_method1(choice) } : #s.method(:s_method2)
end
# Here is how I use this association
def invoke
#pobj.call
end
end
In this example, depending on how T is constructed, T#invoke calls
either S#s_method1 or S#S_method2, but in the case of calling
S#s_method1, the parameter to s_method1 is already fixed at creation
time of the T object. Hence, the following two lines,
T.new('no arguments').invoke
T.new('one argument', 12345).invoke
produce the output
s_method2 no arguments
s_method1 12345 one argument
which is exactly what I need.
Now to my question:
In the case, where choice is nil, i.e. where I want to invoke the
parameterless method s_method2, I can get my callable object in an
elegant way by
#s.method(:s_method2)
In the case where choice is non-nil, I had to construct a Proc object
using `lambda. This not only looks clumsy, but also makes me feel a bit
uncomfortable. We have a closure here, which is connected to the
environment inside the initialize method, and I'm not sure whether this
could cause trouble by causing memory leaks in some circumstances.
Is there an easy way to simply bind a method object (in this case
#s.method(:s_method1) to a fixed argument?
My first idea was to use
#s.method(:s_method1).curry[choice]
but this does not achieve my goal. It would not return a callable Proc object, but instead actually execute s_method1 (this is not a bug, but documented behaviour).
Any other ideas of how my goal could be achieved?
Saving parameters separately
This option is simple, but it might not be what you're looking for :
class T
def initialize(s, choice=nil)
s = S.new(s)
#choice = choice
#pobj = s.method(choice ? :s_method1 : :s_method2)
end
def invoke
#pobj.call(*#choice)
end
end
T.new('no arguments').invoke
T.new('one argument', 12345).invoke
#=> s_method2 no arguments
#=> s_method1 12345 one argument
Method refinements for default parameters (Ruby 2.0+)
# Allows setting default parameters for methods, after they have been defined.
module BindParameters
refine Method do
def default_parameters=(params)
#default_params = params
end
def default_parameters
#default_params || []
end
alias_method :orig_call, :call
def call(*params)
merged_params = params + (default_parameters[params.size..-1] || [])
orig_call(*merged_params)
end
end
end
Here's an example :
def f(string)
puts "Hello #{string}"
end
def g(a, b)
puts "#{a} #{b}"
end
using BindParameters
f_method = method(:f)
f_method.default_parameters = %w(World)
f_method.call('user') # => Hello user
f_method.call # => Hello World
g_method = method(:g)
g_method.default_parameters = %w(Hello World)
g_method.call # => Hello World
g_method.call('Goodbye') # => Goodbye World
g_method.call('Goodbye', 'User') # => Goodbye User
Your code can be rewritten :
class T
using BindParameters
def initialize(s, *choice)
s = S.new(s)
#pobj = s.method(choice.empty? ? :s_method2 : :s_method1)
#pobj.default_parameters = choice
end
def invoke
#pobj.call
end
end
T.new('no arguments').invoke # => s_method2 no arguments
T.new('one argument', 12_345).invoke # => s_method1 12345 one argument
Monkey-Patching Method class (Ruby 1.9+)
If it is acceptable to patch the Method class, you could use :
class Method
def default_parameters=(params)
#default_params = params
end
def default_parameters
#default_params || []
end
alias_method :orig_call, :call
def call(*params)
merged_params = params + (default_parameters[params.size..-1] || [])
orig_call(*merged_params)
end
end
T becomes :
class T
def initialize(s, *choice)
s = S.new(s)
#pobj = s.method(choice.empty? ? :s_method2 : :s_method1)
#pobj.default_parameters = choice
end
def invoke
#pobj.call
end
end
Wrapping Method class (Ruby 1.9+)
This way is probably cleaner if you don't want to pollute Method class :
class MethodWithDefaultParameters
attr_accessor :default_parameters
attr_reader :method
def initialize(receiver, method_symbol)
#method = receiver.public_send(:method, method_symbol)
#default_parameters = []
end
def call(*params)
merged_params = params + (default_parameters[params.size..-1] || [])
method.call(*merged_params)
end
def method_missing(sym, *args)
method.send(sym, *args)
end
end
T becomes :
class T
def initialize(s, *choice)
s = S.new(s)
#pobj = MethodWithDefaultParameters.new(s, choice.empty? ? :s_method2 : :s_method1)
#pobj.default_parameters = choice
end
def invoke
#pobj.call
end
end
Any comment or suggestion are welcome!
I am having trouble creating a method to establish a new hash. I know that it is definitely easier just to declare the hash, however I need to create a method. Here is what I have so far and it keeps generating an error message.
def create_new(hash_name)
hash_name = Hash.new
end
This should create and empty hash^
def add_item(hash_name, item_name, item_quantity)
hash_name[:item_name.to_sym] = item_quantity
end
I keep getting an error message on the above code^ I am trying to update this hash and add a new key value pair with a method
p create_new("grocery_list")
This creates a new empty hash^ however when I call it with the below code is says the hash is undefined
add_item(grocery_list, "pizza", "1")
p grocery_list
You could also turn it into a class if you fancy.
class MyHash
attr_reader :hash
def initialize
#hash = Hash.new
end
def [](key)
self.hash[key]
end
def []=(key, value)
self.hash[key.to_sym] = value
end
end
grocery_list = MyHash.new
grocery_list['pizza'] = 1
> grocery_list.hash
=> {:pizza=>1}
in your create_new method, you define a hash_name local variable. This variable does not exist anywhere but the body of your method. That's what seems to confuse you.
You could express better your intent with :
def create_new
Hash.new
end
def add_item(hash, key, value)
hash[key.to_sym] = value
end
In order to get to what you are trying to do, you will have to store the result of your method in some kind of variable in order to use it :
grocery_list = create_new # grocery_list is now a local variable
add_item(grocery_list, 'pizza', 1)
I'm making a game with a Board class Cell class. The Board class needs to be initialized with a unique instance variable for each Cell. I can hardcode it so that it works, but it seems inelegant and doesn't allow the size of the board to be chosen by the user at runtime. Here's what I have:
class Board
def initialize
#cell_1 = Cell.new(1)
#cell_2 = Cell.new(2)
#cell_3 = Cell.new(3)
#cell_4 = Cell.new(4)
#cell_5 = Cell.new(5)
#cell_6 = Cell.new(6)
#cell_7 = Cell.new(7)
#cell_8 = Cell.new(8)
#cell_9 = Cell.new(0)
#cells = [#cell_1, #cell_2, #cell_3,
#cell_4, #cell_5, #cell_6,
#cell_7, #cell_8, #cell_9]
end
end
I think I could use a loop to create a hash with unique key names pointing to unique Cell objects, but I don't know how I could make unique instance variables with a loop.
If you don't need to create each instance variables (#cell_1, #cell_2, ...), you can use Enumerable#map:
#cells = [*1..8, 0].map { |i| Cell.new(i) }
If you really need to refer every instance variable by name you can do something like this.
class Board
def initialize
#cells = (1..9).to_a.map { |i| Cell.new(i) }
end
def method_missing(method, *args, &block)
if method =~ /^cell_[1-9][0-9]*$/
index = method[/\d+/].to_i
#cells[index-1]
else
super
end
end
end
In this way you can call:
board = Board.new
board.cell_1 #=> first cell
Of course I'd use the solution proposed by #falsetru.
Say I have the following class:
class Cashier
def purchase(amount)
(#purchases ||= []) << amount
end
def total_cash
(#purchases || []).inject(0) {|sum,amount| sum + amount}
end
end
This is for learning purposes only, please ignore how unrealistic this may be.
Now in general, the total_cash could be an expensive call to loop through all the items.
I want to know how I can call .inject ONLY if the #purchases variable is dirty i.e. there was something modified.
How would my class be modified to do this?
The simplest approach would be to maintain another variable to indicate whether or not #purchases is dirty. For example:
class Cashier
def initialize(*args)
# init #purchases and #total_cash
#is_purchases_dirty = false
end
def purchase(amount)
(#purchases ||= []) << amount
#is_purchases_dirty = true
end
def total_cash
return #total_cash unless #is_purchases_dirty
#is_purchases_dirty = false
#total_cash = (#purchases || []).inject(0) {|sum,amount| sum + amount}
return #total_cash
end
end
A cleaner/simpler approach may be to calculate #total_cash each time the setter is called for purchases. However, this means that you need to always use the setter, even within your class. It also means that you will be "hiding" an expensive operation inside of a setter method. You can decide which one you like better.
class Cashier
def purchase(amount)
(#purchases ||= []) << amount
#total_cash = (#purchases || []).inject(0) {|sum,amount| sum + amount}
end
def total_cash
#total_cash
end
end
I would also recommend against your naming scheme for an expensive operation. I would rename total_cash to something like calc_total_cash in order to tell users of your API that this is a relatively expensive call as opposed to a simple getter/setter.
You can take this a step further than the other answers if you wanted. Rather than changing your code to only recalculate when necessary, you can write the code that changes your code. Everybody loves a bit of metaprogramming.
Here's some code that takes the name of a method that performs a potentially long calculation, and a list of names of methods that when called invalidate any previous calculation, and writes the code to wrap the methods and only perform the calculation if necessary, returning the stored value if not.
module ExpensiveCalculation
def recalc_only_if_necessary(meth, *mutators)
aliased_method_name = "__#{meth.object_id}__"
value = "#__#{meth.object_id}_value__"
dirty_flag = "#__#{meth.object_id}_dirty__"
module_eval <<-EOE
alias_method :#{aliased_method_name}, :#{meth}
private :#{aliased_method_name}
def #{meth}(*args, &blk)
#{dirty_flag} = true unless defined? #{dirty_flag}
return #{value} unless #{dirty_flag}
#{value} = #{aliased_method_name}(*args, &blk)
#{dirty_flag} = false
#{value}
end
EOE
mutators.each do |mutator|
aliased_mutator = "__#{meth.object_id}_#{mutator.object_id}__"
module_eval <<-EOE
alias_method :#{aliased_mutator}, :#{mutator}
private :#{aliased_mutator}
def #{mutator}(*args, &blk)
#{dirty_flag} = true
#{aliased_mutator}(*args, &blk)
end
EOE
end
end
# this hook is used to make the new method
# private to the extended class.
def self.extend_object(obj)
super
obj.private_class_method :recalc_only_if_necessary
end
end
By making this available inside your class definition, you can wrap one or many methods easily without changing your existing code:
class Cashier
extend ExpensiveCalculation
def purchase(amount)
(#purchases ||= []) << amount
end
def total_cash
(#purchases || []).inject(0) {|sum,amount| sum + amount}
end
recalc_only_if_necessary :total_cash, :purchase
end
It might not make sense to do something like this if you just want to change one method, but if you have several that you want to change some way techniques like this can be pretty useful.
In the simplest case, you could define an instance variable for the thing you want to mark as dirty. Set it to true when the variable is modified (in your purchase method).
Check for the value in total_cash; if so, use a cached version of the total. Otherwise, compute the new value and store it in the cache.
class Cashier
def purchase(amount)
#purchases_dirty = true
(#purchases ||= []) << amount
end
def total_cash
#total_cash = (#purchases || []).inject(0) do |sum,amount|
sum + amount
end if (#purchases_dirty || #total_cash.nil?)
#purchases_dirty = false
#total_cash
end
end