Thank you, as always, for both the specific answer and the general advice. 
Much appreciated!

On Sunday, June 23, 2019 at 5:19:49 PM UTC-5, Mike Bayer wrote:
>
>
>
> On Sun, Jun 23, 2019, at 5:45 PM, Andrew Martin wrote:
>
> I have a generic CRUDService for my web app. It's a pattern that was 
> loosely suggested to me by Mike a while back. I've probably not implemented 
> it the way he intended, but it works pretty well in a pretty small amount 
> of code.
>
>
> The main work, however, that needs to be done is in the read_all method. 
> It needs to be able to handle different filters depending on which view is 
> calling it. Which it can as it is now. But I want to fidget with the 
> defaults a little. Basically, I almost never want to show objects that have 
> been soft deleted. Only admin users/views should be able to fidget with 
> things that we've "deleted". The way the method works now is that if I 
> don't pass any filters in at all, it sets a filter to 
> cls.is_deleted==False. But I'd like to be able to inspect incoming filters. 
> Say the view looks at the resource context and determines that a view 
> should only be allowed to see objects based on some ACL rule. I might 
> create a filter for that view controller that looks like 
> filters = (Project.usergroup==User.usergroup)
>
> (defining the applicable usergroup for a given context is a separate 
> service, but the relationship should be obvious, I think?)
>
> and then have the view query with CRUDService.read_all(Project, 
> request.dbsession, filters). The way I've got it now, that would just 
> replace the default filter, and I would have to add 
> Project.is_deleted==False to the tuple of filters every time I call this. I 
> would like to be able to inspect the filters that are passed into the 
> read_all (which, honestly, I should rename and call it read_many) method 
> and say, if there isn't any reference to the is_deleted column in the 
> filters, then set that as false, otherwise, accept the value in the 
> function parameter. But I find the filter's tuple values quite opaque. Once 
> they are instantiated, they are class-specific, and I'm trying to get at a 
> general way of understanding them. I'll note that this column is guaranteed 
> to be present in the framework. I've modified the declarative base such 
> that every object has the is_deleted. I appreciate any help you might be 
> able to give. It's very likely I'm overthinking this like a numbskull and 
> the answer is painfully obvious. Here's the service class:
>
>
> you can inspect a ColumnElement structure to look for a certain column 
> like this:
>
> from sqlalchemy.sql import visitors
> for elem in visitors.iterate(Project.usergroup == User.usergroup, {}):
>    if isinstance(elem, Column) and elem.name == "is_deleted":
>        return True
>
> internally, the ORM usually uses visitors.traverse(expression, {}, 
> {"column": visit_column}) but the above is easier to illustrate quickly.
>
> If it were me, I'd likely keep this as an explicit option on read_all(), 
> e.g. read_all(... include_deleted=True), much simpler and won't be called 
> "magic" by anyone, but it can go either way.
>
> also if you know me at all you'd know I would not do the "s.commit()" 
> inside all of the CR/U/D methods.   Your methods that do a "read()" are 
> beginning a transaction and leaving it open implicitly (there's no 
> rollback() or commit() at the end, nor should there be), whereas your CRUD 
> methods are fully committing the transaction for individual 
> INSERT/UPDATE/DELETE statements, and this inconsistency and the difficulty 
> it introduces in what happens when multiple CRUDService methods are called 
> sequentially will eventually lead to architectural problems.
>  
>
>
>
> from pyramid.request import Request
> from crudites.models.meta import Base
> from sqlalchemy.orm.session import Session
> from uuid import UUID
> from typing import List, Tuple, Dict
> from crudites.services.id_service import IDService
>
>
> class CRUDService:
>     '''TODO: docstring'''
>
>     @staticmethod
>     def create(cls: Base, s: Session) -> bool:
>         unique_id, public_id = IDService.create_db_ids()
>         cls.unique_id=unique_id
>         cls.public_id=public_id
>         s.add(cls)
>         s.commit()
>         return True
>
>     @staticmethod
>     def read_by_pkid(cls: Base, s: Session, id: int) -> Base:
>         q = s.query(cls).get(id)
>         return q
>
>     @staticmethod
>     def read_by_unique_id(cls: Base, s: Session, id: UUID) -> Base:
>         q = s.query(cls).filter_by(unique_id=id).first()
>         return q
>
>     @staticmethod
>     def read_by_public_id(cls: Base, s: Session, id: str) -> Base:
>         q = s.query(cls).filter_by(public_id=id).first()
>         return q
>
>     @staticmethod
>     def read_all(cls: Base, s: Session, filters: Tuple=None) -> List[Base]:
>         if not filters:
>             filters = (cls.is_deleted==False,)
>         q = s.query(cls).filter(*filters).all()
>         return q
>
>     @staticmethod
>     def update(cls: Base, s: Session) -> bool:
>         s.add(cls)
>         s.commit()
>         return True
>
>     @staticmethod
>     def delete(cls: Base, s: Session)-> bool:
>         cls.is_deleted=True
>         s.add(cls)
>         s.commit()
>         return True
>
>
>
>
> Thanks,
> -andrew
>
>
> --
> SQLAlchemy - 
> The Python SQL Toolkit and Object Relational Mapper
>  
> http://www.sqlalchemy.org/
>  
> To post example code, please provide an MCVE: Minimal, Complete, and 
> Verifiable Example. See http://stackoverflow.com/help/mcve for a full 
> description.
> --- 
> You received this message because you are subscribed to the Google Groups 
> "sqlalchemy" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to sqlal...@googlegroups.com <javascript:>.
> To post to this group, send email to sqlal...@googlegroups.com 
> <javascript:>.
> Visit this group at https://groups.google.com/group/sqlalchemy.
> To view this discussion on the web visit 
> https://groups.google.com/d/msgid/sqlalchemy/da43b503-1e7a-4a0e-bd32-619d96163c1c%40googlegroups.com
>  
> <https://groups.google.com/d/msgid/sqlalchemy/da43b503-1e7a-4a0e-bd32-619d96163c1c%40googlegroups.com?utm_medium=email&utm_source=footer>
> .
> For more options, visit https://groups.google.com/d/optout.
>
>
>

-- 
SQLAlchemy - 
The Python SQL Toolkit and Object Relational Mapper

http://www.sqlalchemy.org/

To post example code, please provide an MCVE: Minimal, Complete, and Verifiable 
Example.  See  http://stackoverflow.com/help/mcve for a full description.
--- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at https://groups.google.com/group/sqlalchemy.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/sqlalchemy/8de1508c-8d98-4e88-b838-d886dc10a9ef%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to