In this post, we will share with you how to use the autofill feature from CODENERIX GenForm and GenModelForm. If you want to visit the general documentation, please follow this link.

What does the autofill feature?

This feature helps the developer to interconnect several fields into a dynamic field, giving the user a better experience while using the ERP.

Imagine that you have “Country“, “Region“, and “City” selectors, and you need to filter one field with the other so the user can choose data from the filtered results of the previous field. It is easier to see it with an example:

  • Let’s say the user selects “Spain” as a “Country“.
  • In the “Region” selector, the user should be able to choose only Spanish Regions and not any region from any other country.
  • The user chooses “Andalusia” from all the Spanish Regions.
  • In the “City” selector again, the user should see only Andalusian cities and not Madrid or Valencia.

The same example can be shown with “Family“, “Category“, and “Subcategory“:

Let’s see what we should expect from the browser:

Family” (Familia) is a static selector. We choose “Ordenadores (23)


We click on the dynamic select for “Category” (Categoría), and an XHR request happens


The XHR request included the selected ID from “Family” (Familia), so “Categories” are filtered with this “Family


In the answer, the information for this selector is coming (including “id” and “label“)


We choose “Portátiles (LAPTOPS)” from the Dynamic Select, and another XHR request happens to refresh data in the selector.


We click on the dynamic select for “Subcategory” (Subcategoría), and an XHR request happens.


The XHR request included the selected ID from “Category” (Category); again, several elements came from the server.


We click on the dynamic select for “Subcategory” (Subcategoría), and an XHR request happens.

We can see in the public ERP example that the autofill feature is cool and can lead to smaller selectors. We actually encourage the usage of Dynamic Selectors over Static Selectors because they can be tuned in a very granular way and lead to smaller answers from the server, reducing the overhead also on the database.

Let’s see how this was configured in CODENERIX

To use the autofill feature, you have to add a “Meta” class to your GenForm or GenModelForm class and declare inside the Meta class a new attribute named “autofill”, and it should look similar to:

autofill = {
    # Search of attr3 based in attr2
    "attr3": ["select", 3, "related_name_foreignkey_url", "attr2"],
    # Search based in its own pk
    "attr5": ["select", 3, "related_name_foreignkey_url", "__pk__"],
    # Search based in his own pk and attr2.
    "attr4": ["select", 3, "related_name_foreignkey_url", "__pk__", "attr2"],
}

Autofill is an attribute designed to fill foreign key attributes with data based on a textbox, any other information from the form or an AngularJS function. The structure is a dictionary with the attribute name as a key and a list as a value which will contain elements using the next format:

  • position[0]: Type of the selector that you need to create:
    • select: shows a box that takes a single choice.
    • multiselect: shows a box which lets you select multiple choices.
  • position[1]: minimum number of letters required in the textbox to start the execution of a search. When this number of chars is reached, the field will start an XHR request to the server querying for data to fill the field. To request all records to choose from, you should write ‘*’.
  • position[2]: Related name of the URL for the GenForeignKey class that will attend the request to fetch the field’s data.
  • position[3-n]: (Optional fields) The rest of the fields are filters used to send the pk (primary key) of a selected attribute. Usually is used to take the value of an attribute and perform the search using it. There are two options for using these filters:
    • attrName: sends the current value, if it exists, from a selected attribute.
    • pk: send the pk (primary key) of the own object.

In our previous examples, the request looked like this:

Request: https://erp.codenerix.com/products/categorys/foreign/*?def=1&filter={“FeatureForm_family”:”1″}

and the autofill field was filled as follows:

class Meta:
	autofill = {
		"FeatureForm_category": [
			"select",
			3,
			"CDNX_products_categorys_foreign",
			"FeatureForm_family"
		]
	}

In action:

  • The software built the URL using the related URL name “CDNX_products_categorys_foreign“.
  • It searched on the click for every register “*“.
  • If we wrote in the search box, the string would be sent if it had 3 characters or more.
  • Example URL for the search “hola” would be: /products/categorys/foreign/hola?def=1&filter={“FeatureForm_family”:”1″}
  • It sent the ID from the selected element in the “Family” selector, which would actually be the pk “1“.

Let’s see how does it look the GenForeignKey class

Following the example before, here is the resulting GenForeignKey class that would answer any request to this field. This is actually one of the most complex GenForeignKey you may build. Under this one, there is a simpler example:

class CategoryForeign(GenCategoryUrl, GenForeignKey):
    model = Category
    label = "{<LANGUAGE_CODE>__name} ({code})"

    def get_foreign(self, queryset, search, filters):
        # Filter with search string
        query = [Q(code__icontains=search), ]
        for lang in settings.LANGUAGES_DATABASES:
            query.append(Q(**{"{}__name__icontains".format(lang.lower()): search}))

        qs = queryset.filter(
            reduce(operator.or_, query)
        )
        family = filters.get('FeatureForm_family', None)
        if family is None:
            family = filters.get('AttributeForm_family', None)
        if family is None:
            family = filters.get('FeatureSpecialForm_family', None)
        if family is None:
            family = filters.get('ProductForm_family', None)
        if family is None:
            family = filters.get('ProductFormCreate_family', None)
        if family is None:
            family = filters.get('ProductFormCreateCustom_family', None)

        if family:
            qs = qs.filter(family__pk=family)

        return qs[:settings.LIMIT_FOREIGNKEY]

This is actually one of the most complex GenForeignKey you may build.

class ProductForeign(GenForeignKey):
    model = Product
    label = "{name}"
    public = True

    def get_foreign(self, queryset, search, filters):
        qsobject = Q(name__icontains=search)
        qs = queryset.filter(qsobject).order_by("name")
        return qs[: settings.LIMIT_FOREIGNKEY]

References

The examples shown in this post were taken from “django-codenerix-products” package reading from forms.py and views.py

Remember to visit: