Database

development area

A plethora, a cornucopia, a totally ridiculous number of models, fields, and other resources that may be used as a starting point for your database.

Version: 0.4.0-d

Database Modules Overview

Name

Version

Stage

Maintainer

archived

0.10.0-d

development

Shawn Davis <shawn@superdjango.com>

audit

0.11.0-d

development

Shawn Davis <shawn@superdjango.com>

calculated

0.6.0-p

planning

Shawn Davis <shawn@superdjango.com>

completed

0.11.0-d

development

Shawn Davis <shawn@superdjango.com>

datetime

0.8.0-d

development

Shawn Davis <shawn@superdjango.com>

default

0.6.0-x

experimental

Shawn Davis <shawn@superdjango.com>

displayed

0.4.0-a

alpha

Shawn Davis <shawn@superdjango.com>

eav

0.6.0-x

experimental

Shawn Davis <shawn@superdjango.com>

expiration

0.7.0-p

planning

Shawn Davis <shawn@superdjango.com>

history

0.7.0-p

planning

Shawn Davis <shawn@superdjango.com>

locked

0.7.1-p

planning

Shawn Davis <shawn@superdjango.com>

lookups

0.9.0-d

development

Shawn Davis <shawn@superdjango.com>

owned

0.12.1-d

development

Shawn Davis <shawn@superdjango.com>

parent

0.7.0-x

experimental

Shawn Davis <shawn@superdjango.com>

polymorphic

0.4.0-x

experimental

Shawn Davis <shawn@superdjango.com>

primary

0.7.0-x

experimental

Shawn Davis <shawn@superdjango.com>

published

0.10.0-d

development

Shawn Davis <shawn@superdjango.com>

random

0.6.0-x

experimental

Shawn Davis <shawn@superdjango.com>

resolved

0.3.0-d

development

Shawn Davis <shawn@superdjango.com>

reviewed

0.3.0-d

development

Shawn Davis <shawn@superdjango.com>

revised

0.2.0-x

experimental

Shawn Davis <shawn@superdjango.com>

slug

0.4.0-x

experimental

Shawn Davis <shawn@superdjango.com>

sorted

0.7.0-x

experimental

Shawn Davis <shawn@superdjango.com>

timed

0.6.0-x

experimental

Shawn Davis <shawn@superdjango.com>

trashed

0.6.1-x

experimental

Shawn Davis <shawn@superdjango.com>

utils

0.1.0-x

experimental

Shawn Davis <shawn@superdjango.com>

The resources provided by SuperDjango DB general consist of abstract models that may be used for various common (and some not so common) purposes.

Note

SuperDjango DB does not (and will never) provide concrete models.

Archived Models

development library

Version: 0.10.0-d

Provides support for a simple archive workflow.

Developer Reference: Archived Models

Provides

Usage

from superdjango.db.archived.managers import ArchivedManager
from superdjango.db.archived.models import ArchivedByModel

class StrategicPlan(ArchivedByModel):
    objects = ArchivedManager()

archived_plans = StrategicPlan.objects.archived()
unarchived_plans = StrategicPlan.objects.unarchived()

Audit Models

development library

Version: 0.11.0-d

Adds support for added by/modified by/viewed by (and added/modified/viewed date/time) to a model.

Developer Reference: Audit Models

Provides

  • Admin : base, filters

  • Models : fields, abstract, mixins

  • Utilities

Calculated Fields

planning library

Version: 0.6.0-p

Provides support for standard calculated fields as well as creating your own calculated fields.

Developer Reference: Calculated Fields

Provides

  • Models : field mixins, fields

Abstract

Calculated fields can be useful when aggregating data or connecting (concatenating) strings. This library provides various fields for use in automatic calculation on save.

Completed Models

development library

Version: 0.11.0-d

Provides the ability to mark a record as complete.

Developer Reference: Completed Models

Provides

Abstract

A completion workflow is often needed for tasks, projects, and other common types of web application records. This library provides support for such a workflow.

Usage

