signoffs.core.models.fields#

Custom model fields and relation descriptors

Module Contents#

Classes#

RelatedSignoffDescriptor

Descriptor that provides a Signoff backed by a OneToOneField to a Signet model, with services to manage the relation on the defining model. Class access yields the Signoff Type class. Instance access yields a Signoff instance wrapping the related Signet object (which may be None, i.e. unsigned Signoff)

SignoffOneToOneField

A normal OneToOneField with a signoff id so its type can be determined before it is assigned a value

SignoffSet

A descriptor that injects a “reverse relation” manager, filtered for the specific Signoff type. The signoff_type MUST be backed by a Signet with a FK relation to the owning model signet_set_accessor is a string with a path to the reverse relation manager on the owning model - it may use ‘__’ to traverse relations and include method calls e.g., ‘my_signet_manager__get_signet_set’

RelatedApprovalDescriptor

Descriptor that provides an Approval backed by a OneToOneField to a Stamp model, with services to manage the relation on the defining model. Class access yields the Approval Type class. Instance access yields an Approval instance wrapping the related Stamp object (which will be created if it doesn’t yet exist, i.e. uninitiated Approval)

ApprovalSet

A descriptor that injects a “reverse relation” manager, filtered for the specific Approval Type. The approval_type MUST be backed by a Stamp with a FK relation to the owning model

Functions#

SignoffField

Convenience method for constructing from minimal inputs: (1) a sensible OneToOneField(signoff_type.signetModel); and (2) an RelatedSignoffDescriptor(signoff_type) signoff_type may be a Signoff Type or a registered(!) signoff id. Default parameter rationale: null=True, on_delete=SET_NULL make sensible defaults for a Signet relation since presence/absence is semantic; think twice before using other values! reverse related_name from Signet generally not so useful, consider disabling with ‘+’

SignoffSingle

A thin wrapper for SignoffSet that configures the correct SignoffSetManager for a single signoff

ApprovalSignoffSet

A thin wrapper for SignoffSet that configures the correct signet_set_accessor for Approvals

ApprovalSignoffSingle

A thin wrapper for SignoffSet that configures the correct signet_set_accessor for a single Approval signoff

ApprovalField

Convenience method for constructing from minimal inputs: (1) a sensible OneToOneField(approval_type.stampModel); and (2) an RelatedApprovalDescriptor(approval_type) approval_type may be an Approval Type or a registered(!) approval id. Default parameter rationale: null=True, on_delete=SET_NULL make sensible defaults, as a deleting an approval Stamp should not cascade to its “owner” and the stamp is simply re-created on next access; think twice before using other values! related_name defines “reverse relation” from Stamp to the approval subject (object declaring the ApprovalField) this is not used internally, but could be very useful e.g., when approval permissions need context of subject Wanrning: the name of this field needs to be unquie for each ApprovalField

API#

class signoffs.core.models.fields.RelatedSignoffDescriptor(signoff_type, signet_field)[source]#

Descriptor that provides a Signoff backed by a OneToOneField to a Signet model, with services to manage the relation on the defining model. Class access yields the Signoff Type class. Instance access yields a Signoff instance wrapping the related Signet object (which may be None, i.e. unsigned Signoff)

Initialization

Manage a OneToOne signet_field using the given Signoff Type (or signoff id str)

__set_name__(owner, name)[source]#

Grab the field named used by owning class to refer to this descriptor

Raises ImproperlyConfigured if the signet_field model relation is not same as the signoff_type Signet model

__get__(instance, owner=None)[source]#
class signoffs.core.models.fields.SignoffOneToOneField(*args, signoff_id, **kwargs)[source]#

Bases: django.db.models.OneToOneField

A normal OneToOneField with a signoff id so its type can be determined before it is assigned a value

Initialization

deconstruct()[source]#
signoffs.core.models.fields.SignoffField(signoff_type, on_delete=models.SET_NULL, null=True, related_name='+', **kwargs)[source]#

Convenience method for constructing from minimal inputs: (1) a sensible OneToOneField(signoff_type.signetModel); and (2) an RelatedSignoffDescriptor(signoff_type) signoff_type may be a Signoff Type or a registered(!) signoff id. Default parameter rationale: null=True, on_delete=SET_NULL make sensible defaults for a Signet relation since presence/absence is semantic; think twice before using other values! reverse related_name from Signet generally not so useful, consider disabling with ‘+’

In the example::

from signoffs.signoffs import SimpleSignoff

applicant_signoff = SimpleSignoff.register('myapp.signoff.applicant', ...)

# the verbose / explicit way...
class Application(models.Model):
    # ...
    applicant_signet = OneToOneField(applicant_signoff.signetModel, on_delete=models.SET_NULL,
                                     signoff_type=applicant_signoff,
                                     null=True, )
    applicant_signoff = RelatedSignoffDescriptor(applicant_signoff, applicant_signet)

# the concise convenient way...
class Application(models.Model):
    # ...
    applicant_signoff, applicant_signet = SignoffField(applicant_signoff)

In either case above: Application.applicant_signet is a “normal” One-to-One relation to the Signet model Application().applicant_signet is a Signet instance or None (if signoff is not signed)

``Application.applicant_signoff`` is a RelatedSignoffDescriptor that ultimately injects a Signoff instance
``Application().applicant_signoff`` is a Signoff instance of the given signoff_type backed by the applicant_signet
        relation.  This Signoff will update the instance's applicant_signet relation whenever it is save()'ed
class signoffs.core.models.fields.SignoffSet(signoff_type: Union[str, Type[signoffs.core.signoffs.AbstractSignoff]] = None, signet_set_accessor='signatories', signoff_set_manager=None, **kwargs)[source]#

A descriptor that injects a “reverse relation” manager, filtered for the specific Signoff type. The signoff_type MUST be backed by a Signet with a FK relation to the owning model signet_set_accessor is a string with a path to the reverse relation manager on the owning model - it may use ‘__’ to traverse relations and include method calls e.g., ‘my_signet_manager__get_signet_set’

In the example::

class VacationSignet(AbstractSignet):
    vacation = models.ForeignKey(Vacation, on_delete=models.CASCADE, related_name='signatories')

class Vacation(models.Model):
    employee = models.CharField(max_length=128)
    # ...
    hr_signoffs = SignoffSet('test_app.hr_signoff')
    mngr_signoffs = SignoffSet('test_app.mngr_signoff')

Vacation.signatories is the “normal” ReverseManyToOneDescriptor instance to access the related Signets. Vacation.hr_signoffs and Vacation.mngr_signoffs are 2 distinct Signoff Types (Signoff sub-classes), while Vacation().hr_signoffs and Vacation().mngr_signoffs are SignoffSetManager instances used to access the set of signoffs of that particular type, backed by the vacation instance.signatories.

See managers.SignoffSetManager for access API.

Initialization

Inject a Signoff; kwargs passed directly through to AbstractSignoff.init id defaults to the descriptor name if not specified

signoff_set_manager#

None

__set_name__(owner, name)[source]#

Grab the field named used by owning class to refer to this descriptor

static _is_valid_signoff_type(signoff_type)[source]#
signoff_type()#

Lazy evaluation for signoff_type to allow all signoffs to register before resolving.

signet_set_owner(instance)[source]#

Raises ImproperlyConfigured if the signet_set_accessor is not a relation to a Signet Manager or queryset

get_signoffs_manager(instance)[source]#

Return a signoff_set_manager for signet set instance.signet_set_accessor

__get__(instance, owner=None)[source]#

Use the enclosing instance to construct and return a SignoffSetManager instance, and replace descriptor with that object

signoffs.core.models.fields.SignoffSingle(*args, **kwargs)[source]#

A thin wrapper for SignoffSet that configures the correct SignoffSetManager for a single signoff

signoffs.core.models.fields.ApprovalSignoffSet(*args, **kwargs)[source]#

A thin wrapper for SignoffSet that configures the correct signet_set_accessor for Approvals

signoffs.core.models.fields.ApprovalSignoffSingle(*args, **kwargs)[source]#

A thin wrapper for SignoffSet that configures the correct signet_set_accessor for a single Approval signoff

class signoffs.core.models.fields.RelatedApprovalDescriptor(approval_type, stamp_field)[source]#

Descriptor that provides an Approval backed by a OneToOneField to a Stamp model, with services to manage the relation on the defining model. Class access yields the Approval Type class. Instance access yields an Approval instance wrapping the related Stamp object (which will be created if it doesn’t yet exist, i.e. uninitiated Approval)

Initialization

Manage a OneToOne Stamp field using the given Approval Type or approval id

__set_name__(owner, name)[source]#

Grab the field named used by owning class to refer to this descriptor

Raises ImproperlyConfigured if the stamp_field model relation is not same as the approval_type Stamp model

__get__(instance, owner=None)[source]#

Return an approval obj. wrapping the stamp_field on instance access, or the approval_type

signoffs.core.models.fields.ApprovalField(approval_type, on_delete=models.SET_NULL, null=True, related_name='+', **kwargs)[source]#

Convenience method for constructing from minimal inputs: (1) a sensible OneToOneField(approval_type.stampModel); and (2) an RelatedApprovalDescriptor(approval_type) approval_type may be an Approval Type or a registered(!) approval id. Default parameter rationale: null=True, on_delete=SET_NULL make sensible defaults, as a deleting an approval Stamp should not cascade to its “owner” and the stamp is simply re-created on next access; think twice before using other values! related_name defines “reverse relation” from Stamp to the approval subject (object declaring the ApprovalField) this is not used internally, but could be very useful e.g., when approval permissions need context of subject Wanrning: the name of this field needs to be unquie for each ApprovalField

In the example::

from signoffs.approvals import ApprovalSignoff
from signoffs.models import ApprovalSignet, Stamp

@register(id=myapp.application_approval)
class ApplicationApproval(ApprovalSignoff):
    stampModel = Stamp
    S = ApprovalSignoff

    reg_signoff = S.register('application.approval.signoff.registration', ...)
    deposit_signoff = S.register('application.approval.signoff.deposit', ...)
    final_approval = S.register('application.approval.signoff.final', ...)

    signing_order = SigningOrder(
        pm.InSeries(
            pm.InParallel(
                ret_signoff,
                deposit_signoff,
            )
            final_approval,
        )
    )

# The verbose way:
class Application(models.Model):
    # ...
    approval_stamp = OneToOneField(ApplicationApproval.stampModel, on_delete=models.SET_NULL, null=True)
    approval = RelatedApprovalDescriptor(ApplicationApproval, approval_stamp)

# The convenient way:
class Application(models.Model):
    # ...
    approval, approval_stamp = ApprovalField(ApplicationApproval)

In both cases above: Application.approval_stamp is a “normal” One-to-One relation to the backing Stamp model Application().approval_stamp is a Stamp instance or None (if approval is not initiated)

``Application.approval`` is a RelatedApprovalDescriptor that ultimately injects an ApplicationApproval instance
``Application().approval`` is an ApplicationApproval instance, backed by the OneToOne approval_stamp relation.
        This approval will update the application's OneToOne relation whenever it is save()'ed
class signoffs.core.models.fields.ApprovalSet(approval_type: Union[str, Type[signoffs.core.approvals.AbstractApproval]] = None, stamp_set_accessor='stamp_set', **kwargs)[source]#

A descriptor that injects a “reverse relation” manager, filtered for the specific Approval Type. The approval_type MUST be backed by a Stamp with a FK relation to the owning model

In the example::

class BuildingPermit(AbstractApprovalStamp):
    building = ForeignKey('Building', related_name='permits')

@register(id='myapp.building_approval')
class ConstructionApproval(BaseApproval):
    stampModel = BuildingPermit
    S = ApprovalSignoff

    planning_signoff = S.register('building.approval.signoff.planning', ...)
    electrical_signoff = S.register('building.approval.signoff.electrical', ...)
    plumbing_signoff = S.register('building.approval.signoff.plumbing', ...)
    inspection_signoff = S.register('building.approval.signoff.inspection', ...)
    final_approval = S.register('building.approval.signoff.final', ...)

    signing_order = SigningOrder(
        InSeries(
            planning_signoff,
            InParallel(
                electrical_signoff,
                plumbing_signoff,
            )
            AtLeastN(inspection_signoff, 1),
            final_approval,
        )
    )

class Building(Model):
    approvals = ApprovalSet(ConstructionApproval, stamp_set_accessor='permits')

BuildingPermit.signatories is a “normal” ReverseManyToOneDescriptor instance to access the related Signets. ConstructionApproval().signing_order defines a SigningOrderManager to match pattern with the stamp’s signets. Building().approvals is an ApprovalSetManager instance providing a unified API for accessing the set of approvals, backed by building instance.permits.

See ApprovalSetManager for access API.

Initialization

Inject ApprovalSetManager; kwargs passed directly through to ApprovalSetManager.init approval_type may be an Approval Type class or a registered approval id

__set_name__(owner, name)[source]#

Grab the field named used by owning class to refer to this descriptor

static _is_valid_approval_type(approval_type)[source]#
approval_type()#

Raises ImproperlyConfigured if the stamp_set_accessor is not a relation to a Stamp Manager or queryset

__get__(instance, owner=None)[source]#

Use the enclosing instance to construct a ApprovalSetManager instance, then replace descriptor with that object