django-loose-fk


Namedjango-loose-fk JSON
Version 1.0.4 PyPI version JSON
download
home_pagehttps://github.com/maykinmedia/django-loose-fk
SummaryDjango Loose FK handles local or remote "ForeignKey" references.
upload_time2024-04-05 10:35:01
maintainerNone
docs_urlNone
authorMaykin Media
requires_pythonNone
licenseMIT
keywords foreignkey url reference decentralization integrity
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ===============
Django Loose FK
===============

:Version: 1.0.4
:Source: https://github.com/maykinmedia/django-loose-fk
:Keywords: ``ForeignKey``, ``URL reference``, ``decentralization``, ``integrity``

|build-status| |code-quality| |black| |coverage|

|python-versions| |django-versions| |pypi-version|

Django Loose FK handles local or remote "ForeignKey" references.

In a decentralized API landscape various providers can offer the same type of
data, while your own API also provides this. The django model field allows
you to handle this transparently and present a unified, clean Python API.

.. contents::

.. section-numbering::

Features
========

* Always work with Django model instances
* Automatically added check constraints
* Pluggable interface to fetch remote objects
* Automatically supports DRF Hyperlinked serializers and serializer fields

Installation
============

Requirements
------------

* Python 3.7 or above
* setuptools 30.3.0 or above
* Django 3.2 or newer


Install
-------

.. code-block:: bash

    pip install django-loose-fk

.. warning::

    You must also make sure ``ALLOWED_HOSTS`` is a list of actual domains, and not
    a wildcard. When loose-fk gets a URL to load, it first looks up if the domain
    is a local domain and if so, will load the actual local database record.

Usage
=====

At the core sits a (virtual) django model field.

.. code-block:: python

    from django_loose_fk.fields import FkOrURLField

    class SomeModel(models.Model):
        name = models.CharField(max_length=100)


    class OtherModel(models.Model):
        local = models.ForeignKey(SomeModel, on_delete=models.CASCADE, blank=True, null=True)
        remote = models.URLField(blank=True)
        relation = FkOrURLField(fk_field="local", url_field="remote")


You can now create objects with either local instances or URLs:

.. code-block:: python

    some_local = SomeModel.objects.get()
    OtherModel.objects.create(relation=some_local)

    OtherModel.objects.create(relation="https://example.com/remote.json")


Accessing the attribute will always yield an instance:

.. code-block:: python

    >>> other = OtherModel.objects.get(id=1)  # local FK
    >>> other.relation
    <SomeModel (pk: 1)>

    >>> other = OtherModel.objects.get(id=2)  # remote URL
    >>> other.relation
    <SomeModel (pk: None)>

In the case of a remote URL, the URL will be fetched and the JSON response used
as init kwargs for a model instance. The ``.save()`` method is blocked for
remote instances to prevent mistakes.

Loaders
-------

Loaders are pluggable interfaces to load data. The default loader is
``django_loose_fk.loaders.RequestsLoader``, which depends on the ``requests``
library to fetch the data.

You can specify a global default loader with the setting ``DEFAULT_LOOSE_FK_LOADER``

.. code-block:: python

    DEFAULT_LOOSE_FK_LOADER = "django_loose_fk.loaders.RequestsLoader"

or override the loader on a per-field basis:

.. code-block:: python

    from django_loose_fk.loaders import RequestsLoader

    class MyModel(models.Model):
        ...

        relation = FkOrURLField(
            fk_field="local",
            url_field="remote",
            loader=RequestsLoader()
        )

Local and remote urls
---------------------

If several services are hosted within the same domain, it could be tricky to separate
local and remote urls. In this case an additional setting ``LOOSE_FK_LOCAL_BASE_URLS`` can be used
to define an explicit list of allowed prefixes for local urls.

.. code-block:: python

    LOOSE_FK_LOCAL_BASE_URLS = [
        "https://api.example.nl/ozgv-t/zaken/",
        "https://api.example.nl/ozgv-t/catalogi/",
    ]


.. |build-status| image:: https://github.com/maykinmedia/django-loose-fk/workflows/Run%20CI/badge.svg
    :alt: Build status
    :target: https://github.com/maykinmedia/django-loose-fk/actions?query=workflow%3A%22Run+CI%22

.. |code-quality| image:: https://github.com/maykinmedia/django-loose-fk/workflows/Code%20quality%20checks/badge.svg
     :alt: Code quality checks
     :target: https://github.com/maykinmedia/django-loose-fk/actions?query=workflow%3A%22Code+quality+checks%22

.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
    :target: https://github.com/psf/black

.. |coverage| image:: https://codecov.io/gh/maykinmedia/django-loose-fk/branch/main/graph/badge.svg
    :target: https://codecov.io/gh/maykinmedia/django-loose-fk
    :alt: Coverage status

.. |python-versions| image:: https://img.shields.io/pypi/pyversions/django-loose-fk.svg

.. |django-versions| image:: https://img.shields.io/pypi/djversions/django-loose-fk.svg

.. |pypi-version| image:: https://img.shields.io/pypi/v/django-loose-fk.svg
    :target: https://pypi.org/project/django-loose-fk/

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/maykinmedia/django-loose-fk",
    "name": "django-loose-fk",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": "ForeignKey, URL reference, decentralization, integrity",
    "author": "Maykin Media",
    "author_email": "support@maykinmedia.nl",
    "download_url": "https://files.pythonhosted.org/packages/bb/22/d884fee3639dc55561d66fb55a5c87cb854ec0b993d60f6f4bbba664dbea/django-loose-fk-1.0.4.tar.gz",
    "platform": null,
    "description": "===============\nDjango Loose FK\n===============\n\n:Version: 1.0.4\n:Source: https://github.com/maykinmedia/django-loose-fk\n:Keywords: ``ForeignKey``, ``URL reference``, ``decentralization``, ``integrity``\n\n|build-status| |code-quality| |black| |coverage|\n\n|python-versions| |django-versions| |pypi-version|\n\nDjango Loose FK handles local or remote \"ForeignKey\" references.\n\nIn a decentralized API landscape various providers can offer the same type of\ndata, while your own API also provides this. The django model field allows\nyou to handle this transparently and present a unified, clean Python API.\n\n.. contents::\n\n.. section-numbering::\n\nFeatures\n========\n\n* Always work with Django model instances\n* Automatically added check constraints\n* Pluggable interface to fetch remote objects\n* Automatically supports DRF Hyperlinked serializers and serializer fields\n\nInstallation\n============\n\nRequirements\n------------\n\n* Python 3.7 or above\n* setuptools 30.3.0 or above\n* Django 3.2 or newer\n\n\nInstall\n-------\n\n.. code-block:: bash\n\n    pip install django-loose-fk\n\n.. warning::\n\n    You must also make sure ``ALLOWED_HOSTS`` is a list of actual domains, and not\n    a wildcard. When loose-fk gets a URL to load, it first looks up if the domain\n    is a local domain and if so, will load the actual local database record.\n\nUsage\n=====\n\nAt the core sits a (virtual) django model field.\n\n.. code-block:: python\n\n    from django_loose_fk.fields import FkOrURLField\n\n    class SomeModel(models.Model):\n        name = models.CharField(max_length=100)\n\n\n    class OtherModel(models.Model):\n        local = models.ForeignKey(SomeModel, on_delete=models.CASCADE, blank=True, null=True)\n        remote = models.URLField(blank=True)\n        relation = FkOrURLField(fk_field=\"local\", url_field=\"remote\")\n\n\nYou can now create objects with either local instances or URLs:\n\n.. code-block:: python\n\n    some_local = SomeModel.objects.get()\n    OtherModel.objects.create(relation=some_local)\n\n    OtherModel.objects.create(relation=\"https://example.com/remote.json\")\n\n\nAccessing the attribute will always yield an instance:\n\n.. code-block:: python\n\n    >>> other = OtherModel.objects.get(id=1)  # local FK\n    >>> other.relation\n    <SomeModel (pk: 1)>\n\n    >>> other = OtherModel.objects.get(id=2)  # remote URL\n    >>> other.relation\n    <SomeModel (pk: None)>\n\nIn the case of a remote URL, the URL will be fetched and the JSON response used\nas init kwargs for a model instance. The ``.save()`` method is blocked for\nremote instances to prevent mistakes.\n\nLoaders\n-------\n\nLoaders are pluggable interfaces to load data. The default loader is\n``django_loose_fk.loaders.RequestsLoader``, which depends on the ``requests``\nlibrary to fetch the data.\n\nYou can specify a global default loader with the setting ``DEFAULT_LOOSE_FK_LOADER``\n\n.. code-block:: python\n\n    DEFAULT_LOOSE_FK_LOADER = \"django_loose_fk.loaders.RequestsLoader\"\n\nor override the loader on a per-field basis:\n\n.. code-block:: python\n\n    from django_loose_fk.loaders import RequestsLoader\n\n    class MyModel(models.Model):\n        ...\n\n        relation = FkOrURLField(\n            fk_field=\"local\",\n            url_field=\"remote\",\n            loader=RequestsLoader()\n        )\n\nLocal and remote urls\n---------------------\n\nIf several services are hosted within the same domain, it could be tricky to separate\nlocal and remote urls. In this case an additional setting ``LOOSE_FK_LOCAL_BASE_URLS`` can be used\nto define an explicit list of allowed prefixes for local urls.\n\n.. code-block:: python\n\n    LOOSE_FK_LOCAL_BASE_URLS = [\n        \"https://api.example.nl/ozgv-t/zaken/\",\n        \"https://api.example.nl/ozgv-t/catalogi/\",\n    ]\n\n\n.. |build-status| image:: https://github.com/maykinmedia/django-loose-fk/workflows/Run%20CI/badge.svg\n    :alt: Build status\n    :target: https://github.com/maykinmedia/django-loose-fk/actions?query=workflow%3A%22Run+CI%22\n\n.. |code-quality| image:: https://github.com/maykinmedia/django-loose-fk/workflows/Code%20quality%20checks/badge.svg\n     :alt: Code quality checks\n     :target: https://github.com/maykinmedia/django-loose-fk/actions?query=workflow%3A%22Code+quality+checks%22\n\n.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg\n    :target: https://github.com/psf/black\n\n.. |coverage| image:: https://codecov.io/gh/maykinmedia/django-loose-fk/branch/main/graph/badge.svg\n    :target: https://codecov.io/gh/maykinmedia/django-loose-fk\n    :alt: Coverage status\n\n.. |python-versions| image:: https://img.shields.io/pypi/pyversions/django-loose-fk.svg\n\n.. |django-versions| image:: https://img.shields.io/pypi/djversions/django-loose-fk.svg\n\n.. |pypi-version| image:: https://img.shields.io/pypi/v/django-loose-fk.svg\n    :target: https://pypi.org/project/django-loose-fk/\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Django Loose FK handles local or remote \"ForeignKey\" references.",
    "version": "1.0.4",
    "project_urls": {
        "Homepage": "https://github.com/maykinmedia/django-loose-fk"
    },
    "split_keywords": [
        "foreignkey",
        " url reference",
        " decentralization",
        " integrity"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "e9e88079b69af7fd6200c16be3857f54a0eb794a2570c5de106740b01b472ad7",
                "md5": "c2c95a92a4a5535bd0e751567b80e7d9",
                "sha256": "827065a92f3cdb574b33b05063c777168a9a682ab4a402dbd682b3e9a5e944c8"
            },
            "downloads": -1,
            "filename": "django_loose_fk-1.0.4-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "c2c95a92a4a5535bd0e751567b80e7d9",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 36391,
            "upload_time": "2024-04-05T10:35:00",
            "upload_time_iso_8601": "2024-04-05T10:35:00.076914Z",
            "url": "https://files.pythonhosted.org/packages/e9/e8/8079b69af7fd6200c16be3857f54a0eb794a2570c5de106740b01b472ad7/django_loose_fk-1.0.4-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "bb22d884fee3639dc55561d66fb55a5c87cb854ec0b993d60f6f4bbba664dbea",
                "md5": "ce5c61b36733d3b5b779137431b891ae",
                "sha256": "6bfc899a549945fa9ee7f872ee900108cee3debe461fc8c6626181a5ace8b613"
            },
            "downloads": -1,
            "filename": "django-loose-fk-1.0.4.tar.gz",
            "has_sig": false,
            "md5_digest": "ce5c61b36733d3b5b779137431b891ae",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 28147,
            "upload_time": "2024-04-05T10:35:01",
            "upload_time_iso_8601": "2024-04-05T10:35:01.594714Z",
            "url": "https://files.pythonhosted.org/packages/bb/22/d884fee3639dc55561d66fb55a5c87cb854ec0b993d60f6f4bbba664dbea/django-loose-fk-1.0.4.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-04-05 10:35:01",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "maykinmedia",
    "github_project": "django-loose-fk",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "django-loose-fk"
}
        
Elapsed time: 0.45277s