Last active
December 22, 2015 15:29
-
-
Save xfenix/6492944 to your computer and use it in GitHub Desktop.
Outputs tree resource (add /tree to resource url), cached
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class ExtjsTreeResource(ExtJsModelResource): | |
max_limit = 1000 | |
cache_group = '' | |
cache_ttl = 3600 | |
cache_url_params = ['offset', 'page', 'limit', 'node'] | |
tree_parent_key = '' | |
tree_childs_key = 'childs' | |
tree_id_key = 'id' | |
def prepend_urls(self): | |
return [ | |
url( | |
r"^(?P<resource_name>%s)/tree%s$" % ( | |
self._meta.resource_name, | |
trailing_slash() | |
), | |
self.wrap_view('get_tree'), | |
name='api_get_tree' | |
), | |
] | |
def build_prepare(self): | |
self.tree_items = {} | |
self.tree_parents = {} | |
self.tree_roots = {} | |
def build_tree_wrap(self, items, request): | |
""" | |
Wrapper for build tree | |
This is necessary method, since we do not | |
know which of the items is "root" (because offset and etc), | |
and which leaves are lost | |
""" | |
tree = [] | |
tree_ids = [] | |
for item in items: | |
pk = item.pk | |
tree_ids.append(pk) | |
self.tree_items[pk] = item | |
self.tree_parents[pk] = self.get_parent(item) | |
# check potential roots | |
for key, item in self.tree_items.items(): | |
parent = self.tree_parents[key] | |
if parent is None or parent not in tree_ids: | |
self.tree_roots[item.pk] = item | |
# build tree branches | |
for item in self.tree_roots.values(): | |
tree.append( | |
self.build_tree_item(item, request) | |
) | |
return tree | |
def build_tree(self, request, root): | |
""" | |
Recursive tree builder | |
""" | |
tree = [] | |
for item in self.tree_items.values(): | |
if self.tree_parents[item.pk] == root: | |
tree.append(self.build_tree_item(item, request)) | |
return tree | |
def build_tree_item(self, item, request): | |
""" | |
Build current tree item with children | |
""" | |
preprocessed = self.full_dehydrate( | |
self.build_bundle( | |
obj=item, | |
request=request | |
), | |
for_list=True | |
) | |
processed = preprocessed.data | |
try: | |
del self.tree_items[item.pk] | |
del self.tree_roots[item.pk] | |
except: | |
pass | |
processed[self.tree_childs_key] = self.build_tree(request, item.pk) | |
processed['leaf'] = False if processed[self.tree_childs_key] else True | |
return processed | |
def build_filters(self, filters=None): | |
if 'id' in filters: | |
if filters[self.tree_id_key] == 'root': | |
del filters[self.tree_id_key] | |
else: | |
tmp = filters[self.tree_id_key] | |
try: | |
tmp = tmp.split('/')[-2] | |
except: | |
try: | |
tmp = int(tmp) | |
except: | |
tmp = 0 | |
filters[self.tree_id_key] = tmp | |
build = super(ExtjsTreeResource, self).build_filters(filters) | |
return build | |
def apply_filters(self, request, applicable_filters): | |
key = '%s__exact' % self.tree_id_key | |
branch = None | |
if key in applicable_filters: | |
now = int(applicable_filters[key]) | |
del applicable_filters[key] | |
tree = [item for item in self._meta.queryset.all()] | |
branch = self.get_branch(now, tree) | |
# branch = self.get_branch(now) | |
qset = super(ExtjsTreeResource, self).apply_filters( | |
request, applicable_filters | |
) | |
return qset.filter(pk__in=branch) if branch else qset | |
def get_parent(self, item): | |
return getattr( | |
getattr(item, self.tree_parent_key, None), 'pk', None | |
) | |
def get_branch(self, pk, tree): | |
""" | |
Calculate all children of item with | |
primary key = pk, and return full list of | |
their primary keys | |
""" | |
branch = [] | |
for item in tree: | |
if self.get_parent(item) == pk: | |
branch.append(item.pk) | |
branch = branch + self.get_branch(item.pk, tree) | |
return branch | |
def get_cache_key(self, request): | |
parts = [] | |
params = self.cache_url_params + [self.tree_id_key] | |
for param in params: | |
if param in request.GET: | |
parts.append(param + '_' + str(request.GET[param])) | |
return ''.join([self.cache_group, str(self._meta.max_limit)] + parts) | |
def get_tree(self, request, **kwargs): | |
""" | |
Main method | |
""" | |
# proxy POST requests (creation) | |
if request.META['REQUEST_METHOD'] == 'POST': | |
return self.post_list(request, **kwargs) | |
# check cache, if exists, return it back | |
cache = CacheGroup(self.cache_group, self.cache_ttl) | |
ckey = self.get_cache_key(request) | |
cached_response = cache.get(ckey) | |
if cached_response: | |
return cached_response | |
""" | |
This part of code actually copy of tastypie's get_list | |
You can find this method here, on line 1254 (actual at 12.09.13): | |
https://github.com/toastdriven/django-tastypie/blob/master/tastypie/resources.py#L1254 | |
""" | |
self.method_check(request, allowed=['get']) | |
# self.is_authenticated(request) | |
self.throttle_check(request) | |
self.build_prepare() | |
self._meta.max_limit = self.max_limit | |
cname = self._meta.collection_name | |
paginator = self._meta.paginator_class( | |
request.GET, | |
self.apply_sorting( | |
self.obj_get_list( | |
bundle=self.build_bundle(request=request), | |
**self.remove_api_resource_names(kwargs) | |
), | |
options=request.GET | |
), | |
resource_uri=self.get_resource_uri(), | |
limit=self._meta.max_limit, | |
max_limit=self._meta.max_limit, | |
collection_name=cname | |
) | |
serialize = paginator.page() | |
serialize[cname] = self.build_tree_wrap(serialize[cname], request) | |
serialize = self.alter_list_data_to_serialize(request, serialize) | |
self.log_throttled_access(request) | |
response = self.create_response(request, serialize) | |
cache.set(ckey, response) | |
return response | |
def wrap_obj_invalidate(self, obj, bundle, **kwargs): | |
CacheGroup.invalidate(self.cache_group) | |
method = getattr(super(ExtjsTreeResource, self), 'obj_' + obj) | |
return method(bundle, **kwargs) | |
def obj_create(self, bundle, **kwargs): | |
return self.wrap_obj_invalidate('create', bundle, **kwargs) | |
def obj_update(self, bundle, **kwargs): | |
return self.wrap_obj_invalidate('update', bundle, **kwargs) | |
def obj_delete(self, bundle, **kwargs): | |
return self.wrap_obj_invalidate('delete', bundle, **kwargs) | |
# db version of get branch | |
# def get_branch(self, id): | |
# q = self._meta.queryset.filter(**{self.tree_parent_key: id}) | |
# branch = [] | |
# if q: | |
# for item in q: | |
# branch.append(item.pk) | |
# branch = branch + self.get_branch(item.pk) | |
# return branch | |
# else: | |
# return [] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment