Skip to main content

django-federated-foreign-key

A GenericForeignKey drop-in replacement with ability to point to items in another server. Install with pip:

pip install django-federated-foreign-key

PyPI version

GenericForeignKey

A GenericForeignKey allows pointing to objects of any type. To do this, it uses 2 existing fields on the model:

  • A reference to a ContentType entry, usually named content_type and
  • An id of the related object, usually named object_id

https://docs.djangoproject.com/en/5.2/ref/contrib/contenttypes/#django.contrib.contenttypes.fields.GenericForeignKey

Difference for Federated Foreign Key

The limitation that FederatedForeignKey address is that ContentType is only designed to reference models that exist within the local system, and thus, GenericForeignKey as well. It is taken as obvious that object_id (the related object id) is the id of the object, in another table, in the same database.

The intent of FederatedForeignKey is to provide the same interface, but expand this to allow referencing objects in different databases.

Usage

Add federated_foreign_key to INSTALLED_APPS and define FEDERATION_PROJECT_NAME in your Django settings:

FEDERATION_PROJECT_NAME = 'project_a'
INSTALLED_APPS = [
    'django.contrib.contenttypes',
    'federated_foreign_key',
    # your apps...
]

Use FederatedForeignKey in place of GenericForeignKey together with GenericContentType.

Example

from federated_foreign_key.fields import FederatedForeignKey
from federated_foreign_key.models import GenericContentType

class Reference(models.Model):
    content_type = models.ForeignKey(GenericContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = FederatedForeignKey("content_type", "object_id")

Referencing a remote object

If the referenced item does not exist in the local database, content_object becomes a RemoteObject placeholder. GenericContentType.model_class() returns a stand‑in class that can be used for isinstance checks.

remote_ct = GenericContentType.objects.create(
    project="project_b",
    app_label="testapp",
    model="book",
)
cls = remote_ct.model_class()
ref = Reference.objects.create(content_type=remote_ct, object_id=42)
assert isinstance(ref.content_object, cls)

Development

Install development requirements and run linting and tests:

pip install -r requirements-dev.txt
pip install -e .
pip install -e example_project
ruff check .
flake8
pytest -q
# Run Django's contenttypes tests as shipped in `dj_tests/`
# Use --parallel=1 to avoid multiprocessing issues
python dj_tests/runtests.py contenttypes_tests --parallel=1

Demo

Run the included run_demo.sh script to start two demo servers. The main example_project exposes /books/ which lists local books and books fetched from the remote_project server. SQLite database files are created in each project directory when the script sets the DATABASE_NAME environment variable.

./run_demo.sh

Provided that is running, go visit in your browser the URL:

http://localhost:8000/books/

You should see the example_project showing the reqest, but in the logs from the remote_project, it should show requests coming from the example_project. It shows the names being filled in with values from the remote server.

Remote Model Use Cases

There are 3 cases that this is likely to be used for. This library doesn't provide you with everything you need, just the relationship to store a content_object that may be non-local.

  1. Same model, different servers, different data
  2. Shared model, different servers, different data
  3. Different model, on remote server

Same model, different servers

This is what is illustrated in the demo. Consider that you have 2 book shops, and they both have their own listings of books. A book listing will be duplicate, in the sense that both entries reference the same book. But maybe the two shops have different prices. This helps present a unified list of shopping options for books. Each entry could have its own shopping cart icon, to buy that book from that server, at that cost.

In this case, you would need to create a GenericContentType for the remote model. This can have the same (app_label, model_name) as the local model, which is also a GenericContentType. However, the model is also unique on project name, so this is allowed.

Synchronized tables

In this case we assume a table is sychronized by some other mechanism. This is not in scope of this project, but could be done by something like the DAB resource_registry app.

https://github.com/ansible/django-ansible-base/tree/devel/ansible_base/resource_registry

To avoid setting a specific owner of the table, a value of "shared" for the project name will signify that all servers should treat the item as a local object (within the same DB).

Different model on remote server

Finally, it would likely to useful to reference a model that doesn't exist locally. Say that you were a book shop, but you didn't cary any audiobooks. Perhaps another server has an Audiobook model, and you want to reference audiobooks.

This is a good example of where you would need to create a GenericContentType, where the (app_label, model_name) combination does not exist locally.

Illustration

This shows how FederatedForeignKey works compared to GenericForeignKey.

The object_id values specify the row location in all cases.

graph TD
  %% First scenario: GenericForeignKey
  subgraph GenericForeignKey
    GFKPointerTable[Generic FK Table]
    ContentType[ContentType Table]
    TargetTable[Target Model Table]

    GFKPointerTable -->|content_type_id| ContentType
    GFKPointerTable -->|object_id| TargetTable
  end

  %% Second scenario: FederatedForeignKey
  subgraph FederatedForeignKey
    FFKPointerTable[Federated FK Table]
    GenericContentType[GenericContentType Table]
    LocalTargetTable[Local Target Table]

    FFKPointerTable -->|generic_content_type_id| GenericContentType
    FFKPointerTable -->|object_id| LocalTargetTable
  end

  subgraph External Database
    RemoteTargetTable[Remote Target Table]
  end

  FFKPointerTable -->|object_id| RemoteTargetTable

Metadata

Release files for django-federated-foreign-key 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-federated-foreign-key 0.1.3
File Size Uploaded Provenance
django_federated_foreign_key-0.1.3.tar.gz 48.8 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for django-federated-foreign-key 0.1.3
File Interpreter ABI Platform
django_federated_foreign_key-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 67.2 kB

Release files / django_federated_foreign_key-0.1.3.tar.gz

Download URL django_federated_foreign_key-0.1.3.tar.gz
Size 48.8 kB
Tags Source
SHA-256 checksum
How to use checksums
3b6814e0d5e6152d6b24c4443e6771ba690d238352468e34592d8e87dd56d0fc
BLAKE2b-256 checksum
How to use checksums
749449b75cb3136053e2c2fc9bb0eeb23b0388b9e637785795797b637c0fd6c8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 3, 2025.

Transparency log

Release files / django_federated_foreign_key-0.1.3-py3-none-any.whl

Download URL django_federated_foreign_key-0.1.3-py3-none-any.whl
Size 18.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
93a9c81f52dc71ab33a9e1b194cb3984c3669bc563cc0fd4719a56ffce987488
BLAKE2b-256 checksum
How to use checksums
fb74cb90e82750fbf2717a5cf5047f4f214c745b1d81be6b4eafdd6ea6019d77
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.12.9

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 3, 2025.

Transparency log

Security for django-federated-foreign-key 0.1.3

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for django_federated_foreign_key-0.1.3.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 3, 2025.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page