Models

For the most part, models provided by plugin writers are just regular Django Models.

Note

One slight variation is that the validation is primarily handled in the Django Rest Framework Serializer. .clean() is not called.

Most plugins will implement:
  • model(s) for the specific content type(s) used in the plugin, should be subclassed from Content model

  • model(s) for the plugin specific remote(s), should be subclassed from Remote model

Adding Model Fields

Each subclassed Model will typically store attributes that are specific to the content type. These attributes need to be added to the model as fields. You can use any of Django’s field types for your fields. See the Django field documentation, for more in-depth information on using these fields.

Note

It is required to declare the default_related_name.

The TYPE class attribute is used for filtering purposes.

class FileContent(Content):
    """
    The "file" content type.

    Fields:
        digest (str): The SHA256 HEX digest.
    """
    TYPE = 'file'
    digest = models.TextField(null=False)

    class Meta:
        default_related_name = "%(app_label)s_%(model_name)s"

Here we create a new field digest using Django’s TextField. After adding/modifying a model, you can make and run database migrations with:

pulpcore-manager makemigrations $PLUGIN_APP_LABEL
pulpcore-manager migrate

Warning

Do not use settings directly in the model fields, it will lead to a data migration that is specific to the users installation in case those settings change.

If you recognize this syntax, it is because pulpcore-manager is manage.py configured with DJANGO_SETTINGS_MODULE="pulpcore.app.settings". You can use it anywhere you normally would use manage.py or django-admin.

Uniqueness

Model uniqueness (which will also be used as the natural key) is defined by an inner class Meta. Pulp Core enforces uniqueness constraints at the database level.

Adding to the simplified FileContent above:

class FileContent(Content):
    """
    The "file" content type.
    Content of this type represents a single file uniquely
    identified by path and SHA256 digest.
    Fields:
        digest (str): The SHA256 HEX digest.
    """

    TYPE = 'file'

    digest = models.TextField(null=False)

    class Meta:
        # Note the comma, this must be a tuple.
        unique_together = ('digest',)
        default_related_name = "%(app_label)s_%(model_name)s"

In this example the Content’s uniqueness enforced on a single field digest. For a multi-field uniqueness, simply add other fields.

class FileContent(Content):
    """
    The "file" content type.
    Content of this type represents a single file uniquely
    identified by path and SHA256 digest.
    Fields:
        relative_path (str): The file relative path.
        digest (str): The SHA256 HEX digest.
    """

    TYPE = 'file'

    relative_path = models.TextField(null=False)
    digest = models.TextField(null=False)

    class Meta:
        default_related_name = "%(app_label)s_%(model_name)s"
        unique_together = (
           'relative_path',
           'digest',
        )

The example above ensures that content is unique on relative_path and digest together.

ForeignKey Gotchas

The orphan cleanup operation performs mass-deletion of Content units that are not associated with any repository. Any ForeignKey relationships that refer to Content with a deletion relationship of PROTECT will cause Orphan cleanup errors like:

django.db.models.deletion.ProtectedError: ("Cannot delete some instances of model 'MyContent'
because they are referenced through a protected foreign key: 'MyOtherContent.mycontent'"