from superdjango.db.completed.managers import CompletedManager
from superdjango.db.completed.models import CompletedByModel

class Task(CompletedByModel):
    objects = CompletedManager()

completed_tasks = Task.objects.complete()
incomplete_tasks = Task.objects.incomplete()

Date/Time Models

development library

Version: 0.8.0-d

Fields and abstract models for working with dates and datetimes.

Developer Reference: Date/Time Models

Provides

Default Models

experimental library

Version: 0.6.0-x

Provides the ability to mark a record as the default of it’s type.

Developer Reference: Default Models

Provides

Abstract

Records that are identified as “the default” are useful for establishing intelligent defaults for things like configuration options and lookup (validation) data.

Usage

Support you have an AddressType lookup and that only one address type may be the default for new records that refer to address type.

# myapp/models.py
from django.db import models
from superdjango.db.default.managers import DefaultManager
from superdjango.db.default.models import DefaultModel
from superdjango.db.lookups.models import StringLookupModel
from django.db.models.signals import pre_save

class AddressType(DefaultModel, StringLookupModel):
    objects = DefaultManager()
    # ...

Set the required receiver:

# myapp/receivers.py
from .models import AddressType

pre_save.connect(
    AddressType.allow_one_default,
    AddressType,
    dispatch_uid="addresstype_allow_one_default"
)

To require that a default address type always exists, use the require_one_default instead:

# Or to require a default record.
pre_save.connect(
    AddressType.require_one_default,
    AddressType,
    dispatch_uid="addresstype_require_one_default"
)

Load receivers in your apps.py file:

from django.apps import AppConfig

class DefaultConfig(AppConfig):
    name = 'myapp'
    label = 'myapp'

    def ready(self):
        from . import receivers

To implement the default in a form:

from django import forms

class EmployeeAddress(forms.ModelForm):

    class Meta:
        model = EmployeeAddress
        fields = [
            "address_type",
            # ...
        ]

    def __init__(*args, **kwargs):
        super(EmployeeForm, self).__init__(*args, **kwargs)

        self.fields['address_type'].initial = AddressType.objects.default()

Displayed Models

alpha library

Version: 0.4.0-a

Provides a consistent means of acquiring the human friendly name of a model instance.

Developer Reference: Displayed Models

Provides

Abstract

What is a display name? A display name is a proper name, label, or title for a specific model instance. Django models already have a mechanism for providing this representation by defining the __str__() method.

Why use the displayed mixin or model, then? The superdjango.db.displayed.mixins.DisplayNameMixin formalizes a simple API for acquiring the proper name of a record. The __str__() method defaults to get_display_name(), but may be overridden to provide something different if desired.

The displayed package also defines get_choice_name() which allows the proper name to be different when the record is displayed as a choice.

Note

Certain components of SuperDjango UI utilize a model’s implementation of both get_choice_name() and get_display_name(). The superdjango.db.displayed.mixins.DisplayNameMixin provides the basis for this functionality.

Usage

Simply extend superdjango.db.displayed.mixins.DisplayNameMixin and implement get_display_name() or alternatively extend superdjango.db.displayed.models.DisplayNameModel to also cache the the choice and display name values on save().

EAV Models

experimental library

Version: 0.6.0-x

Support for the Entity Attribute Value (EAV) approach to adding custom fields.

Developer Reference: EAV Models

Provides

Abstract

The Entity Attribute Value model (EAV) is a means of adding custom attributes to an existing model in a dynamic fashion. This can be useful for creating user-defined fields without interfering with core data models.

Usage

In the examples that follow, we implement a user profile model to which we wish to attach custom fields using the EAV pattern.

# myapp/models.py
from superdjango.db.eav.models import AttributeModel, ValueModel

class Profile(models.Model):
    user = models.OneToOneField(AUTH_USER_MODEL, related_name="profile")
    # ... additional "hard coded" fields as desired ...

class ExtraAttribute(AttributeModel):
    pass

