Coverage for lino/mixins/polymorphic.py : 43%

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
# -*- coding: UTF-8 -*- # Copyright 2009-2015 Luc Saffre # License: BSD (see file COPYING for details)
"""
"""Returns the MTI child in `self.child_model` if it exists, otherwise return None.
""" try: return self.child_model.objects.get(pk=obj.pk) except self.child_model.DoesNotExist: return None
assert len(ar.selected_rows) == 1 self.run_on_row(ar.selected_rows[0], ar)
if self.get_child(obj) is None: return False return super(DeleteChild, self).get_action_permission(ar, obj, state)
if ar is not None: pre_remove_child.send( sender=obj, request=ar.request, child=self.child_model) mti.delete_child(obj, self.child_model, ar) ar.set_response(refresh=True)
parent_link_field = self.child_model._meta.parents.get( obj.__class__, None) if parent_link_field is None: return False if self.get_child(obj) is not None: return False
return super(InsertChild, self).get_action_permission(ar, obj, state)
if ar is not None: pre_add_child.send( sender=obj, request=ar.request, child=self.child_model) mti.insert_child(obj, self.child_model, full_clean=True) ar.set_response(refresh=True)
"""Mixin for models that use Multiple Table Inheritance to implement polymorphism.
Subclassed e.g. by :class:`lino.modlib.contacts.models.Partner`: the recipient of an invoice can be a person, a company, a client, a job provider, an employee..., and a given partner can be both a person and an employee at the same time.
Note that not every usage of Multiple Table Inheritance means polymorphism. For example `lino_cosi.lib.ledger.Voucher` has a pointer to the journal which knows which specialization to use. A given voucher has always exactly one specialization.
.. attribute:: mti_navigator
A virtual field which defines buttons for switching between the different views.
Usage example in :doc:`/tutorials/mti/index` and :doc:`/tutorials/letsmti/index`.
"""
def on_analyze(cls, site):
# TODO: it would be nice to have them sorted in some # understandable way, but that's not trivial. First # attempts are not satisfying: # # def f(a, b): # level = 0 # if b in a.__bases__: # level -= a.__bases__.index(b) # if a in b.__bases__: # level += b.__bases__.index(a) # return level # models.sort(f)
"""Return the specified specialization or `None`.
For example if you have two models `Place(Model)` and `Restaurant(Place)` and a `Place` instance ``p`` which is *not* also a Restaurant, then `p.get_mti_child('restaurant')` will return `None`.
""" for a in args: try: return getattr(self, a) except ObjectDoesNotExist: pass #~ return self
"""Overrides :meth:`lino.core.model.Model.disable_delete`.
In case of polymorphy, the user can ask to delete any MTI instance of a polymorphic entity. Deleting one instance will delete all other instances as well.
Before deleting one polymorphic instance, we ask all other instances for their vetos.
Cascade-related objects are deleted independently of the instance that initiated deletion.
""" # ask all other forms of this for vetos: for m in self._mtinav_models: if m is not self.__class__: obj = mti.get_child(self, m) if obj is not None: ignore_models = set(self._mtinav_models) ignore_models.remove(m) msg = m._lino_ddh.disable_delete_on_object( obj, ignore_models) if msg is not None: return msg
# Now ask my own model, ignoring all other forms because they # have been asked. ignore_models = set(self._mtinav_models) ignore_models.remove(self.__class__) return self.__class__._lino_ddh.disable_delete_on_object( self, ignore_models=ignore_models)
"""""" elems = [] if ar is None: return elems sep = None for m in self._mtinav_models: item = None if self.__class__ is m: item = [E.b(str(m._meta.verbose_name))] else: obj = mti.get_child(self, m) if obj is None: # parent link field p = m._meta.parents.get(self.__class__, None) if p is not None: item = [str(m._meta.verbose_name)] k = InsertChild.name_prefix + m.__name__.lower() ba = ar.actor.get_action_by_name(k) if ba and ba.get_row_permission(ar, self, None): # btn = ar.row_action_button(self, ba, _("+")) btn = ar.row_action_button(self, ba, _(u"➕")) # Heavy Plus Sign U+2795 # btn = ar.row_action_button(self, ba, # icon_name='add') item += [" [", btn, "]"]
else: item = [ar.obj2html(obj, m._meta.verbose_name)] # no DeleteChild for my parents if self.__class__ in m.mro(): k = DeleteChild.name_prefix + m.__name__.lower() ba = ar.actor.get_action_by_name(k) if ba and ba.get_row_permission(ar, self, None): # btn = ar.row_action_button(self, ba, _("-")) btn = ar.row_action_button(self, ba, _(u"❌")) # Cross Mark U+274C item += [" [", btn, "]"]
if item is not None: if sep is None: sep = ', ' else: elems.append(sep) elems += item return elems
def mti_navigator(self, ar): buttons = self.get_mti_buttons(ar) return E.span(*buttons)
|