def create_field(
    name,
    ftype,
    embedded_doc_type=None,
    subfield=None,
    fields=None,
    db_field=None,
    description=None,
    info=None,
    read_only=False,
    created_at=None,
    **kwargs,
):
    """Creates the field defined by the given specification.

    .. note::

        This method is used exclusively to create user-defined (non-default)
        fields. Any parameters accepted here must be stored on
        :class:`fiftyone.core.odm.dataset.SampleFieldDocument` or else datasets
        will "lose" any additional decorations when they are loaded from the
        database.

    Args:
        name: the field name
        ftype: the field type to create. Must be a subclass of
            :class:`fiftyone.core.fields.Field`
        embedded_doc_type (None): the
            :class:`fiftyone.core.odm.BaseEmbeddedDocument` type of the field.
            Only applicable when ``ftype`` is
            :class:`fiftyone.core.fields.EmbeddedDocumentField`
        subfield (None): the :class:`fiftyone.core.fields.Field` type of the
            contained field. Only applicable when ``ftype`` is
            :class:`fiftyone.core.fields.ListField` or
            :class:`fiftyone.core.fields.DictField`
        fields (None): a list of :class:`fiftyone.core.fields.Field` instances
            defining embedded document attributes. Only applicable when
            ``ftype`` is :class:`fiftyone.core.fields.EmbeddedDocumentField`
        db_field (None): the database field to store this field in. By default,
            ``name`` is used
        description (None): an optional description
        info (None): an optional info dict
        read_only (False): whether the field should be read-only
        created_at (None): the datetime the field was created

    Returns:
        a :class:`fiftyone.core.fields.Field`
    """
    if db_field is None:
        if issubclass(ftype, fof.ObjectIdField) and not name.startswith("_"):
            db_field = "_" + name
        else:
            db_field = name

    field_kwargs = dict(
        null=True,  # all user-defined fields are nullable
        db_field=db_field,
        description=description,
        info=info,
        read_only=read_only,
        created_at=created_at,
    )
    field_kwargs.update(kwargs)

    if fields is not None:
        fields = [
            create_field(**f) if not isinstance(f, fof.Field) else f
            for f in fields
        ]

    if issubclass(ftype, (fof.ListField, fof.DictField)):
        if subfield is not None:
            if inspect.isclass(subfield):
                if issubclass(subfield, fof.EmbeddedDocumentField):
                    subfield = subfield(embedded_doc_type)
                else:
                    subfield = subfield()

            if not isinstance(subfield, fof.Field):
                raise ValueError(
                    "Invalid subfield type %s; must be a subclass of %s"
                    % (type(subfield), fof.Field)
                )

            if (
                isinstance(subfield, fof.EmbeddedDocumentField)
                and fields is not None
            ):
                subfield.fields = fields

            field_kwargs["field"] = subfield
    elif issubclass(ftype, fof.EmbeddedDocumentField):
        if embedded_doc_type is None or not issubclass(
            embedded_doc_type, fooe.BaseEmbeddedDocument
        ):
            raise ValueError(
                "Invalid embedded_doc_type %s; must be a subclass of %s"
                % (embedded_doc_type, fooe.BaseEmbeddedDocument)
            )

        field_kwargs["document_type"] = embedded_doc_type
        field_kwargs["fields"] = fields or []

    field = ftype(**field_kwargs)
    field.name = name

    return field
Creates the field defined by the given specification.

    .. note::

        This method is used exclusively to create user-defined (non-default)
        fields. Any parameters accepted here must be stored on
        :class:`fiftyone.core.odm.dataset.SampleFieldDocument` or else datasets
        will "lose" any additional decorations when they are loaded from the
        database.

    Args:
        name: the field name
        ftype: the field type to create. Must be a subclass of
            :class:`fiftyone.core.fields.Field`
        embedded_doc_type (None): the
            :class:`fiftyone.core.odm.BaseEmbeddedDocument` type of the field.
            Only applicable when ``ftype`` is
            :class:`fiftyone.core.fields.EmbeddedDocumentField`
        subfield (None): the :class:`fiftyone.core.fields.Field` type of the
            contained field. Only applicable when ``ftype`` is
            :class:`fiftyone.core.fields.ListField` or
            :class:`fiftyone.core.fields.DictField`
        fields (None): a list of :class:`fiftyone.core.fields.Field` instances
            defining embedded document attributes. Only applicable when
            ``ftype`` is :class:`fiftyone.core.fields.EmbeddedDocumentField`
        db_field (None): the database field to store this field in. By default,
            ``name`` is used
        description (None): an optional description
        info (None): an optional info dict
        read_only (False): whether the field should be read-only
        created_at (None): the datetime the field was created

    Returns:
        a :class:`fiftyone.core.fields.Field`
    
fiftyone/core/odm/utils.py