---
url: "https://vueda.dev/v3/reference/api/py/vueda.info.serializers/ModelInfoSerializer.md"
title: "ModelInfoSerializer"
---

# ModelInfoSerializer

## Overview

A serializer for providing metadata about models, including fields, actions, and permissions.

This is a read-only serializer.

Effectively, this is a custom model serializer for content types.

## `Meta` {#Meta}

### Source {#Meta-source}

`server/vueda/info/serializers.py:149`

## `canonical` {#canonical}

### Signature {#canonical-signature}

`canonical(unknown)`

#### Parameters {#canonical-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="canonical-param-unknown">unknown</span> |  | yes |  |  |

## `customize_schema_request_data` {#customize_schema_request_data}

### Signature {#customize_schema_request_data-signature}

`customize_schema_request_data(self, request_data)`

#### Parameters {#customize_schema_request_data-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="customize_schema_request_data-param-request_data">request_data</span> |  | yes |  |  |

### Source {#customize_schema_request_data-source}

`server/vueda/info/serializers.py:1630`

## `customize_schema_response_data` {#customize_schema_response_data}

### Signature {#customize_schema_response_data-signature}

`customize_schema_response_data(self, auto_schema, response_data)`

#### Parameters {#customize_schema_response_data-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="customize_schema_response_data-param-auto_schema">auto_schema</span> |  | yes |  |  |
| <span id="customize_schema_response_data-param-response_data">response_data</span> |  | yes |  |  |

### Source {#customize_schema_response_data-source}

`server/vueda/info/serializers.py:1637`

## `get_annotation_ordering_type` {#get_annotation_ordering_type}

The semantic type of a queryset annotation a client may order by.

An annotation has no model field behind it to read a type from, so the type comes from the
expression's own ``output_field``. Django resolves that for most expressions and refuses to
guess for some — a ``Coalesce`` over mixed types raises ``FieldError`` rather than pick one —
so an annotation whose type can't be resolved falls back to ``"alpha"``, the same way a column
of an unmapped type does.

A path that applies transforms to an annotation, such as ``ordered__year``, is typed from the
output of its last transform. ``ordered.year`` is ``numeric`` even when ``ordered`` is a date.

### Signature {#get_annotation_ordering_type-signature}

`get_annotation_ordering_type(queryset, annotation_name)`

#### Parameters {#get_annotation_ordering_type-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="get_annotation_ordering_type-param-queryset">queryset</span> |  | yes |  |  |
| <span id="get_annotation_ordering_type-param-annotation_name">annotation_name</span> |  | yes |  |  |

### Source {#get_annotation_ordering_type-source}

`server/vueda/info/serializers.py:697`

## `get_choices_data` {#get_choices_data}

Get the choices data for a field, widget, or filter object.

We are careful to avoid evaluating querysets, as this can be expensive. Accessing .choices will evaluate the
 queryset for certain fields and filter objects.

:param field: A rest_framework field.
:type field: rest_framework.fields.Field
:param widget: A django.forms widget.
:type widget: django.forms.widgets.Widget
:param filter_obj: A django_filters filter object.
:type filter_obj: django_filters.filters.Filter
:return: The literal choices, or True if the choices are queryset-based, or False if there are no choices.
:rtype: Union[List, Tuple, bool]

### Signature {#get_choices_data-signature}

`get_choices_data(field, widget, filter_obj)`

#### Parameters {#get_choices_data-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="get_choices_data-param-field">field</span> |  | yes |  |  |
| <span id="get_choices_data-param-widget">widget</span> |  | yes |  |  |
| <span id="get_choices_data-param-filter_obj">filter_obj</span> |  | no | `None` |  |

### Source {#get_choices_data-source}

`server/vueda/info/serializers.py:1147`

## `get_choices_meta` {#get_choices_meta}

Get the metadata for queryset-backed choices.