class ExtraAttributeValue(ValueModel):
    attribute = models.ForeignKey(ExtraAttribute, related_name="values")

    profile = models.ForeignKey(Profile, related_name="attributes")

    def get_attribute(self):
        return self.attribute

ExtraAttribute defines the available attributes that may be populated with actual values in ExtraAttributeValue.

# myapp/forms.py
from superdjango.forms import EAVModelForm
from .models import Profile, ExtraAttribute, ExtraAttributeValue

class ProfileForm(EAVModelForm):
    attribute_model = ExtraAttribute
    entity_name = "profile"
    value_model = ExtraAttributeValue

    # ... add fields and meta as usual ...

Defining the attribute and value models, as well as the entity name is required. The optional attribute_name defaults to “attribute” which is what we used in ExtraAttributeValue to define the attribute model to which the value is connected.

Expiration Models

planning library

Version: 0.7.0-p

Work with records that have an expiration date and time.

Developer Reference: Expiration Models

Provides

Abstract

Some records represent a time-limited scope for their availability or use. The expiration library provides support for an expiration workflow.

Usage

Suppose you have a proposal model that needs to expire after a certain date and time.

# myapp/models.py
from superdjango.db.expiration.managers import ExpirationManager
from superdjango.db.expiration.models import ExpirationModel

class Proposal(ExpirationModel):
    objects = ExpirationManager()
    # ...

In the list view (for example, as displayed to clients), you may utilize the unexpired method on the manager to display only proposals that are current:

from superdjango.views import ListView
from .models import Proposal

class ProposalList(ListView):
    model = Proposal

    def get_queryset(self):
        return self.model.objects.unexpired()

A management command could be created to check expiration and update the expired models.

# myapp/management/commands/update_expired_proposals.py
from django.core.management.base import BaseCommand
from myapp.models import Proposal

class Command(BaseCommand):

    def handle(self, *args, **options):
        qs = Proposal.objects.filter(has_expired__is_null=True)
        for row in qs:
            if row.is_past_expiration():
                row.has_expired = True
                row.save()

History Models

planning library

Version: 0.7.0-p

Abstract model for implementing audit history on models.

Developer Reference: History Models

Provides

  • Admin : base

  • Constants

  • Models : abstract

  • UI : base

  • Utilities

Abstract

The history package provides an abstract model for implementing a log of historical activity on other models. For a practical implementation, see superdjango.contrib.history.

Usage

Implementing Audit History

You may create your own history by extending superdjango.db.history.models.HistoryModel.

# myapp/models.py
from superdjango.db.history.models import HistoryModel

class History(HistoryModel):

    class Meta:
        get_latest_by = "added_dt"
        ordering = ["-added_dt"]
        verbose_name = _("Log Entry")
        verbose_name_plural = _("Log Entries")

See the admin and ui modules of this package for implementing Django Admin and SuperDjango UI user interfaces.

Creating an Audit Trail

Your view must call the log method:

# myapp/views.py
from .models import History

class CreateTodo(CreateView):
    ...

    def form_valid(self, form):
        self.object = form.save()

        History.log(self.object, self.request.user, History.CREATE)

        ...

Tracking Changes to Fields

The superdjango.db.history.utils.FieldChange may be used to assist with tracking changes to fields. The superdjango.db.history.utils.get_field_changes() is also provided as a shortcut for creating FieldChange instances.

# myapp/models.py
from superdjango.db.history.models import HistoryModel

class History(HistoryModel):

    field_changes = models.TextField(
        blank=True,
        null=True
    )

    class Meta:
        get_latest_by = "added_dt"
        ordering = ["-added_dt"]
        verbose_name = _("Log Entry")
        verbose_name_plural = _("Log Entries")

    @classmethod
    def log_field_changes(cls, instance, verb, fields=None):
        if fields is None:
            return

        a = list()
        for change in fields:
            a.append(str(change))

        instance.field_changes = "\n".join(a)

# myapp/views.py
from superdjango.db.history.utils import get_field_changes
from .models import History

