Exemplo n.º 1
0
   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 ''
Exemplo n.º 2
0
def model_from_name(name):
    model = gettag.from_name(name)
    if not model:
        raise TemplateSyntaxError, "Invalid model provided: %s" % name
    return model