Only queryset-backed choices get metadata. A field whose choices are literal values
(``ChoiceFilter``, ``TypedChoiceFilter``, a model field's ``choices``, ...) reports those values
directly, so it needs no ``app_label``/``model`` pointer for the client to follow.

Filters that build their choices from the values currently stored in a column
(``AllValuesFilter``, ``AllValuesMultipleFilter``) also look like literal choices here, but their
set is dynamic and must be read from the choices endpoint. Their callers detect them by type and
resolve the metadata themselves; do not try to detect them here. In particular, do not test for a
``model`` attribute: ``BaseFilterSet.__init__`` assigns ``model`` to *every* filter it holds, so
that test passes for all filters on an instantiated filterset.

:param field: The field that the choices are attached to.
:type field: Union[django_filters.filters.Filter, rest_framework.fields.Field]
:param choices: The choices data or True if the choices are queryset-based or False if there are no choices.
:type choices: Union[List, Tuple, bool]
:return: The metadata for the queryset-backed choices, or None if the choices are not queryset-backed.
:rtype: Optional[django.db.models.options.Options]

### Signature {#get_choices_meta-signature}

`get_choices_meta(field, choices)`

#### Parameters {#get_choices_meta-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="get_choices_meta-param-field">field</span> |  | yes |  |  |
| <span id="get_choices_meta-param-choices">choices</span> |  | yes |  |  |

### Source {#get_choices_meta-source}

`server/vueda/info/serializers.py:1222`

## `get_default_ordering_data` {#get_default_ordering_data}

Metadata for every term of a default ordering (a model's ``Meta.ordering``, or a viewset's
own ``ordering``). The result is empty when any one of those terms can't be resolved.

A default ordering only means something as a whole: rows arrive sorted by the first term,
then by the second, and so on. Reporting only the terms that do resolve would tell the
client the rows are sorted in an order they aren't, so an unresolvable term drops the whole
default ordering rather than part of it. A term that names a field the model does not have
is unresolvable, and the ``vueda_info.E006`` system check reports it. A term that names
several fields at once, such as a ``Concat`` of two columns, is unresolvable as well: it has
no single name that stands for the sort it performs (see ``UnnameableOrderingTermError``).
``VuedaOrderingFilter`` still accepts an explicit ``?o=`` request on each of those fields.
What is dropped is the claim about how the rows currently arrive, not the fields themselves.

A term naming an annotation that ``queryset`` carries, or a transform on one, is reported
under that name and typed from the annotation (see ``get_ordering_data``). ``queryset`` is the
viewset's own, built without a request, so it carries every annotation that its
``get_queryset`` and its manager add. A model's ``Meta.ordering`` with no viewset to read is
resolved without one, and a name there that isn't a model field drops the default as any
unknown name does.

### Signature {#get_default_ordering_data-signature}

`get_default_ordering_data(self, model, ordering, queryset)`

#### Parameters {#get_default_ordering_data-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_default_ordering_data-param-model">model</span> |  | yes |  |  |
| <span id="get_default_ordering_data-param-ordering">ordering</span> |  | yes |  |  |
| <span id="get_default_ordering_data-param-queryset">queryset</span> |  | no | `None` |  |

### Source {#get_default_ordering_data-source}

`server/vueda/info/serializers.py:721`

## `get_default_ordering_field_names` {#get_default_ordering_field_names}

Every field a default ordering references, mapped to that field's semantic type, in the order
the terms name them. An annotation that ``queryset`` carries counts as a field here, typed the
way ``get_ordering_data`` types it.

This is the field-by-field view of a default ordering, as opposed to the term-by-term view
``get_default_ordering_data`` reports. The two differ for a term that names more than one
field: such a term has no single name to report the *default* under (see
``UnnameableOrderingTermError``), but each field it names is still a valid explicit ``?o=``
target, so each is still a field to advertise.

A name that is an annotation on ``queryset``, or a transform on one, is included under that
name, typed the way ``get_ordering_data`` types it. A path that resolves to neither a field nor
an annotation is skipped, for the same reason it is elsewhere: DRF would raise on it, so there is nothing to
offer. The ``vueda_info.E006`` system check reports the declaration.

:param model: The model the ordering is declared against.
:type model: Type[django.db.models.Model]
:param ordering: The default ordering declaration.
:type ordering: Union[str, Iterable]
:param queryset: The viewset's own queryset, whose annotations the ordering may name. None
    for a model's ``Meta.ordering`` with no viewset to read.
:type queryset: Optional[django.db.models.QuerySet]
:return: Field path -&gt; semantic type, in declaration order.
:rtype: Dict[str, str]

### Signature {#get_default_ordering_field_names-signature}

`get_default_ordering_field_names(self, model, ordering, queryset)`

#### Parameters {#get_default_ordering_field_names-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_default_ordering_field_names-param-model">model</span> |  | yes |  |  |
| <span id="get_default_ordering_field_names-param-ordering">ordering</span> |  | yes |  |  |
| <span id="get_default_ordering_field_names-param-queryset">queryset</span> |  | no | `None` |  |

### Source {#get_default_ordering_field_names-source}

`server/vueda/info/serializers.py:992`

## `get_default_ordering_field_sources` {#get_default_ordering_field_sources}

Replicates ``rest_framework.filters.OrderingFilter.get_default_valid_fields``: the field
sources DRF allows ordering on when a viewset doesn't declare ``ordering_fields``.

The serializer is built with this serializer's own ``context``, which carries the ``view``.
Reading ``fields`` runs ``get_fields()``, and a serializer may need the view to decide what
those fields are. DRF passes a context here for the same reason.

The ``view`` in that context is the model-info viewset, not the viewset being described, so a
serializer that varies its field set by ``view.action`` is resolved against ``retrieve`` on
``/info/`` rather than against ``list`` on the endpoint the client will call. Such a
serializer can advertise a different set of ordering fields than that endpoint accepts. There
is no context that fits every serializer. DRF's own ``get_default_valid_fields`` passes
``{"request": request}`` with no ``view`` at all. Passing that here would make the two agree for
a serializer that accepts a missing view. It would also make ``/info/`` raise for a serializer
that requires a view, which already fails on the real list request. Declare ``ordering_fields``
on a viewset whose serializer varies its fields by action, which takes both this method and
DRF's out of the picture.

### Signature {#get_default_ordering_field_sources-signature}

`get_default_ordering_field_sources(self, model)`

#### Parameters {#get_default_ordering_field_sources-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_default_ordering_field_sources-param-model">model</span> |  | yes |  |  |

### Source {#get_default_ordering_field_sources-source}

`server/vueda/info/serializers.py:1041`

## `get_model_actions` {#get_model_actions}

Get the actions for a model and their own metadata.
Actions will be sorted by method action, followed by sorted extra actions.

Each CRUD action is checked against its own required permission through
:func:`vueda.core.permissions.check_action_permission`, called with no instance --
model-scope discovery decides through ``has_permission`` alone, the same model-level
check a CRUD action's own per-object discovery relies on before it ever reaches a row, so
a matching workflow-state grant can settle a model-level denial here too. A model-level
action describes what a requester might do on some instance of the model, not a guarantee
that holds for every instance; the per-object surfaces (an object's own
``available_actions``) decide that separately, through ``has_object_permission``.

### Signature {#get_model_actions-signature}

`get_model_actions(self, instance)`

#### Parameters {#get_model_actions-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_model_actions-param-instance">instance</span> |  | yes |  |  |

### Source {#get_model_actions-source}

`server/vueda/info/serializers.py:453`

## `get_model_column_totals` {#get_model_column_totals}

The column totals a ``list`` request may ask for.

A section of its own rather than a flag on each entry of ``model_fields``, because the two
don't line up: ``model_fields`` is built from the canonical serializer, while
``column_totals`` lives on the viewset and names its totals after the client's columns. A
total named ``product_price`` that no serializer field matches would have nowhere to be
reported, and a client's display columns include ``foo__bar`` names ``model_fields`` doesn't
carry either.

``fields`` are the declared total names, in declaration order; a client asks for the ones
its visible columns can render.

The section reports which totals exist, not the parameter that asks for them. A client
sends the parameter from its own constant, so a project that changes
``settings.COLUMN_TOTALS_PARAM`` needs a matching client change. Reporting the parameter
name here is part of the wider parameter-name discovery work, which lands after v3.0.0
rather than a parameter at a time.

A viewset with no ``column_totals``, or one whose declaration isn't a mapping, reports no
fields -- the same thing its ``list`` action offers. ``vueda_info.E013`` reports the
misconfigured declaration itself.

Read from the ``column_totals`` attribute rather than through
``VuedaViewSet.get_declared_column_totals()``, which is what ``list`` and the OpenAPI schema
call. Registration stores the viewset class, and that hook is an instance method whose
override is free to depend on the request -- there is no instance here to ask, and no
request to ask about. So ``column_totals`` is the advertised set: a viewset narrowing its
totals per request through that hook still advertises everything it declares, and a client
asking for one the hook withheld gets the 400 that ``get_requested_column_totals`` raises.
A total that no declaration carries cannot be advertised here at all.

### Signature {#get_model_column_totals-signature}

`get_model_column_totals(self, instance)`

#### Parameters {#get_model_column_totals-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_model_column_totals-param-instance">instance</span> |  | yes |  |  |

### Source {#get_model_column_totals-source}

`server/vueda/info/serializers.py:576`

## `get_model_expands` {#get_model_expands}

Get the expands for a model and their own metadata.

### Signature {#get_model_expands-signature}

`get_model_expands(self, instance)`

#### Parameters {#get_model_expands-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_model_expands-param-instance">instance</span> |  | yes |  |  |

### Source {#get_model_expands-source}

`server/vueda/info/serializers.py:539`

## `get_model_field_choices` {#get_model_field_choices}

Get the choices for a model field.

:param field: A rest_framework field.
:type field: rest_framework.fields.Field
:param widget: A django.forms widget.
:type widget: django.forms.widgets.Widget
:param serializer: A rest_framework serializer.
:type serializer: rest_framework.serializers.Serializer
:return: Returns a tuple, with the first value being the choices list or True if the choices are model-based,
  or False if there are no choices. The second value is the metadata for the model-based choices.
:rtype: Tuple[Union[List, Tuple, bool], Optional[django.db.models.options.Options]]

### Signature {#get_model_field_choices-signature}

`get_model_field_choices(self, field, widget, serializer)`

#### Parameters {#get_model_field_choices-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_model_field_choices-param-field">field</span> |  | yes |  |  |
| <span id="get_model_field_choices-param-widget">widget</span> |  | yes |  |  |
| <span id="get_model_field_choices-param-serializer">serializer</span> |  | yes |  |  |

### Source {#get_model_field_choices-source}

`server/vueda/info/serializers.py:1254`

## `get_model_fields` {#get_model_fields}

Get the fields for a model and their own metadata.

### Signature {#get_model_fields-signature}

`get_model_fields(self, instance)`

#### Parameters {#get_model_fields-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_model_fields-param-instance">instance</span> |  | yes |  |  |

### Source {#get_model_fields-source}

`server/vueda/info/serializers.py:430`

## `get_model_fields_data` {#get_model_fields_data}

### Signature {#get_model_fields_data-signature}

`get_model_fields_data(self, serializer, excluded_fields, context)`

#### Parameters {#get_model_fields_data-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_model_fields_data-param-serializer">serializer</span> |  | yes |  |  |
| <span id="get_model_fields_data-param-excluded_fields">excluded_fields</span> |  | no | `frozenset()` |  |
| <span id="get_model_fields_data-param-context">context</span> |  | no | `None` |  |

### Source {#get_model_fields_data-source}

`server/vueda/info/serializers.py:346`

## `get_model_fields_db_field_type` {#get_model_fields_db_field_type}

### Signature {#get_model_fields_db_field_type-signature}

`get_model_fields_db_field_type(field_name, model_field, many)`

#### Parameters {#get_model_fields_db_field_type-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="get_model_fields_db_field_type-param-field_name">field_name</span> |  | yes |  |  |
| <span id="get_model_fields_db_field_type-param-model_field">model_field</span> |  | yes |  |  |
| <span id="get_model_fields_db_field_type-param-many">many</span> |  | yes |  |  |

### Source {#get_model_fields_db_field_type-source}

`server/vueda/info/serializers.py:282`

## `get_model_fields_max_data` {#get_model_fields_max_data}

### Signature {#get_model_fields_max_data-signature}

`get_model_fields_max_data(self, field, model_field)`

#### Parameters {#get_model_fields_max_data-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_model_fields_max_data-param-field">field</span> |  | yes |  |  |
| <span id="get_model_fields_max_data-param-model_field">model_field</span> |  | yes |  |  |

### Source {#get_model_fields_max_data-source}

`server/vueda/info/serializers.py:253`

## `get_model_fields_min_data` {#get_model_fields_min_data}

### Signature {#get_model_fields_min_data-signature}

`get_model_fields_min_data(self, field, model_field)`

#### Parameters {#get_model_fields_min_data-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_model_fields_min_data-param-field">field</span> |  | yes |  |  |
| <span id="get_model_fields_min_data-param-model_field">model_field</span> |  | yes |  |  |

### Source {#get_model_fields_min_data-source}

`server/vueda/info/serializers.py:224`

## `get_model_fields_model_field_type` {#get_model_fields_model_field_type}

### Signature {#get_model_fields_model_field_type-signature}

`get_model_fields_model_field_type(field_name, model_field, many)`

#### Parameters {#get_model_fields_model_field_type-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="get_model_fields_model_field_type-param-field_name">field_name</span> |  | yes |  |  |
| <span id="get_model_fields_model_field_type-param-model_field">model_field</span> |  | yes |  |  |
| <span id="get_model_fields_model_field_type-param-many">many</span> |  | yes |  |  |

### Source {#get_model_fields_model_field_type-source}

`server/vueda/info/serializers.py:302`

## `get_model_fields_serializer_field_type` {#get_model_fields_serializer_field_type}

### Signature {#get_model_fields_serializer_field_type-signature}

`get_model_fields_serializer_field_type(field_name, model_field, many)`

#### Parameters {#get_model_fields_serializer_field_type-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="get_model_fields_serializer_field_type-param-field_name">field_name</span> |  | yes |  |  |
| <span id="get_model_fields_serializer_field_type-param-model_field">model_field</span> |  | yes |  |  |
| <span id="get_model_fields_serializer_field_type-param-many">many</span> |  | yes |  |  |

### Source {#get_model_fields_serializer_field_type-source}

`server/vueda/info/serializers.py:320`

## `get_model_filtering` {#get_model_filtering}

Get the filtering fields for a model and their own metadata.

### Signature {#get_model_filtering-signature}

`get_model_filtering(self, instance)`

#### Parameters {#get_model_filtering-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_model_filtering-param-instance">instance</span> |  | yes |  |  |

### Source {#get_model_filtering-source}

`server/vueda/info/serializers.py:1456`

## `get_model_filtering_choices` {#get_model_filtering_choices}

### Signature {#get_model_filtering_choices-signature}

`get_model_filtering_choices(self, filterset, filter_obj, field, widget)`

#### Parameters {#get_model_filtering_choices-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_model_filtering_choices-param-filterset">filterset</span> |  | yes |  |  |
| <span id="get_model_filtering_choices-param-filter_obj">filter_obj</span> |  | yes |  |  |
| <span id="get_model_filtering_choices-param-field">field</span> |  | yes |  |  |
| <span id="get_model_filtering_choices-param-widget">widget</span> |  | yes |  |  |

### Source {#get_model_filtering_choices-source}

`server/vueda/info/serializers.py:1306`

## `get_model_filtering_decimal_places` {#get_model_filtering_decimal_places}

### Signature {#get_model_filtering_decimal_places-signature}

`get_model_filtering_decimal_places(field, model_field)`

#### Parameters {#get_model_filtering_decimal_places-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="get_model_filtering_decimal_places-param-field">field</span> |  | yes |  |  |
| <span id="get_model_filtering_decimal_places-param-model_field">model_field</span> |  | yes |  |  |

### Source {#get_model_filtering_decimal_places-source}

`server/vueda/info/serializers.py:1337`

## `get_model_filtering_error_messages` {#get_model_filtering_error_messages}

### Signature {#get_model_filtering_error_messages-signature}

`get_model_filtering_error_messages(filter_obj, field)`

#### Parameters {#get_model_filtering_error_messages-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="get_model_filtering_error_messages-param-filter_obj">filter_obj</span> |  | yes |  |  |
| <span id="get_model_filtering_error_messages-param-field">field</span> |  | yes |  |  |

### Source {#get_model_filtering_error_messages-source}

`server/vueda/info/serializers.py:1348`

## `get_model_filtering_fallback_label` {#get_model_filtering_fallback_label}

A title cased label built from ``filter_name``, for a filter whose path django-filter can't name.

It is built the way ``django_filters.utils.label_for_filter`` builds one, with the filter's
name in place of the verbose field names: ``exclude`` first for a negated filter, then the name
made readable the way Django labels a form field that declares no label
(``django.forms.utils.pretty_name``), then the lookup expression in words. A lookup expression
that the name already ends with is taken off the name first, so
``customer_formatted_name_icontains`` reads ``Customer Formatted Name Contains`` rather than
repeating the lookup. A dot in a public filter name reads as a space.

:param filter_obj: The filter to label.
:type filter_obj: django_filters.filters.Filter
:param filter_name: The filter's name, as ``model_filtering`` reports it.
:type filter_name: str
:return: The label.
:rtype: str

### Signature {#get_model_filtering_fallback_label-signature}

`get_model_filtering_fallback_label(filter_obj, filter_name)`

#### Parameters {#get_model_filtering_fallback_label-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="get_model_filtering_fallback_label-param-filter_obj">filter_obj</span> |  | yes |  |  |
| <span id="get_model_filtering_fallback_label-param-filter_name">filter_name</span> |  | yes |  |  |

### Source {#get_model_filtering_fallback_label-source}

`server/vueda/info/serializers.py:1079`

## `get_model_filtering_input_type` {#get_model_filtering_input_type}

### Signature {#get_model_filtering_input_type-signature}

`get_model_filtering_input_type(filter_obj, field, widget)`

#### Parameters {#get_model_filtering_input_type-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="get_model_filtering_input_type-param-filter_obj">filter_obj</span> |  | yes |  |  |
| <span id="get_model_filtering_input_type-param-field">field</span> |  | yes |  |  |
| <span id="get_model_filtering_input_type-param-widget">widget</span> |  | yes |  |  |

### Source {#get_model_filtering_input_type-source}

`server/vueda/info/serializers.py:1362`

## `get_model_filtering_label` {#get_model_filtering_label}

The filter's declared label, or a title cased label generated from its field name and lookup
expression when it doesn't declare one.

``declared_label`` has to come from the filter as declared on the filterset *class*, not from
the bound copy a filterset instance holds. ``Filter.label`` is a property that generates a
label with ``label_for_filter`` the first time it is read on a model-bound filter and then
caches it onto the filter, and building ``Filter.field`` reads it. So by the time we get here
every bound filter reports a label, and none of them can still tell us whether that label was
declared or generated.

django-filter builds a label from the verbose names of the model fields that the path names.
A path it can't follow, such as a related ``formatted_name`` reached through a lookup
expression or a path that ends in a transform, gives it nothing to name, and it labels the
filter ``[invalid name]``. The label falls back to ``filter_name`` instead, made readable the
way Django labels a form field that declares no label (``django.forms.utils.pretty_name``):
``customer_name`` becomes ``Customer Name``. A dotted public name reads the same way, so
``customer.formatted_name`` becomes ``Customer Formatted Name``.

### Signature {#get_model_filtering_label-signature}

`get_model_filtering_label(filter_obj, model, declared_label, filter_name)`

#### Parameters {#get_model_filtering_label-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="get_model_filtering_label-param-filter_obj">filter_obj</span> |  | yes |  |  |
| <span id="get_model_filtering_label-param-model">model</span> |  | yes |  |  |
| <span id="get_model_filtering_label-param-declared_label">declared_label</span> |  | no | `None` |  |
| <span id="get_model_filtering_label-param-filter_name">filter_name</span> |  | no | `None` |  |

### Source {#get_model_filtering_label-source}

`server/vueda/info/serializers.py:1112`

## `get_model_filtering_max_digits` {#get_model_filtering_max_digits}

### Signature {#get_model_filtering_max_digits-signature}

`get_model_filtering_max_digits(field, model_field)`

#### Parameters {#get_model_filtering_max_digits-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="get_model_filtering_max_digits-param-field">field</span> |  | yes |  |  |
| <span id="get_model_filtering_max_digits-param-model_field">model_field</span> |  | yes |  |  |

### Source {#get_model_filtering_max_digits-source}

`server/vueda/info/serializers.py:1375`

## `get_model_filtering_max_length` {#get_model_filtering_max_length}

### Signature {#get_model_filtering_max_length-signature}

`get_model_filtering_max_length(field, model_field)`

#### Parameters {#get_model_filtering_max_length-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="get_model_filtering_max_length-param-field">field</span> |  | yes |  |  |
| <span id="get_model_filtering_max_length-param-model_field">model_field</span> |  | yes |  |  |

### Source {#get_model_filtering_max_length-source}

`server/vueda/info/serializers.py:1386`

## `get_model_filtering_max_value` {#get_model_filtering_max_value}

### Signature {#get_model_filtering_max_value-signature}

`get_model_filtering_max_value(self, field, model_field)`

#### Parameters {#get_model_filtering_max_value-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_model_filtering_max_value-param-field">field</span> |  | yes |  |  |
| <span id="get_model_filtering_max_value-param-model_field">model_field</span> |  | yes |  |  |

### Source {#get_model_filtering_max_value-source}

`server/vueda/info/serializers.py:1397`

## `get_model_filtering_min_length` {#get_model_filtering_min_length}

### Signature {#get_model_filtering_min_length-signature}

`get_model_filtering_min_length(field, model_field)`

#### Parameters {#get_model_filtering_min_length-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="get_model_filtering_min_length-param-field">field</span> |  | yes |  |  |
| <span id="get_model_filtering_min_length-param-model_field">model_field</span> |  | yes |  |  |

### Source {#get_model_filtering_min_length-source}

`server/vueda/info/serializers.py:1406`

## `get_model_filtering_min_value` {#get_model_filtering_min_value}

### Signature {#get_model_filtering_min_value-signature}

`get_model_filtering_min_value(self, field, model_field)`

#### Parameters {#get_model_filtering_min_value-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_model_filtering_min_value-param-field">field</span> |  | yes |  |  |
| <span id="get_model_filtering_min_value-param-model_field">model_field</span> |  | yes |  |  |

### Source {#get_model_filtering_min_value-source}

`server/vueda/info/serializers.py:1417`

## `get_model_filtering_type` {#get_model_filtering_type}

### Signature {#get_model_filtering_type-signature}

`get_model_filtering_type(self, filter_name, filter_obj, field)`

#### Parameters {#get_model_filtering_type-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_model_filtering_type-param-filter_name">filter_name</span> |  | yes |  |  |
| <span id="get_model_filtering_type-param-filter_obj">filter_obj</span> |  | yes |  |  |
| <span id="get_model_filtering_type-param-field">field</span> |  | yes |  |  |

### Source {#get_model_filtering_type-source}

`server/vueda/info/serializers.py:1297`

## `get_model_filtering_validators` {#get_model_filtering_validators}

### Signature {#get_model_filtering_validators-signature}

`get_model_filtering_validators(field)`

#### Parameters {#get_model_filtering_validators-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="get_model_filtering_validators-param-field">field</span> |  | yes |  |  |

### Source {#get_model_filtering_validators-source}

`server/vueda/info/serializers.py:1426`

## `get_model_ordering` {#get_model_ordering}

Return the ``model_ordering`` section: the ordering a list request gets when it sends no
``o``, and every field that ``o`` may name.

``default`` lists field names in order. It comes from the viewset's ``ordering`` when that is
declared, and from the model's ``Meta.ordering`` otherwise. The two are never merged, and a
viewset ``ordering`` that fails to resolve does not fall back to the model's. A model with no
canonical viewset still reports its ``Meta.ordering``, and ``fields`` then holds only the
fields that ordering reads.

``fields`` lists a ``name`` and a ``type`` per field, built from the viewset's
``ordering_fields`` the way DRF's ``OrderingFilter`` reads that attribute:

- An explicit list reports each entry, whether it is a plain name or a
  ``(field_name, label)`` pair. The label is never reported.
- ``"__all__"`` reports the model's own fields by field name (a foreign key as ``customer``,
  never ``customer_id``) and every annotation that the viewset's ``get_queryset`` adds.
