summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCoprDistGit <infra@openeuler.org>2023-05-05 08:20:25 +0000
committerCoprDistGit <infra@openeuler.org>2023-05-05 08:20:25 +0000
commite3eda03cfe84546054eea00d065ae7bfde94b873 (patch)
treecdca91ec9ed19f5688e7863f81f5305a12822883
parente8f4369a2d78739803251b58960abf582d4806db (diff)
automatic import of python-django-entangledopeneuler20.03
-rw-r--r--.gitignore1
-rw-r--r--python-django-entangled.spec685
-rw-r--r--sources1
3 files changed, 687 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index e69de29..33329df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1 @@
+/django-entangled-0.5.4.tar.gz
diff --git a/python-django-entangled.spec b/python-django-entangled.spec
new file mode 100644
index 0000000..75cc42d
--- /dev/null
+++ b/python-django-entangled.spec
@@ -0,0 +1,685 @@
+%global _empty_manifest_terminate_build 0
+Name: python-django-entangled
+Version: 0.5.4
+Release: 1
+Summary: Edit JSON field using Django Model Form
+License: MIT
+URL: https://github.com/jrief/django-entangled
+Source0: https://mirrors.nju.edu.cn/pypi/web/packages/01/74/a45def5d823c66b133af6e62c6ab3e28562db938e964d7ca40a467c5e4f8/django-entangled-0.5.4.tar.gz
+BuildArch: noarch
+
+Requires: python3-django
+
+%description
+# django-entangled
+
+Edit JSON-Model Fields using a Standard Django Form.
+
+[![Build Status](https://travis-ci.org/jrief/django-entangled.svg?branch=master)](https://travis-ci.org/jrief/django-entangled)
+[![Coverage](https://codecov.io/github/jrief/django-entangled/coverage.svg?branch=master)](https://codecov.io/github/jrief/django-entangled?branch=master)
+[![PyPI](https://img.shields.io/pypi/pyversions/django-entangled.svg)]()
+[![PyPI version](https://img.shields.io/pypi/v/django-entangled.svg)](https://https://pypi.python.org/pypi/django-entangled)
+[![PyPI](https://img.shields.io/pypi/l/django-entangled.svg)]()
+
+
+## Use-Case
+
+A Django Model may contain fields which accept arbitrary data stored as JSON. Django itself, provides a
+[JSON field](https://docs.djangoproject.com/en/stable/ref/models/fields/#django.db.models.JSONField) to store arbitrary
+serializable data.
+
+When creating a form from a model, the input field associated with a JSON field, typically is a `<textarea ...></textarea>`.
+This textarea widget is very inpracticable for editing, because it just contains a textual representation of that
+object notation. One possibility is to use a generic [JSON editor](https://github.com/josdejong/jsoneditor),
+which with some JavaScript, transforms the widget into an attribute-value-pair editor. This approach however requires
+to manage the field keys ourself. It furthermore prevents us from utilizing all the nice features provided by the Django
+form framework, such as field validation, normalization of data and the usage of foreign keys.
+
+By using **django-entangled**, one can use a Django `ModelForm`, and store all,
+or a subset of that form fields in one or more JSON fields inside of the associated model.
+
+
+## Installation
+
+Simply install this Django app, for instance by invoking:
+
+```bash
+pip install django-entangled
+```
+
+There is no need to add any configuration directives to the project's `settings.py`.
+
+
+## Example
+
+Say, we have a Django model to describe a bunch of different products. The name and the price fields are common to all
+products, whereas the properties can vary depending on its product type. Since we don't want to create a different
+product model for each product type, we use a JSON field to store these arbitrary properties.
+
+```python
+from django.db import models
+
+class Product(models.Model):
+ name = models.CharField(max_length=50)
+
+ price = models.DecimalField(max_digits=5, decimal_places=2)
+
+ properties = models.JSONField()
+```
+
+In a typical form editing view, we would create a form inheriting from
+[ModelForm](https://docs.djangoproject.com/en/stable/topics/forms/modelforms/#modelform) and refer to this model using
+the `model` attribute in its `Meta`-class. Then the `properties`-field would show up as unstructured JSON, rendered
+inside a `<textarea ...></textarea>`. This definitely is not what we want! Instead we create a typical Django Form using
+the alternative class `EntangledModelForm`.
+
+```python
+from django.contrib.auth import get_user_model
+from django.forms import fields, models
+from entangled.forms import EntangledModelForm
+from .models import Product
+
+class ProductForm(EntangledModelForm):
+ color = fields.RegexField(
+ regex=r'^#[0-9a-f]{6}$',
+ )
+
+ size = fields.ChoiceField(
+ choices=[('s', "small"), ('m', "medium"), ('l', "large"), ('xl', "extra large")],
+ )
+
+ tenant = models.ModelChoiceField(
+ queryset=get_user_model().objects.filter(is_staff=True),
+ )
+
+ class Meta:
+ model = Product
+ entangled_fields = {'properties': ['color', 'size', 'tenant']} # fields provided by this form
+ untangled_fields = ['name', 'price'] # these fields are provided by the Product model
+```
+
+In case our form inherits from another `ModelForm`, rewrite the class declarartion as:
+
+```python
+class ProductForm(EntangledModelFormMixin, BaseProductForm):
+ ...
+```
+
+In addition we add a special dictionary named `entangled_fields` to our `Meta`-options. In this dictionary, the key
+(here `'properties'`) refers to the JSON-field in our model `Product`. The value (here `['color', 'size', 'tenant']`)
+is a list of named form fields, declared in our form- or base-class of thereof. This allows us to assign all standard
+Django form fields to arbitrary JSON fields declared in our Django model. Moreover, we can even use a `ModelChoiceField`
+or a `ModelMultipleChoiceField` to refer to another model object using a
+[generic relation](https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/#generic-relations)
+
+Since in this form we also want to access the non-JSON fields from our Django model, we add a list named
+`untangled_fields` to our `Meta`-options. In this list, (here `['name', 'price']`) we refer to the non-JSON fields
+in our model `Product`. From both of these iterables, `entangled_fields` and `untangled_fields`, the parent class
+`EntangledModelForm` then builds the `Meta`-option `fields`, otherwise required. Therefore you should not
+use `fields` to declare this list, but rather rely on `entangled_fields` and `untangled_fields`.
+
+We can use this form in any Django form view. A typical use-case, is the built-in Django `ModelAdmin`:
+
+```python
+from django.contrib import admin
+from .models import Product
+from .forms import ProductForm
+
+@admin.register(Product)
+class ProductAdmin(admin.ModelAdmin):
+ form = ProductForm
+```
+
+Since the form used by this `ModelAdmin`-class
+[can not be created dynamically](https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.form),
+we have to declare it explicitly using the `form`-attribute. This is the only change which has to be performed, in
+order to store arbitrary content inside our JSON model-fields.
+
+
+## Nested Data Structures
+
+Sometimes it can be desirable to store the data in a nested hierarchy of dictionaries, rather than having all
+attribute-value-pairs in the first level of our JSON field. This can for instance be handy when merging more than one
+form, all themselves ineriting from `EntangledModelFormMixin`.
+
+Say that we have different types of products, all of which share the same base product form:
+
+```python
+from django.contrib.auth import get_user_model
+from django.forms import models
+from entangled.forms import EntangledModelFormMixin
+from .models import Product
+
+class BaseProductForm(EntangledModelFormMixin):
+ tenant = models.ModelChoiceField(
+ queryset=get_user_model().objects.filter(is_staff=True),
+ )
+
+ class Meta:
+ model = Product
+ entangled_fields = {'properties': ['tenant']}
+ untangled_fields = ['name', 'price']
+```
+
+In order to specialize our base product towards, say clothing, we typically would inherit from the base form
+and add some additional fields, here `color` and `size`:
+
+```python
+from django.forms import fields
+from .forms import BaseProductForm
+from .models import Product
+
+class ClothingProductForm(BaseProductForm):
+ color = fields.RegexField(
+ regex=r'^#[0-9a-f]{6}$',
+ )
+
+ size = fields.ChoiceField(
+ choices=[('s', "small"), ('m', "medium"), ('l', "large"), ('xl', "extra large")],
+ )
+
+ class Meta:
+ model = Product
+ entangled_fields = {'properties': ['color', 'size']}
+ retangled_fields = {'color': 'variants.color', 'size': 'variants.size'}
+```
+
+By adding a name mapping from our existing field names, we can group the fields `color` and `size`
+into a sub-dictionary named `variants` inside our `properties` fields. Such a field mapping is
+declared through the optional Meta-option `retangled_fields`. In this dictionary, all entries are
+optional; if a field name is missing, it just maps to itself.
+
+This mapping table can also be used to map field names to other keys inside the resulting JSON
+datastructure. This for instance is handy to map fields containg an underscore into field-names
+containing instead a dash.
+
+
+## Caveats
+
+Due to the nature of JSON, indexing and thus building filters or sorting rules based on the fields content is not as
+simple, as with standard model fields. Therefore, this approach is best suited, if the main focus is to store data,
+rather than digging through data.
+
+Foreign keys are stored as `"fieldname": {"model": "appname.modelname", "pk": 1234}` in our JSON field, meaning that
+we have no database constraints. If a target object is deleted, that foreign key points to nowhere. Therefore always
+keep in mind, that we don't have any referential integrity and hence must write our code in a defensive manner.
+
+
+## Contributing to the Project
+
+* Please ask question on the [discussion board](https://github.com/jrief/django-entangled/discussions).
+* Ideas for new features shall as well be discussed on that board.
+* The [issue tracker](https://github.com/jrief/django-entangled/issues) shall *exclusively* be used to report bugs.
+* Except for very small fixes (typos etc.), do not open a pull request without an issue.
+* Before writing code, adopt your IDE to respect the project's [.editorconfig](https://github.com/jrief/django-entangled/blob/master/.editorconfig).
+
+
+[![Twitter Follow](https://img.shields.io/twitter/follow/jacobrief?style=social)](https://twitter.com/jacobrief)
+
+
+%package -n python3-django-entangled
+Summary: Edit JSON field using Django Model Form
+Provides: python-django-entangled
+BuildRequires: python3-devel
+BuildRequires: python3-setuptools
+BuildRequires: python3-pip
+%description -n python3-django-entangled
+# django-entangled
+
+Edit JSON-Model Fields using a Standard Django Form.
+
+[![Build Status](https://travis-ci.org/jrief/django-entangled.svg?branch=master)](https://travis-ci.org/jrief/django-entangled)
+[![Coverage](https://codecov.io/github/jrief/django-entangled/coverage.svg?branch=master)](https://codecov.io/github/jrief/django-entangled?branch=master)
+[![PyPI](https://img.shields.io/pypi/pyversions/django-entangled.svg)]()
+[![PyPI version](https://img.shields.io/pypi/v/django-entangled.svg)](https://https://pypi.python.org/pypi/django-entangled)
+[![PyPI](https://img.shields.io/pypi/l/django-entangled.svg)]()
+
+
+## Use-Case
+
+A Django Model may contain fields which accept arbitrary data stored as JSON. Django itself, provides a
+[JSON field](https://docs.djangoproject.com/en/stable/ref/models/fields/#django.db.models.JSONField) to store arbitrary
+serializable data.
+
+When creating a form from a model, the input field associated with a JSON field, typically is a `<textarea ...></textarea>`.
+This textarea widget is very inpracticable for editing, because it just contains a textual representation of that
+object notation. One possibility is to use a generic [JSON editor](https://github.com/josdejong/jsoneditor),
+which with some JavaScript, transforms the widget into an attribute-value-pair editor. This approach however requires
+to manage the field keys ourself. It furthermore prevents us from utilizing all the nice features provided by the Django
+form framework, such as field validation, normalization of data and the usage of foreign keys.
+
+By using **django-entangled**, one can use a Django `ModelForm`, and store all,
+or a subset of that form fields in one or more JSON fields inside of the associated model.
+
+
+## Installation
+
+Simply install this Django app, for instance by invoking:
+
+```bash
+pip install django-entangled
+```
+
+There is no need to add any configuration directives to the project's `settings.py`.
+
+
+## Example
+
+Say, we have a Django model to describe a bunch of different products. The name and the price fields are common to all
+products, whereas the properties can vary depending on its product type. Since we don't want to create a different
+product model for each product type, we use a JSON field to store these arbitrary properties.
+
+```python
+from django.db import models
+
+class Product(models.Model):
+ name = models.CharField(max_length=50)
+
+ price = models.DecimalField(max_digits=5, decimal_places=2)
+
+ properties = models.JSONField()
+```
+
+In a typical form editing view, we would create a form inheriting from
+[ModelForm](https://docs.djangoproject.com/en/stable/topics/forms/modelforms/#modelform) and refer to this model using
+the `model` attribute in its `Meta`-class. Then the `properties`-field would show up as unstructured JSON, rendered
+inside a `<textarea ...></textarea>`. This definitely is not what we want! Instead we create a typical Django Form using
+the alternative class `EntangledModelForm`.
+
+```python
+from django.contrib.auth import get_user_model
+from django.forms import fields, models
+from entangled.forms import EntangledModelForm
+from .models import Product
+
+class ProductForm(EntangledModelForm):
+ color = fields.RegexField(
+ regex=r'^#[0-9a-f]{6}$',
+ )
+
+ size = fields.ChoiceField(
+ choices=[('s', "small"), ('m', "medium"), ('l', "large"), ('xl', "extra large")],
+ )
+
+ tenant = models.ModelChoiceField(
+ queryset=get_user_model().objects.filter(is_staff=True),
+ )
+
+ class Meta:
+ model = Product
+ entangled_fields = {'properties': ['color', 'size', 'tenant']} # fields provided by this form
+ untangled_fields = ['name', 'price'] # these fields are provided by the Product model
+```
+
+In case our form inherits from another `ModelForm`, rewrite the class declarartion as:
+
+```python
+class ProductForm(EntangledModelFormMixin, BaseProductForm):
+ ...
+```
+
+In addition we add a special dictionary named `entangled_fields` to our `Meta`-options. In this dictionary, the key
+(here `'properties'`) refers to the JSON-field in our model `Product`. The value (here `['color', 'size', 'tenant']`)
+is a list of named form fields, declared in our form- or base-class of thereof. This allows us to assign all standard
+Django form fields to arbitrary JSON fields declared in our Django model. Moreover, we can even use a `ModelChoiceField`
+or a `ModelMultipleChoiceField` to refer to another model object using a
+[generic relation](https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/#generic-relations)
+
+Since in this form we also want to access the non-JSON fields from our Django model, we add a list named
+`untangled_fields` to our `Meta`-options. In this list, (here `['name', 'price']`) we refer to the non-JSON fields
+in our model `Product`. From both of these iterables, `entangled_fields` and `untangled_fields`, the parent class
+`EntangledModelForm` then builds the `Meta`-option `fields`, otherwise required. Therefore you should not
+use `fields` to declare this list, but rather rely on `entangled_fields` and `untangled_fields`.
+
+We can use this form in any Django form view. A typical use-case, is the built-in Django `ModelAdmin`:
+
+```python
+from django.contrib import admin
+from .models import Product
+from .forms import ProductForm
+
+@admin.register(Product)
+class ProductAdmin(admin.ModelAdmin):
+ form = ProductForm
+```
+
+Since the form used by this `ModelAdmin`-class
+[can not be created dynamically](https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.form),
+we have to declare it explicitly using the `form`-attribute. This is the only change which has to be performed, in
+order to store arbitrary content inside our JSON model-fields.
+
+
+## Nested Data Structures
+
+Sometimes it can be desirable to store the data in a nested hierarchy of dictionaries, rather than having all
+attribute-value-pairs in the first level of our JSON field. This can for instance be handy when merging more than one
+form, all themselves ineriting from `EntangledModelFormMixin`.
+
+Say that we have different types of products, all of which share the same base product form:
+
+```python
+from django.contrib.auth import get_user_model
+from django.forms import models
+from entangled.forms import EntangledModelFormMixin
+from .models import Product
+
+class BaseProductForm(EntangledModelFormMixin):
+ tenant = models.ModelChoiceField(
+ queryset=get_user_model().objects.filter(is_staff=True),
+ )
+
+ class Meta:
+ model = Product
+ entangled_fields = {'properties': ['tenant']}
+ untangled_fields = ['name', 'price']
+```
+
+In order to specialize our base product towards, say clothing, we typically would inherit from the base form
+and add some additional fields, here `color` and `size`:
+
+```python
+from django.forms import fields
+from .forms import BaseProductForm
+from .models import Product
+
+class ClothingProductForm(BaseProductForm):
+ color = fields.RegexField(
+ regex=r'^#[0-9a-f]{6}$',
+ )
+
+ size = fields.ChoiceField(
+ choices=[('s', "small"), ('m', "medium"), ('l', "large"), ('xl', "extra large")],
+ )
+
+ class Meta:
+ model = Product
+ entangled_fields = {'properties': ['color', 'size']}
+ retangled_fields = {'color': 'variants.color', 'size': 'variants.size'}
+```
+
+By adding a name mapping from our existing field names, we can group the fields `color` and `size`
+into a sub-dictionary named `variants` inside our `properties` fields. Such a field mapping is
+declared through the optional Meta-option `retangled_fields`. In this dictionary, all entries are
+optional; if a field name is missing, it just maps to itself.
+
+This mapping table can also be used to map field names to other keys inside the resulting JSON
+datastructure. This for instance is handy to map fields containg an underscore into field-names
+containing instead a dash.
+
+
+## Caveats
+
+Due to the nature of JSON, indexing and thus building filters or sorting rules based on the fields content is not as
+simple, as with standard model fields. Therefore, this approach is best suited, if the main focus is to store data,
+rather than digging through data.
+
+Foreign keys are stored as `"fieldname": {"model": "appname.modelname", "pk": 1234}` in our JSON field, meaning that
+we have no database constraints. If a target object is deleted, that foreign key points to nowhere. Therefore always
+keep in mind, that we don't have any referential integrity and hence must write our code in a defensive manner.
+
+
+## Contributing to the Project
+
+* Please ask question on the [discussion board](https://github.com/jrief/django-entangled/discussions).
+* Ideas for new features shall as well be discussed on that board.
+* The [issue tracker](https://github.com/jrief/django-entangled/issues) shall *exclusively* be used to report bugs.
+* Except for very small fixes (typos etc.), do not open a pull request without an issue.
+* Before writing code, adopt your IDE to respect the project's [.editorconfig](https://github.com/jrief/django-entangled/blob/master/.editorconfig).
+
+
+[![Twitter Follow](https://img.shields.io/twitter/follow/jacobrief?style=social)](https://twitter.com/jacobrief)
+
+
+%package help
+Summary: Development documents and examples for django-entangled
+Provides: python3-django-entangled-doc
+%description help
+# django-entangled
+
+Edit JSON-Model Fields using a Standard Django Form.
+
+[![Build Status](https://travis-ci.org/jrief/django-entangled.svg?branch=master)](https://travis-ci.org/jrief/django-entangled)
+[![Coverage](https://codecov.io/github/jrief/django-entangled/coverage.svg?branch=master)](https://codecov.io/github/jrief/django-entangled?branch=master)
+[![PyPI](https://img.shields.io/pypi/pyversions/django-entangled.svg)]()
+[![PyPI version](https://img.shields.io/pypi/v/django-entangled.svg)](https://https://pypi.python.org/pypi/django-entangled)
+[![PyPI](https://img.shields.io/pypi/l/django-entangled.svg)]()
+
+
+## Use-Case
+
+A Django Model may contain fields which accept arbitrary data stored as JSON. Django itself, provides a
+[JSON field](https://docs.djangoproject.com/en/stable/ref/models/fields/#django.db.models.JSONField) to store arbitrary
+serializable data.
+
+When creating a form from a model, the input field associated with a JSON field, typically is a `<textarea ...></textarea>`.
+This textarea widget is very inpracticable for editing, because it just contains a textual representation of that
+object notation. One possibility is to use a generic [JSON editor](https://github.com/josdejong/jsoneditor),
+which with some JavaScript, transforms the widget into an attribute-value-pair editor. This approach however requires
+to manage the field keys ourself. It furthermore prevents us from utilizing all the nice features provided by the Django
+form framework, such as field validation, normalization of data and the usage of foreign keys.
+
+By using **django-entangled**, one can use a Django `ModelForm`, and store all,
+or a subset of that form fields in one or more JSON fields inside of the associated model.
+
+
+## Installation
+
+Simply install this Django app, for instance by invoking:
+
+```bash
+pip install django-entangled
+```
+
+There is no need to add any configuration directives to the project's `settings.py`.
+
+
+## Example
+
+Say, we have a Django model to describe a bunch of different products. The name and the price fields are common to all
+products, whereas the properties can vary depending on its product type. Since we don't want to create a different
+product model for each product type, we use a JSON field to store these arbitrary properties.
+
+```python
+from django.db import models
+
+class Product(models.Model):
+ name = models.CharField(max_length=50)
+
+ price = models.DecimalField(max_digits=5, decimal_places=2)
+
+ properties = models.JSONField()
+```
+
+In a typical form editing view, we would create a form inheriting from
+[ModelForm](https://docs.djangoproject.com/en/stable/topics/forms/modelforms/#modelform) and refer to this model using
+the `model` attribute in its `Meta`-class. Then the `properties`-field would show up as unstructured JSON, rendered
+inside a `<textarea ...></textarea>`. This definitely is not what we want! Instead we create a typical Django Form using
+the alternative class `EntangledModelForm`.
+
+```python
+from django.contrib.auth import get_user_model
+from django.forms import fields, models
+from entangled.forms import EntangledModelForm
+from .models import Product
+
+class ProductForm(EntangledModelForm):
+ color = fields.RegexField(
+ regex=r'^#[0-9a-f]{6}$',
+ )
+
+ size = fields.ChoiceField(
+ choices=[('s', "small"), ('m', "medium"), ('l', "large"), ('xl', "extra large")],
+ )
+
+ tenant = models.ModelChoiceField(
+ queryset=get_user_model().objects.filter(is_staff=True),
+ )
+
+ class Meta:
+ model = Product
+ entangled_fields = {'properties': ['color', 'size', 'tenant']} # fields provided by this form
+ untangled_fields = ['name', 'price'] # these fields are provided by the Product model
+```
+
+In case our form inherits from another `ModelForm`, rewrite the class declarartion as:
+
+```python
+class ProductForm(EntangledModelFormMixin, BaseProductForm):
+ ...
+```
+
+In addition we add a special dictionary named `entangled_fields` to our `Meta`-options. In this dictionary, the key
+(here `'properties'`) refers to the JSON-field in our model `Product`. The value (here `['color', 'size', 'tenant']`)
+is a list of named form fields, declared in our form- or base-class of thereof. This allows us to assign all standard
+Django form fields to arbitrary JSON fields declared in our Django model. Moreover, we can even use a `ModelChoiceField`
+or a `ModelMultipleChoiceField` to refer to another model object using a
+[generic relation](https://docs.djangoproject.com/en/stable/ref/contrib/contenttypes/#generic-relations)
+
+Since in this form we also want to access the non-JSON fields from our Django model, we add a list named
+`untangled_fields` to our `Meta`-options. In this list, (here `['name', 'price']`) we refer to the non-JSON fields
+in our model `Product`. From both of these iterables, `entangled_fields` and `untangled_fields`, the parent class
+`EntangledModelForm` then builds the `Meta`-option `fields`, otherwise required. Therefore you should not
+use `fields` to declare this list, but rather rely on `entangled_fields` and `untangled_fields`.
+
+We can use this form in any Django form view. A typical use-case, is the built-in Django `ModelAdmin`:
+
+```python
+from django.contrib import admin
+from .models import Product
+from .forms import ProductForm
+
+@admin.register(Product)
+class ProductAdmin(admin.ModelAdmin):
+ form = ProductForm
+```
+
+Since the form used by this `ModelAdmin`-class
+[can not be created dynamically](https://docs.djangoproject.com/en/stable/ref/contrib/admin/#django.contrib.admin.ModelAdmin.form),
+we have to declare it explicitly using the `form`-attribute. This is the only change which has to be performed, in
+order to store arbitrary content inside our JSON model-fields.
+
+
+## Nested Data Structures
+
+Sometimes it can be desirable to store the data in a nested hierarchy of dictionaries, rather than having all
+attribute-value-pairs in the first level of our JSON field. This can for instance be handy when merging more than one
+form, all themselves ineriting from `EntangledModelFormMixin`.
+
+Say that we have different types of products, all of which share the same base product form:
+
+```python
+from django.contrib.auth import get_user_model
+from django.forms import models
+from entangled.forms import EntangledModelFormMixin
+from .models import Product
+
+class BaseProductForm(EntangledModelFormMixin):
+ tenant = models.ModelChoiceField(
+ queryset=get_user_model().objects.filter(is_staff=True),
+ )
+
+ class Meta:
+ model = Product
+ entangled_fields = {'properties': ['tenant']}
+ untangled_fields = ['name', 'price']
+```
+
+In order to specialize our base product towards, say clothing, we typically would inherit from the base form
+and add some additional fields, here `color` and `size`:
+
+```python
+from django.forms import fields
+from .forms import BaseProductForm
+from .models import Product
+
+class ClothingProductForm(BaseProductForm):
+ color = fields.RegexField(
+ regex=r'^#[0-9a-f]{6}$',
+ )
+
+ size = fields.ChoiceField(
+ choices=[('s', "small"), ('m', "medium"), ('l', "large"), ('xl', "extra large")],
+ )
+
+ class Meta:
+ model = Product
+ entangled_fields = {'properties': ['color', 'size']}
+ retangled_fields = {'color': 'variants.color', 'size': 'variants.size'}
+```
+
+By adding a name mapping from our existing field names, we can group the fields `color` and `size`
+into a sub-dictionary named `variants` inside our `properties` fields. Such a field mapping is
+declared through the optional Meta-option `retangled_fields`. In this dictionary, all entries are
+optional; if a field name is missing, it just maps to itself.
+
+This mapping table can also be used to map field names to other keys inside the resulting JSON
+datastructure. This for instance is handy to map fields containg an underscore into field-names
+containing instead a dash.
+
+
+## Caveats
+
+Due to the nature of JSON, indexing and thus building filters or sorting rules based on the fields content is not as
+simple, as with standard model fields. Therefore, this approach is best suited, if the main focus is to store data,
+rather than digging through data.
+
+Foreign keys are stored as `"fieldname": {"model": "appname.modelname", "pk": 1234}` in our JSON field, meaning that
+we have no database constraints. If a target object is deleted, that foreign key points to nowhere. Therefore always
+keep in mind, that we don't have any referential integrity and hence must write our code in a defensive manner.
+
+
+## Contributing to the Project
+
+* Please ask question on the [discussion board](https://github.com/jrief/django-entangled/discussions).
+* Ideas for new features shall as well be discussed on that board.
+* The [issue tracker](https://github.com/jrief/django-entangled/issues) shall *exclusively* be used to report bugs.
+* Except for very small fixes (typos etc.), do not open a pull request without an issue.
+* Before writing code, adopt your IDE to respect the project's [.editorconfig](https://github.com/jrief/django-entangled/blob/master/.editorconfig).
+
+
+[![Twitter Follow](https://img.shields.io/twitter/follow/jacobrief?style=social)](https://twitter.com/jacobrief)
+
+
+%prep
+%autosetup -n django-entangled-0.5.4
+
+%build
+%py3_build
+
+%install
+%py3_install
+install -d -m755 %{buildroot}/%{_pkgdocdir}
+if [ -d doc ]; then cp -arf doc %{buildroot}/%{_pkgdocdir}; fi
+if [ -d docs ]; then cp -arf docs %{buildroot}/%{_pkgdocdir}; fi
+if [ -d example ]; then cp -arf example %{buildroot}/%{_pkgdocdir}; fi
+if [ -d examples ]; then cp -arf examples %{buildroot}/%{_pkgdocdir}; fi
+pushd %{buildroot}
+if [ -d usr/lib ]; then
+ find usr/lib -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/lib64 ]; then
+ find usr/lib64 -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/bin ]; then
+ find usr/bin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+if [ -d usr/sbin ]; then
+ find usr/sbin -type f -printf "/%h/%f\n" >> filelist.lst
+fi
+touch doclist.lst
+if [ -d usr/share/man ]; then
+ find usr/share/man -type f -printf "/%h/%f.gz\n" >> doclist.lst
+fi
+popd
+mv %{buildroot}/filelist.lst .
+mv %{buildroot}/doclist.lst .
+
+%files -n python3-django-entangled -f filelist.lst
+%dir %{python3_sitelib}/*
+
+%files help -f doclist.lst
+%{_docdir}/*
+
+%changelog
+* Fri May 05 2023 Python_Bot <Python_Bot@openeuler.org> - 0.5.4-1
+- Package Spec generated
diff --git a/sources b/sources
new file mode 100644
index 0000000..21c7685
--- /dev/null
+++ b/sources
@@ -0,0 +1 @@
+b406309c300a3ab2278855a8d65dcf45 django-entangled-0.5.4.tar.gz