{"id":29582291,"url":"https://github.com/alancoding/django-federated-foreign-key","last_synced_at":"2025-07-19T21:34:23.866Z","repository":{"id":300469614,"uuid":"1006258622","full_name":"AlanCoding/django-federated-foreign-key","owner":"AlanCoding","description":"A GenericForeignKey drop-in with ability to point to items in another server","archived":false,"fork":false,"pushed_at":"2025-06-30T03:27:45.000Z","size":181,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"devel","last_synced_at":"2025-06-30T03:40:32.433Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AlanCoding.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-06-21T21:10:06.000Z","updated_at":"2025-06-30T03:27:49.000Z","dependencies_parsed_at":"2025-06-21T22:31:48.727Z","dependency_job_id":null,"html_url":"https://github.com/AlanCoding/django-federated-foreign-key","commit_stats":null,"previous_names":["alancoding/django-federated-foreign-key"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/AlanCoding/django-federated-foreign-key","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlanCoding%2Fdjango-federated-foreign-key","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlanCoding%2Fdjango-federated-foreign-key/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlanCoding%2Fdjango-federated-foreign-key/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlanCoding%2Fdjango-federated-foreign-key/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AlanCoding","download_url":"https://codeload.github.com/AlanCoding/django-federated-foreign-key/tar.gz/refs/heads/devel","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AlanCoding%2Fdjango-federated-foreign-key/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266019657,"owners_count":23864916,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-07-19T21:34:23.502Z","updated_at":"2025-07-19T21:34:23.821Z","avatar_url":"https://github.com/AlanCoding.png","language":"Python","readme":"# django-federated-foreign-key\nA `GenericForeignKey` drop-in replacement with ability to point to items in another server.\nInstall with pip:\n\n```bash\npip install django-federated-foreign-key\n```\n\n[![PyPI version](https://img.shields.io/pypi/v/django-federated-foreign-key.svg)](https://pypi.org/project/django-federated-foreign-key/)\n\n## GenericForeignKey\n\nA `GenericForeignKey` allows pointing to objects of any type.\nTo do this, it uses 2 existing fields on the model:\n - A reference to a `ContentType` entry, usually named `content_type` and\n - An id of the related object, usually named `object_id`\n\nhttps://docs.djangoproject.com/en/5.2/ref/contrib/contenttypes/#django.contrib.contenttypes.fields.GenericForeignKey\n\n## Difference for Federated Foreign Key\n\nThe limitation that `FederatedForeignKey` address is that `ContentType` is only designed to\nreference models that exist within the local system, and thus, `GenericForeignKey` as well.\nIt is taken as obvious that `object_id` (the related object id) is the id of the object, in another table, in the same database.\n\nThe intent of `FederatedForeignKey` is to provide the same interface,\nbut expand this to allow referencing objects in different databases.\n\n## Usage\n\nAdd `federated_foreign_key` to `INSTALLED_APPS` and define `FEDERATION_PROJECT_NAME` in your Django settings:\n\n```python\nFEDERATION_PROJECT_NAME = 'project_a'\nINSTALLED_APPS = [\n    'django.contrib.contenttypes',\n    'federated_foreign_key',\n    # your apps...\n]\n```\n\nUse `FederatedForeignKey` in place of `GenericForeignKey` together with `GenericContentType`.\n\n### Example\n\n```python\nfrom federated_foreign_key.fields import FederatedForeignKey\nfrom federated_foreign_key.models import GenericContentType\n\nclass Reference(models.Model):\n    content_type = models.ForeignKey(GenericContentType, on_delete=models.CASCADE)\n    object_id = models.PositiveIntegerField()\n    content_object = FederatedForeignKey(\"content_type\", \"object_id\")\n```\n\n#### Referencing a remote object\n\nIf the referenced item does not exist in the local database, `content_object`\nbecomes a `RemoteObject` placeholder. `GenericContentType.model_class()`\nreturns a stand‑in class that can be used for `isinstance` checks.\n\n```python\nremote_ct = GenericContentType.objects.create(\n    project=\"project_b\",\n    app_label=\"testapp\",\n    model=\"book\",\n)\ncls = remote_ct.model_class()\nref = Reference.objects.create(content_type=remote_ct, object_id=42)\nassert isinstance(ref.content_object, cls)\n```\n\n### Development\n\nInstall development requirements and run linting and tests:\n\n```bash\npip install -r requirements-dev.txt\npip install -e .\npip install -e example_project\nruff check .\nflake8\npytest -q\n# Run Django's contenttypes tests as shipped in `dj_tests/`\n# Use --parallel=1 to avoid multiprocessing issues\npython dj_tests/runtests.py contenttypes_tests --parallel=1\n```\n\n## Demo\n\nRun the included `run_demo.sh` script to start two demo servers.\nThe main `example_project` exposes `/books/` which lists local books and\nbooks fetched from the `remote_project` server. SQLite database files are\ncreated in each project directory when the script sets the `DATABASE_NAME`\nenvironment variable.\n\n```bash\n./run_demo.sh\n```\n\nProvided that is running, go visit in your browser the URL:\n\nhttp://localhost:8000/books/\n\nYou should see the example_project showing the reqest,\nbut in the logs from the remote_project, it should show\nrequests coming from the example_project.\nIt shows the names being filled in with values from the remote server.\n\n## Remote Model Use Cases\n\nThere are 3 cases that this is likely to be used for.\nThis library doesn't provide you with everything you need, just the relationship\nto store a `content_object` that may be non-local.\n\n1. Same model, different servers, different data\n2. Shared model, different servers, different data\n3. Different model, on remote server\n\n### Same model, different servers\n\nThis is what is illustrated in the demo.\nConsider that you have 2 book shops, and they both have their own listings of books.\nA book listing will be duplicate, in the sense that both entries reference the same book.\nBut maybe the two shops have different prices.\nThis helps present a unified list of shopping options for books.\nEach entry could have its own shopping cart icon, to buy that book from that server, at that cost.\n\nIn this case, you would need to create a `GenericContentType` for the remote model.\nThis can have the same (app_label, model_name) as the local model, which is also a `GenericContentType`.\nHowever, the model is also unique on project name, so this is allowed.\n\n### Synchronized tables\n\nIn this case we assume a table is sychronized by some other mechanism.\nThis is not in scope of this project, but could be done by something like the DAB resource_registry app.\n\nhttps://github.com/ansible/django-ansible-base/tree/devel/ansible_base/resource_registry\n\nTo avoid setting a specific owner of the table, a value of \"shared\" for the project name\nwill signify that all servers should treat the item as a local object (within the same DB).\n\n### Different model on remote server\n\nFinally, it would likely to useful to reference a model that doesn't exist locally.\nSay that you were a book shop, but you didn't cary any audiobooks.\nPerhaps another server has an `Audiobook` model, and you want to reference audiobooks.\n\nThis is a good example of where you would need to create a `GenericContentType`,\nwhere the (app_label, model_name) combination does not exist locally.\n\n## Illustration\n\nThis shows how FederatedForeignKey works compared to GenericForeignKey.\n\nThe `object_id` values specify the row location in all cases.\n\n```mermaid\ngraph TD\n  %% First scenario: GenericForeignKey\n  subgraph GenericForeignKey\n    GFKPointerTable[Generic FK Table]\n    ContentType[ContentType Table]\n    TargetTable[Target Model Table]\n\n    GFKPointerTable --\u003e|content_type_id| ContentType\n    GFKPointerTable --\u003e|object_id| TargetTable\n  end\n\n  %% Second scenario: FederatedForeignKey\n  subgraph FederatedForeignKey\n    FFKPointerTable[Federated FK Table]\n    GenericContentType[GenericContentType Table]\n    LocalTargetTable[Local Target Table]\n\n    FFKPointerTable --\u003e|generic_content_type_id| GenericContentType\n    FFKPointerTable --\u003e|object_id| LocalTargetTable\n  end\n\n  subgraph External Database\n    RemoteTargetTable[Remote Target Table]\n  end\n\n  FFKPointerTable --\u003e|object_id| RemoteTargetTable\n```\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falancoding%2Fdjango-federated-foreign-key","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falancoding%2Fdjango-federated-foreign-key","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falancoding%2Fdjango-federated-foreign-key/lists"}