GenericForeignKey that can reference objects across projects
Project description
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
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
ContentTypeentry, usually namedcontent_typeand - An id of the related object, usually named
object_id
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")
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:
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.
- Same model, different servers, different data
- Shared model, different servers, different data
- 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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_federated_foreign_key-0.1.2.tar.gz.
File metadata
- Download URL: django_federated_foreign_key-0.1.2.tar.gz
- Upload date:
- Size: 47.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a7046524da404049d34bc9c37cbeca558ce5b0aea0bdc8a2b2dbc5a23db6852
|
|
| MD5 |
52d11829fe97890ccc42516a369938b9
|
|
| BLAKE2b-256 |
4f5c654d61d93f21d441e8b8f5f3cb7de941ceeada69576b228faf452e9baa64
|
Provenance
The following attestation bundles were made for django_federated_foreign_key-0.1.2.tar.gz:
Publisher:
release.yml on AlanCoding/django-federated-foreign-key
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_federated_foreign_key-0.1.2.tar.gz -
Subject digest:
9a7046524da404049d34bc9c37cbeca558ce5b0aea0bdc8a2b2dbc5a23db6852 - Sigstore transparency entry: 257887622
- Sigstore integration time:
-
Permalink:
AlanCoding/django-federated-foreign-key@22d72c4554b99dac774758a0f75143f9d5c0daed -
Branch / Tag:
refs/tags/0.1.2 - Owner: https://github.com/AlanCoding
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@22d72c4554b99dac774758a0f75143f9d5c0daed -
Trigger Event:
release
-
Statement type:
File details
Details for the file django_federated_foreign_key-0.1.2-py3-none-any.whl.
File metadata
- Download URL: django_federated_foreign_key-0.1.2-py3-none-any.whl
- Upload date:
- Size: 17.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c1b804a92a5145262b16571100f61690019d0093ce5c2f53d02a129654db1794
|
|
| MD5 |
3a1054c692caa3b6a663caabbf452c2b
|
|
| BLAKE2b-256 |
90265f3f4b018fbedd9421809b6ca311019bcc709d6f94c23f98f4f554f85819
|
Provenance
The following attestation bundles were made for django_federated_foreign_key-0.1.2-py3-none-any.whl:
Publisher:
release.yml on AlanCoding/django-federated-foreign-key
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_federated_foreign_key-0.1.2-py3-none-any.whl -
Subject digest:
c1b804a92a5145262b16571100f61690019d0093ce5c2f53d02a129654db1794 - Sigstore transparency entry: 257887632
- Sigstore integration time:
-
Permalink:
AlanCoding/django-federated-foreign-key@22d72c4554b99dac774758a0f75143f9d5c0daed -
Branch / Tag:
refs/tags/0.1.2 - Owner: https://github.com/AlanCoding
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@22d72c4554b99dac774758a0f75143f9d5c0daed -
Trigger Event:
release
-
Statement type: