Пример #1
0
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self._people = ControlButton('<i class="icon users"></i>People',
                                     css=' circular ',
                                     label_visible=False,
                                     default='window.location="/app/people/";')
        self._contracts = ControlButton(
            '<i class="icon file outline"></i>Contracts',
            css=' circular ',
            label_visible=False,
            default='window.location="/app/contracts/";')
        self._proposals = ControlButton(
            '<i class="icon file"></i>Proposals',
            css=' circular ',
            label_visible=False,
            default='window.location="/app/proposals/";')

        self._orders = ControlButton('<i class="icon dollar"></i>Orders',
                                     css=' circular ',
                                     label_visible=False,
                                     default='window.location="/app/orders/";')

        self.formset = [
            'h2:Human resources',
            no_columns('_people', '_contracts', '_proposals'), '-',
            'h2:Orders',
            no_columns('_orders')
        ]
Пример #2
0
    def __init__(self, *args, **kwargs):
        super(DefaultApp, self).__init__(*args, **kwargs)

        self._css_btn = ControlButton(
            '<i class="icon toggle on" ></i>Toggle css',
            default=self.__toggle_css_evt,
            label_visible=False)
        self._toggle_btn = ControlButton(
            '<i class="icon eye" ></i>Toggle visibility',
            default=self.__toggle_visibility_evt,
            label_visible=False)
        self._copy_btn = ControlButton(
            '<i class="icon copy outline" ></i>Copy the text',
            default=self.__copy_text_evt,
            label_visible=False)
        self._input = ControlText(
            'Type something here and press the copy button',
            changed_event=self.__input_changed_evt)
        self._text = ControlTextArea('Result')
        self._combo = ControlCombo('Combo',
                                   items=[('Item 1', 1), ('Item 2', 2),
                                          ('Item 3', 3)])
        self._check = ControlCheckBox('Check box')
        self._list = ControlList('List')
        self._label = ControlLabel('Label',
                                   default='Use the label for a dynamic text')

        self.formset = [
            no_columns('_toggle_btn', '_copy_btn', '_css_btn'), ' ', '_input',
            '_text', {
                'Free text': [
                    'h1:Header 1', 'h2:Header 2', 'h3:Header 3', 'h4:Header 4',
                    'h5:Header 5', 'h1-right:Header 1', 'h2-right:Header 2',
                    'h3-right:Header 3', 'h4-right:Header 4',
                    'h5-right:Header 5', '-', 'Free text here',
                    'msg:Message text', 'info:Info message',
                    'warning:Warning message', 'alert:Alert message'
                ],
                'Segments': [
                    'The next example has a segment',
                    segment('_combo', '_check', css='secondary'), '_list',
                    '_label'
                ]
            }
        ]
Пример #3
0
    def __init__(self, *args, **kwargs):
        """
        :param str title: Title of the app. By default will assume the value in the class variable TITLE.
        :param django.db.models.Model model: Model the App will manages. By default will assume the value in the class variable MODEL.
        :param class editform_class: Class used to generate the edition form. By default will assume the value in the class variable EDITFORM_CLASS.
        :param int parent_pk: (optional) Used to generate the inline interface. Primary key of the parent model
        :param Model parent_model: (optional) Used to generate the inline interface. Parent model
        """
        title                = kwargs.get('title', self.TITLE)
        self.model           = kwargs.get('model', self.MODEL)
        self.editmodel_class = kwargs.get('editform_class', self.EDITFORM_CLASS)
        self.addmodel_class  = kwargs.get('addform_class', self.ADDFORM_CLASS if self.ADDFORM_CLASS else self.editmodel_class)
        
        # Set the class to behave as inline ModelAdmin ########
        self.parent_field = None
        self.parent_pk    = kwargs.get('parent_pk',    None)
        self.parent_model = kwargs.get('parent_model', None)
        
        if self.parent_model and self.parent_pk:
            self.set_parent(self.parent_model, self.parent_pk)
        
        has_add_permission  = self.has_add_permission()  and self.addmodel_class  is not None
        has_edit_permission = self.has_edit_permission() and self.editmodel_class is not None

        BaseWidget.__init__(self, title)
        
        #######################################################
        self._list = self.CONTROL_LIST(
            'List',
            list_display = self.LIST_DISPLAY  if self.LIST_DISPLAY  else [],
            list_filter  = self.LIST_FILTER   if self.LIST_FILTER   else [],
            search_fields= self.SEARCH_FIELDS if self.SEARCH_FIELDS else [],
            rows_per_page= self.LIST_ROWS_PER_PAGE,
            n_pages      = self.LIST_N_PAGES
        )

        has_details = (self.USE_DETAILS_TO_ADD or self.USE_DETAILS_TO_EDIT) and (has_add_permission or has_edit_permission)
        if has_details:
            self._details = ControlEmptyWidget('Details', visible=False)
        
        ##############################################
        # Check if the add button should be included
        if has_add_permission:

            self._add_btn = ControlButton(
                self.ADD_BTN_LABEL,
                label_visible=False,
                default=self.show_create_form
            )
            if self.parent_model: self._add_btn.css = 'tiny basic blue'
        ##############################################

        self.toolbar = self.get_toolbar_buttons(has_add_permission=has_add_permission)

        if self.parent_model:
            self.formset = [
                self.toolbar,
                '_details' if has_details else None,
                '_list',
            ]
        else:
            self.formset = [
                '_details' if has_details else None,
                segment( 
                    self.toolbar,
                    '_list'
                ),
            ]
        
        # if the user has edit permission then 
        if has_edit_permission:
            # events
            self._list.item_selection_changed_event = self.__list_item_selection_changed_event

        
        #if it is a inline app, add the title to the header
        
        if self.parent_model and self.title:
            self.formset = ['h3:'+str(title)]+self.formset

        self.populate_list()
Пример #4
0
    def __init__(self, *args, **kwargs):
        """
        :param str title: Title of the app. By default will assume the value in the class variable TITLE.
        :param django.db.models.Model model: Model with the App will represent. By default will assume the value in the class variable MODEL.
        :param list(ModelAdmin) inlines: Sub models to show in the interface
        :param list(str) fieldsets: Organization of the fields
        :param int parent_pk: Parent model key
        :param django.db.models.Model parent_model: Parent model class
        :param int pk: Model register to manage
        """

        BaseWidget.__init__(self, *args, **kwargs)

        self.model = kwargs.get('model', self.MODEL)
        self.inlines = kwargs.get('inlines', self.INLINES)
        self.fieldsets = kwargs.get('fieldsets', self.FIELDSETS)
        self.readonly = kwargs.get('readonly', self.READ_ONLY)
        self.has_cancel_btn = kwargs.get('has_cancel_btn', self.HAS_CANCEL_BTN)

        if self.fieldsets is None: self.fieldsets = self.FIELDSETS

        self._auto_fields = []
        self._callable_fields = []
        self.edit_fields = []
        self.edit_buttons = []
        self.inlines_apps = []
        self.inlines_controls_name = []
        self.inlines_controls = []

        self.object_pk = None

        # used to configure the interface to inline
        # it will filter the dataset by the foreign key
        self.parent_field = None
        self.parent_pk = kwargs.get('parent_pk', None)
        self.parent_model = kwargs.get('parent_model', None)
        if self.parent_model and self.parent_pk:
            self.__set_parent(self.parent_model, self.parent_pk)
        #######################################################

        # buttons
        self._save_btn = ControlButton(self.SAVE_BTN_LABEL)
        self._create_btn = ControlButton(self.CREATE_BTN_LABEL)
        self._remove_btn = ControlButton(self.REMOVE_BTN_LABEL,
                                         css='red basic')
        if self.has_cancel_btn:
            self._cancel_btn = ControlButton(self.CANCEL_BTN_LABEL,
                                             css='gray basic')

        if self.parent_model:
            self._save_btn.css += ' tiny'
            self._create_btn.css += ' tiny'
            self._remove_btn.css += ' tiny'
            if self.has_cancel_btn:
                self._cancel_btn.css += ' tiny'

        self.edit_buttons.append(self._save_btn)
        self.edit_buttons.append(self._create_btn)
        self.edit_buttons.append(self._remove_btn)
        if self.has_cancel_btn:
            self.edit_buttons.append(self._cancel_btn)

        self.edit_fields += self.edit_buttons
        for field in self.edit_fields:
            field.hide()

        # events
        self._create_btn.value = self.__create_btn_event
        self._remove_btn.value = self.__remove_btn_event
        self._save_btn.value = self.__save_btn_event
        if self.has_cancel_btn:
            self._cancel_btn.value = self.cancel_btn_event

        self._create_btn.label_visible = False
        self._remove_btn.label_visible = False
        self._save_btn.label_visible = False
        if self.has_cancel_btn:
            self._cancel_btn.label_visible = False

        self.create_model_formfields()
        pk = kwargs.get('pk', None)
        if pk:
            self.object_pk = pk
            self.show_edit_form()
        else:
            self.show_create_form()

        for inline in self.inlines:
            self.formset.append(inline.__name__)
Пример #5
0
class EditFormAdmin(BaseWidget):

    MODEL = None  #: class: Model to manage
    TITLE = None  #: str: Title of the application
    INLINES = []  #: list(class): Sub models to show in the interface
    FIELDSETS = None  #: Formset of the edit form
    READ_ONLY = []  #: list(str): List of readonly fields
    HAS_CANCEL_BTN = True  #: bool: Flag to show or hide the cancel button

    #: str: Label for the save button
    SAVE_BTN_LABEL = '<i class="save icon"></i> Save'
    #: str: Label for the create button
    CREATE_BTN_LABEL = '<i class="plus icon"></i> Create'
    #: str: Label for the cancel button
    CANCEL_BTN_LABEL = '<i class="hide icon"></i> Close'
    #: str: Label for the delete button
    REMOVE_BTN_LABEL = '<i class="trash outline icon"></i> Remove'
    #: str: Label for the popup window for the delete confirmation
    POPUP_REMOVE_TITLE = 'The next objects are going to be affected or removed'

    def __init__(self, *args, **kwargs):
        """
        :param str title: Title of the app. By default will assume the value in the class variable TITLE.
        :param django.db.models.Model model: Model with the App will represent. By default will assume the value in the class variable MODEL.
        :param list(ModelAdmin) inlines: Sub models to show in the interface
        :param list(str) fieldsets: Organization of the fields
        :param int parent_pk: Parent model key
        :param django.db.models.Model parent_model: Parent model class
        :param int pk: Model register to manage
        """

        BaseWidget.__init__(self, *args, **kwargs)

        self.model = kwargs.get('model', self.MODEL)
        self.inlines = kwargs.get('inlines', self.INLINES)
        self.fieldsets = kwargs.get('fieldsets', self.FIELDSETS)
        self.readonly = kwargs.get('readonly', self.READ_ONLY)
        self.has_cancel_btn = kwargs.get('has_cancel_btn', self.HAS_CANCEL_BTN)

        if self.fieldsets is None: self.fieldsets = self.FIELDSETS

        self._auto_fields = []
        self._callable_fields = []
        self.edit_fields = []
        self.edit_buttons = []
        self.inlines_apps = []
        self.inlines_controls_name = []
        self.inlines_controls = []

        self.object_pk = None

        # used to configure the interface to inline
        # it will filter the dataset by the foreign key
        self.parent_field = None
        self.parent_pk = kwargs.get('parent_pk', None)
        self.parent_model = kwargs.get('parent_model', None)
        if self.parent_model and self.parent_pk:
            self.__set_parent(self.parent_model, self.parent_pk)
        #######################################################

        # buttons
        self._save_btn = ControlButton(self.SAVE_BTN_LABEL)
        self._create_btn = ControlButton(self.CREATE_BTN_LABEL)
        self._remove_btn = ControlButton(self.REMOVE_BTN_LABEL,
                                         css='red basic')
        if self.has_cancel_btn:
            self._cancel_btn = ControlButton(self.CANCEL_BTN_LABEL,
                                             css='gray basic')

        if self.parent_model:
            self._save_btn.css += ' tiny'
            self._create_btn.css += ' tiny'
            self._remove_btn.css += ' tiny'
            if self.has_cancel_btn:
                self._cancel_btn.css += ' tiny'

        self.edit_buttons.append(self._save_btn)
        self.edit_buttons.append(self._create_btn)
        self.edit_buttons.append(self._remove_btn)
        if self.has_cancel_btn:
            self.edit_buttons.append(self._cancel_btn)

        self.edit_fields += self.edit_buttons
        for field in self.edit_fields:
            field.hide()

        # events
        self._create_btn.value = self.__create_btn_event
        self._remove_btn.value = self.__remove_btn_event
        self._save_btn.value = self.__save_btn_event
        if self.has_cancel_btn:
            self._cancel_btn.value = self.cancel_btn_event

        self._create_btn.label_visible = False
        self._remove_btn.label_visible = False
        self._save_btn.label_visible = False
        if self.has_cancel_btn:
            self._cancel_btn.label_visible = False

        self.create_model_formfields()
        pk = kwargs.get('pk', None)
        if pk:
            self.object_pk = pk
            self.show_edit_form()
        else:
            self.show_create_form()

        for inline in self.inlines:
            self.formset.append(inline.__name__)

    #################################################################################
    #### PROPERTIES #################################################################
    #################################################################################

    @property
    def model_object(self):
        """
        django.db.models.Model object: Return the current object in edition.
        """

        if self.object_pk is None:
            return None
        else:
            queryset = self.model.objects.all()

            # check if the model has a query_set function
            # if so use it to get the data for visualization
            if hasattr(self.model, 'get_queryset'):
                request = PyFormsMiddleware.get_request()
                queryset = self.model.get_queryset(request, queryset)

            return queryset.get(pk=self.object_pk)

    #################################################################################
    #### FUNCTIONS ##################################################################
    #################################################################################

    def get_buttons_row(self):
        """
        This function generate the formset configuration for the save, create,
        cancel and remove buttons,

        Returns:
            list(str): Returns the formset configuration that will be append to
            the end of the fieldsets.
        """
        buttons = ['_save_btn', '_create_btn']
        if self.has_cancel_btn:
            buttons = buttons + ['_cancel_btn', ' ']
        buttons = buttons + ['_remove_btn']
        return [no_columns(*buttons)]

    def hide_form(self):
        """
        This functions hides the create and edit form. 
        """
        if self.parent and hasattr(self.parent, 'hide_form'):
            self.parent.hide_form()
        else:
            for field in self.edit_fields:
                field.hide()
            for field in self.inlines_controls:
                field.hide()

    def show_form(self):
        """
        This shows the create and edit form. 
        """
        for field in self.edit_fields:
            field.show()
        for field in self.inlines_controls:
            field.show()

    def cancel_btn_event(self):
        """
        Event called when the cancel button is pressed
        """
        self.hide_form()

    def autocomplete_search(self, keyword, field):
        """
        Function used by a combobox to get the items dynamically

        :param str keyword: Keyword for filter the results
        :param django.db.models.fields.Field field: Django field where the autocomplete will be applied
        
        Returns:
            list(dict): Results for the search in the format
        
            .. code-block:: python
                    
                [{'name':name, 'value':id, 'text':text}, ...]

        """
        query = field.related_model.objects.all()
        query = self.related_field_queryset(field, query)

        if keyword:
            if hasattr(field.related_model, 'autocomplete_search_fields'):
                or_filter = Q()
                for search_field in field.related_model.autocomplete_search_fields(
                ):
                    or_filter.add(Q(**{search_field: keyword}), Q.OR)
            else:
                or_filter = Q(pk=keyword)
        else:
            or_filter = Q()

        try:
            return [{
                'name': str(o),
                'value': o.pk,
                'text': str(o)
            } for o in query.filter(or_filter)]
        except:
            return []

    def related_field_queryset(self, field, queryset):
        """
        Function called to manages the query for related fields like ForeignKeys and ManyToMany.
        
        :param django.db.models.fields.Field field: Related django field.
        :param django.db.models.query.QuerySet queryset: Default queryset for the related field.

        Returns:
            django.db.models.query.QuerySet: Results for the search in the format.
        """
        return queryset

    def update_related_field(self, field, pyforms_field, queryset):
        """
        Function called update the related fields like ForeignKeys and ManyToMany.
        
        :param django.db.models.fields.Field field: Related django field.
        :param ControlBase pyforms_field: Pyforms field that will be updated.
        :param django.db.models.query.QuerySet queryset: Default queryset for the related field.
        """
        pass
        """
        if isinstance(field, models.ForeignKey):
            pass
            #Foreign key
            #pyforms_field.clear_items()
            #if field.null:
            #    pyforms_field.add_item( '', '-1' )           
            #for instance in query:
            #    pyforms_field.add_item( str(instance), instance.pk )

        elif isinstance(field, models.ManyToManyField):
            #Many to Many field
            #pyforms_field.queryset = query
            pass
        """

    def show_create_form(self):
        """
        This function prepares the fields to be shown as create form.
        """

        #check if it has permissions to add new registers
        if ( self.parent and hasattr(self.parent, 'has_add_permission') ) and \
           not self.parent.has_add_permission():
            raise Exception('Your user does not have permissions to add')

        fields2show = self.get_visible_fields_names()

        self.__update_related_fields()

        # clear all the fields
        for field_name in fields2show:
            if hasattr(self, field_name):
                pyforms_field = getattr(self, field_name)
                pyforms_field.value = None

        for field in self.edit_fields:
            field.show()

        for inline in self.inlines_controls:
            inline.hide()

        self._save_btn.hide()
        self._remove_btn.hide()

    def update_callable_fields(self):
        if not self._callable_fields: return

        obj = self.model_object
        if obj is None: return
        for field_name in self._callable_fields:
            pyforms_field = getattr(self, field_name)
            value = getattr(obj, field_name)()
            pyforms_field.value = value

    def update_autonumber_fields(self):
        if not self._auto_fields: return

        obj = self.model_object
        if obj is None: return
        for field_name in self._auto_fields:
            pyforms_field = getattr(self, field_name)
            value = getattr(obj, field_name)
            pyforms_field.value = value

    def show_edit_form(self, pk=None):
        """
        This function prepares the fields to be shown as edit form.

        :param int pk: Primiry key of the object to be show in the edit form.

        Returns:
            :django.db.models.Model object: Returns the object in edition.
        """

        if pk: self.object_pk = pk
        for field in self.edit_fields:
            field.show()
        for field in self.inlines_controls:
            field.show()
        self._create_btn.hide()

        self.__update_related_fields()

        obj = self.model_object
        fields2show = self.get_visible_fields_names()

        for field_name in fields2show:

            if hasattr(self, field_name) and hasattr(obj, field_name):
                pyforms_field = getattr(self, field_name)
                value = getattr(obj, field_name)

                try:
                    field = self.model._meta.get_field(field_name)
                except FieldDoesNotExist:
                    try:
                        field = getattr(self.model, field_name)
                    except AttributeError:
                        continue

                if callable(field) and not isinstance(field, models.Model):
                    pyforms_field.value = value()

                elif field_name in self.readonly:

                    if isinstance(field, models.ManyToManyField):
                        pyforms_field.value = ';'.join(
                            [str(o) for o in value.all()])

                    elif isinstance(value, datetime.datetime):
                        if not value:
                            pyforms_field.value = ''
                        else:
                            value = timezone.localtime(value)
                            pyforms_field.value = value.strftime(
                                '%Y-%m-%d %H:%M')

                    elif isinstance(value, datetime.date):
                        if not value:
                            pyforms_field.value = ''
                        else:
                            pyforms_field.value = value.strftime('%Y-%m-%d')

                    else:
                        pyforms_field.value = value

                elif isinstance(field, models.AutoField):
                    pyforms_field.value = value

                elif isinstance(field, models.FileField):
                    pyforms_field.value = value.url if value else None

                elif isinstance(field, models.ImageField):
                    pyforms_field.value = value.url if value else None

                elif isinstance(field, models.ForeignKey):
                    pyforms_field.value = value.pk if value else None

                elif isinstance(field, models.ManyToManyField):
                    pyforms_field.value = [str(o.pk) for o in value.all()]

                else:
                    pyforms_field.value = value

        self.inlines_apps = []
        for inline in self.inlines:
            pyforms_field = getattr(self, inline.__name__)
            pyforms_field._name = inline.__name__
            app = inline(parent_model=self.model, parent_pk=self.object_pk)
            self.inlines_apps.append(app)
            pyforms_field.value = app
            pyforms_field.show()

        return obj

    def delete_event(self):
        """
        Function called to delete the current object in edition.

        Returns:
            :bool: True if the object was deleted with success, False if not.
        """
        if self.object_pk:
            obj = self.model_object
            obj.delete()
            self.object_pk = None
            self._remove_btn.hide()
            self._create_btn.show()
            self._save_btn.hide()
            for field in self.inlines_controls:
                field.hide()
            return True
        else:
            return False

    def popup_remove_handler(self, popup=None, button=None):
        """
        Function that handles the buttons events of the object delete confirmation popup.

        :param BaseWidget popup: Popup application.
        :param str button: Label of the pressed button. 
        """
        if button == self.CANCEL_BTN_LABEL:
            popup.close()
        elif button == self.REMOVE_BTN_LABEL:
            if self.delete_event():
                self.success('The object was deleted with success!',
                             'Success!')
                popup.close()
            else:
                popup.warning('The object was not deleted!', 'Warning!')

    def create_newobject(self):
        """
        Function called to create a new object of the model.

        Returns:
            :django.db.models.Model object: Created object
        """
        return self.model()

    def save_event(self):
        """
        Function called when the save is called.

        Returns:
            :django.db.models.Model object: Created object or None if the object was not saved with success.
        """
        fields2show = self.get_visible_fields_names()

        try:
            obj = self.model_object

            ## create an object if does not exists ####
            if obj is None:
                #check if it has permissions to add new registers
                if ( self.parent and hasattr(self.parent, 'has_add_permission') ) and \
                   not self.parent.has_add_permission():
                    raise Exception(
                        'Your user does not have permissions to add')

                obj = self.create_newobject()
            ###########################################

            # if it is working as an inline edition form #
            if self.parent_field:
                setattr(obj, self.parent_field.name,
                        self.parent_model.objects.get(pk=self.parent_pk))
            ##############################################

            for field in self.model._meta.get_fields():
                if field.name not in fields2show: continue
                if field.name in self.readonly: continue

                pyforms_field = getattr(self, field.name)
                value = pyforms_field.value

                if isinstance(field, models.AutoField):
                    continue

                elif isinstance(field, models.FileField):
                    getattr(self, field.name).error = False
                    value = getattr(self, field.name).value
                    if value:
                        try:
                            os.makedirs(
                                os.path.join(settings.MEDIA_ROOT,
                                             field.upload_to))
                        except os.error as e:
                            pass

                        paths = [p for p in value.split('/') if len(p) > 0][1:]
                        from_path = os.path.join(settings.MEDIA_ROOT, *paths)
                        if os.path.exists(from_path):
                            to_path = os.path.join(settings.MEDIA_ROOT,
                                                   field.upload_to,
                                                   os.path.basename(value))
                            os.rename(from_path, to_path)

                            url = '/'.join([field.upload_to] +
                                           [os.path.basename(value)])
                            if url[0] == '/': url = url[1:]
                            setattr(obj, field.name, url)
                    elif field.null:
                        setattr(obj, field.name, None)
                    else:
                        setattr(obj, field.name, '')

                elif isinstance(field, models.ForeignKey):
                    if value is not None:
                        try:
                            value = field.related_model.objects.get(pk=value)
                        except:
                            self.alert('The field [{0}] has an error.'.format(
                                field.verbose_name))
                            pyforms_field.error = True
                    else:
                        value = None
                    setattr(obj, field.name, value)

                elif not isinstance(field, models.ManyToManyField):
                    pyforms_field.error = False
                    setattr(obj, field.name, value)

            try:
                obj.full_clean()
            except ValidationError as e:
                html = '<ul class="list">'
                for field_name, messages in e.message_dict.items():

                    try:
                        getattr(self, field_name).error = True

                        label = get_lookup_verbose_name(self.model, field_name)
                        html += '<li><b>{0}</b>'.format(label.capitalize())

                        field_error = True

                    except FieldDoesNotExist:
                        field_error = False
                    except AttributeError:
                        field_error = False

                    if field_error: html += '<ul>'
                    for msg in messages:
                        html += '<li>{0}</li>'.format(msg)
                    if field_error: html += '</ul></li>'

                html += '</ul>'
                self.alert(html)
                return None

            obj.save()

            for field in self.model._meta.get_fields():

                if isinstance(field, models.ManyToManyField) and hasattr(
                        self, field.name):
                    values = getattr(self, field.name).value
                    field_instance = getattr(obj, field.name)

                    objs = field.related_model.objects.filter(pk__in=values)
                    values_2_remove = field_instance.all().exclude(
                        pk__in=[o.pk for o in objs])

                    for o in values_2_remove:
                        field_instance.remove(o)

                    values_2_add = objs.exclude(
                        pk__in=[o.pk for o in field_instance.all()])
                    for o in values_2_add:
                        field_instance.add(o)

            self.object_pk = obj.pk

            self.update_callable_fields()
            self.update_autonumber_fields()

            return obj

        except Exception as e:
            traceback.print_exc()
            self.alert(str(e))

            return None

    #################################################################################
    #### PRIVATE FUNCTIONS ##########################################################
    #################################################################################

    def __set_parent(self, parent_model, parent_pk):
        """
        Set the form to work as inline

        :param django.db.models.Model parent_model: Parent model.
        :param int parent_pk: Parent object primary key.
        """
        self.parent_pk = parent_pk
        self.parent_model = parent_model

        for field in self.model._meta.get_fields():
            if isinstance(field, models.ForeignKey):
                if parent_model == field.related_model:
                    self.parent_field = field
                    break

    def get_visible_fields_names(self):
        """
        Function called to get names of the visible fields.

        Returns:
            :list(str): List names of the visible fields.
        """
        if self.fieldsets:
            fields = get_fieldsets_strings(self.fieldsets)
        else:
            fields = []
            for field in self.model._meta.get_fields():

                if field.one_to_many: continue
                if field.one_to_one and field.name.endswith('_ptr'): continue

                fields.append(field.name)

        if self.parent_field:
            try:
                fields.remove(self.parent_field.name)
            except ValueError:
                pass

        return [field for field in fields if field is not None]

    def __update_related_fields(self):
        """
        Update all related fields 
        """
        fields2show = self.get_visible_fields_names()
        formset = []

        for field in self.model._meta.get_fields():

            if not isinstance(field,
                              (models.ForeignKey, models.ManyToManyField)):
                continue

            if field.name not in fields2show:
                continue  #only update this field if is visible
            if field.name in self.readonly: continue

            pyforms_field = getattr(self, field.name)
            queryset = self.related_field_queryset(
                field, field.related_model.objects.all())

            self.update_related_field(field, pyforms_field, queryset)

    def create_model_formfields(self):
        """
        Create the model edition form.
        """
        fields2show = self.get_visible_fields_names()
        formset = []

        for field_name in fields2show:

            # if the field already exists then ignore the creation
            if hasattr(self, field_name): continue

            try:
                field = self.model._meta.get_field(field_name)
            except FieldDoesNotExist:
                try:
                    field = getattr(self.model, field_name)
                except AttributeError:
                    continue

            pyforms_field = None

            if not (callable(field) and not isinstance(field, models.Model)):
                label = get_lookup_verbose_name(self.model, field_name)

            if callable(field) and not isinstance(field, models.Model):
                label = getattr(field, 'short_description') if hasattr(
                    field, 'short_description') else field_name
                pyforms_field = ControlText(label.capitalize(), readonly=True)
                self._callable_fields.append(field_name)

            elif field.name in self.readonly:

                if isinstance(field, models.TextField):
                    pyforms_field = ControlTextArea(label.capitalize(),
                                                    readonly=True)
                else:
                    pyforms_field = ControlText(label.capitalize(),
                                                readonly=True)

            elif isinstance(field, models.AutoField):
                pyforms_field = ControlText(label.capitalize(), readonly=True)
                self._auto_fields.append(field_name)

            elif isinstance(field, models.Field) and field.choices:
                pyforms_field = ControlCombo(label.capitalize(),
                                             items=[(c[1], c[0])
                                                    for c in field.choices])
            elif isinstance(field, models.BigIntegerField):
                pyforms_field = ControlInteger(label.capitalize())
            elif isinstance(field, models.BooleanField):
                pyforms_field = ControlCheckBox(label.capitalize())
            elif isinstance(field, models.DateTimeField):
                pyforms_field = ControlDateTime(label.capitalize())
            elif isinstance(field, models.DateField):
                pyforms_field = ControlDate(label.capitalize())
            elif isinstance(field, models.DecimalField):
                pyforms_field = ControlFloat(label.capitalize())
            elif isinstance(field, models.FileField):
                pyforms_field = ControlFileUpload(label.capitalize())
            elif isinstance(field, models.FloatField):
                pyforms_field = ControlFloat(label.capitalize())
            elif isinstance(field, models.ImageField):
                pyforms_field = ControlFileUpload(label.capitalize())
            elif isinstance(field, models.IntegerField):
                pyforms_field = ControlInteger(label.capitalize())
            elif isinstance(field, models.TextField):
                pyforms_field = ControlTextArea(label.capitalize())
            elif isinstance(field, models.NullBooleanField):
                pyforms_field = ControlCombo(label.capitalize(),
                                             items=[('Unknown', None),
                                                    ('Yes', True),
                                                    ('No', False)])
            elif isinstance(field, models.ForeignKey):
                url = "/pyforms/autocomplete/{app_id}/{field_name}/{{query}}/".format(
                    app_id=self.uid, field_name=field.name)
                pyforms_field = ControlAutoComplete(label.capitalize(),
                                                    items_url=url,
                                                    model=field.related_model)

            elif isinstance(field, models.ManyToManyField):
                url = "/pyforms/autocomplete/{app_id}/{field_name}/{{query}}/".format(
                    app_id=self.uid, field_name=field.name)
                pyforms_field = ControlAutoComplete(label.capitalize(),
                                                    items_url=url,
                                                    model=field.related_model,
                                                    multiple=True)
            else:
                pyforms_field = ControlText(label.capitalize())

            # add the field to the application
            if pyforms_field is not None:
                setattr(self, field_name, pyforms_field)
                formset.append(field_name)
                self.edit_fields.append(pyforms_field)

        #Create the inlines edition forms.
        self.inlines_controls_name = []
        self.inlines_controls = []
        for inline in self.inlines:
            pyforms_field = ControlEmptyWidget()
            pyforms_field.name = inline.__name__
            pyforms_field._parent = self
            setattr(self, inline.__name__, pyforms_field)
            self.inlines_controls_name.append(inline.__name__)
            self.inlines_controls.append(pyforms_field)

        self.formset = self.fieldsets if self.fieldsets else formset
        self.formset = self.formset + self.get_buttons_row()

    def __create_btn_event(self):
        """
        Event called by the create button
        """
        self.object_pk = None
        obj = self.save_event()
        if obj:
            self._create_btn.hide()
            self._save_btn.show()
            self._remove_btn.show()
            for i, field in enumerate(self.inlines_controls):
                app = self.inlines_apps[i]
                app.populate_list()
                app.parent_pk = obj.pk
                field.show()
            self.success(
                'The object <b>{0}</b> was saved with success!'.format(obj),
                'Success!')

    def __save_btn_event(self):
        """
        Event called by the save button
        """
        obj = self.save_event()
        if obj:
            self.success(
                'The object <b>{0}</b> was saved with success!'.format(obj),
                'Success!')

    def __remove_btn_event(self):
        """
        Event called by the remove button
        """
        def related_objects(obj):
            objects = []
            for rel in list(obj.__class__._meta.related_objects):
                f = {rel.field.name: obj}
                rel_objects = rel.related_model.objects.filter(**f)

                for o in rel_objects:
                    objects.append((o, related_objects(o)))
            return objects

        def related_objects_html(objects):
            html = "<ul>"
            for o, objs in objects:
                html += "<li>"
                html += "{1}: <b>{0}</b>".format(
                    str(o), o.__class__._meta.verbose_name.title())
                if len(objs) > 0:
                    html += related_objects_html(objs)
                html += "</li>"
            html += "</ul>"
            return html

        if self.object_pk:
            obj = self.model_object

            objects = obj, related_objects(obj)
            html = related_objects_html([objects])

            popup = self.warning_popup(
                html,
                self.POPUP_REMOVE_TITLE,
                buttons=[self.REMOVE_BTN_LABEL, self.CANCEL_BTN_LABEL],
                handler=self.popup_remove_handler)
            popup.button_0.css = 'basic red'
Пример #6
0
class ModelAdmin(BaseWidget):
    """
    Class used to generate automatically an admin interface for a specific model

    """

    MODEL           = None  #: class: Model to manage
    TITLE           = None  #: str: Title of the application
    EDITFORM_CLASS  = EditFormAdmin #: class: Edit form class

    INLINES         = []    #: list(class): Sub models to show in the interface
    LIST_FILTER     = None  #: list(str): List of filters fields
    LIST_DISPLAY    = None  #: list(str): List of fields to display in the table
    SEARCH_FIELDS   = None  #: list(str): Fields to be used in the search

    FIELDSETS       = None  #: Formset of the edit form
    CONTROL_LIST    = ControlQueryList #: class: Control to be used in to list the values
    READ_ONLY       = []    #: list(str): List of readonly fields 

    LIST_ROWS_PER_PAGE = 10 #: int: number of rows to show per page
    LIST_N_PAGES = 5        #: int: number of pages to show in the list bottom

    #: str: Label of the add button
    ADD_BTN_LABEL = '<i class="plus icon"></i> Add'
    

    def __init__(self, *args, **kwargs):
        """
        :param str title: Title of the app. By default will assume the value in the class variable TITLE.
        :param django.db.models.Model model: Model the App will manages. By default will assume the value in the class variable MODEL.
        :param class editform_class: Class used to generate the edition form. By default will assume the value in the class variable EDITFORM_CLASS.
        :param int parent_pk: (optional) Used to generate the inline interface. Primary key of the parent model
        :param Model parent_model: (optional) Used to generate the inline interface. Parent model
        """
        title                = kwargs.get('title', self.TITLE)
        self.model           = kwargs.get('model', self.MODEL)
        self.editmodel_class = kwargs.get('editform_class', self.EDITFORM_CLASS)
        
        # Set the class to behave as inline ModelAdmin ########
        self.parent_field = None
        self.parent_pk    = kwargs.get('parent_pk',    None)
        self.parent_model = kwargs.get('parent_model', None)
        
        if self.parent_model and self.parent_pk:
            self.set_parent(self.parent_model, self.parent_pk)
        
        has_add_permission = self.has_add_permission()
        has_edit_permission = self.has_edit_permission()

        BaseWidget.__init__(self, title)
        
        #######################################################
        self._list = self.CONTROL_LIST(
            'List',
            list_display = self.LIST_DISPLAY  if self.LIST_DISPLAY  else [],
            list_filter  = self.LIST_FILTER   if self.LIST_FILTER   else [],
            search_fields= self.SEARCH_FIELDS if self.SEARCH_FIELDS else [],
            rows_per_page= self.LIST_ROWS_PER_PAGE,
            n_pages      = self.LIST_N_PAGES
        )

        if has_add_permission or has_edit_permission:
            self._details = ControlEmptyWidget('Details', visible=False)
        
        ##############################################
        # Check if the add button should be included
        if has_add_permission:

            self._add_btn = ControlButton(
                self.ADD_BTN_LABEL,
                label_visible=False,
                default=self.show_create_form
            )
            if self.parent_model: self._add_btn.css = 'tiny basic blue'
        ##############################################

        if self.parent_model:
            self.formset = [
                '_add_btn' if has_add_permission else None,
                '_list',
                '_details' if has_add_permission or has_edit_permission else None,
            ]
        else:
            self.formset = [
                segment( 
                    '_add_btn' if has_add_permission else None,
                    '_list'
                ),
                '_details' if has_add_permission or has_edit_permission else None,
            ]
        
        # if the user has edit permission then 
        if has_add_permission:
            # events
            self._list.item_selection_changed_event = self.__list_item_selection_changed_event

        
        #if it is a inline app, add the title to the header
        
        if self.parent_model:
            self.formset = ['h3:'+str(title)]+self.formset

        self.populate_list()

    #################################################################################
    #### PROPERTIES #################################################################
    #################################################################################

    @property
    def selected_row_object(self):
        """
        django.db.models.Model: Return the current selected row object. If no row is selected return None.
        """
        if int(self._list.selected_row_id)<0: return None
        return self._list.value.get(pk=self._list.selected_row_id)

    #################################################################################
    #### FUNCTIONS ##################################################################
    #################################################################################

    def populate_list(self):
        """
            Function called to configure the CONTROL_LIST to display the data
        """
        self._list.value = self.__get_queryset()


    def get_queryset(self, request, queryset):
        """
            The function retrives the queryset used to polulate the list.
            
            :param django.db.models.query.QuerySet queryset: 
                Default queryset used to populate the list.
                This queryset may have already applied the next filters:
                - If this class is being used as a inline app, the filters to select only the rows related with the parent app are applied. 
                - If the model being managed by this class has a function called get_queryset(request, queryset), the filters applied by this function are applied. (this helps maintaining the visualization rules on the side of the model)

            Returns:
                django.db.models.query.QuerySet: Returns the queryset used to populate the list.
        """
        
        return queryset

    
        
    def hide_form(self):
        """
        Function called to hide the form
        """
        # only if the button exists: 
        # if there is not add permission the add button is not created.
        if hasattr(self, '_add_btn'): self._add_btn.show()
        
        self._list.show()
        self._list.selected_row_id = -1
        self.populate_list()
        self._details.hide()

    def show_create_form(self):
        """
        Show an empty for for creation
        """
        # if there is no add permission then does not show the form
        if not self.has_add_permission(): return
        
        self._add_btn.hide()
        self._list.hide()
        self._details.show()

        createform = self.editmodel_class(
            title='Create', 
            model=self.model, 
            inlines=self.INLINES,
            parent_model=self.parent_model,
            parent_pk=self.parent_pk,
            fieldsets=self.FIELDSETS,
            readonly=self.READ_ONLY,
            parent_win=self
        )

        self._details.value  = createform




    def show_edit_form(self, pk=None):
        """
        Show the edition for for a specific object

        :param int pk: Primary key of the object to edit
        """
        # if there is no edit permission then does not show the form
        if not self.has_edit_permission(): return

        # only if the button exists: 
        # if there is not add permission the add button is not created.
        if hasattr(self, '_add_btn'): self._add_btn.hide()

        self._list.hide()       
        self._details.show()
        
    
        # create the edit form a add it to the empty widget details
        # override the function hide_form to make sure the list is shown after the user close the edition form
        editform = self.editmodel_class(
            title='Edit', 
            model=self.model, 
            pk=pk, 
            inlines=self.INLINES,
            parent_model=self.parent_model,
            parent_pk=self.parent_pk,
            fieldsets=self.FIELDSETS,
            readonly=self.READ_ONLY,
            parent_listapp=self
        )
        self._details.value = editform
        


    def set_parent(self, parent_model, parent_pk):
        """
        Function called to set prepare the Application to work as an inline
        
        :param django.db.models.Model parent_model: Model of the parent Edition form
        :param int parent_pk: Primary key of the parent object
        """
        
        self.parent_pk      = parent_pk
        self.parent_model   = parent_model

        for field in self.model._meta.get_fields():
            if isinstance(field, models.ForeignKey):
                if parent_model == field.related_model:
                    self.parent_field = field
                    break

    
    def has_add_permission(self):
        """
        Function called to check if one has permission to add new objects.
        
        Returns:
            bool: True if has add permission, False otherwise.
        """
        return True

    def has_edit_permission(self):
        """
        Function called to check if one has permission to edit the objects.
        
        Returns:
            bool: True if has edit permission, False otherwise.
        """
        return True

    #################################################################################
    #### PRIVATE FUNCTIONS ##########################################################
    #################################################################################


    def __list_item_selection_changed_event(self):
        """
        Event called when a row is selected. It shows the edition for row.
        """
        obj = self.selected_row_object
        if obj:
            self.object_pk = obj.pk
            self.show_edit_form(obj.pk)

    def __get_queryset(self):
        """
        
        """
        queryset = self.model.objects.all()

        #used to filter the model for inline fields
        if self.parent_field: 
            queryset = queryset.filter(**{self.parent_field.name: self.parent_pk})

        # check if the model has a query_set function
        # if so use it to get the data for visualization
        request  = PyFormsMiddleware.get_request()

        if hasattr(self.model, 'get_queryset'):
            queryset = self.model.get_queryset(request, queryset)
        
        return self.get_queryset(request, queryset)
Пример #7
0
class DefaultApp(BaseWidget):

    TITLE = 'Demo app'

    LAYOUT_POSITION = 0

    def __init__(self, *args, **kwargs):
        super(DefaultApp, self).__init__(*args, **kwargs)

        self._css_btn = ControlButton(
            '<i class="icon toggle on" ></i>Toggle css',
            default=self.__toggle_css_evt,
            label_visible=False)
        self._toggle_btn = ControlButton(
            '<i class="icon eye" ></i>Toggle visibility',
            default=self.__toggle_visibility_evt,
            label_visible=False)
        self._copy_btn = ControlButton(
            '<i class="icon copy outline" ></i>Copy the text',
            default=self.__copy_text_evt,
            label_visible=False)
        self._input = ControlText(
            'Type something here and press the copy button',
            changed_event=self.__input_changed_evt)
        self._text = ControlTextArea('Result')
        self._combo = ControlCombo('Combo',
                                   items=[('Item 1', 1), ('Item 2', 2),
                                          ('Item 3', 3)])
        self._check = ControlCheckBox('Check box')
        self._list = ControlList('List')
        self._label = ControlLabel('Label',
                                   default='Use the label for a dynamic text')

        self.formset = [
            no_columns('_toggle_btn', '_copy_btn', '_css_btn'), ' ', '_input',
            '_text', {
                'Free text': [
                    'h1:Header 1', 'h2:Header 2', 'h3:Header 3', 'h4:Header 4',
                    'h5:Header 5', 'h1-right:Header 1', 'h2-right:Header 2',
                    'h3-right:Header 3', 'h4-right:Header 4',
                    'h5-right:Header 5', '-', 'Free text here',
                    'msg:Message text', 'info:Info message',
                    'warning:Warning message', 'alert:Alert message'
                ],
                'Segments': [
                    'The next example has a segment',
                    segment('_combo', '_check', css='secondary'), '_list',
                    '_label'
                ]
            }
        ]

    def __toggle_css_evt(self):
        if self._css_btn.css == 'basic green':
            self._css_btn.css = 'inverted red'
            self._label.css = 'red'
            self._css_btn.label = '<i class="icon toggle on" ></i>Toggle css'
        else:
            self._css_btn.css = 'basic green'
            self._label.css = 'inverted green'
            self._css_btn.label = '<i class="icon toggle off" ></i>Toggle css'

    def __toggle_visibility_evt(self):

        if self._input.visible:
            self._input.hide()
        else:
            self._input.show()

        if self._text.visible:
            self._text.hide()
        else:
            self._text.show()

        if self._copy_btn.visible:
            self._copy_btn.hide()
        else:
            self._copy_btn.show()

        if self._css_btn.visible:
            self._css_btn.hide()
        else:
            self._css_btn.show()

    def __copy_text_evt(self):

        self._text.value = self._input.value

    def __input_changed_evt(self):
        print(self._input.value)