ponyorm / pony

Pony Object Relational Mapper
Apache License 2.0
3.63k stars 245 forks source link

Default values per record #645

Open nburns opened 2 years ago

nburns commented 2 years ago

https://docs.ponyorm.org/api_reference.html?highlight=before_update#cmdoption-arg-default

It would be nice to have the ability to set the default value of an attribute based on an individual model. It looks like when default is a callable it is only ever evaluated once. It would be really handy for code organization and encapsulation to be able to have pony evaluate a function for every new record creation.

site-packages/pony/orm/core.py(2190)validate()
   2185             return val
   2186         assert val is not NOT_LOADED
   2187         if val is DEFAULT:
   2188             default = attr.default
   2189             if default is None: return None
-> 2190             if callable(default): val = default()
   2191             else: val = default
   2192
   2193         entity = attr._get_entity(obj, entity)
   2194         reverse = attr.reverse

Maybe we could check the arrity or provide a different attribute for per record defaults

class Fruit(db.Entity):
    flavor = Required(str, default=lambda fruit: garden_get_fruit(fruit))

or

class Fruit(db.Entity):
    flavor = Required(str, instance_default=lambda fruit: garden_get_fruit(fruit))
nburns commented 2 years ago

I'd be interested in submitting a PR if this is something we could agree on

sashaaero commented 2 years ago

Hello. What do you mean by fruit lambda argument? Is it current object? Some attributes' values are unknown before you insert. If you want highly rely on other attrubutes there are several ways to do it.

  1. Use hybrid property, if your flavor value is something that you don't really want to store in your database, and it's okay to be read-only

    class Fruit(db.Entity):
    # ... attributes
    
    @property
    def flavor(self):
        return garden_get_fruit(self)
  2. Use some tricks with before_insert

    class Fruit(db.Entity):
    EMPTY_VALUE = '...'
    
    flavor = Required(str, default=EMPTY_VALUE)
    
    def before_insert(self):
        if self.flavor == self.EMPTY_VALUE:
            self.flavor = garden_get_fruit(self)
nburns commented 2 years ago

What do you mean by fruit lambda argument? Is it current object?

Yes it would be the current object.

Then we wouldn't have to rely on creating some object to represent the empty value.