CommentClientError now has sane subclasses that are meaningfully distinct, and each subclass is handled appropriately. Errors raised by the requests library are no longer handled by turning them into CommentClientErrors, since there is no meaningful handling we can do, and this way we will get more visibility into why errors are occurring. Also, HTTP status codes from the comments service indicating client error are correctly passed through to the client.
132 lines
4.4 KiB
Python
132 lines
4.4 KiB
Python
from .utils import *
|
|
|
|
|
|
class Model(object):
|
|
|
|
accessible_fields = ['id']
|
|
updatable_fields = ['id']
|
|
initializable_fields = ['id']
|
|
base_url = None
|
|
default_retrieve_params = {}
|
|
|
|
DEFAULT_ACTIONS_WITH_ID = ['get', 'put', 'delete']
|
|
DEFAULT_ACTIONS_WITHOUT_ID = ['get_all', 'post']
|
|
DEFAULT_ACTIONS = DEFAULT_ACTIONS_WITH_ID + DEFAULT_ACTIONS_WITHOUT_ID
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
self.attributes = extract(kwargs, self.accessible_fields)
|
|
self.retrieved = False
|
|
|
|
def __getattr__(self, name):
|
|
if name == 'id':
|
|
return self.attributes.get('id', None)
|
|
try:
|
|
return self.attributes[name]
|
|
except KeyError:
|
|
if self.retrieved or self.id is None:
|
|
raise AttributeError("Field {0} does not exist".format(name))
|
|
self.retrieve()
|
|
return self.__getattr__(name)
|
|
|
|
def __setattr__(self, name, value):
|
|
if name == 'attributes' or name not in self.accessible_fields:
|
|
super(Model, self).__setattr__(name, value)
|
|
else:
|
|
self.attributes[name] = value
|
|
|
|
def __getitem__(self, key):
|
|
if key not in self.accessible_fields:
|
|
raise KeyError("Field {0} does not exist".format(key))
|
|
return self.attributes.get(key)
|
|
|
|
def __setitem__(self, key, value):
|
|
if key not in self.accessible_fields:
|
|
raise KeyError("Field {0} does not exist".format(key))
|
|
self.attributes.__setitem__(key, value)
|
|
|
|
def items(self, *args, **kwargs):
|
|
return self.attributes.items(*args, **kwargs)
|
|
|
|
def get(self, *args, **kwargs):
|
|
return self.attributes.get(*args, **kwargs)
|
|
|
|
def to_dict(self):
|
|
self.retrieve()
|
|
return self.attributes
|
|
|
|
def retrieve(self, *args, **kwargs):
|
|
if not self.retrieved:
|
|
self._retrieve(*args, **kwargs)
|
|
self.retrieved = True
|
|
return self
|
|
|
|
def _retrieve(self, *args, **kwargs):
|
|
url = self.url(action='get', params=self.attributes)
|
|
response = perform_request('get', url, self.default_retrieve_params)
|
|
self.update_attributes(**response)
|
|
|
|
@classmethod
|
|
def find(cls, id):
|
|
return cls(id=id)
|
|
|
|
def update_attributes(self, *args, **kwargs):
|
|
for k, v in kwargs.items():
|
|
if k in self.accessible_fields:
|
|
self.__setattr__(k, v)
|
|
else:
|
|
raise AttributeError("Field {0} does not exist".format(k))
|
|
|
|
def updatable_attributes(self):
|
|
return extract(self.attributes, self.updatable_fields)
|
|
|
|
def initializable_attributes(self):
|
|
return extract(self.attributes, self.initializable_fields)
|
|
|
|
@classmethod
|
|
def before_save(cls, instance):
|
|
pass
|
|
|
|
@classmethod
|
|
def after_save(cls, instance):
|
|
pass
|
|
|
|
def save(self):
|
|
self.before_save(self)
|
|
if self.id: # if we have id already, treat this as an update
|
|
url = self.url(action='put', params=self.attributes)
|
|
response = perform_request('put', url, self.updatable_attributes())
|
|
else: # otherwise, treat this as an insert
|
|
url = self.url(action='post', params=self.attributes)
|
|
response = perform_request('post', url, self.initializable_attributes())
|
|
self.retrieved = True
|
|
self.update_attributes(**response)
|
|
self.after_save(self)
|
|
|
|
def delete(self):
|
|
url = self.url(action='delete', params=self.attributes)
|
|
response = perform_request('delete', url)
|
|
self.retrieved = True
|
|
self.update_attributes(**response)
|
|
|
|
@classmethod
|
|
def url_with_id(cls, params={}):
|
|
return cls.base_url + '/' + str(params['id'])
|
|
|
|
@classmethod
|
|
def url_without_id(cls, params={}):
|
|
return cls.base_url
|
|
|
|
@classmethod
|
|
def url(cls, action, params={}):
|
|
if cls.base_url is None:
|
|
raise CommentClientRequestError("Must provide base_url when using default url function")
|
|
if action not in cls.DEFAULT_ACTIONS:
|
|
raise ValueError("Invalid action {0}. The supported action must be in {1}".format(action, str(cls.DEFAULT_ACTIONS)))
|
|
elif action in cls.DEFAULT_ACTIONS_WITH_ID:
|
|
try:
|
|
return cls.url_with_id(params)
|
|
except KeyError:
|
|
raise CommentClientRequestError("Cannot perform action {0} without id".format(action))
|
|
else: # action must be in DEFAULT_ACTIONS_WITHOUT_ID now
|
|
return cls.url_without_id()
|