def render(self, context): # determine desired object type try: model = model_from_name(self.params['app_model']) except TemplateSyntaxError: model = model_from_name(Variable(self.params['app_model']).resolve(context)) model_name = model.__name__.lower() # Base queryset to filter on manager = model.live if hasattr(model, 'live') and isinstance(model.live, Manager) else model.objects opts = gettag.from_model(model) if opts.get('is_dynamic', False) and model_name not in opts.get('non_dynamic_names', ''): dType = DynamicType.objects.get(pk=gettag.from_model(model).get('dynamic_map').get(model_name, None)) q = manager.filter(dynamic_type=dType) else: q = manager.all() # flag to mark if a value was set before reaching the end of the render # function because of a special case valueSet = False # handle with clause if 'with_field' in self.params: field = self.params['with_field'] # allow custom function to handle with clause for this model type custom_with = gettag.from_model(model).get('with', None) if callable(custom_with): cfield = Variable(self.params['with_field']).resolve(context) if 'with_value' in self.params: cvalue = Variable(self.params['with_value']).resolve(context) result = custom_with(cfield, cvalue) else: result = custom_with(cfield) if isinstance(result, QuerySet): # if the function returned a queryset, then we can continue on q = result else: # otherwise just return the value context[self.varname] = result return '' else: if not field in model._meta.get_all_field_names(): raise TemplateSyntaxError, "get tag: field '%s' does not exist on model %s" % (field, model.__name__) elif 'with_value' in self.params and isinstance(model._meta.get_field(field).rel, ManyToManyRel): raise TemplateSyntaxError, "get tag: cannot match a value to m2m field '%s' on model %s" % (field, model.__name__) filter_params = {} if 'with_value' in self.params: # can only resolve a variable value or a quoted string, # but users could put an unquoted string/non-variable, # so we catch that exception and move on try: value = Variable(self.params['with_value']).resolve(context) except VariableDoesNotExist: value = self.params['with_value'] if value == 'None': value = None filter_params[str(field)] = value else: # only return results that have matching related objects # pk > 0 is a hack, as annotations/aggregations are not fast enough # (difficult to index properly + complicated joins) filter_params[str("%s__pk__gt" % field)] = 0 q = q.filter(**filter_params) # handle 'in' clause(s) if 'in' in self.params: stack = [model] filter_params = {} exclude_params = {} for clause in self.params['in']: # if the provided type isn't in the map, try to resolve it as a variable if gettag.from_name(clause['type']): ctype = clause['type'] else: try: ctype = Variable(clause['type']).resolve(context) if isinstance(ctype, int): ctype = ContentType.objects.get(id=ctype_id)['name'] except VariableDoesNotExist: ctype = clause['type'] type_model = model_from_name(ctype) stack.append(type_model) # can only resolve a variable value or a quoted string, # but users could put an unquoted string/non-variable, # so we catch that exception and move on try: value = Variable(clause['obj']).resolve(context) value_in_context = True except VariableDoesNotExist: value = clause['obj'] value_in_context = False # allow for a custom function to handle this clause for this model type custom_in = gettag.from_model(gettag.from_name(ctype)).get('in', None) if callable(custom_in): result = custom_in(q, value) if isinstance(result, QuerySet): q = result continue else: context[self.varname] = result return '' # try to find the related field related_field = '' is_virtual = False for m in reversed(stack[:-1]): rf = model_relationship_field(m, type_model) if rf: if related_field: related_field = '%s__%s' % (rf, related_field) else: related_field = rf type_model = m # track relationship backwards # if we didn't find a related field... if not related_field and len(model._meta.virtual_fields) == 0: raise TemplateSyntaxError, "get tag: invalid parameter in 'in %s %s' clause" % (ctype, clause['obj']) elif len(model._meta.virtual_fields) > 0: is_virtual = True filter_params[model._meta.virtual_fields[0].ct_field] = ContentType.objects.get_for_model(type_model).id related_field = model._meta.virtual_fields[0].fk_field # if this is a list of only one value (e.g., from a previous limit 1 call), # then we want the object itself # we split 'obj' clause in case the passed value is a member of the context variable if value_in_context and isinstance(value, list) and len(value) == 1: value = value[0] # if this type has a callable name field, apply it field_name = gettag.from_model(gettag.from_name(ctype)).get('name') if callable(field_name) and not isinstance(value, type_model): value = field_name(value) # if this type has a filter set, apply it filter_func = gettag.from_model(gettag.from_name(ctype)).get('filter', None) if filter_func: f = filter_func(value) related_field = '%s__%s' % (related_field, f[0]) value = f[1] # otherwise, if there is not filter and it wasn't in the context, # then we need to suffix the appropriate field name elif clause['obj'] not in context: related_field = '%s__%s' % (related_field, field_name) if not is_virtual: if clause['exclude']: exclude_params[related_field] = value else: filter_params[related_field] = value else: if clause['exclude']: exclude_params[related_field] = value else: filter_params[related_field] = str(value.pk) q = q.exclude(**exclude_params) q = q.filter(**filter_params) # handle 'from last' clause if 'from_side' in self.params and 'from_timeperiod' in self.params: latest_field = model._meta.get_latest_by if not latest_field: raise TemplateSyntaxError, "get tag: %s does not support 'from last' clause" % model_name latest_field = latest_field.strip('-') # remove a minus sign if necessary if self.params['from_side'] == 'last': start = date.today() - timedelta(**{self.params['from_timeperiod']: int(self.params['from_x']) }) end = date.today() + timedelta(days=1) elif self.params['from_side'] == 'next': start = date.today() end = date.today() + timedelta(**{self.params['from_timeperiod']: int(self.params['from_x']) }) else: # == 'week' if self.params['from_timeperiod'] == 'today': day = date.today() else: day = resolve_param(self.params['from_timeperiod'], context) if not (isinstance(day, datetime) or isinstance(day, date)): day = resolve_param(self.params['from_timeperiod'], context, func=datetime) offset = datetime.isoweekday(day) % 7 #sunday is first day of week start = day + timedelta(-offset) end = day + timedelta(7-offset-1) p = { '%s__range' % latest_field: (start, end)} q = q.filter(**p) # handle 'order by' clause if 'order_by' in self.params: order_by = resolve_param(self.params['order_by'],context, str) q = q.order_by(order_by) # handle 'limit' clause if 'limit' in self.params: limit = resolve_param(self.params['limit'], context, int) # handle optional 'offset' clause if 'offset' in self.params: offset = resolve_param(self.params['offset'], context, int) q = q[offset:offset+limit] else: q = q[:limit] # if only one object was requested, then don't return a list if limit == 1: context[self.varname] = q[0] valueSet = True # if the value wasn't set previously in this function if not valueSet: context[self.varname] = q return ''
def model_from_name(name): model = gettag.from_name(name) if not model: raise TemplateSyntaxError, "Invalid model provided: %s" % name return model