Pyramid resource: In plain English - python

I've been reading on the ways to implement authorization (and authentication) to my newly created Pyramid application. I keep bumping into the concept called "Resource". I am using python-couchdb in my application and not using RDBMS at all, hence no SQLAlchemy. If I create a Product object like so:
class Product(mapping.Document):
item = mapping.TextField()
name = mapping.TextField()
sizes = mapping.ListField()
Can someone please tell me if this is also called the resource? I've been reading the entire documentation of Pyramids, but no where does it explain the term resource in plain simple english (maybe I'm just stupid). If this is the resource, does this mean I just stick my ACL stuff in here like so:
class Product(mapping.Document):
__acl__ = [(Allow, AUTHENTICATED, 'view')]
item = mapping.TextField()
name = mapping.TextField()
sizes = mapping.ListField()
def __getitem__(self, key):
return <something>
If I were to also use Traversal, does this mean I add the getitem function in my python-couchdb Product class/resource?
Sorry, it's just really confusing with all the new terms (I came from Pylons 0.9.7).
Thanks in advance.

I think the piece you are missing is the traversal part. Is Product
the resource? Well it depends on what your traversal produces, it
could produce products.....
Perhaps it might be best to walk this through from the view back to
how it gets configured when the application is created...
Here's a typical view.
#view_config(context=Product, permission="view")
def view_product(context, request):
pass # would do stuff
So this view gets called when context is an instance of Product. AND
if the acl attribute of that instance has the "view"
permission. So how would an instance of Product become context?
This is where the magic of traversal comes in. The very logic of
traversal is simply a dictionary of dictionaries. So one way that this
could work for you is if you had a url like
/product/1
Somehow, some resource needs to be traversed by the segments of the
url to determine a context so that a view can be determined. What if
we had something like...
class ProductContainer(object):
"""
container = ProductContainer()
container[1]
>>> <Product(1)>
"""
def __init__(self, request, name="product", parent=None):
self.__name__ = name
self.__parent__ = parent
self._request = request
def __getitem__(self, key):
p = db.get_product(id=key)
if not p:
raise KeyError(key)
else:
p.__acl__ = [(Allow, Everyone,"view")]
p.__name__ = key
p.__parent__ = self
return p
Now this is covered in the documentation and I'm attempting to boil it
down to the basics you need to know. The ProductContainer is an object
that behaves like a dictionary. The "name" and "parent"
attributes are required by pyramid in order for the url generation
methods to work right.
So now we have a resource that can be traversed. How do we tell
pyramid to traverse ProductContainer? We do that through the
Configurator object.
config = Configurator()
config.add_route(name="product",
path="/product/*traverse",
factory=ProductContainer)
config.scan()
application = config.make_wsgi_app()
The factory parameter expects a callable and it hands it the current
request. It just so happens that ProductContainer.init will do
that just fine.
This might seem a little much for such a simple example, but hopefully
you can imagine the possibilities. This pattern allows for very
granular permission models.
If you don't want/need a very granular permission model such as row
level acl's you probably don't need traversal, instead you can use
routes with a single root factory.
class RootFactory(object):
def __init__(self, request):
self._request = request
self.__acl__ = [(Allow, Everyone, "view")] # todo: add more acls
#view_config(permission="view", route_name="orders")
def view_product(context, request):
order_id, product_id = request.matchdict["order_id"], request.matchdict["product_id"]
pass # do what you need to with the input, the security check already happened
config = Configurator(root_factory=RootFactory)
config.add_route(name="orders",
path="/order/{order_id}/products/{product_id}")
config.scan()
application = config.make_wsgi_app()
note: I did the code example from memory, obviously you need all the necessary imports etc. in other words this isn't going to work as a copy/paste

Have you worked through http://michael.merickel.org/projects/pyramid_auth_demo/ ? If not, I suspect it may help. The last section http://michael.merickel.org/projects/pyramid_auth_demo/object_security.html implements the pattern you're after (note the example "model" classes inherit from nothing more complex than object).

Related

Elegant Way to Deal with Marshmallow Altering a SQLAlchemy Object