- An undeclared ``ordering_fields`` (``None``, DRF's default) reports the canonical
  serializer's readable fields by ``source`` (see ``get_default_ordering_field_sources``).
  Model properties and fields with no orderable path are left out.
- An empty list offers nothing beyond the default ordering's fields.

Every field that the default ordering reads is added to ``fields``, because
``VuedaOrderingFilter`` accepts ``o`` for each of them. Only the fields named in the
reported ``default`` carry ``ascending``.

Names and types follow these rules:

- Each name is the dotted public form that a client sends back in ``o``.
- ``"pk"`` is reported as the field behind it: ``id``, or each field of a
  ``CompositePrimaryKey`` (see ``expand_ordering_pk``).
- ``formatted_name`` is reported under that name when the model stores it as a column or
  reaches it through ``formatted_name_lookup_expression``, and is typed from the column it
  lands on. A ``formatted_name`` computed by ``get_formatted_name()`` has no column, so it is
  left out (see ``ordering_path_field``). A related ``formatted_name`` is reported under
  its declared path, dotted.
- A path that ends in a transform is reported under its own dotted path, typed from the
  transform's output field. ``created__year`` is reported as
  ``{"name": "created.year", "type": "numeric"}``.
- A scalar function over one column is reported under that column, typed from the column,
  with ``ascending`` from the term's direction. ``Lower("name").desc()`` is reported as
  ``{"name": "name", "type": "alpha", "ascending": false}``, and ``Length("name")`` is typed
  ``alpha``.
- An annotation is typed from its ``output_field``, or ``"alpha"`` when Django cannot resolve
  one (see ``get_annotation_ordering_type``). A path that applies transforms to an annotation,
  such as ``ordered__year``, is reported as ``ordered.year`` and typed from the last
  transform's output. Both are reported whether ``ordering_fields`` or the default ordering
  names them.
- ``"?"`` in ``ordering_fields`` names no field, so it is not reported, although DRF accepts
  ``o=?`` for it and returns the rows in random order.
- An entry that resolves to no field is left out, and the ``vueda_info.E006`` system check
  reports it.

``default`` is reported whole or not at all (see ``get_default_ordering_data``). It is empty
when any term resolves to no field or annotation, or reads more than one column or none. Each
column that a multi-column term reads still appears in ``fields``, without ``ascending``.

The queryset is built from the viewset class with no request. A ``get_queryset`` that reads
``self.request`` raises here, so the model-info request fails. The ``vueda_info.E006`` system
check skips that viewset for the same reason.

### Signature {#get_model_ordering-signature}

`get_model_ordering(self, instance)`

#### Parameters {#get_model_ordering-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_model_ordering-param-instance">instance</span> |  | yes |  |  |

### Source {#get_model_ordering-source}

`server/vueda/info/serializers.py:774`

## `get_model_permissions` {#get_model_permissions}

Get the permissions for a model. Read-only serializers only ever expose list/retrieve
actions, so their create/update/delete permissions (which may still exist in the
database, since the model itself keeps the standard CRUD permission set) are filtered
out here rather than restricted on the model.

### Signature {#get_model_permissions-signature}

`get_model_permissions(self, instance)`

#### Parameters {#get_model_permissions-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_model_permissions-param-instance">instance</span> |  | yes |  |  |

### Source {#get_model_permissions-source}

`server/vueda/info/serializers.py:193`

## `get_ordering_data` {#get_ordering_data}

Metadata for one ordering term: the field name a client sends to request it, that field's
semantic type, and (unless ``include_ascending`` is false) the direction the term sorts in.

A term is a plain field name, optionally ``-`` prefixed, or any expression ``order_by()``
takes — an ``F(...).asc(nulls_first=True)``, or a scalar function such as ``Lower("name")`` or
``Coalesce("nickname", Value(""))``. Ordering expressions are mentioned near the bottom of
https://docs.djangoproject.com/en/5.2/ref/models/options/#ordering.

The name reported is the dotted public form of the field path as declared, not the path it
resolved through: a ``formatted_name`` ordering is reported as ``formatted_name``, which is
the name the client sends back in ``?o=`` (dotted, for a path that crosses a relation) and
the name the queryset annotation carries, not the lookup expression behind it. The type comes
from the column that path lands on, so it describes the field the client orders by rather
than what a function wrapped around it returns — ``Length("name")`` reports ``name`` as
``alpha``, not the integer the expression sorts on.

A path that ends in a transform is different, because the transform is part of the name that
the client sends back. ``created__year`` is reported as ``created.year`` and typed from the
transform's output field, so it is ``numeric`` rather than ``datetime``.

When ``queryset`` is given, a term that names one of its annotations, or a transform on one,
is reported under that name and typed from the annotation (see
``get_annotation_ordering_type``). A path that resolves as a model field is typed from the field
first, so a ``formatted_name`` reached through a lookup expression keeps the type of the column
it lands on. Without ``queryset``, an annotation name resolves to nothing, as any unknown name
does.

Raises ``UnnameableOrderingTermError`` for a term that references no field or more than one, since
neither has a single name a client could send back. A path with no ``?o=`` name that reads back
as the same path raises it too (see ``has_public_ordering_name``). Raises ``FieldDoesNotExist``
for a path that names nothing.

### Signature {#get_ordering_data-signature}

`get_ordering_data(self, model, order_by, include_ascending, queryset)`

#### Parameters {#get_ordering_data-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_ordering_data-param-model">model</span> |  | yes |  |  |
| <span id="get_ordering_data-param-order_by">order_by</span> |  | yes |  |  |
| <span id="get_ordering_data-param-include_ascending">include_ascending</span> |  | no | `True` |  |
| <span id="get_ordering_data-param-queryset">queryset</span> |  | no | `None` |  |

### Source {#get_ordering_data-source}

`server/vueda/info/serializers.py:619`

## `get_schema_operation_parameters` {#get_schema_operation_parameters}

### Signature {#get_schema_operation_parameters-signature}

`get_schema_operation_parameters(self, operation_id, parameters)`

#### Parameters {#get_schema_operation_parameters-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_schema_operation_parameters-param-operation_id">operation_id</span> |  | yes |  |  |
| <span id="get_schema_operation_parameters-param-parameters">parameters</span> |  | no | `()` |  |

### Source {#get_schema_operation_parameters-source}

`server/vueda/info/serializers.py:1619`

## `get_verbose_name` {#get_verbose_name}

### Signature {#get_verbose_name-signature}

`get_verbose_name(self, instance)`

#### Parameters {#get_verbose_name-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_verbose_name-param-instance">instance</span> | `object` | yes |  |  |

#### Returns {#get_verbose_name-returns}

`str`

### Source {#get_verbose_name-source}

`server/vueda/info/serializers.py:166`

## `get_verbose_name_plural` {#get_verbose_name_plural}

### Signature {#get_verbose_name_plural-signature}

`get_verbose_name_plural(self, instance)`

#### Parameters {#get_verbose_name_plural-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_verbose_name_plural-param-instance">instance</span> | `object` | yes |  |  |

#### Returns {#get_verbose_name_plural-returns}

`str`

### Source {#get_verbose_name_plural-source}

`server/vueda/info/serializers.py:169`

## `get_workflow_enabled` {#get_workflow_enabled}

Whether the model enables ``class Vueda.Workflow``, so a client may offer workflow controls.

This describes the interface, not what the viewer may do. The workflow endpoints still decide
which transitions a user may see and take.

### Signature {#get_workflow_enabled-signature}

`get_workflow_enabled(self, instance)`

#### Parameters {#get_workflow_enabled-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| self |  | yes |  |  |
| <span id="get_workflow_enabled-param-instance">instance</span> | `object` | yes |  |  |

#### Returns {#get_workflow_enabled-returns}

`bool`

### Source {#get_workflow_enabled-source}

`server/vueda/info/serializers.py:172`

## `names_queryset_annotation` {#names_queryset_annotation}

Whether ``path`` names an annotation that ``queryset`` carries, either by its own name or with
transforms that Django can apply to it, such as ``launched__year``.

A plain annotation name always counts, even when Django can't determine its type. Such an
annotation, such as a ``Coalesce`` over mixed types, makes every query on the queryset fail
with Django's own ``FieldError`` until it declares an ``output_field``, so it is not an ordering
mistake. A path with transforms counts only when they resolve, so a misspelled
``launched__yaer`` names nothing.

### Signature {#names_queryset_annotation-signature}

`names_queryset_annotation(queryset, path)`

#### Parameters {#names_queryset_annotation-parameters}

| Name | Type | Required | Default | Description |
| --- | --- | --- | --- | --- |
| <span id="names_queryset_annotation-param-queryset">queryset</span> |  | yes |  |  |
| <span id="names_queryset_annotation-param-path">path</span> |  | yes |  |  |

### Source {#names_queryset_annotation-source}

`server/vueda/info/serializers.py:682`

## `data` {#data}

### Source {#data-source}

`server/vueda/info/serializers.py:180`

## `verbose_name` {#verbose_name}

## `verbose_name_plural` {#verbose_name_plural}

## `workflow_enabled` {#workflow_enabled}

## Source

`server/vueda/info/serializers.py:136`
