Skip to content

E. First step to develop a new module for Lucterios

Laurent GAY edited this page Aug 16, 2017 · 6 revisions

Prerequisite: To install Lucterios V2 and have an instance created.
In the reste of this tutorial, we call "dev" your working instance and we suppose you want to create the module "foobar"

Create new module

We are in the Django's world, so a Lucterios module is, in fact, a Django application using Lucterios capabilities
So it's very simple:

python manager_dev.py startapp foobar

Then, you need to add and update this in your foobar/__init__.py:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
__version__ = "my version number"
__title__ = "my title"

To add this new module in your "dev" instance, use this command:

lucterios_admin.py modif --name dev --module lucterios.contacts,foobar

note1: If you want that foobar either in a sub package (ex mypackage.foobar) will require manually moved after recreating package mypackage and its __init__.py

note2: To be assure the multi-language and translation, add this inclusion in your modules:

from django.utils.translation import ugettext_lazy as _

see: https://docs.djangoproject.com/fr/1.9/topics/i18n/translation/

note3: To be compatible to Python 2.x and Python 3.x, we recommend to add in top of each module:

# -*- coding: utf-8 -*-
from __future__ import unicode_literals

and to use the module django.utils.six to convert string value.
see: https://docs.djangoproject.com/en/1.9/topics/python3/

Create models

Models are mainly Django's models with some little differences.

First, in lucterios, you need to inherit of lucterios.framework.models.LucteriosModel and not Django's original inheritance

For example, studies the LegalEntity model of contacts:

class LegalEntity(AbstractContact):
    name = models.CharField(_('name'), max_length=100, blank=False)
    structure_type = models.ForeignKey('StructureType', verbose_name=_('structure type'), \
                                        null=True, on_delete=models.SET_NULL)
    identify_number = models.CharField(_('identify number'), max_length=100, blank=True)

    @classmethod
    def get_show_fields(cls):
        ident_field = ['name', 'structure_type']
        ident_field.extend(super(LegalEntity, cls).get_show_fields())
        ident_field.append('identify_number')
        res_fields = {_('001@Identity'):ident_field, _('002@Management'):['responsability_set']}
        return res_fields

    @classmethod
    def get_edit_fields(cls):
        res_fields = ['name', 'structure_type']
        res_fields.extend(super(LegalEntity, cls).get_edit_fields())
        res_fields.append('identify_number')
        return res_fields

    @classmethod
    def get_search_fields(cls):
        res_fields = ['name', 'structure_type']
        res_fields.extend(super(LegalEntity, cls).get_search_fields())
        res_fields.extend(['identify_number', 'responsability_set.individual.firstname', \
                    'responsability_set.individual.lastname', 'responsability_set.functions'])
        return res_fields

    @classmethod
    def get_default_fields(cls):
        return ["name", 'tel1', 'tel2', 'email']

    @classmethod
    def get_print_fields(cls):
        return ["name", 'structure_type', 'address', 'postal_code', 'city', 'country', 'tel1', \                   
                'tel2', 'email', 'comment', 'identify_number', 'OUR_DETAIL']

    def __str__(self):
        return self.name

    def can_delete(self):
        msg = AbstractContact.can_delete(self)
        if msg == '':
            if self.id == 1:  # pylint: disable=no-member
                msg = _("You cannot delete this legal entity!")
        return msg

    class Meta(object):
        # pylint: disable=no-init
        verbose_name = _('legal entity')
        verbose_name_plural = _('legal entities')

class Responsability(LucteriosModel):
    individual = models.ForeignKey(Individual, verbose_name=_('individual'), null=False)
    legal_entity = models.ForeignKey(LegalEntity, verbose_name=_('legal entity'), null=False)
    functions = models.ManyToManyField(Function, verbose_name=_('functions'), blank=True)
    ...

Then, to use default form generation, you need to add a few tables:

get_default_fields()
List of fields present in auto generated grid or list.
It's a list structure.
If not override, this method return list of Django field names.

get_show_fields()
List of fields present in auto generated show form.
Its a dico structure of list with each key is the label of a new tab in the screen. The key empty correspond to the header of screen without tab.
If it is a list, all field will be in header of screen without tab.
In the key, number before '@' sign will be remove: it's user to sort tab.
If you want include many-to-one field, add it '_set' in suffix to the class name lower of this field linked.
If not override, this method return the list returned by get_default_fields().

get_edit_fields()
List of fields present in auto generated edit form.
There are the same syntax that for get_show_fields method.
If not override, this method return the list returned by get_show_fields().

get_search_fields()
List of fields present in auto generated search form.
It's a list structure.
If not override, this method return the list returned by get_default_fields().

get_print_fields()
List of fields present in report editor for print list or label.
It's a list structure.
If not override, this method return the list returned by get_default_fields().

Create editors

Editors are optional module to customize default editors.

For example, studies the LegalEntity editor of contacts:

class LegalEntityEditor(AbstractContactEditor):

    def edit(self, xfer):
        if self.item.id == 1:  # pylint: disable=no-member
            xfer.remove_component('lbl_structure_type')
            xfer.remove_component('structure_type')
        return AbstractContactEditor.edit(self, xfer)

    def show(self, xfer):
        if self.item.id == 1:  # pylint: disable=no-member
            xfer.remove_component('lbl_structure_type')
            xfer.remove_component('structure_type')
        return AbstractContactEditor.show(self, xfer)

Some additional functions 'edit', 'show', 'can_delete', 'saving' may be added to customize specific behavior of these editor.

Create view

Here is one of the powerfull adds of Lucterios

You will have to add one class by action but, as long as you keep using default forms, you just need a few lines per class.
example:

@ActionsManage.affect('LegalEntity', 'show')
@MenuManage.describ('contacts.change_legalentity')
class LegalEntityShow(XferShowEditor):
    caption = _("Show legal entity")
    icon = "legalEntity.png"
    model = LegalEntity
    field_id = 'legal_entity'

@MenuManage.describ('contacts.change_individual', FORMTYPE_NOMODAL, 'contact.actions', _('To find a legal entity following a set of criteria.'))
class LegalEntitySearch(XferSearchEditor):
    caption = _("Legal entity search")
    icon = "legalEntityFind.png"
    model = LegalEntity
    field_id = 'legal_entity'

This class and it's decorators is sufficient to add or edit a custom field in an Individual of the contact module.

  • ActionsManage.affect: To assign the action to an accessible button to view other
    => generic tag used: add, modify, show, edit, del, print, listing, label
  • MenuManage.describ: To set needed right to access (class 'permission' of Django).
    Optional parameters using to define this action like menu item (not a menu item if not define):
    • modal: FORMTYPE_MODAL or FORMTYPE_NOMODAL
    • menu_parent: parent's name of the menu item
    • menu_desc: test description of the menu item You should check lucterios.contacts.views_contacts to understand the full syntax.

By default, django creates 3 permission by entity model: change, add and delete. The names are made this way:

change_<lower case entity name> add_<lower case entity name> delete_<lower case entity name>

To define other permissions, you need to add them the django's way: see django's permissions documentation

In those classes, inheritance is the main aspect:
1 XferListEditor: To display the registration list of a model
2 XferShowEditor: To display (read-only) recording of the model
3 XferAddEditor: To edit (writing) a record of the model. Safeguard action is included in.
4 XferDelete: To delete a record
5 XferSearchEditor: To display a record search window
6 XferPrintAction: To print a 'show' was associated
7 XferPrintListing: To make a list of print
8 XferPrintLabel: To make a label printing
9 XferContainerCustom, XferSave, XferContainerAcknowledge ..: They are more basic view to make customized screens. 95% of the modules do not have needs (at least, that's my goal)