class UpdateTodo(UpdateView):
    ...

    def form_valid(self, form):
        self.object = form.save()

        field_changes = get_field_changes(form, self.model, record=self.object)

        History.log(self.object, self.request.user, History.UPDATE, fields=field_changes)

        ...

You could also implement a separate model to record each field change and connect it to the history record.

# myapp/models.py
from superdjango.db.history.models import HistoryModel

class History(HistoryModel):
    ...

    @classmethod
    def log_field_changes(cls, instance, verb, fields=None):
        if fields is None:
            return

        a = list()
        for change in fields:
            field_history = FieldHistory(
                field_label=change.field_label,
                field_name=change.field_name
                history=instance,
                new_value=change.new_value,
                old_value=change.old_value,
            )
            field_history.save()

class FieldHistory(models.Model):

    field_label = models.CharField(
        max_length=256
    )

    field_name = models.CharField(
        max_length=256
    )

    history = models.ForeignKey(
        History,
        on_delete=models.CASCADE,
        related_name="field_changes",
        verbose_name=_("History")
    )

    new_value = models.TextField(
        blank=True,
        null=True
    )

    old_value = models.TextField(
        blank=True,
        null=True
    )

    class Meta:
        ordering = ["field_label"]
        verbose_name = _("Field History")
        verbose_name_plural = _("Field Histories")

    def __str__(self):
        return "%s: %s -> %s" % (self.field_label, self.old_value, self.new_value)

Note that custom views or UI would be required to display the field history records.

Locked Models

planning library

Version: 0.7.1-p

Work with records that have been locked from update or delete.

Developer Reference: Locked Models

Provides

Abstract

Record locking prevents delete or update actions. This library provides model-level support for locked records.

Usage

Supporting Record Locking

While the superdjango.db.locked.models.LockedModel provides support for locking a record, additional work is required in the UI to prevent changes from occurring.

SuperDjango UI supports locked models in the check_lock() and check_permission() methods of superdjango.ui.options.interfaces.BaseModelUI. Also, the provided superdjango.db.locked.admin.BaseLockedModelAdmin provides built-in support for the Django Admin.

Note

In both of the implementations above, the super user always retains the permission to delete and update locked records.

Custom views, however, will need to implement support for checking and dealing with locks.

Record Lock or Unlock Permissions

The provided resources assume that a super user can always lock or unlock records, and may work with records regardless of their locked status. Although it requires additional customization, you may implement custom permissions that allow authorized users to work with record locking.

# myapp/models.py
from superdjango.db.locked.models import LockedModel

class Employee(LockedModel):
    # ...

    class Meta:
        permissions = [
            ("can_deleted_locked_employee", "Can delete a locked employee record."),
            ("can_lock_employee", "Can lock an employee record."),
            ("can_unlock_employee", "Can unlock an employee record."),
            ("can_update_locked_employee", "Can update a locked employee record."),
    ]

With these permissions in place you would need to potentially do the following depending on your needs:

  1. Override the check_lock() method of superdjango.ui.options.interfaces.BaseModelUI to check the custom permissions.

  2. Override the has_delete_permission() and has_change_permission() methods of superdjango.db.locked.admin.BaseLockedModelAdmin to check custom permissions.

  3. Deal with these permissions in any custom views you have created.

Lookup Models

development library

Version: 0.9.0-d

Provides pre-defined models for managing lookup (aka validation) data.

Developer Reference: Lookup Models

Provides

Abstract

The need for lookup data (also known as validation data) is common to perhaps all Web applications. Handling such data in a consistent manager can be very beneficial to a project – there is no need to guess as to the available fields on a lookup.

Owned Models

development library

Version: 0.12.1-d

Provides a random character field.

Developer Reference: Owned Models

Provides

Parent-Tree Models

experimental library

Version: 0.7.0-x

Provides a simple model for implementing parent-child-sibling relationships.

Developer Reference: Parent-Tree Models

Provides

  • Compat

  • Models : abstract

  • Utilities

