summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--python-django-konst.spec967
-rw-r--r--sources1
3 files changed, 969 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index e69de29..1409400 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1 @@
+/django-konst-2.0.0.tar.gz
diff --git a/python-django-konst.spec b/python-django-konst.spec
new file mode 100644
index 0000000..bd42c34
--- /dev/null
+++ b/python-django-konst.spec
@@ -0,0 +1,967 @@
+%global _empty_manifest_terminate_build 0
+Name: python-django-konst
+Version: 2.0.0
+Release: 1
+Summary: Convenient constants fields for Django
+License: MIT
+URL: https://github.com/lukeburden/django-konst
+Source0: https://mirrors.nju.edu.cn/pypi/web/packages/86/69/83a4fad274fd20f38a046f1e3d803d8b04eed00fdd71f2cec8dc655f00ec/django-konst-2.0.0.tar.gz
+BuildArch: noarch
+
+Requires: python3-django
+
+%description
+# Django Konst
+
+[![](https://img.shields.io/pypi/v/django-konst.svg)](https://pypi.python.org/pypi/django-konst/)
+[![](https://img.shields.io/badge/license-MIT-blue.svg)](https://pypi.python.org/pypi/django-konst/)
+[![CircleCI](https://circleci.com/gh/lukeburden/django-konst.svg?style=svg)](https://circleci.com/gh/lukeburden/django-konst)
+[![Codecov](https://codecov.io/gh/lukeburden/django-konst/branch/master/graph/badge.svg)](https://codecov.io/gh/lukeburden/django-konst)
+[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
+
+
+## django-konst
+
+`django-konst` is a utility for Django that makes the definition, use and storage of both integer and string based constants easy and readable. It avoids passing of constants
+into template contexts and makes evaluation concise and readable.
+
+It also makes exposure of these constants via forms and DRF serializers simple.
+
+### Definition ###
+
+Constants can be defined with friendly names, backed by either integers or text.
+
+#### Constants ####
+
+```python
+from konst import Constant
+
+# states backed by integers
+states = Constants(
+ Constant(pending=0),
+ Constant(active=1),
+ Constant(inactive=2)
+)
+
+# and another set of constants backed by strings
+colours = Constants(
+ Constant(red="FF0000"),
+ Constant(green="00FF00"),
+ Constant(yellow="FFFF80"),
+ Constant(white="FFFFFF")
+)
+
+```
+
+#### Constant groups ####
+
+At times, it will be necessary to group constants and test membership within that group.
+To achieve this, `django-konst` provides a `ConstantGroup` class.
+
+```python
+from konst import Constant, ConstantGroup
+
+# states backed by integers
+states = Constants(
+ Constant(active=0),
+ Constant(cancelled_ontime=1),
+ Constant(cancelled_late=2),
+ ConstantGroup(
+ "cancelled",
+ ("cancelled_ontime", "cancelled_late")
+ )
+)
+
+```
+
+#### Within a model ####
+
+While not strictly necessary, it is advisable to effectively namespace your constants
+by defining them in the scope of a model definition. This means you have your constants
+wherever you have the model class, as well as any model instance.
+
+
+```python
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+from konst import Constant, ConstantGroup, Constants
+from konst.models.fields import (
+ ConstantChoiceCharField,
+ ConstantChoiceField
+)
+
+class Apple(models.Model):
+
+ purposes = Constants(
+ Constant(cooking=0, label=_("Cook me!")),
+ Constant(eating=1, label=_("Eat me!")),
+ Constant(juicing=2, label=_("Juice me!")),
+ Constant(ornamental=3, label=_("Just look how pretty I am!")),
+ ConstantGroup(
+ "culinary", ("cooking", "eating", "juicing")
+ )
+ )
+ colours = Constants(
+ Constant(red="FF0000", label=_("red")),
+ Constant(green="00FF00", label=_("green")),
+ Constant(yellow="FFFF80", label=_("yellow")),
+ Constant(white="FFFFFF", label=_("white")),
+ )
+
+ name = models.CharField(max_length=30)
+ purpose = ConstantChoiceField(constants=purposes)
+ colour = ConstantChoiceCharField(constants=colours, max_length=30)
+
+```
+
+
+### Use ###
+
+The entire point of this library is to make the use of constants defined in this way
+easy and concise.
+
+#### In code ####
+
+```python
+apple = Apple.objects.get(name='Granny Smith')
+apple.purpose.cooking
+True
+apple.colour.red
+True
+apple.colour.green
+False
+
+# we don't care about the specific purpose, just whether it is as food
+# or not, so use the ConstantGroup!
+apple.purpose.culinary
+True
+
+```
+
+#### In templates ####
+
+```
+{% if apple.purpose.eating %}
+ You should bite this {{ apple.name }}!
+{% endif %}
+```
+
+#### With Django's ORM ####
+
+```python
+red_apples = Apple.objects.filter(colour=Apple.colours.red)
+culinary_apples = Apple.objects.filter(
+ purpose__in=Apple.purposes.culinary
+)
+
+```
+
+#### With Django Rest Framework ####
+
+Using the `konst.extras.drf.fields.ConstantChoiceField` serializer field with the
+Django Rest Framework it is possible to both output and receive constant values.
+
+```python
+from konst.extras.drf.fields import ConstantChoiceField
+
+from rest_framework import serializers
+
+
+class AppleSerializer(serializers.ModelSerializer):
+
+ purpose = ConstantChoiceField(Apple.purposes)
+ colour = ConstantChoiceField(Apple.colours)
+
+ class Meta:
+ model = Apple
+ fields = (
+ "name", "purpose", "colour"
+ )
+
+
+# let's see how it handles bad values
+serializer = AppleSerializer(
+ data={
+ "name": "Fuji",
+ "colour": "blue",
+ "purpose": "dicing"
+ }
+)
+serializer.is_valid()
+False
+serializer.errors
+{
+ 'colour': [u'"blue" is not a valid choice.'],
+ 'purpose': [u'"dicing" is not a valid choice.']
+}
+
+
+# and now how it handles some good values
+serializer = AppleSerializer(
+ data={
+ "name": "Fuji",
+ "colour": "red",
+ "purpose": "eating"
+ }
+)
+serializer.is_valid()
+True
+
+
+# let's create a database entry!
+instance = serializer.save()
+
+
+# and now our instance can be interacted with neatly
+instance.colour.red
+True
+
+
+# finally, let's see how this looks when rendering JSON
+AppleSerializer(instance=instance).data
+{
+ "name": "Fuji",
+ "colour": "red",
+ "purpose": "eating"
+}
+
+```
+
+## Gotchas
+
+### Setting a field on a model to the value of a constant rather than the Constant object
+
+When the Django ORM instantiates a model instance from the database, it will ensure that any `ConstantChoiceField` or `ConstantChoiceCharField` fields have values set to `Constant` instances.
+
+If you create or modify a model instance using a raw, hard-coded constant value, you're likely to hit errors along the lines of "AttributeError: 'int' object has no attribute 'attribute-name'".
+
+Take the example below where the caller sets the colour of a model instance to the underlying value for the `Constant`:
+
+```python
+apple = Apple.objects.get(name='Granny Smith')
+apple.purpose = 1 # constant for `eating` .. I know, who on earth would eat a granny smith straight up?!
+apple.save()
+```
+
+Django will happily persist the data correctly, but let's say you have a `post_save` signal (or any other code, really) that does something further with the instance:
+
+```python
+from django.db.models.signals import post_save
+from django.dispatch import receiver
+
+from .models import Apple
+
+@receiver(post_save, sender=Apple)
+def apple_updated_receiver(sender, instance, created, *args, **kwargs):
+ if not created and instance.purpose.eating:
+ print(f"Oh my, {instance.name} is now for eating!")
+
+```
+
+This code will sadly raise `AttributeError: 'int' object has no attribute 'purpose'`, as `instance.purpose` is just the integer we set it to before saving.
+
+The good news is that you can avoid this by *always* setting `django-konst` fields on instances to `Constant` instances such that downstream code can happily handle the instance as if it'd come straight out of the database:
+
+```python
+apple = Apple.objects.get(name='Granny Smith')
+apple.purpose = Apple.purposes.eating
+apple.save()
+```
+
+Regardless of whether you're using `django-konst` for your constants, it's good practice to not hard-code constant values in order to avoid subtle mistakes and to ease changes in the future.
+
+
+## Contribute
+
+`django-konst` supports a variety of Python and Django versions. It's best if you test each one of these before committing. Our [Circle CI Integration](https://circleci.com) will test these when you push but knowing before you commit prevents from having to do a lot of extra commits to get the build to pass.
+
+### Environment Setup
+
+In order to easily test on all these Pythons and run the exact same thing that CI will execute you'll want to setup [pyenv](https://github.com/yyuu/pyenv) and install the Python versions outlined in [tox.ini](tox.ini).
+
+If you are on Mac OS X, it's recommended you use [brew](http://brew.sh/). After installing `brew` run:
+
+```bash
+brew install pyenv pyenv-virtualenv pyenv-virtualenvwrapper
+```
+
+Next, install the various python versions we want to test against and create a virtualenv specifically for `django-konst`:
+
+```bash
+pyenv install 3.6.10
+pyenv install 3.7.6
+pyenv install 3.8.1
+pyenv virtualenv 3.8.1 konst
+pyenv activate konst
+pip install detox
+pyenv shell konst 3.6.10 3.7.6
+```
+
+Now ensure the `konst` virtualenv is activated, make the other python versions also on our path, and run the tests!
+
+
+```bash
+pyenv shell konst 3.6.10 3.7.6
+detox
+```
+
+This will execute the test environments in parallel as defined in the `tox.ini`.
+
+
+
+
+%package -n python3-django-konst
+Summary: Convenient constants fields for Django
+Provides: python-django-konst
+BuildRequires: python3-devel
+BuildRequires: python3-setuptools
+BuildRequires: python3-pip
+%description -n python3-django-konst
+# Django Konst
+
+[![](https://img.shields.io/pypi/v/django-konst.svg)](https://pypi.python.org/pypi/django-konst/)
+[![](https://img.shields.io/badge/license-MIT-blue.svg)](https://pypi.python.org/pypi/django-konst/)
+[![CircleCI](https://circleci.com/gh/lukeburden/django-konst.svg?style=svg)](https://circleci.com/gh/lukeburden/django-konst)
+[![Codecov](https://codecov.io/gh/lukeburden/django-konst/branch/master/graph/badge.svg)](https://codecov.io/gh/lukeburden/django-konst)
+[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
+
+
+## django-konst
+
+`django-konst` is a utility for Django that makes the definition, use and storage of both integer and string based constants easy and readable. It avoids passing of constants
+into template contexts and makes evaluation concise and readable.
+
+It also makes exposure of these constants via forms and DRF serializers simple.
+
+### Definition ###
+
+Constants can be defined with friendly names, backed by either integers or text.
+
+#### Constants ####
+
+```python
+from konst import Constant
+
+# states backed by integers
+states = Constants(
+ Constant(pending=0),
+ Constant(active=1),
+ Constant(inactive=2)
+)
+
+# and another set of constants backed by strings
+colours = Constants(
+ Constant(red="FF0000"),
+ Constant(green="00FF00"),
+ Constant(yellow="FFFF80"),
+ Constant(white="FFFFFF")
+)
+
+```
+
+#### Constant groups ####
+
+At times, it will be necessary to group constants and test membership within that group.
+To achieve this, `django-konst` provides a `ConstantGroup` class.
+
+```python
+from konst import Constant, ConstantGroup
+
+# states backed by integers
+states = Constants(
+ Constant(active=0),
+ Constant(cancelled_ontime=1),
+ Constant(cancelled_late=2),
+ ConstantGroup(
+ "cancelled",
+ ("cancelled_ontime", "cancelled_late")
+ )
+)
+
+```
+
+#### Within a model ####
+
+While not strictly necessary, it is advisable to effectively namespace your constants
+by defining them in the scope of a model definition. This means you have your constants
+wherever you have the model class, as well as any model instance.
+
+
+```python
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+from konst import Constant, ConstantGroup, Constants
+from konst.models.fields import (
+ ConstantChoiceCharField,
+ ConstantChoiceField
+)
+
+class Apple(models.Model):
+
+ purposes = Constants(
+ Constant(cooking=0, label=_("Cook me!")),
+ Constant(eating=1, label=_("Eat me!")),
+ Constant(juicing=2, label=_("Juice me!")),
+ Constant(ornamental=3, label=_("Just look how pretty I am!")),
+ ConstantGroup(
+ "culinary", ("cooking", "eating", "juicing")
+ )
+ )
+ colours = Constants(
+ Constant(red="FF0000", label=_("red")),
+ Constant(green="00FF00", label=_("green")),
+ Constant(yellow="FFFF80", label=_("yellow")),
+ Constant(white="FFFFFF", label=_("white")),
+ )
+
+ name = models.CharField(max_length=30)
+ purpose = ConstantChoiceField(constants=purposes)
+ colour = ConstantChoiceCharField(constants=colours, max_length=30)
+
+```
+
+
+### Use ###
+
+The entire point of this library is to make the use of constants defined in this way
+easy and concise.
+
+#### In code ####
+
+```python
+apple = Apple.objects.get(name='Granny Smith')
+apple.purpose.cooking
+True
+apple.colour.red
+True
+apple.colour.green
+False
+
+# we don't care about the specific purpose, just whether it is as food
+# or not, so use the ConstantGroup!
+apple.purpose.culinary
+True
+
+```
+
+#### In templates ####
+
+```
+{% if apple.purpose.eating %}
+ You should bite this {{ apple.name }}!
+{% endif %}
+```
+
+#### With Django's ORM ####
+
+```python
+red_apples = Apple.objects.filter(colour=Apple.colours.red)
+culinary_apples = Apple.objects.filter(
+ purpose__in=Apple.purposes.culinary
+)
+
+```
+
+#### With Django Rest Framework ####
+
+Using the `konst.extras.drf.fields.ConstantChoiceField` serializer field with the
+Django Rest Framework it is possible to both output and receive constant values.
+
+```python
+from konst.extras.drf.fields import ConstantChoiceField
+
+from rest_framework import serializers
+
+
+class AppleSerializer(serializers.ModelSerializer):
+
+ purpose = ConstantChoiceField(Apple.purposes)
+ colour = ConstantChoiceField(Apple.colours)
+
+ class Meta:
+ model = Apple
+ fields = (
+ "name", "purpose", "colour"
+ )
+
+
+# let's see how it handles bad values
+serializer = AppleSerializer(
+ data={
+ "name": "Fuji",
+ "colour": "blue",
+ "purpose": "dicing"
+ }
+)
+serializer.is_valid()
+False
+serializer.errors
+{
+ 'colour': [u'"blue" is not a valid choice.'],
+ 'purpose': [u'"dicing" is not a valid choice.']
+}
+
+
+# and now how it handles some good values
+serializer = AppleSerializer(
+ data={
+ "name": "Fuji",
+ "colour": "red",
+ "purpose": "eating"
+ }
+)
+serializer.is_valid()
+True
+
+
+# let's create a database entry!
+instance = serializer.save()
+
+
+# and now our instance can be interacted with neatly
+instance.colour.red
+True
+
+
+# finally, let's see how this looks when rendering JSON
+AppleSerializer(instance=instance).data
+{
+ "name": "Fuji",
+ "colour": "red",
+ "purpose": "eating"
+}
+
+```
+
+## Gotchas
+
+### Setting a field on a model to the value of a constant rather than the Constant object
+
+When the Django ORM instantiates a model instance from the database, it will ensure that any `ConstantChoiceField` or `ConstantChoiceCharField` fields have values set to `Constant` instances.
+
+If you create or modify a model instance using a raw, hard-coded constant value, you're likely to hit errors along the lines of "AttributeError: 'int' object has no attribute 'attribute-name'".
+
+Take the example below where the caller sets the colour of a model instance to the underlying value for the `Constant`:
+
+```python
+apple = Apple.objects.get(name='Granny Smith')
+apple.purpose = 1 # constant for `eating` .. I know, who on earth would eat a granny smith straight up?!
+apple.save()
+```
+
+Django will happily persist the data correctly, but let's say you have a `post_save` signal (or any other code, really) that does something further with the instance:
+
+```python
+from django.db.models.signals import post_save
+from django.dispatch import receiver
+
+from .models import Apple
+
+@receiver(post_save, sender=Apple)
+def apple_updated_receiver(sender, instance, created, *args, **kwargs):
+ if not created and instance.purpose.eating:
+ print(f"Oh my, {instance.name} is now for eating!")
+
+```
+
+This code will sadly raise `AttributeError: 'int' object has no attribute 'purpose'`, as `instance.purpose` is just the integer we set it to before saving.
+
+The good news is that you can avoid this by *always* setting `django-konst` fields on instances to `Constant` instances such that downstream code can happily handle the instance as if it'd come straight out of the database:
+
+```python
+apple = Apple.objects.get(name='Granny Smith')
+apple.purpose = Apple.purposes.eating
+apple.save()
+```
+
+Regardless of whether you're using `django-konst` for your constants, it's good practice to not hard-code constant values in order to avoid subtle mistakes and to ease changes in the future.
+
+
+## Contribute
+
+`django-konst` supports a variety of Python and Django versions. It's best if you test each one of these before committing. Our [Circle CI Integration](https://circleci.com) will test these when you push but knowing before you commit prevents from having to do a lot of extra commits to get the build to pass.
+
+### Environment Setup
+
+In order to easily test on all these Pythons and run the exact same thing that CI will execute you'll want to setup [pyenv](https://github.com/yyuu/pyenv) and install the Python versions outlined in [tox.ini](tox.ini).
+
+If you are on Mac OS X, it's recommended you use [brew](http://brew.sh/). After installing `brew` run:
+
+```bash
+brew install pyenv pyenv-virtualenv pyenv-virtualenvwrapper
+```
+
+Next, install the various python versions we want to test against and create a virtualenv specifically for `django-konst`:
+
+```bash
+pyenv install 3.6.10
+pyenv install 3.7.6
+pyenv install 3.8.1
+pyenv virtualenv 3.8.1 konst
+pyenv activate konst
+pip install detox
+pyenv shell konst 3.6.10 3.7.6
+```
+
+Now ensure the `konst` virtualenv is activated, make the other python versions also on our path, and run the tests!
+
+
+```bash
+pyenv shell konst 3.6.10 3.7.6
+detox
+```
+
+This will execute the test environments in parallel as defined in the `tox.ini`.
+
+
+
+
+%package help
+Summary: Development documents and examples for django-konst
+Provides: python3-django-konst-doc
+%description help
+# Django Konst
+
+[![](https://img.shields.io/pypi/v/django-konst.svg)](https://pypi.python.org/pypi/django-konst/)
+[![](https://img.shields.io/badge/license-MIT-blue.svg)](https://pypi.python.org/pypi/django-konst/)
+[![CircleCI](https://circleci.com/gh/lukeburden/django-konst.svg?style=svg)](https://circleci.com/gh/lukeburden/django-konst)
+[![Codecov](https://codecov.io/gh/lukeburden/django-konst/branch/master/graph/badge.svg)](https://codecov.io/gh/lukeburden/django-konst)
+[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/ambv/black)
+
+
+## django-konst
+
+`django-konst` is a utility for Django that makes the definition, use and storage of both integer and string based constants easy and readable. It avoids passing of constants
+into template contexts and makes evaluation concise and readable.
+
+It also makes exposure of these constants via forms and DRF serializers simple.
+
+### Definition ###
+
+Constants can be defined with friendly names, backed by either integers or text.
+
+#### Constants ####
+
+```python
+from konst import Constant
+
+# states backed by integers
+states = Constants(
+ Constant(pending=0),
+ Constant(active=1),
+ Constant(inactive=2)
+)
+
+# and another set of constants backed by strings
+colours = Constants(
+ Constant(red="FF0000"),
+ Constant(green="00FF00"),
+ Constant(yellow="FFFF80"),
+ Constant(white="FFFFFF")
+)
+
+```
+
+#### Constant groups ####
+
+At times, it will be necessary to group constants and test membership within that group.
+To achieve this, `django-konst` provides a `ConstantGroup` class.
+
+```python
+from konst import Constant, ConstantGroup
+
+# states backed by integers
+states = Constants(
+ Constant(active=0),
+ Constant(cancelled_ontime=1),
+ Constant(cancelled_late=2),
+ ConstantGroup(
+ "cancelled",
+ ("cancelled_ontime", "cancelled_late")
+ )
+)
+
+```
+
+#### Within a model ####
+
+While not strictly necessary, it is advisable to effectively namespace your constants
+by defining them in the scope of a model definition. This means you have your constants
+wherever you have the model class, as well as any model instance.
+
+
+```python
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+
+from konst import Constant, ConstantGroup, Constants
+from konst.models.fields import (
+ ConstantChoiceCharField,
+ ConstantChoiceField
+)
+
+class Apple(models.Model):
+
+ purposes = Constants(
+ Constant(cooking=0, label=_("Cook me!")),
+ Constant(eating=1, label=_("Eat me!")),
+ Constant(juicing=2, label=_("Juice me!")),
+ Constant(ornamental=3, label=_("Just look how pretty I am!")),
+ ConstantGroup(
+ "culinary", ("cooking", "eating", "juicing")
+ )
+ )
+ colours = Constants(
+ Constant(red="FF0000", label=_("red")),
+ Constant(green="00FF00", label=_("green")),
+ Constant(yellow="FFFF80", label=_("yellow")),
+ Constant(white="FFFFFF", label=_("white")),
+ )
+
+ name = models.CharField(max_length=30)
+ purpose = ConstantChoiceField(constants=purposes)
+ colour = ConstantChoiceCharField(constants=colours, max_length=30)
+
+```
+
+
+### Use ###
+
+The entire point of this library is to make the use of constants defined in this way
+easy and concise.
+
+#### In code ####
+
+```python
+apple = Apple.objects.get(name='Granny Smith')
+apple.purpose.cooking
+True
+apple.colour.red
+True
+apple.colour.green
+False
+
+# we don't care about the specific purpose, just whether it is as food
+# or not, so use the ConstantGroup!
+apple.purpose.culinary
+True
+
+```
+
+#### In templates ####
+
+```
+{% if apple.purpose.eating %}
+ You should bite this {{ apple.name }}!
+{% endif %}
+```
+
+#### With Django's ORM ####
+
+```python
+red_apples = Apple.objects.filter(colour=Apple.colours.red)
+culinary_apples = Apple.objects.filter(
+ purpose__in=Apple.purposes.culinary
+)
+
+```
+
+#### With Django Rest Framework ####
+
+Using the `konst.extras.drf.fields.ConstantChoiceField` serializer field with the
+Django Rest Framework it is possible to both output and receive constant values.
+
+```python
+from konst.extras.drf.fields import ConstantChoiceField
+
+from rest_framework import serializers
+
+
+class AppleSerializer(serializers.ModelSerializer):
+
+ purpose = ConstantChoiceField(Apple.purposes)
+ colour = ConstantChoiceField(Apple.colours)
+
+ class Meta:
+ model = Apple
+ fields = (
+ "name", "purpose", "colour"
+ )
+
+
+# let's see how it handles bad values
+serializer = AppleSerializer(
+ data={
+ "name": "Fuji",
+ "colour": "blue",
+ "purpose": "dicing"
+ }
+)
+serializer.is_valid()
+False
+serializer.errors
+{
+ 'colour': [u'"blue" is not a valid choice.'],
+ 'purpose': [u'"dicing" is not a valid choice.']
+}
+
+
+# and now how it handles some good values
+serializer = AppleSerializer(
+ data={
+ "name": "Fuji",
+ "colour": "red",
+ "purpose": "eating"
+ }
+)
+serializer.is_valid()
+True
+
+
+# let's create a database entry!
+instance = serializer.save()
+
+
+# and now our instance can be interacted with neatly
+instance.colour.red
+True
+
+
+# finally, let's see how this looks when rendering JSON
+AppleSerializer(instance=instance).data
+{
+ "name": "Fuji",
+ "colour": "red",
+ "purpose": "eating"
+}
+
+```
+
+## Gotchas
+
+### Setting a field on a model to the value of a constant rather than the Constant object
+
+When the Django ORM instantiates a model instance from the database, it will ensure that any `ConstantChoiceField` or `ConstantChoiceCharField` fields have values set to `Constant` instances.
+
+If you create or modify a model instance using a raw, hard-coded constant value, you're likely to hit errors along the lines of "AttributeError: 'int' object has no attribute 'attribute-name'".
+
+Take the example below where the caller sets the colour of a model instance to the underlying value for the `Constant`:
+
+```python
+apple = Apple.objects.get(name='Granny Smith')
+apple.purpose = 1 # constant for `eating` .. I know, who on earth would eat a granny smith straight up?!
+apple.save()
+```
+
+Django will happily persist the data correctly, but let's say you have a `post_save` signal (or any other code, really) that does something further with the instance:
+
+```python
+from django.db.models.signals import post_save
+from django.dispatch import receiver
+
+from .models import Apple
+
+@receiver(post_save, sender=Apple)
+def apple_updated_receiver(sender, instance, created, *args, **kwargs):
+ if not created and instance.purpose.eating:
+ print(f"Oh my, {instance.name} is now for eating!")
+
+```
+
+This code will sadly raise `AttributeError: 'int' object has no attribute 'purpose'`, as `instance.purpose` is just the integer we set it to before saving.
+
+The good news is that you can avoid this by *always* setting `django-konst` fields on instances to `Constant` instances such that downstream code can happily handle the instance as if it'd come straight out of the database:
+
+```python
+apple = Apple.objects.get(name='Granny Smith')
+apple.purpose = Apple.purposes.eating
+apple.save()
+```
+
+Regardless of whether you're using `django-konst` for your constants, it's good practice to not hard-code constant values in order to avoid subtle mistakes and to ease changes in the future.
+
+
+## Contribute
+
+`django-konst` supports a variety of Python and Django versions. It's best if you test each one of these before committing. Our [Circle CI Integration](https://circleci.com) will test these when you push but knowing before you commit prevents from having to do a lot of extra commits to get the build to pass.
+
+### Environment Setup
+
+In order to easily test on all these Pythons and run the exact same thing that CI will execute you'll want to setup [pyenv](https://github.com/yyuu/pyenv) and install the Python versions outlined in [tox.ini](tox.ini).
+
+If you are on Mac OS X, it's recommended you use [brew](http://brew.sh/). After installing `brew` run:
+
+```bash
+brew install pyenv pyenv-virtualenv pyenv-virtualenvwrapper
+```
+
+Next, install the various python versions we want to test against and create a virtualenv specifically for `django-konst`:
+
+```bash
+pyenv install 3.6.10
+pyenv install 3.7.6
+pyenv install 3.8.1
+pyenv virtualenv 3.8.1 konst
+pyenv activate konst
+pip install detox
+pyenv shell konst 3.6.10 3.7.6
+```
+
+Now ensure the `konst` virtualenv is activated, make the other python versions also on our path, and run the tests!
+
+
+```bash
+pyenv shell konst 3.6.10 3.7.6
+detox
+```
+
+This will execute the test environments in parallel as defined in the `tox.ini`.
+
+
+
+
+%prep
+%autosetup -n django-konst-2.0.0
+
+%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-konst -f filelist.lst
+%dir %{python3_sitelib}/*
+
+%files help -f doclist.lst
+%{_docdir}/*
+
+%changelog
+* Wed May 17 2023 Python_Bot <Python_Bot@openeuler.org> - 2.0.0-1
+- Package Spec generated
diff --git a/sources b/sources
new file mode 100644
index 0000000..797338d
--- /dev/null
+++ b/sources
@@ -0,0 +1 @@
+2ccf0c6791cda003dba23c7f520f52ea django-konst-2.0.0.tar.gz