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 [email protected].
> To post to this group, send email to [email protected].
> 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 [email protected].
To post to this group, send email to [email protected].
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/a0e411df-e7c6-4d7d-b79b-154ac622c18c%40www.fastmail.com.
For more options, visit https://groups.google.com/d/optout.