SmartField for Django not working

Feature(s) impacted

SmartFields not showing up for the Django integration.

Observed behavior

I tried the Django code sample from the official documentation:

Unfortunately, I have not been able to create a single SmartField no matter how badly I try. Here is a code sample:

import uuid

from django.db import models
from django_forest.utils.collection import Collection


class Address(models.Model):
    id = models.UUIDField(default=uuid.uuid4, primary_key=True)
    contact_name = models.CharField(max_length=256, blank=True)
    contact_last_name = models.CharField(max_length=256, blank=True)


class AddressForest(Collection):

    def load(self):
        self.name = "account_address"
        self.fields = [
            {
                'field': 'full_contact_name',
                'type': 'String',
                'get': self.get_fullname
            },
        ]

    def get_fullname(self, obj):
        print("Trying to get FullName for address...")
        return "%s %s" % (obj.contact_name, obj.contact_last_name)


Collection.register(AddressForest, Address)

I tried doing everything, moving the AddressForest class to a different file, I tried putting it into the main project directory, I tried checking if there is a Forest setting that I need to review but nothing works.

Also, I tried the self.name property inside the load function and outside of it without luck. I also tried the account_address name without success as well as Address, and address without new results. Whenever I try to edit the layout in the Forest Admin UI I check for hidden fields without success. I checked the schema file but the SmartField is never registered.

{
      "name": "account_address",
      "is_virtual": false,
      "icon": null,
      "is_read_only": false,
      "is_searchable": true,
      "only_for_relationships": false,
      "pagination_type": "page",
      "search_fields": null,
      "actions": [],
      "segments": [],
      "fields": [
        {
          "field": "id",
          "type": "String",
          "is_filterable": true,
          "is_sortable": true,
          "is_read_only": false,
          "is_required": false,
          "is_virtual": false,
          "default_value": null,
          "integration": null,
          "reference": null,
          "inverse_of": null,
          "relationship": null,
          "widget": null
        },
        {
          "field": "contact_name",
          "type": "String",
          "is_filterable": true,
          "is_sortable": true,
          "is_read_only": false,
          "is_required": false,
          "is_virtual": false,
          "default_value": null,
          "integration": null,
          "reference": null,
          "inverse_of": null,
          "relationship": null,
          "widget": null,
          "validations": [
            {
              "type": "is shorter than",
              "message": "Ensure this value has at most 256 characters",
              "value": 256
            },
            {
              "type": "is present",
              "message": "Ensure this value is not null or not empty"
            }
          ]
        },
        {
          "field": "contact_last_name",
          "type": "String",
          "is_filterable": true,
          "is_sortable": true,
          "is_read_only": false,
          "is_required": false,
          "is_virtual": false,
          "default_value": null,
          "integration": null,
          "reference": null,
          "inverse_of": null,
          "relationship": null,
          "widget": null,
          "validations": [
            {
              "type": "is shorter than",
              "message": "Ensure this value has at most 256 characters",
              "value": 256
            },
            {
              "type": "is present",
              "message": "Ensure this value is not null or not empty"
            }
          ]
        },
      ]
    },

Expected behavior

I expected to see the SmartField somewhere and no luck so far. I tried looking for an example on the Github repositories for Django but no luck. All I need to find is a very good example of a SmartField since I’m blocked on my progress due to a massive table where adding a new field would slow the performance and imply massive downtime.

Failure Logs

No failure logs were reported, BUT I did write a print statement when joining the two string fields and never got it to show up anywhere on the logs.

Context

  • Project name: Kiara Dashboard
  • Team name: Operations
  • Environment name: Development
  • Agent type & version: Django 1.4.4 Python 3.10.9
  • Recent changes made on your end if any: Nope

Hi guys! I also tried to use the Question model I found in the library tests for SmartFields without success.

class Question(models.Model):
    question_text = models.CharField(max_length=200, validators=[MinLengthValidator(1)], default='how are you?')
    pub_date = models.DateTimeField('date published', default=datetime.datetime(2020, 5, 17))

    def __str__(self):
        return self.question_text


