Coverage for lino/core/dbtables.py : 65%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# Copyright 2009-2015 Luc Saffre # License: BSD (see file COPYING for details)
This defines the :class:`Table` class. """ # import six # str = six.text_type
else: ContentType = None
#~ if cl is Table or len(cl.__bases__) == 0: #~ return #~ myattrs = set(cl.__dict__.keys()) for b in cl.__bases__: for k in base_attrs(b): yield k for k in list(b.__dict__.keys()): yield k
if not isinstance(qs, QuerySet): # TODO: filter also simple lists return qs return qs.filter(quick_search_filter(qs.model, search_text))
#~ logger.info("20130425 quick_search_filter(%s,%r)",model,search_text) q = models.Q() for fn in model.quick_search_fields: kw = {prefix + fn + "__icontains": search_text} q = q | models.Q(**kw)
if search_text.isdigit(): for field in model._meta.fields: if isinstance(field, (models.IntegerField, models.AutoField)): kw = {prefix + field.name: int(search_text)} q = q | models.Q(**kw) return q
""" Converts a `filter` request in the format used by :extux:`Ext.ux.grid.GridFilters` into a `Django field lookup <http://docs.djangoproject.com/en/1.2/ref/models/querysets/#field-lookups>`_ on a :class:`django.db.models.query.QuerySet`.
:param qs: the queryset to be modified. :param gridfilters: a list of dictionaries, each having 3 keys `field`, `type` and `value`.
""" if not isinstance(qs, QuerySet): raise NotImplementedError('TODO: filter also simple lists') q = models.Q() print(20150506, gridfilters) for flt in gridfilters: field = get_field(qs.model, flt['field']) flttype = flt['type'] kw = {} if flttype == 'string': if isinstance(field, models.CharField): kw[field.name + "__icontains"] = flt['value'] q = q & models.Q(**kw) elif isinstance(field, models.ForeignKey): q = q & quick_search_filter( field.rel.model, flt['value'], field.name + "__") #~ rq = models.Q() #~ search_field = field.rel.model.grid_search_field #~ for search_field in field.rel.model.quick_search_fields: #~ search_field = getattr(field.rel.model,'grid_search_field',None) #~ if search_field is not None: #~ rq = rq | models.Q(**{field.name+"__%s__icontains" % search_field : flt['value']}) #~ q = q & rq else: raise NotImplementedError(repr(flt)) elif flttype == 'numeric': cmp = str(flt['comparison']) if cmp == 'eq': cmp = 'exact' kw[field.name + "__" + cmp] = flt['value'] q = q & models.Q(**kw) elif flttype == 'boolean': kw[field.name] = flt['value'] # kw[field.name + "__equals"] = flt['value'] q = q & models.Q(**kw) elif flttype == 'date': v = datetime.date(*settings.SITE.parse_date(flt['value'])) #~ v = parse_js_date(flt['value'],field.name) cmp = str(flt['comparison']) if cmp == 'eq': cmp = 'exact' kw[field.name + "__" + cmp] = v q = q & models.Q(**kw) #~ print kw else: raise NotImplementedError(repr(flt)) return qs.filter(q)
return rptclass.app_label + '.' + rptclass.__name__
#~ def de_verbose_name(de): #~ if isinstance(de,models.Field): #~ return de.verbose_name #~ return de.name
# TODO : move these global variables to a better place #~ rptname_choices = []
#~ config_dirs = []
return False return False
#~ logger.debug("20120103 register_report %s", rpt.actor_id)
#~ logger.debug("20111113 %s is an abstract report", rpt)
#~ for name,v in rpt.__dict__.items(): #~ for name in rpt.__class__.__dict__.keys(): #~ for name in dir(rpt): #~ v = getattr(rpt,name) #~ if isinstance(v,Group): #~ v.name = name #~ v.add_to_table(rpt) #~ rpt.custom_groups = rpt.custom_groups + [v] #~ if isinstance(v,ComputedColumn): #~ v.name = name #~ v.add_to_table(rpt) #~ d = dict() #~ d.update(rpt.computed_columns) #~ d[name] = v #~ rpt.computed_columns = d
#~ if rpt.model._meta.abstract:
#~ rptname_choices.append((rpt.actor_id, rpt.get_label())) #~ rptname_choices.append(rpt.actor_id)
#~ logger.debug("20120102 register %s : master report", rpt.actor_id) #~ logger.debug("register %s : generic slave for %r", rpt.actor_id, rpt.master_key) else: #~ logger.debug("20120102 register %s : slave for %r", rpt.actor_id, rpt.master_key)
"""This is being called at startup.
- Each model can receive a number of "slaves". Slaves are tables whose data depends on an instance of another model (their master).
- For each model we want to find out the "default table". The "choices table" for a foreignkey field is also currently simply the pointed model's default table. :modattr:`_lino_default_table`
"""
#~ logger.debug("20111113 Register Table actors...")
# Not getattr but __dict__.get because of the mixins.Listings # trick. #~ rpt = getattr(model,'_lino_default_table',None) #~ logger.debug('20111113 %s._lino_default_table = %s',model,rpt) raise Exception("table_factory() failed for %r." % model) #~ rpt.collect_actions()
raise Exception("20150216 unresolved master") # rpt.master = resolve_model(rpt.master) #~ logger.debug("20111113 %s: slave for %s",rpt.actor_id, rpt.master.__name__) #~ logger.debug("Assigned %d slave reports to their master.",len(slave_reports))
#~ logger.debug("reports.setup() done")
if isinstance(rr, TableRequest): return rr.actor.master_key == name return False
#~ def model2report(m): return m(*args)
"""An :class:`AbstractTable <lino.core.tables.AbstractTable>` that works on a Django Model using a QuerySet.
A Table inherits from :class:`AbstractTable <lino.core.tables.AbstractTable>` and adds attributes like :attr:`model` and :attr:`master` and :attr:`master_key` who are important because Lino handles relations automagically.
Another class of attributes are `filter`, `exclude` and `sort_order` which are thin wrappers to Django's query lookup parameters of same name.
See :class:`dd.Table`.
"""
"""The model on which this table iterates.
"""
""" The user profile(s) for which we want a screenshot of this table. """
""" Set this to `False` if this Table should *not* become the Model's default table.
"""
"""(No longer used; see :srcref:`docs/tickets/44`). Whether multi-line text fields in Grid views should be expanded in by default or not.
"""
""" Handler for uploaded files. Same remarks as for :attr:`lino.core.actors.Actor.disabled_fields`. """
def get_chooser_for_field(self, fieldname): return ch
kw.update(master_instance=master_instance)
def column_choices(self): return [de.name for de in self.wildcard_data_elems()]
def get_screenshot_requests(self, language): if self.model is None: return if self.model._meta.abstract: return if self is not self.model._lino_default_table: return
profiles2user = dict() for u in settings.SITE.user_model.objects.filter(language=language): if u.profile and u.profile.name in self.screenshot_profiles and not u.profile in profiles2user: profiles2user[u.profile] = u for user in list(profiles2user.values()): #~ if user.profile.name != 'admin': return #~ yield self.default_action.request(user=user) # and self.default_action is not self.detail_action: if self.detail_action: yield self.detail_action.request(user=user)
#~ @classmethod #~ def elem_filename_root(cls,elem): #~ return elem._meta.app_label + '.' + elem.__class__.__name__ + '-' + str(elem.pk) def get_detail_sets(self): """ Yield a list of (app_label,name) tuples for which the kernel should try to create a Detail Set. """ if self.model is not None: def yield_model_detail_sets(m): for b in m.__bases__: if issubclass(b, models.Model) and b is not models.Model: for ds in yield_model_detail_sets(b): yield ds yield m._meta.app_label + '/' + m.__name__
for ds in yield_model_detail_sets(self.model): yield ds
for s in super(Table, self).get_detail_sets(): yield s
#~ @classmethod #~ def find_field(cls,model,name): #~ for vf in cls.model._meta.virtual_fields: #~ if vf.name == name: #~ return vf #~ return cls.model._meta.get_field(name)
def get_widget_options(cls, name, **options):
def get_pk_field(self):
def get_row_by_pk(self, ar, pk): """Implements :meth:`get_row_by_pk <lino.core.actors.Actor.get_row_by_pk>` for a database table.
""" except ValueError: return None except self.model.DoesNotExist: return None
def disabled_actions(self, ar, obj): #~ u = ar.get_user() #~ if ba.action.show_in_bbar and not obj.get_row_permission(u,state,ba.action): #~ if a.show_in_bbar and not a.get_action_permission(ar.get_user(),obj,state): d[ba.action.action_name] = True #~ if ba.action.action_name == 'do_clear_cache': #~ logger.info("20121127 %s %s", obj, d) #~ if obj.__class__.__name__ == 'Note': #~ logger.info("20120920 %s %s %r", obj, d,obj.__class__.get_row_permission)
def wildcard_data_elems(self):
def is_valid_row(self, row): return isinstance(row, self.model)
def get_actor_label(self): #~ return self._label or self.__name__ return super(Table, self).get_actor_label()
def class_init(self):
self.model = None
fields.fields_list(self.model, self.hidden_columns))
fields.fields_list(self.model, self.active_fields))
# self.simple_parameters |= self.model.simple_parameters
else: pass #~ logger.info("%s disables model action '%s'",self,k) else: existing_value, actions.Action): raise Exception( "%s cannot install model action %s " "because name is already used " "for %r" % self, k, existing_value)
'handle_uploaded_files', #~ 'get_row_permission', #~ 'disable_editing', ): #~ logger.debug('20120731 Install model method %s from %r to %r',name,self.model,self) #~ 'dictproxy' object does not support item assignment: #~ self.__dict__[name] = model2actor(m)
# x = self.model._meta.get_field_by_name(self.master_key) # fk, remote, direct, m2m = x # assert direct # assert not m2m master_model = fk.choicelist.item_class else: raise Exception( "Unsupported master_key {0} ({1})".format( fk, fk.__class__)) fk = vf master_model = ContentType break msg = "No field '{0}' in {1}".format( self.master_key, self.model) raise Exception(INVALID_MK.format( self.master_key, self, msg)) else: msg = "Cannot handle master key {0}".format(df) msg += " (20150820 virtual fields: {0})".format( [vf.name for vf in self.model._meta.virtual_fields]) raise Exception(INVALID_MK.format( self.master_key, self, msg)) # raise Exception( # "%s : invalid master class for master_key " # "%r in %s" % ( # self, self.master_key, self.model)) else: # self.hidden_columns |= set([fk.name])
raise Exception("%s.order_by is %r (must be a list or tuple)" % (self, self.order_by)) # good idea, but doesn't yet work for foreign fields, # e.g. order_by = ['content_type__app_label'] for fieldname in self.order_by: if fieldname.startswith('-'): fieldname = fieldname[1:] try: fk, remote, direct, m2m = self.model._meta.get_field_by_name( fieldname) assert direct assert not m2m except models.FieldDoesNotExist: raise Exception("Unknown fieldname %r in %s.order_by" % (fieldname, self))
def do_setup(self):
#~ AbstractTable.do_setup(self)
else:
def is_abstract(self): or self.model is Model \ or self.model._meta.abstract: #~ logger.info('20120621 %s : no real table',h)
def disabled_fields(cls, obj, ar):
def get_row_permission(cls, obj, ar, state, ba): """Returns True if the given action is allowed for the given instance `obj` and the given user.
""" return True
def disable_delete(self, obj, ar): """ Return either `None` if the given `obj` *is allowed* to be deleted by action request `ar`, or a string with a message explaining why, if not. """ #~ logger.info("20130225 dbtables.disable_delete") return "No delete_action" #~ print "20130222 ar is %r" % ar #~ logger.info("20130225 dbtables.disable_delete no permission") return _("You have no permission to delete this row.")
def get_data_elem(self, name): """ Adds the possibility to specify :class:`remote fields <lino.core.fields.RemoteField>` in a layout template. """ #~ cc = AbstractTable.get_data_elem(self,name)
self.model, models.Model): raise Exception( "%s.model is %r (and not a Model subclass)" % (self, self.model))
# logger.info("20120202 Table.get_data_elem found nothing")
def get_queryset(self, ar): """ Return an iterable over the items processed by this table. Override this to use e.g. select_related() or to return a list.
Return a customized default queryset
Example::
def get_queryset(self): return self.model.objects.select_related('country', 'city')
"""
def get_request_queryset(self, rr): """Build a Queryset for the specified request on this table.
Upon first call, this will also lazily install Table.queryset which will be reused on every subsequent call.
The return value is othe of the following:
- a Django queryset - a list or tuple
- If you override this, you may turn this method into a generator. The only advantage of this is syntax, since the yeld objects will be stored in a tuple.
""" return self.model.objects.none() return self.model.objects.none() qs = qs.filter(**kw)
#~ qs = qs.exclude(rr.exclude)
v = getattr(rr.param_values, k) if v: qs = qs.filter(**{k: v})
qs = qs.filter(self.filter)
qs = qs.filter(rr.filter)
#~ logger.info("20120111 known values %r",rr.known_values) d[k + "__isnull"] = True else: #~ d[k+"__exact"] = v
# TODO: use Q object instead of dict
qs = add_quick_search_filter(qs, rr.quick_search) qs = add_gridfilters(qs, rr.gridfilters) qs = qs.extra(**extra) #~ logger.info("20120122 order_by %s",order_by) logger.info("%s %s", self.debug_sql, qs.query)
def create_instance(self, ar, **kw): """ Create a model instance using the specified keyword args, calling also :meth:`lino.core.model.Model.on_create`. """ #~ print 20120630, "Actor.create_instance", kw
#~ @classmethod #~ def ajax_update(self,request): #~ print request.POST #~ return HttpResponse("1", mimetype='text/x-json')
""" Automatically define a Table class for the specified model. This is used during kernel setup to create default tables for models who have no Table. """ #~ logger.info('table_factory(%s)',model.__name__) if issubclass(model, rpt.model): #~ if issubclass(rpt.model,model): bases = (rpt,) #~ bases = (rpt.__class__,) #~ logger.info('table_factory(%s) : bases is %s',model.__name__,bases)
rpt = actors.get_actor(rptname) return rpt.column_choices()
|