| Total Complexity | 40 |
| Total Lines | 142 |
| Duplicated Lines | 0 % |
Complex classes like MongoDBAccess often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
| 1 | # Licensed to the StackStorm, Inc ('StackStorm') under one or more |
||
| 137 | class MongoDBAccess(object): |
||
| 138 | """Database object access class that provides general functions for a model type.""" |
||
| 139 | |||
| 140 | def __init__(self, model): |
||
| 141 | self.model = model |
||
| 142 | |||
| 143 | def get_by_name(self, value): |
||
| 144 | return self.get(name=value, raise_exception=True) |
||
| 145 | |||
| 146 | def get_by_id(self, value): |
||
| 147 | return self.get(id=value, raise_exception=True) |
||
| 148 | |||
| 149 | def get_by_ref(self, value): |
||
| 150 | return self.get(ref=value, raise_exception=True) |
||
| 151 | |||
| 152 | def get_by_pack(self, value): |
||
| 153 | return self.get(pack=value, raise_exception=True) |
||
| 154 | |||
| 155 | def get(self, exclude_fields=None, *args, **kwargs): |
||
| 156 | raise_exception = kwargs.pop('raise_exception', False) |
||
| 157 | |||
| 158 | instances = self.model.objects(**kwargs) |
||
| 159 | |||
| 160 | if exclude_fields: |
||
| 161 | instances = instances.exclude(*exclude_fields) |
||
| 162 | |||
| 163 | instance = instances[0] if instances else None |
||
| 164 | log_query_and_profile_data_for_queryset(queryset=instances) |
||
| 165 | |||
| 166 | if not instance and raise_exception: |
||
| 167 | raise ValueError('Unable to find the %s instance. %s' % (self.model.__name__, kwargs)) |
||
| 168 | return instance |
||
| 169 | |||
| 170 | def get_all(self, *args, **kwargs): |
||
| 171 | return self.query(*args, **kwargs) |
||
| 172 | |||
| 173 | def count(self, *args, **kwargs): |
||
| 174 | result = self.model.objects(**kwargs).count() |
||
| 175 | log_query_and_profile_data_for_queryset(queryset=result) |
||
| 176 | return result |
||
| 177 | |||
| 178 | def query(self, offset=0, limit=None, order_by=None, exclude_fields=None, |
||
| 179 | **filters): |
||
| 180 | order_by = order_by or [] |
||
| 181 | exclude_fields = exclude_fields or [] |
||
| 182 | eop = offset + int(limit) if limit else None |
||
| 183 | |||
| 184 | # Process the filters |
||
| 185 | # Note: Both of those functions manipulate "filters" variable so the order in which they |
||
| 186 | # are called matters |
||
| 187 | filters, order_by = self._process_datetime_range_filters(filters=filters, order_by=order_by) |
||
| 188 | filters = self._process_null_filters(filters=filters) |
||
| 189 | |||
| 190 | result = self.model.objects(**filters) |
||
| 191 | |||
| 192 | if exclude_fields: |
||
| 193 | result = result.exclude(*exclude_fields) |
||
| 194 | |||
| 195 | result = result.order_by(*order_by) |
||
| 196 | result = result[offset:eop] |
||
| 197 | log_query_and_profile_data_for_queryset(queryset=result) |
||
| 198 | |||
| 199 | return result |
||
| 200 | |||
| 201 | def distinct(self, *args, **kwargs): |
||
| 202 | field = kwargs.pop('field') |
||
| 203 | result = self.model.objects(**kwargs).distinct(field) |
||
| 204 | log_query_and_profile_data_for_queryset(queryset=result) |
||
| 205 | return result |
||
| 206 | |||
| 207 | def aggregate(self, *args, **kwargs): |
||
| 208 | return self.model.objects(**kwargs)._collection.aggregate(*args, **kwargs) |
||
| 209 | |||
| 210 | def insert(self, instance): |
||
| 211 | instance = self.model.objects.insert(instance) |
||
| 212 | return self._undo_dict_field_escape(instance) |
||
| 213 | |||
| 214 | def add_or_update(self, instance): |
||
| 215 | instance.save() |
||
| 216 | return self._undo_dict_field_escape(instance) |
||
| 217 | |||
| 218 | def update(self, instance, **kwargs): |
||
| 219 | return instance.update(**kwargs) |
||
| 220 | |||
| 221 | def delete(self, instance): |
||
| 222 | return instance.delete() |
||
| 223 | |||
| 224 | def delete_by_query(self, **query): |
||
| 225 | qs = self.model.objects.filter(**query) |
||
| 226 | qs.delete() |
||
| 227 | log_query_and_profile_data_for_queryset(queryset=qs) |
||
| 228 | # mongoengine does not return anything useful so cannot return anything meaningful. |
||
| 229 | return None |
||
| 230 | |||
| 231 | def _undo_dict_field_escape(self, instance): |
||
| 232 | for attr, field in instance._fields.iteritems(): |
||
| 233 | if isinstance(field, stormbase.EscapedDictField): |
||
| 234 | value = getattr(instance, attr) |
||
| 235 | setattr(instance, attr, field.to_python(value)) |
||
| 236 | return instance |
||
| 237 | |||
| 238 | def _process_null_filters(self, filters): |
||
| 239 | result = copy.deepcopy(filters) |
||
| 240 | |||
| 241 | null_filters = {k: v for k, v in six.iteritems(filters) |
||
| 242 | if v is None or (type(v) in [str, unicode] and str(v.lower()) == 'null')} |
||
| 243 | |||
| 244 | for key in null_filters.keys(): |
||
| 245 | result['%s__exists' % (key)] = False |
||
| 246 | del result[key] |
||
| 247 | |||
| 248 | return result |
||
| 249 | |||
| 250 | def _process_datetime_range_filters(self, filters, order_by=None): |
||
| 251 | ranges = {k: v for k, v in filters.iteritems() |
||
| 252 | if type(v) in [str, unicode] and '..' in v} |
||
| 253 | |||
| 254 | order_by_list = copy.deepcopy(order_by) if order_by else [] |
||
| 255 | for k, v in ranges.iteritems(): |
||
| 256 | values = v.split('..') |
||
| 257 | dt1 = isotime.parse(values[0]) |
||
| 258 | dt2 = isotime.parse(values[1]) |
||
| 259 | |||
| 260 | k__gte = '%s__gte' % k |
||
| 261 | k__lte = '%s__lte' % k |
||
| 262 | if dt1 < dt2: |
||
| 263 | query = {k__gte: dt1, k__lte: dt2} |
||
| 264 | sort_key, reverse_sort_key = k, '-' + k |
||
| 265 | else: |
||
| 266 | query = {k__gte: dt2, k__lte: dt1} |
||
| 267 | sort_key, reverse_sort_key = '-' + k, k |
||
| 268 | del filters[k] |
||
| 269 | filters.update(query) |
||
| 270 | |||
| 271 | if reverse_sort_key in order_by_list: |
||
| 272 | idx = order_by_list.index(reverse_sort_key) |
||
| 273 | order_by_list.pop(idx) |
||
| 274 | order_by_list.insert(idx, sort_key) |
||
| 275 | elif sort_key not in order_by_list: |
||
| 276 | order_by_list = [sort_key] + order_by_list |
||
| 277 | |||
| 278 | return filters, order_by_list |
||
| 279 |