class QuestionForest(Collection):
    name = 'Question'

    def load(self):
        self.fields = [
            {
                'field': 'foo',
                'type': 'String',
                'get': self.get_foo,
                'set': self.set_foo,
            },
            {
                'field': 'question_text',  # override existing field
                'type': 'String',
            }
        ]
        self.segments = [
            {
                'name': 'Best questions',
                'where': self.best_questions
            }
        ]

        self.actions = [
            {
                'name': 'Mark as Live',
            },
            {
                'name': 'Upload Legal Docs',
                'endpoint': '/foo/upload_legal_docs',
                'http_method': 'GET',
                'download': True,
                'type': 'single',
                'fields': [
                    {
                        'field': 'Certificate of Incorporation',
                        'description': 'The legal document relating to the formation of a company or corporation.',
                        'type': 'File',
                        'isRequired': True
                    }, {
                        'field': 'Proof of address',
                        'description': '(Electricity, Gas, Water, Internet, Landline & Mobile Phone Invoice / Payment Schedule) no older than 3 months of the legal representative of your company',
                        'type': 'File',
                        'isRequired': True
                    }, {
                        'field': 'Company bank statement',
                        'description': 'PDF including company name as well as IBAN',
                        'type': 'File',
                        'isRequired': True
                    }, {
                        'field': 'Valid proof of ID',
                        'description': 'ID card or passport if the document has been issued in the EU, EFTA, or EEA / ID card or passport + resident permit or driving licence if the document has been issued outside the EU, EFTA, or EEA of the legal representative of your company',
                        'type': 'File',
                        'isRequired': True
                    }
                ]
            },
            {
                'name': 'Send invoice',
                'type': 'single',
                'fields': [
                    {
                        'field': 'country',
                        'type': 'Enum',
                        'enums': ['FR', 'US']
                    },
                    {
                        'field': 'city',
                        'type': 'String',
                        'hook': 'cityChange'
                    },
                    {
                        'field': 'zip code',
                        'type': 'String',
                        'hook': 'zipCodeChange'
                    },
                ],
                'hooks': {
                    'load': self.invoice_load,
                    'change': {
                        'cityChange': self.invoice_change_city,
                        'zipCodeChange': self.invoice_change_zip_code,
                    },
                },
            }
        ]

    def get_foo(self, obj):
        return obj.question_text + 'foo'

    def set_foo(self, obj, value):
        obj.question_text = f'{value}-foo'
        return obj

    def best_questions(self, obj):
        questions = Question.objects.raw('''SELECT tests_question.id, COUNT(tests_choices.*)
            FROM tests_question
            JOIN tests_choices ON tests_choices.question_id = question.id
            GROUP BY question.id
            ORDER BY count DESC
            LIMIT 5;''')
        return {'id__in': [question.id for question in questions]}

    def invoice_load(self, fields, request, *args, **kwargs):
        country = next((x for x in fields if x['field'] == 'country'), None)
        country['value'] = 'FR'
        return fields

    def invoice_change_city(self, fields, request, changed_field, *args, **kwargs):
        zip_code = next((x for x in fields if x['field'] == 'zip code'), None)
        zip_code['value'] = '83'
        return fields

    def invoice_change_zip_code(self, fields, request, changed_field, *args, **kwargs):
        return fields


Collection.register(QuestionForest, Question)

No result either… I also added the name inside the load, outside the load, upper case, lower case, with the package name (account) prepended, copied and pasted the collection name I found on the schema file generated but no luck either.

Hey @Emanuel_Avendano :wave:

Did you create a forest folder inside your app?
On my end:

image

init.py

from .company import CompanyForest

and

company.py

from django_forest.utils.collection import Collection
from polls.models import Company


class CompanyForest(Collection):
    def load(self):
        self.fields = [
            {
                'field': 'nameWithCreatedAt',
                'type': 'String',
                'get': self.get_name_with_created_at,
            },
        ]

    def get_name_with_created_at(self, obj):
        return f'{obj.name} {obj.created_at}'


Collection.register(CompanyForest, Company)

seems to work as expected.

Let me know if that helps.

Hi @jeffladiray !

It did work! Thanks a lot for your help, much appreciated! I think the documentation needs to clarify that the forest directory needs to be created inside the directory generated after the execution of the django-admin startapp [app_name] command.The directory generated is going to have the same name as the [app_name] parameter from the previous command. I was putting it in the wrong directory, hence never finding the smart fields.