Simple app to add configuration options to a Django project.
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'develop'

+396 -89
+8 -1
HISTORY.rst
··· 3 3 History 4 4 ------- 5 5 6 + 1.2 (2019-07-26) 7 + +++++++++++++++++ 8 + 9 + * Added admin to user options. 10 + * Added integration with Django Rest Framework. 11 + * Added tests. 12 + 6 13 1.1 (2019-07-01) 7 14 +++++++++++++++++ 8 15 9 - * Added validation on types before saving (Thanks to @aaloy!) 16 + * Added validation on types before saving (Thanks to @aaloy!). 10 17 11 18 1.0 (2018-10-2) 12 19 +++++++++++++++++
+2
Pipfile
··· 7 7 setuptools = "*" 8 8 twine = "*" 9 9 wheel = "*" 10 + django-rest-framework = "*" 11 + factory-boy = "*" 10 12 11 13 [packages] 12 14 six = ">=1.0"
+129 -39
Pipfile.lock
··· 1 1 { 2 2 "_meta": { 3 3 "hash": { 4 - "sha256": "e93d0dd2d7b77e1f50699ba8c9c4b7125496ad3c7ed038db73cf5799192354cc" 4 + "sha256": "536ddd1e652ce4388b976a93d120103b6e4ae256c2354287a07206683e7673d4" 5 5 }, 6 6 "pipfile-spec": 6, 7 7 "requires": { ··· 18 18 "default": { 19 19 "django": { 20 20 "hashes": [ 21 - "sha256:7f246078d5a546f63c28fc03ce71f4d7a23677ce42109219c24c9ffb28416137", 22 - "sha256:ea50d85709708621d956187c6b61d9f9ce155007b496dd914fdb35db8d790aec" 21 + "sha256:4d23f61b26892bac785f07401bc38cbf8fa4cec993f400e9cd9ddf28fd51c0ea", 22 + "sha256:6e974d4b57e3b29e4882b244d40171d6a75202ab8d2402b8e8adbd182e25cf0c" 23 23 ], 24 24 "index": "pypi", 25 - "version": "==2.1" 25 + "version": "==2.2.3" 26 26 }, 27 27 "pytz": { 28 28 "hashes": [ 29 - "sha256:a061aa0a9e06881eb8b3b2b43f05b9439d6583c206d0a6c340ff72a7b6669053", 30 - "sha256:ffb9ef1de172603304d9d2819af6f5ece76f2e85ec10692a524dd876e72bf277" 29 + "sha256:303879e36b721603cc54604edcac9d20401bdbe31e1e4fdee5b9f98d5d31dfda", 30 + "sha256:d747dd3d23d77ef44c6a3526e274af6efeb0a6f1afd5a69ba4d5be4098c8e141" 31 31 ], 32 - "version": "==2018.5" 32 + "version": "==2019.1" 33 33 }, 34 34 "six": { 35 35 "hashes": [ 36 - "sha256:70e8a77beed4562e7f14fe23a786b54f6296e34344c23bc42f07b15018ff98e9", 37 - "sha256:832dc0e10feb1aa2c68dcc57dbb658f1c7e65b9b61af69048abc87a2db00a0eb" 36 + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", 37 + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" 38 38 ], 39 39 "index": "pypi", 40 - "version": "==1.11.0" 40 + "version": "==1.12.0" 41 + }, 42 + "sqlparse": { 43 + "hashes": [ 44 + "sha256:40afe6b8d4b1117e7dff5504d7a8ce07d9a1b15aeeade8a2d10f130a834f8177", 45 + "sha256:7c3dca29c022744e95b547e867cee89f4fce4373f3549ccd8797d8eb52cdb873" 46 + ], 47 + "version": "==0.3.0" 41 48 } 42 49 }, 43 50 "develop": { 51 + "bleach": { 52 + "hashes": [ 53 + "sha256:213336e49e102af26d9cde77dd2d0397afabc5a6bf2fed985dc35b5d1e285a16", 54 + "sha256:3fdf7f77adcf649c9911387df51254b813185e32b2c6619f690b593a617e19fa" 55 + ], 56 + "version": "==3.1.0" 57 + }, 44 58 "certifi": { 45 59 "hashes": [ 46 - "sha256:376690d6f16d32f9d1fe8932551d80b23e9d393a8578c5633a2ed39a64861638", 47 - "sha256:456048c7e371c089d0a77a5212fb37a2c2dce1e24146e3b7e0261736aaeaa22a" 60 + "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", 61 + "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" 48 62 ], 49 - "version": "==2018.8.24" 63 + "version": "==2019.6.16" 50 64 }, 51 65 "chardet": { 52 66 "hashes": [ ··· 55 69 ], 56 70 "version": "==3.0.4" 57 71 }, 72 + "django-rest-framework": { 73 + "hashes": [ 74 + "sha256:47a8f496fa69e3b6bd79f68dd7a1527d907d6b77f009e9db7cf9bb21cc565e4a" 75 + ], 76 + "index": "pypi", 77 + "version": "==0.1.0" 78 + }, 79 + "djangorestframework": { 80 + "hashes": [ 81 + "sha256:1ca4a5599a5ec31f3d6238a687fcc1dd4c41b1d90edab9ad398fcbf87872b7ba", 82 + "sha256:c3c5edfdbc5dd33f9121bb84305bfd603d2c791f20cff9782772f44a7684a4e4" 83 + ], 84 + "version": "==3.10.1" 85 + }, 86 + "docutils": { 87 + "hashes": [ 88 + "sha256:f33ddb723332c6d6b6d99731ee1fc0c35eb4044a2df5cca1c64c8aa78eaf22cb" 89 + ], 90 + "version": "==0.15.1.post1" 91 + }, 92 + "factory-boy": { 93 + "hashes": [ 94 + "sha256:728df59b372c9588b83153facf26d3d28947fc750e8e3c95cefa9bed0e6394ee", 95 + "sha256:faf48d608a1735f0d0a3c9cbf536d64f9132b547dae7ba452c4d99a79e84a370" 96 + ], 97 + "index": "pypi", 98 + "version": "==2.12.0" 99 + }, 100 + "faker": { 101 + "hashes": [ 102 + "sha256:96ad7902706f2409a2d0c3de5132f69b413555a419bacec99d3f16e657895b47", 103 + "sha256:b3bb64aff9571510de6812df45122b633dbc6227e870edae3ed9430f94698521" 104 + ], 105 + "version": "==2.0.0" 106 + }, 58 107 "idna": { 59 108 "hashes": [ 60 - "sha256:156a6814fb5ac1fc6850fb002e0852d56c0c8d2531923a51032d1b70760e186e", 61 - "sha256:684a38a6f903c1d71d6d5fac066b58d7768af4de2b832e426ec79c30daa94a16" 109 + "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", 110 + "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" 62 111 ], 63 - "version": "==2.7" 112 + "version": "==2.8" 64 113 }, 65 114 "pkginfo": { 66 115 "hashes": [ 67 - "sha256:5878d542a4b3f237e359926384f1dde4e099c9f5525d236b1840cf704fa8d474", 68 - "sha256:a39076cb3eb34c333a0dd390b568e9e1e881c7bf2cc0aee12120636816f55aee" 116 + "sha256:7424f2c8511c186cd5424bbf31045b77435b37a8d604990b79d4e70d741148bb", 117 + "sha256:a6d9e40ca61ad3ebd0b72fbadd4fba16e4c0e4df0428c041e01e06eb6ee71f32" 69 118 ], 70 - "version": "==1.4.2" 119 + "version": "==1.5.0.1" 120 + }, 121 + "pygments": { 122 + "hashes": [ 123 + "sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", 124 + "sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297" 125 + ], 126 + "version": "==2.4.2" 127 + }, 128 + "python-dateutil": { 129 + "hashes": [ 130 + "sha256:7e6584c74aeed623791615e26efd690f29817a27c73085b78e4bad02493df2fb", 131 + "sha256:c89805f6f4d64db21ed966fda138f8a5ed7a4fdbc1a8ee329ce1b74e3c74da9e" 132 + ], 133 + "version": "==2.8.0" 134 + }, 135 + "readme-renderer": { 136 + "hashes": [ 137 + "sha256:bb16f55b259f27f75f640acf5e00cf897845a8b3e4731b5c1a436e4b8529202f", 138 + "sha256:c8532b79afc0375a85f10433eca157d6b50f7d6990f337fa498c96cd4bfc203d" 139 + ], 140 + "version": "==24.0" 71 141 }, 72 142 "requests": { 73 143 "hashes": [ 74 - "sha256:63b52e3c866428a224f97cab011de738c36aec0185aa91cfacd418b5d58911d1", 75 - "sha256:ec22d826a36ed72a7358ff3fe56cbd4ba69dd7a6718ffd450ff0e9df7a47ce6a" 144 + "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", 145 + "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" 76 146 ], 77 - "version": "==2.19.1" 147 + "version": "==2.22.0" 78 148 }, 79 149 "requests-toolbelt": { 80 150 "hashes": [ 81 - "sha256:42c9c170abc2cacb78b8ab23ac957945c7716249206f90874651971a4acff237", 82 - "sha256:f6a531936c6fa4c6cfce1b9c10d5c4f498d16528d2a54a22ca00011205a187b5" 151 + "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f", 152 + "sha256:968089d4584ad4ad7c171454f0a5c6dac23971e9472521ea3b6d49d610aa6fc0" 83 153 ], 84 - "markers": "python_version != '3.1.*' and python_version < '4' and python_version != '3.3.*' and python_version != '3.2.*' and python_version >= '2.6' and python_version != '3.0.*'", 85 - "version": "==0.8.0" 154 + "version": "==0.9.1" 155 + }, 156 + "six": { 157 + "hashes": [ 158 + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", 159 + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" 160 + ], 161 + "index": "pypi", 162 + "version": "==1.12.0" 163 + }, 164 + "text-unidecode": { 165 + "hashes": [ 166 + "sha256:5a1375bb2ba7968740508ae38d92e1f889a0832913cb1c447d5e2046061a396d", 167 + "sha256:801e38bd550b943563660a91de8d4b6fa5df60a542be9093f7abf819f86050cc" 168 + ], 169 + "version": "==1.2" 86 170 }, 87 171 "tqdm": { 88 172 "hashes": [ 89 - "sha256:5ef526702c0d265d5a960a3b27f3971fac13c26cf0fb819294bfa71fc6026c88", 90 - "sha256:a3364bd83ce4777320b862e3c8a93d7da91e20a95f06ef79bed7dd71c654cafa" 173 + "sha256:14a285392c32b6f8222ecfbcd217838f88e11630affe9006cd0e94c7eff3cb61", 174 + "sha256:25d4c0ea02a305a688e7e9c2cdc8f862f989ef2a4701ab28ee963295f5b109ab" 91 175 ], 92 - "markers": "python_version >= '2.6' and python_version != '3.1.*' and python_version != '3.0.*'", 93 - "version": "==4.25.0" 176 + "version": "==4.32.2" 94 177 }, 95 178 "twine": { 96 179 "hashes": [ 97 - "sha256:08eb132bbaec40c6d25b358f546ec1dc96ebd2638a86eea68769d9e67fe2b129", 98 - "sha256:2fd9a4d9ff0bcacf41fdc40c8cb0cfaef1f1859457c9653fd1b92237cc4e9f25" 180 + "sha256:0fb0bfa3df4f62076cab5def36b1a71a2e4acb4d1fa5c97475b048117b1a6446", 181 + "sha256:d6c29c933ecfc74e9b1d9fa13aa1f87c5d5770e119f5a4ce032092f0ff5b14dc" 99 182 ], 100 183 "index": "pypi", 101 - "version": "==1.11.0" 184 + "version": "==1.13.0" 102 185 }, 103 186 "urllib3": { 104 187 "hashes": [ 105 - "sha256:a68ac5e15e76e7e5dd2b8f94007233e01effe3e50e8daddf69acfd81cb686baf", 106 - "sha256:b5725a0bd4ba422ab0e66e89e030c806576753ea3ee08554382c14e685d117b5" 188 + "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", 189 + "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232" 190 + ], 191 + "version": "==1.25.3" 192 + }, 193 + "webencodings": { 194 + "hashes": [ 195 + "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", 196 + "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923" 107 197 ], 108 - "version": "==1.23" 198 + "version": "==0.5.1" 109 199 }, 110 200 "wheel": { 111 201 "hashes": [ 112 - "sha256:0a2e54558a0628f2145d2fc822137e322412115173e8a2ddbe1c9024338ae83c", 113 - "sha256:80044e51ec5bbf6c894ba0bc48d26a8c20a9ba629f4ca19ea26ecfcf87685f5f" 202 + "sha256:5e79117472686ac0c4aef5bad5172ea73a1c2d1646b808c35926bd26bdfb0c08", 203 + "sha256:62fcfa03d45b5b722539ccbc07b190e4bfff4bb9e3a4d470dd9f6a0981002565" 114 204 ], 115 205 "index": "pypi", 116 - "version": "==0.31.1" 206 + "version": "==0.33.4" 117 207 } 118 208 } 119 209 }
+1 -1
options/__init__.py
··· 9 9 10 10 default_app_config = "options.apps.ConfigurationsConfig" 11 11 12 - VERSION = (1, 1, 0, "final", 0) 12 + VERSION = (1, 2, 0, "final", 0) 13 13 14 14 __version__ = get_version(VERSION)
+9 -1
options/admin.py
··· 1 1 from django.contrib import admin 2 - from options.models import Option 2 + from options.models import Option, UserOption 3 3 4 4 5 5 @admin.register(Option) ··· 8 8 9 9 list_display = ["public_name", "value"] 10 10 search_fields = ["public_name", "name"] 11 + 12 + 13 + @admin.register(UserOption) 14 + class UserOptionAdmin(admin.ModelAdmin): 15 + """Manage configuration user options.""" 16 + 17 + list_display = ["user", "public_name", "value"] 18 + search_fields = ["public_name", "name"]
+23
options/rest_framework/serializers.py
··· 1 + from django.utils.translation import ugettext_lazy as _ 2 + from rest_framework import serializers 3 + 4 + from options.settings import DEFAULT_EXCLUDE_USER_OPTIONS 5 + from options.models import Option, UserOption 6 + 7 + 8 + class OptionSerializer(serializers.ModelSerializer): 9 + class Meta: 10 + model = Option 11 + fields = ["id", "name", "public_name", "type", "value", "is_list"] 12 + 13 + 14 + class UserOptionSerializer(OptionSerializer): 15 + class Meta(OptionSerializer.Meta): 16 + model = UserOption 17 + 18 + def validate_name(self, value): 19 + """Checks if the name is in DEFAULT_EXCLUDE_USER_OPTIONS.""" 20 + if value in DEFAULT_EXCLUDE_USER_OPTIONS: 21 + raise serializers.ValidationError( 22 + _("The name in the option can't be handle by the user.") 23 + )
+24
options/rest_framework/viewsets.py
··· 1 + from rest_framework import viewsets 2 + from rest_framework.permissions import IsAdminUser, IsAuthenticated 3 + 4 + from options.models import Option, UserOption 5 + from options.rest_framework.serializers import OptionSerializer, UserOptionSerializer 6 + 7 + 8 + class OptionViewSet(viewsets.ModelViewSet): 9 + queryset = Option.objects.all() 10 + serializer_class = OptionSerializer 11 + permission_classes = (IsAdminUser,) 12 + 13 + 14 + class UserOptionViewSet(viewsets.ModelViewSet): 15 + queryset = UserOption.objects.filter_user_customizable() 16 + serializer_class = UserOptionSerializer 17 + permission_classes = (IsAuthenticated,) 18 + 19 + def get_queryset(self): 20 + queryset = super().get_queryset() 21 + return queryset.filter(user=self.request.user) 22 + 23 + def perform_create(self, serializer): 24 + serializer.save(user=self.request.user)
+3 -7
runtests.py
··· 1 - #!/usr/bin/env python 2 - # -*- coding: utf-8 3 - from __future__ import unicode_literals, absolute_import 4 - 5 1 import os 6 2 import sys 7 3 ··· 12 8 13 9 def run_tests(*test_args): 14 10 if not test_args: 15 - test_args = ['tests'] 11 + test_args = ["tests"] 16 12 17 - os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.settings' 13 + os.environ["DJANGO_SETTINGS_MODULE"] = "tests.settings" 18 14 django.setup() 19 15 TestRunner = get_runner(settings) 20 16 test_runner = TestRunner() ··· 22 18 sys.exit(bool(failures)) 23 19 24 20 25 - if __name__ == '__main__': 21 + if __name__ == "__main__": 26 22 run_tests(*sys.argv[1:])
+26 -31
setup.py
··· 1 - # -*- coding: utf-8 -*- 2 - from __future__ import unicode_literals, print_function, division, absolute_import 3 - 4 1 import os 5 2 6 3 from setuptools import setup 7 4 8 - with open(os.path.join(os.path.dirname(__file__), 'README.rst')) as readme: 5 + with open(os.path.join(os.path.dirname(__file__), "README.rst")) as readme: 9 6 README = readme.read() 10 7 11 - with open(os.path.join(os.path.dirname(__file__), 'HISTORY.rst')) as history: 12 - HISTORY = history.read().replace('.. :changelog:', '') 8 + with open(os.path.join(os.path.dirname(__file__), "HISTORY.rst")) as history: 9 + HISTORY = history.read().replace(".. :changelog:", "") 13 10 14 11 # Allow setup.py to be run from any path 15 12 os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) 16 13 17 14 # Dynamically calculate the version based on belt.VERSION. 18 - version = __import__('options').__version__ 15 + version = __import__("options").__version__ 19 16 20 17 21 18 setup( 22 - name='django-simple-options', 19 + name="django-simple-options", 23 20 version=version, 24 21 packages=[ 25 - 'options.management.commands', 26 - 'options.migrations', 27 - 'options', 22 + "options.management.commands", 23 + "options.migrations", 24 + "options.rest_framework", 25 + "options", 28 26 ], 29 27 include_package_data=True, 30 - license='MIT License', 31 - description='Simple app to add configuration options to a Django project', 32 - long_description=README + '\n\n' + HISTORY, 33 - url='https://github.com/marcosgabarda/django-options', 34 - author='Marcos Gabarda', 35 - author_email='hey@marcosgabarda.com', 28 + license="MIT License", 29 + description="Simple app to add configuration options to a Django project", 30 + long_description=README + "\n\n" + HISTORY, 31 + url="https://github.com/marcosgabarda/django-options", 32 + author="Marcos Gabarda", 33 + author_email="hey@marcosgabarda.com", 36 34 classifiers=[ 37 - 'Development Status :: 3 - Alpha', 38 - 'Environment :: Web Environment', 39 - 'Framework :: Django', 40 - 'Intended Audience :: Developers', 41 - 'License :: OSI Approved :: MIT License', 42 - 'Operating System :: OS Independent', 43 - 'Programming Language :: Python', 44 - 'Programming Language :: Python :: 2', 45 - 'Programming Language :: Python :: 3', 46 - 'Topic :: Utilities', 47 - ], 48 - install_requires=[ 49 - 'django>=1.9', 50 - 'six>=1.0', 35 + "Development Status :: 3 - Alpha", 36 + "Environment :: Web Environment", 37 + "Framework :: Django", 38 + "Intended Audience :: Developers", 39 + "License :: OSI Approved :: MIT License", 40 + "Operating System :: OS Independent", 41 + "Programming Language :: Python", 42 + "Programming Language :: Python :: 2", 43 + "Programming Language :: Python :: 3", 44 + "Topic :: Utilities", 51 45 ], 46 + install_requires=["django>=1.9", "six>=1.0"], 52 47 )
+43
tests/factories.py
··· 1 + from django.contrib.auth import get_user_model 2 + from factory import DjangoModelFactory, Faker, post_generation 3 + from factory.fuzzy import FuzzyText 4 + 5 + from options import STRING 6 + from options.models import Option, UserOption 7 + 8 + 9 + class UserFactory(DjangoModelFactory): 10 + 11 + username = Faker("email") 12 + 13 + @post_generation 14 + def password(self, create, extracted, **kwargs): 15 + password = Faker( 16 + "password", 17 + length=42, 18 + special_chars=True, 19 + digits=True, 20 + upper_case=True, 21 + lower_case=True, 22 + ).generate(extra_kwargs={}) 23 + self.set_password(password) 24 + 25 + class Meta: 26 + model = get_user_model() 27 + django_get_or_create = ["username"] 28 + 29 + 30 + class OptionFactory(DjangoModelFactory): 31 + public_name = FuzzyText() 32 + type = STRING 33 + 34 + class Meta: 35 + model = Option 36 + 37 + 38 + class UserOptionFactory(DjangoModelFactory): 39 + public_name = FuzzyText() 40 + type = STRING 41 + 42 + class Meta: 43 + model = UserOption
+13
tests/router.py
··· 1 + from django.urls import include, path 2 + from rest_framework import routers 3 + 4 + from options.rest_framework.viewsets import OptionViewSet, UserOptionViewSet 5 + 6 + 7 + app_name = "api" 8 + 9 + router = routers.DefaultRouter() 10 + router.register("options", viewset=OptionViewSet) 11 + router.register("user-options", viewset=UserOptionViewSet) 12 + 13 + urlpatterns = [path("", include(router.urls))]
+22 -9
tests/settings.py
··· 1 - # -*- coding: utf-8 -*- 2 - from __future__ import unicode_literals, absolute_import 3 - 4 1 import django 5 2 6 3 DEBUG = True ··· 8 5 9 6 SECRET_KEY = "dummy" 10 7 11 - DATABASES = { 12 - "default": { 13 - "ENGINE": "django.db.backends.sqlite3", 14 - "NAME": ":memory:", 15 - } 16 - } 8 + DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3", "NAME": ":memory:"}} 9 + 10 + ROOT_URLCONF = "tests.urls" 17 11 18 12 INSTALLED_APPS = [ 19 13 "django.contrib.auth", ··· 28 22 MIDDLEWARE = () 29 23 else: 30 24 MIDDLEWARE_CLASSES = () 25 + 26 + INSTALLED_APPS += ("rest_framework", "rest_framework.authtoken") 27 + REST_FRAMEWORK = { 28 + "DEFAULT_AUTHENTICATION_CLASSES": ( 29 + "rest_framework.authentication.TokenAuthentication", 30 + "rest_framework.authentication.SessionAuthentication", 31 + ) 32 + } 33 + 34 + from options import STRING 35 + 36 + CONFIGURATION_DEFAULT_OPTIONS = { 37 + "default_option": { 38 + "public_name": "Default Option", 39 + "type": STRING, 40 + "value": "default", 41 + } 42 + } 43 + EXCLUDE_USER_OPTIONS = ["secret_option"]
+5
tests/test_settings.py
··· 1 + from tests import settings as _settings 2 + 3 + TEST_SETTINGS = dict( 4 + (k, getattr(_settings, k)) for k in dir(_settings) if k == k.upper() 5 + )
+84
tests/tests.py
··· 1 + from django.test import override_settings, TestCase 2 + from django.contrib.auth.models import User 3 + from rest_framework.test import APITestCase 4 + from rest_framework import status 5 + 6 + from tests.test_settings import TEST_SETTINGS 7 + from tests.factories import UserFactory, UserOptionFactory, OptionFactory 8 + from options import INT, FLOAT, STRING 9 + from options.models import Option, UserOption 10 + 11 + 12 + @override_settings(**TEST_SETTINGS) 13 + class OptionTests(TestCase): 14 + def test_default_options(self): 15 + value = Option.objects.get_value("default_option", default="ohter") 16 + self.assertEqual("default", value) 17 + 18 + def test_int_conversion_options(self): 19 + name = "int_option" 20 + option = OptionFactory(name=name, value="42", type=INT) 21 + value = Option.objects.get_value(name) 22 + self.assertIsInstance(value, int) 23 + self.assertEqual(42, value) 24 + 25 + def test_str_conversion_options(self): 26 + name = "string_option" 27 + option = OptionFactory(name=name, value="42") 28 + value = Option.objects.get_value(name) 29 + self.assertIsInstance(value, str) 30 + self.assertEqual("42", value) 31 + 32 + def test_float_conversion_options(self): 33 + name = "string_option" 34 + option = OptionFactory(name=name, value="42.5", type=FLOAT) 35 + value = Option.objects.get_value(name) 36 + self.assertIsInstance(value, float) 37 + self.assertAlmostEqual(42.5, value) 38 + 39 + 40 + @override_settings(**TEST_SETTINGS) 41 + class UserOptionTests(TestCase): 42 + def test_custom_user_options(self): 43 + user = UserFactory() 44 + name = "default_option" 45 + expected_value = "user default" 46 + UserOption.objects.create( 47 + name=name, public_name=name, value=expected_value, type=STRING, user=user 48 + ) 49 + value = UserOption.objects.get_value(name, user=user, default="ohter") 50 + self.assertEqual(expected_value, value) 51 + 52 + 53 + @override_settings(**TEST_SETTINGS) 54 + class OptionAPITests(APITestCase): 55 + def test_list_options(self): 56 + admin = UserFactory(is_staff=True) 57 + self.client.force_authenticate(admin) 58 + response = self.client.get("/api/options/", format="json") 59 + self.assertEqual(response.status_code, status.HTTP_200_OK) 60 + data = response.json() 61 + self.assertEqual(1, len(data)) 62 + 63 + def test_list_user_options(self): 64 + user = UserFactory() 65 + name = "default_option" 66 + expected_value = "user default" 67 + UserOptionFactory(name=name, value=expected_value, user=user) 68 + self.client.force_authenticate(user) 69 + response = self.client.get("/api/user-options/", format="json") 70 + self.assertEqual(response.status_code, status.HTTP_200_OK) 71 + data = response.json() 72 + self.assertEqual(1, len(data)) 73 + 74 + def test_list_user_options_with_exclude(self): 75 + user = UserFactory() 76 + name = "default_option" 77 + expected_value = "user default" 78 + UserOptionFactory(name="secret_option", value="secret", user=user) 79 + UserOptionFactory(name=name, value=expected_value, user=user) 80 + self.client.force_authenticate(user) 81 + response = self.client.get("/api/user-options/", format="json") 82 + self.assertEqual(response.status_code, status.HTTP_200_OK) 83 + data = response.json() 84 + self.assertEqual(1, len(data))
+4
tests/urls.py
··· 1 + from django.urls import include, path 2 + 3 + 4 + urlpatterns = [path("api/", include("tests.router", namespace="api"))]