Dependencies

  • anytree (optional) Required to use the diagramming functionality.

This is a simplistic implementation of tree-like functionality using only a parent field that refers to self. It performs well for a relatively small number of records. For thousands records, you may wish to review the following resources:

Graphing

Parent-tree provides a utility for creating a visual graph of a given record and all of its children.

Important

The anytree package is required to utilize this functionality.

The superdjango.db.parent.utils.Diagram class may be used to create an image. Suppose you have an HR tool and you want to present the structure of an organization:

from django.conf import settings
import os
from superdjango.db.parent.utils import Diagram
from hr.organizations.models import Organization

# Load the graphing utility.
org = Organization.objects.get(pk=1)
diagram = Diagram(org)

# Output the image.
graph = diagram.to_graph()
path = os.path.join(settings.MEDIA_ROOT, "hr", "orgs", "%s.jpg" % org.pk)
graph.to_picture(path)

Polymorphic Models

experimental library

Version: 0.4.0-x

Managers and mixins for polymorphic behavior.

Developer Reference: Polymorphic Models

Provides

  • Models : managers, mixins

  • Utilities

Provides assistance with managing Multi-table inheritance in Django.

from superdjango.polymophric import PolymorphicManager, PolymorphicMixin

class Project(models.Model):
    objects = PolymorphicManager()
    # ...

class MobileProject(PolymorphicMixin, Project):
    # ...

class WebSiteProject(PolymorphicMixin, Project):
    # ...

projects = Project.objects.all().select_subclasses()
for project in projects:
    # project will be a specific instance of Project, MobileProject, or WebSiteProject.
    print(project)

Primary Models

experimental library

Version: 0.7.0-x

Provides support for a publishing workflow.

Developer Reference: Primary Models

Provides

Published Models

development library

Version: 0.10.0-d

Provides support for a publishing workflow.

Developer Reference: Published Models

Provides

Random Models

experimental library

Version: 0.6.0-x

Provides a random character field.

Developer Reference: Random Models

Resolved Models

development library

Version: 0.3.0-d

Provides support for a resolution workflow.

Developer Reference: Resolved Models

Provides

Reviewed Models

development library

Version: 0.3.0-d

Provides support for a review workflow.

Developer Reference: Reviewed Models

Provides

Revised Models

experimental library

Version: 0.2.0-x

A simple package that allows saving revisions of a record.

Developer Reference: Revised Models

Provides

Abstract

Record revision means creating a new (or latest) revision of a record whilst saving past revisions of the same record. This package supports a simple record revision system.

We are fully aware of the sundry tools for managing versioned model data. Many of these bring great functionality to the table, and you are encouraged to check these out as an alternative to this package.

Usage

After extending superdjango.db.revised.models.RevisedModel, you may create a new revision of the current record using the save_revision() method.

Sorted Models

experimental library

Version: 0.7.0-x

A model and utilities for implementing record sortation.

Developer Reference: Sorted Models

Provides

Slug Fields

experimental library

Version: 0.4.0-x

Provides support for a automatic slug field.

Developer Reference: Slug Fields

Provides

Abstract

Support for automatic slug fields.

Usage

The automatic slug field will by default create a slug using Django’s slugify() utility. You may provide your own callback function to use for providing the slug.

# myapp/models.py
superdjango.db.slug.fields import AutoSlugField

function underscores_and_lower_case(value):
    return value.replace(" ", "_").lower()

class UploadFile(models.Model):
    file_name = AutoSlugField(from_field="title", slug_using=underscores_and_lower_case)

    title = models.CharField(max_length=128)

    # ...

Timed Models

experimental library

Version: 0.6.0-x

Create records that incorporate start/stop timing functionality.

Developer Reference: Timed Models

Provides

Trashed Models

experimental library

Version: 0.6.1-x

Provides a workflow for trashed, but not deleted, models.

Developer Reference: Trashed Models

Provides

Abstract

A trashed flag allows a single record to be marked as being removed by a user, but not yet removed from the database.