I have found myself in a situation that does not seem to have an elegant solution. Consider the following (pseudo-ish) REST API code
bp = Blueprint('Something', __name__, url_prefix='/api')
class SomeOutputSchema(ma.SQLAlchemyAutoSchema)
class Meta:
model = MyModel
#pre_dump(pass_many=False)
def resolveFilePath(self, ormObject, many):
# ormObject has a parent via a relationship
ormObject.filePath = os.path.join(ormObject.parent.rootDir, ormObject.filePath)
#bp.route("/someRoute")
class SomeClass(MethodView):
def put(self):
ormObject = MyModel(filePath = "/some/relative/path")
db.session.add(ormObject)
db.session.flush()
outputDump = SomeOutputSchema().dump(ormObject)
# Lots of other code that uses outputDump...
# Only commit here in case
# anything goes wrong above
db.session.commit()
return jsonify({"data": outputDump}), 201
I have
A PUT endpoint that will create a new resource, then return the dump of that resource.
An ORM object that has a filePath property. This must be stored as a relative path.
A Marshmallow schema. It has a #pre_dump method to resolve the file path by the use of another property (parent.rootDir)
So basically the process is
Create the new resource
Create a schema dump of that resource to use
Commit
Return the schema dump
So finally, the problem is: outputDump's #pre_dump actually alters ormObject, so that it is now a fully resolved path by the time db.session.commit() is called. My first instinct here was to create a deep copy of ormObject but that fails with
"Parent instance <MyModel at 0x7f31cdd44240> is not bound to a Session; lazy load operation of attribute 'parent' cannot proceed (Background on this error at: http://sqlalche.me/e/14/bhk3)"
It's not that this is a difficult thing to solve, but it seems to be difficult to solve elegantly with my current knowledge. I need the path to be relative for the database, and resolved otherwise.
My current solution is to tell the SomeOutputSchema to skip the #pre_dump in this case, then take the outputDump and then resolve the file paths just after the schema dump. But this feels really gross to me.
I would love to hear any ideas on this, as currently my code feels messy and I don't like the idea of just leaving it and pushing on.
Solved by using a #post_dump and using pass_original=True to get access to the original object
class SomeOutputSchema(ma.SQLAlchemyAutoSchema)
class Meta:
model = MyModel
#post_dump(pass_original=True)
def resolveFilePath(self, data, ormObject, many):
data['filePath'] = os.path.join(ormObject.parent.rootDir, ormObject.filePath)

How to pass optional keyword named group to views.py in Django?

