fairdm.registry.factories#
Factories that generate Django components from a ModelConfiguration.
Each factory builds one component (form, table, filter set, admin, serializer or resource), choosing widgets, columns and filters from the model’s field types.
Module Contents#
Classes#
Base factory for generating Django components from configuration. |
|
Factory for generating ModelForm classes with smart widgets. |
|
Factory for generating Table classes with smart column types. |
|
Narrow every related-record filter to published records, last. |
|
Factory for generating FilterSet classes with smart filter types. |
|
Factory for generating Django Admin ModelAdmin classes. |
|
Factory for generating DRF ModelSerializer classes. |
|
Factory for generating import/export Resource classes. |
Functions#
Return the published records of a model, or |
API#
- class ComponentFactory(model: type[django.db.models.Model], fields: list[str] | None = None)#
Base factory for generating Django components from configuration.
This abstract base class provides common functionality for all component factories. Subclasses implement specific generation logic for Forms, Tables, Filters, etc.
Args: model: The Django model class to generate components for. fields: Field names to use.
Noneuses the model’s safe fields.Initialization
- get_fields() list[str]#
Return the fields to use.
Explicitly provided fields win, then the model’s safe fields as found by
inspector.get_safe_fields().Returns: The field names to use.
- abstractmethod generate() Any#
Generate the component. Subclasses must implement it.
Returns: The generated component class.
Raises: NotImplementedError: Always, on the base class.
- class FormFactory(model: type[django.db.models.Model], fields: list[str] | None = None)#
Bases:
fairdm.registry.factories.ComponentFactoryFactory for generating ModelForm classes with smart widgets.
Dates, large text, email and URL fields get a matching widget, and the form carries a crispy-forms helper with a submit button.
Initialization
- generate() type[django.forms.ModelForm]#
Generate a ModelForm class with smart widgets.
Returns: A ModelForm subclass with smart widgets and a crispy-forms helper.
- get_base_form_class() type[django.forms.ModelForm]#
Return the base ModelForm to build the form on.
Mirrors
TableFactory.get_base_table_class(): a sample or measurement type supplying no form of its own still getsSampleFormMixin’s orMeasurementFormMixin’s widget configuration, dataset scoping and guidance text rather than a bareModelForm.Returns: A form class carrying the mixin for the model’s hierarchy, or
ModelFormfor any other model.
- class TableFactory(model: type[django.db.models.Model], fields: list[str] | None = None)#
Bases:
fairdm.registry.factories.ComponentFactoryFactory for generating Table classes with smart column types.
Dates, emails, URLs and booleans get a matching column. Large text fields are left out of the table.
Initialization
Return the published records of a model, or
Nonewhen publication is moot.The dataset branch goes through
Dataset.all_objects, neverDataset.objects: the default manager excludes PRIVATE datasets, and a published-but-private dataset is the ordinary state, soDataset.objects.published()would leave a filter offering nothing while the table shows that dataset’s rows. Publication is the only test a listing applies.Nonerather than an unscoped queryset, so a caller can tell “every published one” from “not this model’s question” - a content-type filter already scoped to the registered types must not be widened back out.Args: related_model: The model whose records a filter offers.
Returns: A queryset of the published records for a Dataset, Sample or Measurement model, otherwise
None.
- class PublishedChoicesMixin(*args, **kwargs)#
Narrow every related-record filter to published records, last.
Scoping filters as the factory builds them is not enough, in two ways that both leaked.
A generated filter set inherits
SampleFilterMixinorMeasurementFilterMixin, and both assign their own hand-declareddataset(and, for measurements,sample) choice lists in__init__- fromDataset.all_objectsandSample.objectsrespectively, neither of which applies publication. Those assignments happen at instantiation, after any class-level override, so a class-level fix does not survive them.And a registration may supply its own
filterset_class, or overrideget_filterset_class()outright, in which case the factory never runs at all. That is a documented tier of the configuration API, not an edge case.So this is applied by the listing view to whatever filter set it is handed, and it is placed first in the bases, so its
__init__runs the rest of the chain and then re-scopes what that chain assigned. It reads each filter’s own queryset rather than the model field, so a many-to-many or reverse relation django-filter generated for itself is covered by the same pass, and a relation publication says nothing about is left alone.It applies to the listings and to nothing else. Every other page that builds a filter on those two core mixins keeps their behaviour.
Initialization
- classmethod applied_to(filterset_class: type) type#
Put this mixin in front of a filter set, unless it is already there.
Idempotent, because a listing resolves its filter set on every request and a fresh subclass per request would leak classes.
Args: filterset_class: The filter set to scope.
Returns:
filterset_classitself if it already has the mixin, otherwise a subclass with the mixin first.
- class FilterFactory(model: type[django.db.models.Model], fields: list[str] | None = None, search_fields: list[str] | None = None)#
Bases:
fairdm.registry.factories.ComponentFactoryFactory for generating FilterSet classes with smart filter types.
Dates get range filters, choice fields a choice filter, foreign keys a model choice filter, numbers a range filter and text a case-insensitive contains filter. The set also carries a
qfilter over the type’s search fields, and its range filters use the range inputs frommvp_forms.Args: model: The Django model class to generate the filter set for. fields: Field names to use.
Noneuses the model’s safe fields. search_fields: ORM paths theqfilter matches. Empty means the filter set has noqfilter of its own.Initialization
- generate() type[django_filters.FilterSet]#
Generate a django-filter FilterSet class with smart filters.
Fields django-filter cannot resolve are dropped one at a time until it accepts the rest.
Returns: A FilterSet subclass with smart filters.
Raises: AssertionError: django-filter rejected the fields for a reason other than an unresolved field name.
- get_base_filterset_class() type[django_filters.FilterSet]#
Return the base FilterSet to build the filter set on.
Mirrors
TableFactory.get_base_table_class(): a sample or measurement type’s generated filter set carriesSampleFilterMixin’s orMeasurementFilterMixin’s declared filters rather than a bareFilterSet. Both mixins are alreadyFilterSetsubclasses, so, unlike the form factory, no wrapping is needed here.Returns: The mixin for the model’s hierarchy, or
FilterSetfor any other model.
- class AdminFactory(model: type[django.db.models.Model], fields: list[str] | None = None)#
Bases:
fairdm.registry.factories.ComponentFactoryFactory for generating Django Admin ModelAdmin classes.
Generates
list_display,search_fieldsandlist_filter, groups fields into fieldsets, marks timestamps and ids read-only and adds a date hierarchy where the model has a date field.Initialization
- class SerializerFactory(model: type[django.db.models.Model], fields: list[str] | None = None)#
Bases:
fairdm.registry.factories.ComponentFactoryFactory for generating DRF ModelSerializer classes.
A sample or measurement type gets a serializer built on
BaseSampleSerializerorBaseMeasurementSerializer, so it carries the fields common to its kind followed by every field its registration declares. A relation to a project, dataset, sample or measurement is shown as a reference, any other relation as its string form.Initialization
- class ResourceFactory(model: type[django.db.models.Model], fields: list[str] | None = None)#
Bases:
fairdm.registry.factories.ComponentFactoryFactory for generating import/export Resource classes.
Foreign keys are matched on the related model’s natural key where it has one, otherwise on its primary key.
Initialization