@@ -28,7 +28,7 @@ def __get__(self, instance, owner):
2828 owner ._check_abstract ()
2929 q = sa .select ([owner .__table__ ])
3030 if instance is not None :
31- q = instance .append_where_primary_key ( q )
31+ q = q . where ( instance .lookup () )
3232 return q .execution_options (model = weakref .ref (owner ))
3333
3434
@@ -37,7 +37,7 @@ def __get__(self, instance, owner):
3737 def select (* args ):
3838 q = sa .select ([getattr (owner , x ) for x in args ])
3939 if instance is not None :
40- q = instance .append_where_primary_key ( q )
40+ q = q . where ( instance .lookup () )
4141 return q .execution_options (model = weakref .ref (owner ),
4242 return_model = False )
4343 return select
@@ -78,15 +78,18 @@ class UpdateRequest:
7878 specific model instance and its database row.
7979
8080 """
81- def __init__ (self , instance ):
81+ def __init__ (self , instance , lookup = True ):
8282 self ._instance = instance
8383 self ._values = {}
8484 self ._props = {}
8585 self ._literal = True
86- if instance .__table__ is None :
87- self ._pk_values = None
88- else :
89- self ._pk_values = _PrimaryKeyValues (instance )
86+ self ._locator = None
87+ if lookup and instance .__table__ is not None :
88+ try :
89+ self ._locator = instance .lookup ()
90+ except LookupError :
91+ # apply() will fail anyway, but still allow updates()
92+ pass
9093
9194 def _set (self , key , value ):
9295 self ._values [key ] = value
@@ -110,9 +113,9 @@ async def apply(self, bind=None, timeout=DEFAULT):
110113 :return: ``self`` for chaining calls.
111114
112115 """
113- if self ._pk_values is None :
116+ if self ._locator is None :
114117 raise TypeError (
115- 'GINO model {} is abstract, no table is defined ' .format (
118+ 'Model {} has no table, primary key or custom lookup() ' .format (
116119 self ._instance .__class__ .__name__ ))
117120 cls = type (self ._instance )
118121 values = self ._values .copy ()
@@ -146,8 +149,8 @@ async def apply(self, bind=None, timeout=DEFAULT):
146149 opts = dict (return_model = False )
147150 if timeout is not DEFAULT :
148151 opts ['timeout' ] = timeout
149- clause = self ._pk_values . append_where_primary_key (
150- type ( self ._instance ). update
152+ clause = type ( self ._instance ). update . where (
153+ self ._locator ,
151154 ).values (
152155 ** values ,
153156 ).returning (
@@ -252,18 +255,6 @@ def _inspect_alias(target):
252255 return sa .inspection .inspect (target .alias )
253256
254257
255- class _PrimaryKeyValues :
256- def __init__ (self , instance ):
257- self ._values = {}
258- for c in instance .__table__ .primary_key .columns :
259- self ._values [c ] = getattr (instance , c .name )
260-
261- def append_where_primary_key (self , q ):
262- for c , v in self ._values .items ():
263- q = q .where (c == v )
264- return q
265-
266-
267258class CRUDModel (Model ):
268259 """
269260 The base class for models with CRUD support.
@@ -422,8 +413,7 @@ class CRUDModel(Model):
422413 def __init__ (self , ** values ):
423414 super ().__init__ ()
424415 self .__profile__ = None
425- # noinspection PyCallingNonCallable
426- self .update (** values )
416+ self ._update_request_cls (self , False ).update (** values )
427417
428418 @classmethod
429419 def _init_table (cls , sub_cls ):
@@ -541,8 +531,34 @@ def append_where_primary_key(self, q):
541531
542532 await user.query.gino.first()
543533
534+ .. deprecated:: 0.7.6
535+ Use :meth:`lookup` instead.
536+
544537 """
545- return _PrimaryKeyValues (self ).append_where_primary_key (q )
538+ return q .where (self .lookup ()) # pragma: no cover
539+
540+ def lookup (self ):
541+ """
542+ Generate where-clause expression to locate this model instance.
543+
544+ By default this method uses current values of all primary keys, and you
545+ can override it to behave differently. All instance-level CRUD
546+ operations depend on this method internally.
547+
548+ :return:
549+
550+ .. versionadded:: 0.7.6
551+
552+ """
553+ exps = []
554+ for c in self .__table__ .primary_key .columns :
555+ exps .append (c == getattr (self , c .name ))
556+ if exps :
557+ return sa .and_ (* exps )
558+ else :
559+ raise LookupError ('Instance-level CRUD operations not allowed on '
560+ 'models without primary keys or lookup(), please'
561+ ' use model-level CRUD operations instead.' )
546562
547563 def _update (self , ** values ):
548564 return self ._update_request_cls (self ).update (** values )
@@ -551,7 +567,7 @@ async def _delete(self, bind=None, timeout=DEFAULT):
551567 cls = type (self )
552568 # noinspection PyUnresolvedReferences,PyProtectedMember
553569 cls ._check_abstract ()
554- clause = self . append_where_primary_key ( cls .delete )
570+ clause = cls .delete . where ( self . lookup () )
555571 if timeout is not DEFAULT :
556572 clause = clause .execution_options (timeout = timeout )
557573 if bind is None :
0 commit comments