I am working on a search filter of an ecommerce site.
Current Situation:
When user select each platform filter, the platform name will be appended to URL and display the filtered result.
My approach:
url.py
url(r'^search/(?P<product_slug>[0-9a-z-]+)$', CustomSearchView(), name='search_result_detail'),enter code here
url(r'^search/(?P<product_slug>[0-9a-z-]+)_(?P<platform_slug>[0-9a-z-]+)$', CustomSearchView(),
name='search_result_platform'),
url(r'^search/(?P<product_slug>[0-9a-z-]+)_(?P<platform_slug>[0-9a-z-]+)_(?P<platform_slug2>[0-9a-z-]+)$',
CustomSearchView(), name='search_result_platform2'),
url(r'^search/(?P<product_slug>[0-9a-z-]+)_(?P<platform_slug>[0-9a-z-]+)_'
r'(?P<platform_slug2>[0-9a-z-]+)_(?P<platform_slug3>[0-9a-z-]+)$',
CustomSearchView(), name='search_result_platform3'),
Main Question:
I didn't want to limit the filtering number. So if there are 20 platform filters, I need to create 20 URLs. Definitely it's not a smart way. Any other smart way to avoid creating a batches of URL?
views.py
def __call__(self, request, product_slug, platform_slug=None,platform_slug2=None,platform_slug3 = None
,platform_slug4 = None,platform_slug5 = None):
if platform_slug is None:
self.product_review_list = SearchResult.objects.filter(products__slug=product_slug)
else:
self.product_review_list = SearchResult.objects.filter(Q(products__slug=product_slug),
Q(platform__slug=platform_slug)|(Q(platform__slug=platform_slug2)|Q(platform__slug=platform_slug3)
|Q(platform__slug=platform_slug4)|Q(platform__slug=platform_slug5)))
As mentioned in the comments, this kind of filtering is a job for GET parameters, not paths.
The URL pattern should just be r'^search/$', and you call it via /search/?platform_slug=x&platform_slug=y&platform_slug=z. Then, in the view, you can just use __in to filter:
def custom_search_view(request):
product_review_list = SearchResult.objects.filter(products__slug__in=request.GET.getlist('platform_slug')
One other point. You must absolutely not use classes like this in your URLs and then set attributes on self. That is not threadsafe and will cause all sorts of problems. If you really need to use a class, subclass the generic View class and use it in the URLs as CustomSearchView.as_view(), then override get() or post().

Google App Engine base and subclass gets

I want to have a base class called MBUser that has some predefined properties, ones that I don't want to be changed. If the client wants to add properties to MBUser, it is advised that MBUser be subclassed, and any additional properties be put in there.
The API code won't know if the client actually subclasses MBUser or not, but it shouldn't matter. The thinking went that we could just get MBUser by id. So I expected this to work:
def test_CreateNSUser_FetchMBUser(self):
from nsuser import NSUser
id = create_unique_id()
user = NSUser(id = id)
user.put()
# changing MBUser.get.. to NSUser.get makes this test succeed
get_user = MBUser.get_by_id(id)
self.assertIsNotNone(get_user)
Here NSUser is a subclass of MBUser. The test fails.
Why can't I do this?
What's a work around?
Models are defined by their "kind", and a subclass is a different kind, even if it seems the same.
The point of subclassing is not to share values, but to share the "schema" you've created for a given "kind".
A kind map is created on base class ndb.Model (it seems like you're using ndb since you mentioned get_by_id) and each kind is looked up when you do queries like this.
For subclasses, the kind is just defined as the class name:
#classmethod
def _get_kind(cls):
return cls.__name__
I just discovered GAE has a solution for this. It's called the PolyModel:
https://developers.google.com/appengine/docs/python/ndb/polymodelclass

AppEngine elegant way to handle similar requests

I'm making a server that can let clients upload and download data of different models. Is there some elegant way handle the requests?
More precisely, I don't want to do something like this,
app = webapp.WSGIApplication([
('/my_upload_and_download_url/ModelA/(.*)', MyRequestHandlerForA),
('/my_upload_and_download_url/ModelB/(.*)', MyRequestHandlerForB),
('/my_upload_and_download_url/ModelC/(.*)', MyRequestHandlerForC),
])
run_wsgi_app(app)
since what I do inside the handler would all be the same. For example,
class MyRequestHandlerForX(webapp.RequestHandler):
def get(self, key=None):
# return the instance with the designated key
def post(self, key=None):
# create/get the model instance
# iterate through the property list of the instance and set the values
the only difference among the handlers is to create instance for different models. The urls are alike, and the handlers are almost the same.
I checked this post about redirect requests to other handlers, and I've also read some methods to create an instance by a class name; but I think neither of them is good.
Anyone has a good solution?
p.s. This is my first post here. If there is anything inappropriate please tell me, thanks.
How you do this depends largely on the details of your code in the request handler. You can do a fairly generic one like this:
class ModelHandler(webapp.RequestHandler):
def get(self, kind, key):
model = db.class_for_kind(kind)
instance = model.get(key)
# Do something with the instance - eg, print it out
def post(self, kind, key):
model = db.class_for_kind(kind)
instance = model.create_from_request(self.request)
application = webapp.WSGIApplication([
('/foo/([^/]+)/([^/]+)', ModelHandler),
])
def main():
run_wsgi_app(application)
if __name__ == '__main__':
main()
This assumes you define a 'create_from_request' class method on each model class; you probably don't want to do it exactly this way, as it tightly couples model definitions with the forms used to input them; instead, you probably want to store a mapping of kind name or class to handler function, or do your forms and creation entirely automatically by reflecting on the properties of the class. Since you haven't specified what it is about doing this you're unsure about, it's hard to be more specific.
Also note the inclusion of a main() and other boilerplate above; while it will work the way you've pasted it, adding a main is substantially more efficient, as it allows the App Engine runtime to avoid having to evaluate your module on every request.
In your case I'd probably just have everything hit the same url path, and put the specifics in the GET parameters, like /my_upload_and_download_url?model=modelA.
You can also use webapp2 (http://webapp-improved.appspot.com/guide/app.html) which has a bunch of url routing support.
You could parse out the url path and do a look up, like this:
import urlparse
model_lookup = {'ModelA':ModelA,'ModelB':ModelB, 'ModelC':ModelC}
class MyRequestHandler(webapp.RequestHandler):
def get(self):
url = urlparse.urlparse(self.request.uri)
path_model = url.path.replace('/my_upload_and_download_url/','')
model = model_lookup[path_model]
...
Which allows you to use the same class for each path:
app = webapp.WSGIApplication([
('/my_upload_and_download_url/ModelA/(.*)', MyRequestHandler),
('/my_upload_and_download_url/ModelB/(.*)', MyRequestHandler),
('/my_upload_and_download_url/ModelC/(.*)', MyRequestHandler),
])
run_wsgi_app(app)

problems using observer pattern in django

I'm working on a website where I sell products (one class Sale, one class Product). Whenever I sell a product, I want to save that action in a History table and I have decided to use the observer pattern to do this.
That is: my class Sales is the subject and the History class is the observer, whenever I call the save_sale() method of the Sales class I will notify the observers. (I've decided to use this pattern because later I'll also send an email, notify the admin, etc.)
This is my subject class (the Sales class extends from this)
class Subject:
_observers = []
def attach(self, observer):
if not observer in self._observers:
self._observers.append(observer)
def detach(self, observer):
try:
self._observers.remove(observer)
except ValueError:
pass
def notify(self,**kargs):
for observer in self._observers:
observer.update(self,**kargs)
on the view I do something like this
sale = Sale()
sale.user = request.user
sale.product = product
h = History() #here I create the observer
sale.attach(h) #here I add the observer to the subject class
sale.save_sale() #inside this class I will call the notify() method
This is the update method on History
def update(self,subject,**kargs):
self.action = "sale"
self.username = subject.user.username
self.total = subject.product.total
self.save(force_insert=True)
It works fine the first time, but when I try to make another sale, I get an error saying I can't insert into History because of a primary key constraint.
My guess is that when I call the view the second time, the first observer is still in the Subject class, and now I have two history observers listening to the Sales, but I'm not sure if that's the problem (gosh I miss the print_r from php).
What am I doing wrong? When do I have to "attach" the observer? Or is there a better way of doing this?
BTW: I'm using Django 1.1 and I don't have access to install any plugins.
This may not be an acceptable answer since it's more architecture related, but have you considered using signals to notify the system of the change? It seems that you are trying to do exactly what signals were designed to do. Django signals have the same end-result functionality as Observer patterns.
http://docs.djangoproject.com/en/1.1/topics/signals/
I think this is because _observers = [] acts like static shared field. So every instance of Subject changes the _observers instance and it has unwanted side effect.
Initialize this variable in constructor:
class Subject:
def __init__(self):
self._observers = []
#Andrew Sledge's answer indicates a good way of tackling this problem. I would like to suggest an alternate approach.
I had a similar problem and started out using signals. They worked well but I found that my unit tests had become slower as the signals were called each time I loaded an instance of the associated class using a fixture. This added tens of seconds to the test run. There is a work around but I found it clumsy. I defined a custom test runner and disconnected my functions from the signals before loading fixtures. I reconnected them afterwards.
Finally I decided to ditch signals altogether and overrode the appropriate save() methods of models instead. In my case whenever an Order is changed a row is automatically created in and OrderHistory table, among other things. In order to do this I added a function to create an instance of OrderHistory and called it from within the Order.save() method. this also made it possible to test the save() and the function separately.
Take a look at this SO question. It has a discussion about when to override save() versus when to use signals.
Thank you all for your answers, reading about signals gave me another perspective but i dont want to use them because of learning purposes (i wanted to use the observer pattern in web development :P) In the end, i solved doing something like this:
class Sales(models.Model,Subject):
...
def __init__(self):
self._observers = [] #reset observers
self.attach(History()) #attach a History Observer
...
def save(self):
super(Sales,self).save()
self.notify() # notify all observers
now every time i call the save(), the observers will be notified and if i need it, i could add or delete an observer
what do you think? is this a good way to solve it?

Categories

Resources