From 659f04ce9cf0d5f59595cd69ed3ffc3dc01353f8 Mon Sep 17 00:00:00 2001 From: Dariusz Niemczyk Date: Tue, 11 Jul 2023 15:34:35 +0200 Subject: [PATCH] Blackify the code (autoformat) --- auth/backend.py | 39 ++-- auth/pipeline.py | 14 +- auth/views.py | 3 +- spejstore/settings.py | 178 +++++++++--------- spejstore/urls.py | 30 +-- storage/admin.py | 42 ++--- storage/apiviews.py | 33 ++-- storage/apps.py | 2 +- storage/management/commands/importbooks.py | 25 +-- storage/migrations/0001_initial.py | 22 ++- .../0001_squashed_0008_item_state.py | 124 ++++++++---- storage/migrations/0002_auto_20170215_0115.py | 22 ++- storage/migrations/0003_auto_20170424_2002.py | 36 ++-- storage/migrations/0004_auto_20170528_1945.py | 25 ++- storage/migrations/0005.py | 10 +- storage/migrations/0006_category_icon_id.py | 7 +- storage/models.py | 56 +++--- storage/serializers.py | 33 +++- storage/urls.py | 25 ++- storage/views.py | 140 ++++++++------ storage/widgets.py | 22 ++- 21 files changed, 519 insertions(+), 369 deletions(-) diff --git a/auth/backend.py b/auth/backend.py index 363923c..f7899ec 100644 --- a/auth/backend.py +++ b/auth/backend.py @@ -1,36 +1,35 @@ from social_core.backends.oauth import BaseOAuth2 from six.moves.urllib_parse import urlencode, unquote + class HSWawOAuth2(BaseOAuth2): """Hackerspace OAuth authentication backend""" - name = 'hswaw' - ID_KEY = 'username' - AUTHORIZATION_URL = 'https://sso.hackerspace.pl/oauth/authorize' - ACCESS_TOKEN_URL = 'https://sso.hackerspace.pl/oauth/token' - DEFAULT_SCOPE = ['profile:read'] + + name = "hswaw" + ID_KEY = "username" + AUTHORIZATION_URL = "https://sso.hackerspace.pl/oauth/authorize" + ACCESS_TOKEN_URL = "https://sso.hackerspace.pl/oauth/token" + DEFAULT_SCOPE = ["profile:read"] REDIRECT_STATE = False - SCOPE_SEPARATOR = ',' - EXTRA_DATA = [ - ('expires', 'expires_in') - ] + SCOPE_SEPARATOR = "," + EXTRA_DATA = [("expires", "expires_in")] def get_user_details(self, response): """Return user details from Hackerspace account""" personal_email = None - if response.get('personal_email'): - personal_email = response.get('personal_email')[0] + if response.get("personal_email"): + personal_email = response.get("personal_email")[0] - return {'username': response.get('username'), - 'email': response.get('email'), - 'personal_email': personal_email, - } + return { + "username": response.get("username"), + "email": response.get("email"), + "personal_email": personal_email, + } def user_data(self, access_token, *args, **kwargs): """Loads user data from service""" - url = 'https://sso.hackerspace.pl/api/1/profile' - headers = { - 'Authorization': 'Bearer {}'.format(access_token) - } + url = "https://sso.hackerspace.pl/api/1/profile" + headers = {"Authorization": "Bearer {}".format(access_token)} return self.get_json(url, headers=headers) def auth_url(self): @@ -40,4 +39,4 @@ class HSWawOAuth2(BaseOAuth2): params.update(self.get_scope_argument()) params.update(self.auth_extra_arguments()) params = urlencode(params) - return '{0}?{1}'.format(self.authorization_url(), params) + return "{0}?{1}".format(self.authorization_url(), params) diff --git a/auth/pipeline.py b/auth/pipeline.py index 494913f..18770a1 100644 --- a/auth/pipeline.py +++ b/auth/pipeline.py @@ -5,13 +5,19 @@ from django.contrib.auth.models import Group def staff_me_up(backend, details, response, uid, user, *args, **kwargs): user.is_staff = True try: - user.groups.set([Group.objects.get(name='member')]) + user.groups.set([Group.objects.get(name="member")]) except Group.DoesNotExist: pass user.save() def associate_by_personal_email(backend, details, user=None, *args, **kwargs): - return associate_by_email(backend, { - 'email': details.get('personal_email'), - }, user, *args, **kwargs) + return associate_by_email( + backend, + { + "email": details.get("personal_email"), + }, + user, + *args, + **kwargs + ) diff --git a/auth/views.py b/auth/views.py index ae19948..4309b3b 100644 --- a/auth/views.py +++ b/auth/views.py @@ -1,4 +1,5 @@ from django.shortcuts import redirect + def auth_redirect(request): - return redirect('social:begin', 'hswaw') + return redirect("social:begin", "hswaw") diff --git a/spejstore/settings.py b/spejstore/settings.py index 37fe6ef..cc08f06 100644 --- a/spejstore/settings.py +++ b/spejstore/settings.py @@ -12,97 +12,97 @@ https://docs.djangoproject.com/en/1.10/ref/settings/ import os + def env(name, default=None): - return os.getenv('SPEJSTORE_' + name, default) + return os.getenv("SPEJSTORE_" + name, default) # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) -STATIC_ROOT = os.path.join(BASE_DIR, 'build_static') -PROD = os.getenv('SPEJSTORE_ENV') == 'prod' +STATIC_ROOT = os.path.join(BASE_DIR, "build_static") +PROD = os.getenv("SPEJSTORE_ENV") == "prod" # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = env('SECRET_KEY', '#hjthi7_udsyt*9eeyb&nwgw5x=%pk_lnz3+u2tg9@=w3p1m*k') +SECRET_KEY = env("SECRET_KEY", "#hjthi7_udsyt*9eeyb&nwgw5x=%pk_lnz3+u2tg9@=w3p1m*k") # SECURITY WARNING: don't run with debug turned on in production! DEBUG = not PROD -ALLOWED_HOSTS = env('ALLOWED_HOSTS', 'devinventory,inventory.waw.hackerspace.pl,i,inventory').split(',') -LOGIN_REDIRECT_URL = '/admin/' +ALLOWED_HOSTS = env( + "ALLOWED_HOSTS", "devinventory,inventory.waw.hackerspace.pl,i,inventory" +).split(",") +LOGIN_REDIRECT_URL = "/admin/" # Application definition INSTALLED_APPS = [ - 'flat_responsive', - 'django.contrib.admin', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'django.contrib.postgres', - - 'social_django', - - 'django_hstore', - 'tree', - 'django_select2', - 'rest_framework', - 'rest_framework.authtoken', - 'django_markdown2', - - 'storage', + "flat_responsive", + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "django.contrib.postgres", + "social_django", + "django_hstore", + "tree", + "django_select2", + "rest_framework", + "rest_framework.authtoken", + "django_markdown2", + "storage", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'social_django.middleware.SocialAuthExceptionMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "social_django.middleware.SocialAuthExceptionMiddleware", ] -ROOT_URLCONF = 'spejstore.urls' +ROOT_URLCONF = "spejstore.urls" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': ['templates/'], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - 'social_django.context_processors.backends', - 'social_django.context_processors.login_redirect', + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": ["templates/"], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "social_django.context_processors.backends", + "social_django.context_processors.login_redirect", ], }, }, ] -WSGI_APPLICATION = 'spejstore.wsgi.application' +WSGI_APPLICATION = "spejstore.wsgi.application" # Database # https://docs.djangoproject.com/en/1.10/ref/settings/#databases DATABASES = { - 'default': { - 'ENGINE': env('DB_ENGINE', 'django.db.backends.postgresql_psycopg2'), - 'NAME': env('DB_NAME', 'postgres'), - 'USER': env('DB_USER', 'postgres'), - 'PASSWORD': env('DB_PASSWORD', None), - 'HOST': env('DB_HOST', 'db'), - 'PORT': env('DB_PORT', 5432), + "default": { + "ENGINE": env("DB_ENGINE", "django.db.backends.postgresql_psycopg2"), + "NAME": env("DB_NAME", "postgres"), + "USER": env("DB_USER", "postgres"), + "PASSWORD": env("DB_PASSWORD", None), + "HOST": env("DB_HOST", "db"), + "PORT": env("DB_PORT", 5432), } } @@ -112,49 +112,49 @@ DATABASES = { AUTH_PASSWORD_VALIDATORS = [ { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", }, { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", }, ] # select2 -SELECT2_JS = 'js/select2.min.js' -SELECT2_CSS = 'css/select2.min.css' -SELECT2_I18N_PATH = '' +SELECT2_JS = "js/select2.min.js" +SELECT2_CSS = "css/select2.min.css" +SELECT2_I18N_PATH = "" AUTHENTICATION_BACKENDS = ( - 'auth.backend.HSWawOAuth2', - 'django.contrib.auth.backends.ModelBackend', + "auth.backend.HSWawOAuth2", + "django.contrib.auth.backends.ModelBackend", ) SOCIAL_AUTH_PIPELINE = ( - 'social_core.pipeline.social_auth.social_details', - 'social_core.pipeline.social_auth.social_uid', - 'social_core.pipeline.social_auth.social_user', - 'social_core.pipeline.user.get_username', - 'social_core.pipeline.social_auth.associate_by_email', - 'auth.pipeline.associate_by_personal_email', - 'social_core.pipeline.user.create_user', - 'social_core.pipeline.social_auth.associate_user', - 'social_core.pipeline.social_auth.load_extra_data', - 'social_core.pipeline.user.user_details', - 'auth.pipeline.staff_me_up', + "social_core.pipeline.social_auth.social_details", + "social_core.pipeline.social_auth.social_uid", + "social_core.pipeline.social_auth.social_user", + "social_core.pipeline.user.get_username", + "social_core.pipeline.social_auth.associate_by_email", + "auth.pipeline.associate_by_personal_email", + "social_core.pipeline.user.create_user", + "social_core.pipeline.social_auth.associate_user", + "social_core.pipeline.social_auth.load_extra_data", + "social_core.pipeline.user.user_details", + "auth.pipeline.staff_me_up", ) # Internationalization # https://docs.djangoproject.com/en/1.10/topics/i18n/ -LANGUAGE_CODE = 'en-us' -TIME_ZONE = 'UTC' +LANGUAGE_CODE = "en-us" +TIME_ZONE = "UTC" USE_I18N = True USE_L10N = True USE_TZ = True @@ -162,32 +162,32 @@ USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.10/howto/static-files/ -STATIC_URL = '/static/' +STATIC_URL = "/static/" STATICFILES_DIRS = [ os.path.join(BASE_DIR, "static"), - ] +] -MEDIA_URL = '/media/' -MEDIA_ROOT = env('MEDIA_ROOT', os.path.join(BASE_DIR, "media")) +MEDIA_URL = "/media/" +MEDIA_ROOT = env("MEDIA_ROOT", os.path.join(BASE_DIR, "media")) # REST Framework REST_FRAMEWORK = { # Use Django's standard `django.contrib.auth` permissions, # or allow read-only access for unauthenticated users. - 'DEFAULT_PERMISSION_CLASSES': [ - 'rest_framework.permissions.IsAuthenticatedOrReadOnly', + "DEFAULT_PERMISSION_CLASSES": [ + "rest_framework.permissions.IsAuthenticatedOrReadOnly", + ], + "DEFAULT_AUTHENTICATION_CLASSES": [ + "rest_framework.authentication.BasicAuthentication", + "rest_framework.authentication.SessionAuthentication", + "rest_framework.authentication.TokenAuthentication", ], - 'DEFAULT_AUTHENTICATION_CLASSES': [ - 'rest_framework.authentication.BasicAuthentication', - 'rest_framework.authentication.SessionAuthentication', - 'rest_framework.authentication.TokenAuthentication', - ] } -SOCIAL_AUTH_HSWAW_KEY = env('CLIENT_ID') -SOCIAL_AUTH_HSWAW_SECRET = env('SECRET') +SOCIAL_AUTH_HSWAW_KEY = env("CLIENT_ID") +SOCIAL_AUTH_HSWAW_SECRET = env("SECRET") SOCIAL_AUTH_REDIRECT_IS_HTTPS = PROD SOCIAL_AUTH_POSTGRES_JSONFIELD = True -LABEL_API = env('LABEL_API', 'http://label.waw.hackerspace.pl:4567') +LABEL_API = env("LABEL_API", "http://label.waw.hackerspace.pl:4567") diff --git a/spejstore/urls.py b/spejstore/urls.py index ccf0b8d..6d68ff2 100644 --- a/spejstore/urls.py +++ b/spejstore/urls.py @@ -16,19 +16,25 @@ from auth.views import auth_redirect router = routers.DefaultRouter() -router.register(r'items', apiviews.ItemViewSet) -router.register(r'labels', apiviews.LabelViewSet) +router.register(r"items", apiviews.ItemViewSet) +router.register(r"labels", apiviews.LabelViewSet) # Wire up our API using automatic URL routing. # Additionally, we include login URLs for the browsable API. -urlpatterns = ([ - url(r'^admin/login/.*', auth_redirect), -] if settings.PROD else []) + [ - url(r'^admin/', admin.site.urls), - url(r'^select2/', include('django_select2.urls')), - - url(r'^', include('storage.urls')), - url(r'^api/1/', include(router.urls)), -] \ - + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) \ +urlpatterns = ( + ( + [ + url(r"^admin/login/.*", auth_redirect), + ] + if settings.PROD + else [] + ) + + [ + url(r"^admin/", admin.site.urls), + url(r"^select2/", include("django_select2.urls")), + url(r"^", include("storage.urls")), + url(r"^api/1/", include(router.urls)), + ] + + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) +) diff --git a/storage/admin.py b/storage/admin.py index fbdcae1..9ec3bc4 100644 --- a/storage/admin.py +++ b/storage/admin.py @@ -7,8 +7,8 @@ from django_select2.forms import ModelSelect2Widget, Select2MultipleWidget from .models import Item, ItemImage, Category, Label from .widgets import ItemSelectWidget, PropsSelectWidget -class ModelAdminMixin(object): +class ModelAdminMixin(object): def has_add_permission(self, request, obj=None): return request.user.is_authenticated() @@ -24,10 +24,10 @@ class ItemForm(forms.ModelForm): model = Item exclude = [] widgets = { - 'parent': ItemSelectWidget(model=Item), - 'categories': Select2MultipleWidget, - 'props': PropsSelectWidget - } + "parent": ItemSelectWidget(model=Item), + "categories": Select2MultipleWidget, + "props": PropsSelectWidget, + } class ItemImageInline(ModelAdminMixin, admin.TabularInline): @@ -39,30 +39,29 @@ class LabelInline(ModelAdminMixin, admin.TabularInline): model = Label - class ItemAdmin(ModelAdminMixin, admin.ModelAdmin): - list_display = ('_name',) - list_filter = ('categories',) + list_display = ("_name",) + list_filter = ("categories",) form = ItemForm inlines = [ItemImageInline, LabelInline] save_on_top = True def _name(self, obj): - return '-' * obj.get_level() + '> ' + obj.name + return "-" * obj.get_level() + "> " + obj.name def save_model(self, request, obj, form, change): super(ItemAdmin, self).save_model(request, obj, form, change) # Store last input parent to use as default on next creation if obj.parent: - request.session['last-parent'] = str(obj.parent.uuid) + request.session["last-parent"] = str(obj.parent.uuid) else: - request.session['last-parent'] = str(obj.uuid) + request.session["last-parent"] = str(obj.uuid) def get_changeform_initial_data(self, request): data = { - 'parent': request.GET.get('parent') or request.session.get('last-parent') - } + "parent": request.GET.get("parent") or request.session.get("last-parent") + } data.update(super(ItemAdmin, self).get_changeform_initial_data(request)) return data @@ -70,11 +69,9 @@ class ItemAdmin(ModelAdminMixin, admin.ModelAdmin): class Media: js = ( # Required by select2 - 'https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js', - ) - css = { - 'all': ('css/admin.css',) - } + "https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js", + ) + css = {"all": ("css/admin.css",)} def response_action(self, request, queryset): with Item.disabled_tree_trigger(): @@ -85,8 +82,8 @@ class NormalModelAdmin(ModelAdminMixin, admin.ModelAdmin): pass -admin.site.site_title = 'Hackerspace Storage Admin' -admin.site.site_header = 'Hackerspace Storage Admin' +admin.site.site_title = "Hackerspace Storage Admin" +admin.site.site_header = "Hackerspace Storage Admin" admin.site.register(Item, ItemAdmin) admin.site.register(Category, NormalModelAdmin) @@ -94,13 +91,14 @@ admin.site.register(Category, NormalModelAdmin) from django.contrib.auth.models import User from django.contrib.auth.models import Group -User.add_to_class('get_short_name', User.get_username) -User.add_to_class('get_full_name', User.get_username) +User.add_to_class("get_short_name", User.get_username) +User.add_to_class("get_full_name", User.get_username) admin.site.unregister(User) admin.site.unregister(Group) from social_django.admin import UserSocialAuth, Nonce, Association + admin.site.unregister(UserSocialAuth) admin.site.unregister(Nonce) admin.site.unregister(Association) diff --git a/storage/apiviews.py b/storage/apiviews.py index 771d5ee..15f3dc4 100644 --- a/storage/apiviews.py +++ b/storage/apiviews.py @@ -17,7 +17,7 @@ class SmartSearchFilterBackend(filters.BaseFilterBackend): """ def filter_queryset(self, request, queryset, view): - search_query = request.query_params.get('smartsearch', None) + search_query = request.query_params.get("smartsearch", None) if search_query: return apply_smart_search(search_query, queryset) @@ -28,12 +28,13 @@ class LabelViewSet(viewsets.ModelViewSet): """ API endpoint that allows items to be viewed or edited. """ + queryset = Label.objects serializer_class = LabelSerializer - @detail_route(methods=['post'], permission_classes=[AllowAny]) + @detail_route(methods=["post"], permission_classes=[AllowAny]) def print(self, request, pk): - quantity = min(int(request.query_params.get('quantity', 1)), 5) + quantity = min(int(request.query_params.get("quantity", 1)), 5) obj = self.get_object() for _ in range(quantity): obj.print() @@ -44,11 +45,11 @@ class ItemViewSet(viewsets.ModelViewSet): """ API endpoint that allows items to be viewed or edited. """ + queryset = Item.objects serializer_class = ItemSerializer filter_backends = (SmartSearchFilterBackend, filters.OrderingFilter) - ordering_fields = '__all__' - + ordering_fields = "__all__" def get_queryset(self): return Item.get_roots() @@ -63,7 +64,7 @@ class ItemViewSet(viewsets.ModelViewSet): def get_item_by_id_or_label(self, id): try: - item = Item.objects.get(uuid__startswith=id) # look up by short id + item = Item.objects.get(uuid__startswith=id) # look up by short id return item except Item.DoesNotExist: try: @@ -72,10 +73,10 @@ class ItemViewSet(viewsets.ModelViewSet): except Label.DoesNotExist: raise Http404() - @detail_route(methods=['post'], permission_classes=[AllowAny]) + @detail_route(methods=["post"], permission_classes=[AllowAny]) def print(self, request, pk): # todo: deduplicate - quantity = min(int(request.query_params.get('quantity', 1)), 5) + quantity = min(int(request.query_params.get("quantity", 1)), 5) obj = self.get_object() for _ in range(quantity): obj.print() @@ -84,19 +85,27 @@ class ItemViewSet(viewsets.ModelViewSet): @detail_route() def children(self, request, pk): item = self.get_object() - return Response(self.serializer_class(item.get_children().all(), many=True).data) + return Response( + self.serializer_class(item.get_children().all(), many=True).data + ) @detail_route() def ancestors(self, request, pk): item = self.get_object() - return Response(self.serializer_class(item.get_ancestors().all(), many=True).data) + return Response( + self.serializer_class(item.get_ancestors().all(), many=True).data + ) @detail_route() def descendants(self, request, pk): item = self.get_object() - return Response(self.serializer_class(item.get_descendants().all(), many=True).data) + return Response( + self.serializer_class(item.get_descendants().all(), many=True).data + ) @detail_route() def siblings(self, request, pk): item = self.get_object() - return Response(self.serializer_class(item.get_siblings().all(), many=True).data) + return Response( + self.serializer_class(item.get_siblings().all(), many=True).data + ) diff --git a/storage/apps.py b/storage/apps.py index a9bfb2c..628ca68 100644 --- a/storage/apps.py +++ b/storage/apps.py @@ -4,4 +4,4 @@ from django.apps import AppConfig class StorageConfig(AppConfig): - name = 'storage' + name = "storage" diff --git a/storage/management/commands/importbooks.py b/storage/management/commands/importbooks.py index 66d2f5b..eb2edc3 100644 --- a/storage/management/commands/importbooks.py +++ b/storage/management/commands/importbooks.py @@ -3,28 +3,29 @@ from storage.models import Item from io import StringIO import csv + class Command(BaseCommand): - help = 'Imports book library from specified wiki page dump' + help = "Imports book library from specified wiki page dump" def add_arguments(self, parser): - parser.add_argument('parent') - parser.add_argument('file') + parser.add_argument("parent") + parser.add_argument("file") def handle(self, *args, **options): - with open(options['file']) as fd: + with open(options["file"]) as fd: sio = StringIO(fd.read()) - reader = csv.reader(sio, delimiter='|') - parent = Item.objects.get(pk=options['parent']) + reader = csv.reader(sio, delimiter="|") + parent = Item.objects.get(pk=options["parent"]) for line in reader: line = list(map(str.strip, line)) item = Item(parent=parent) item.name = line[2] - item.props['author'] = line[1] - item.props['owner'] = line[3] - item.props['can_borrow'] = line[4] - item.props['borrowed_by'] = line[5] + item.props["author"] = line[1] + item.props["owner"] = line[3] + item.props["can_borrow"] = line[4] + item.props["borrowed_by"] = line[5] item.save() - self.stdout.write(self.style.NOTICE('Book added: %r') % item) + self.stdout.write(self.style.NOTICE("Book added: %r") % item) - self.stdout.write(self.style.SUCCESS('Successfully imported data')) + self.stdout.write(self.style.SUCCESS("Successfully imported data")) diff --git a/storage/migrations/0001_initial.py b/storage/migrations/0001_initial.py index 5dcdccb..6f2556d 100644 --- a/storage/migrations/0001_initial.py +++ b/storage/migrations/0001_initial.py @@ -7,20 +7,26 @@ import django_hstore.fields class Migration(migrations.Migration): - initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='Item', + name="Item", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.TextField()), - ('description', models.TextField()), - ('props', django_hstore.fields.DictionaryField()), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.TextField()), + ("description", models.TextField()), + ("props", django_hstore.fields.DictionaryField()), ], ), ] diff --git a/storage/migrations/0001_squashed_0008_item_state.py b/storage/migrations/0001_squashed_0008_item_state.py index be99617..0c5cf5a 100644 --- a/storage/migrations/0001_squashed_0008_item_state.py +++ b/storage/migrations/0001_squashed_0008_item_state.py @@ -10,8 +10,16 @@ import uuid class Migration(migrations.Migration): - - replaces = [('storage', '0001_initial'), ('storage', '0002_auto_20160929_2125'), ('storage', '0003_auto_20160929_2134'), ('storage', '0004_auto_20160929_2143'), ('storage', '0005_auto_20160929_2151'), ('storage', '0006_auto_20160929_2153'), ('storage', '0007_auto_20160929_2153'), ('storage', '0008_item_state')] + replaces = [ + ("storage", "0001_initial"), + ("storage", "0002_auto_20160929_2125"), + ("storage", "0003_auto_20160929_2134"), + ("storage", "0004_auto_20160929_2143"), + ("storage", "0005_auto_20160929_2151"), + ("storage", "0006_auto_20160929_2153"), + ("storage", "0007_auto_20160929_2153"), + ("storage", "0008_item_state"), + ] initial = True @@ -21,62 +29,114 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='Item', + name="Item", fields=[ - ('name', models.TextField()), - ('description', models.TextField(blank=True)), - ('props', django_hstore.fields.DictionaryField()), - ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), + ("name", models.TextField()), + ("description", models.TextField(blank=True)), + ("props", django_hstore.fields.DictionaryField()), + ( + "uuid", + models.UUIDField( + default=uuid.uuid4, + editable=False, + primary_key=True, + serialize=False, + ), + ), ], ), migrations.CreateModel( - name='ItemImage', + name="ItemImage", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('image', models.ImageField(upload_to='')), - ('item', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to='storage.Item')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("image", models.ImageField(upload_to="")), + ( + "item", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="images", + to="storage.Item", + ), + ), ], ), migrations.CreateModel( - name='Category', + name="Category", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=127)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=127)), ], ), migrations.AddField( - model_name='item', - name='categories', - field=models.ManyToManyField(to='storage.Category'), + model_name="item", + name="categories", + field=models.ManyToManyField(to="storage.Category"), ), migrations.AddField( - model_name='item', - name='owner', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='owned_items', to=settings.AUTH_USER_MODEL), + model_name="item", + name="owner", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="owned_items", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='item', - name='taken_by', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='taken_items', to=settings.AUTH_USER_MODEL), + model_name="item", + name="taken_by", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="taken_items", + to=settings.AUTH_USER_MODEL, + ), ), migrations.AddField( - model_name='item', - name='taken_on', + model_name="item", + name="taken_on", field=models.DateTimeField(blank=True, null=True), ), migrations.AddField( - model_name='item', - name='taken_until', + model_name="item", + name="taken_until", field=models.DateTimeField(blank=True, null=True), ), migrations.AlterField( - model_name='item', - name='description', + model_name="item", + name="description", field=models.TextField(blank=True, null=True), ), migrations.AddField( - model_name='item', - name='state', - field=models.CharField(choices=[('present', 'Present'), ('taken', 'Taken'), ('broken', 'Broken'), ('missing', 'Missing')], default='present', max_length=31), + model_name="item", + name="state", + field=models.CharField( + choices=[ + ("present", "Present"), + ("taken", "Taken"), + ("broken", "Broken"), + ("missing", "Missing"), + ], + default="present", + max_length=31, + ), ), ] diff --git a/storage/migrations/0002_auto_20170215_0115.py b/storage/migrations/0002_auto_20170215_0115.py index b2083b2..116b0f1 100644 --- a/storage/migrations/0002_auto_20170215_0115.py +++ b/storage/migrations/0002_auto_20170215_0115.py @@ -9,22 +9,26 @@ from tree.operations import CreateTreeTrigger class Migration(migrations.Migration): - dependencies = [ - ('storage', '0001_squashed_0008_item_state'), - ('tree', '0001_initial'), + ("storage", "0001_squashed_0008_item_state"), + ("tree", "0001_initial"), ] operations = [ migrations.AddField( - model_name='item', - name='parent', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='storage.Item'), + model_name="item", + name="parent", + field=models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="storage.Item", + ), ), migrations.AddField( - model_name='item', - name='path', + model_name="item", + name="path", field=tree.fields.PathField(), ), - CreateTreeTrigger('storage.Item'), + CreateTreeTrigger("storage.Item"), ] diff --git a/storage/migrations/0003_auto_20170424_2002.py b/storage/migrations/0003_auto_20170424_2002.py index 5fbe73a..4682695 100644 --- a/storage/migrations/0003_auto_20170424_2002.py +++ b/storage/migrations/0003_auto_20170424_2002.py @@ -8,36 +8,42 @@ import django_hstore.fields class Migration(migrations.Migration): - dependencies = [ - ('storage', '0002_auto_20170215_0115'), + ("storage", "0002_auto_20170215_0115"), ] operations = [ migrations.CreateModel( - name='Label', + name="Label", fields=[ - ('id', models.CharField(max_length=64, primary_key=True, serialize=False)), - ('revision', models.IntegerField()), + ( + "id", + models.CharField(max_length=64, primary_key=True, serialize=False), + ), + ("revision", models.IntegerField()), ], ), migrations.AlterModelOptions( - name='item', - options={'ordering': ('path',)}, + name="item", + options={"ordering": ("path",)}, ), migrations.AlterField( - model_name='item', - name='categories', - field=models.ManyToManyField(blank=True, to='storage.Category'), + model_name="item", + name="categories", + field=models.ManyToManyField(blank=True, to="storage.Category"), ), migrations.AlterField( - model_name='item', - name='props', + model_name="item", + name="props", field=django_hstore.fields.DictionaryField(blank=True), ), migrations.AddField( - model_name='label', - name='item', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='labels', to='storage.Item'), + model_name="label", + name="item", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="labels", + to="storage.Item", + ), ), ] diff --git a/storage/migrations/0004_auto_20170528_1945.py b/storage/migrations/0004_auto_20170528_1945.py index 4601db0..ca84295 100644 --- a/storage/migrations/0004_auto_20170528_1945.py +++ b/storage/migrations/0004_auto_20170528_1945.py @@ -7,25 +7,30 @@ import django.utils.timezone class Migration(migrations.Migration): - dependencies = [ - ('storage', '0003_auto_20170424_2002'), + ("storage", "0003_auto_20170424_2002"), ] operations = [ migrations.RemoveField( - model_name='label', - name='revision', + model_name="label", + name="revision", ), migrations.AddField( - model_name='label', - name='created', - field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now), + model_name="label", + name="created", + field=models.DateTimeField( + auto_now_add=True, default=django.utils.timezone.now + ), preserve_default=False, ), migrations.AddField( - model_name='label', - name='style', - field=models.CharField(choices=[('basic_99012_v1', 'Basic Dymo 89x36mm label')], default='basic_99012_v1', max_length=32), + model_name="label", + name="style", + field=models.CharField( + choices=[("basic_99012_v1", "Basic Dymo 89x36mm label")], + default="basic_99012_v1", + max_length=32, + ), ), ] diff --git a/storage/migrations/0005.py b/storage/migrations/0005.py index e08999e..9c6e506 100644 --- a/storage/migrations/0005.py +++ b/storage/migrations/0005.py @@ -4,14 +4,14 @@ from __future__ import unicode_literals from django.db import migrations, models from tree.operations import CreateTreeTrigger, DeleteTreeTrigger, RebuildPaths -class Migration(migrations.Migration): +class Migration(migrations.Migration): dependencies = [ - ('storage', '0004_auto_20170528_1945'), + ("storage", "0004_auto_20170528_1945"), ] operations = [ - DeleteTreeTrigger('storage.Item'), - CreateTreeTrigger('storage.Item', order_by=('name',)), - RebuildPaths('storage.Item') + DeleteTreeTrigger("storage.Item"), + CreateTreeTrigger("storage.Item", order_by=("name",)), + RebuildPaths("storage.Item"), ] diff --git a/storage/migrations/0006_category_icon_id.py b/storage/migrations/0006_category_icon_id.py index 89898d0..17debcd 100644 --- a/storage/migrations/0006_category_icon_id.py +++ b/storage/migrations/0006_category_icon_id.py @@ -6,15 +6,14 @@ from django.db import migrations, models class Migration(migrations.Migration): - dependencies = [ - ('storage', '0005'), + ("storage", "0005"), ] operations = [ migrations.AddField( - model_name='category', - name='icon_id', + model_name="category", + name="icon_id", field=models.CharField(blank=True, max_length=64, null=True), ), ] diff --git a/storage/models.py b/storage/models.py index 8e6fe2b..410ce3e 100644 --- a/storage/models.py +++ b/storage/models.py @@ -14,11 +14,11 @@ import requests STATES = ( - ('present', 'Present'), - ('taken', 'Taken'), - ('broken', 'Broken'), - ('missing', 'Missing'), - ('depleted', 'Depleted'), + ("present", "Present"), + ("taken", "Taken"), + ("broken", "Broken"), + ("missing", "Missing"), + ("depleted", "Depleted"), ) @@ -31,7 +31,7 @@ class Category(models.Model): return self.name class Meta: - ordering = ['name'] + ordering = ["name"] verbose_name_plural = "categories" @@ -42,10 +42,11 @@ class Category(models.Model): # also qrcody w stylu //s/ID (żeby się resolvowało w sieci lokalnej) # Also ID zawierające część name + class Item(models.Model, TreeModelMixin): uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - parent = models.ForeignKey('self', null=True, blank=True) + parent = models.ForeignKey("self", null=True, blank=True) path = PathField() name = models.TextField() @@ -53,9 +54,11 @@ class Item(models.Model, TreeModelMixin): description = models.TextField(blank=True, null=True) state = models.CharField(max_length=31, choices=STATES, default=STATES[0][0]) categories = models.ManyToManyField(Category, blank=True) - owner = models.ForeignKey(User, null=True, blank=True, related_name='owned_items') + owner = models.ForeignKey(User, null=True, blank=True, related_name="owned_items") - taken_by = models.ForeignKey(User, null=True, blank=True, related_name='taken_items') + taken_by = models.ForeignKey( + User, null=True, blank=True, related_name="taken_items" + ) taken_on = models.DateTimeField(blank=True, null=True) taken_until = models.DateTimeField(blank=True, null=True) @@ -65,19 +68,20 @@ class Item(models.Model, TreeModelMixin): def short_id(self): # let's just hope we never have 4 294 967 296 things :) - return str(self.pk)[:8] # collisions? what collisions? + return str(self.pk)[:8] # collisions? what collisions? def __str__(self): - return '- ' * (self.get_level() or 0) + self.name + return "- " * (self.get_level() or 0) + self.name def get_absolute_url(self): from django.urls import reverse - return reverse('item-display', kwargs={'pk': str(self.pk)}) + + return reverse("item-display", kwargs={"pk": str(self.pk)}) def get_or_create_label(self, **kwargs): defaults = { - 'id': re.sub('[^A-Z0-9]', '', self.name.upper())[:16], - } + "id": re.sub("[^A-Z0-9]", "", self.name.upper())[:16], + } defaults.update(kwargs) @@ -92,33 +96,35 @@ class Item(models.Model, TreeModelMixin): def print(self): # todo: deduplicate resp = requests.post( - '{}/api/1/print/{}'.format(settings.LABEL_API, self.short_id())) + "{}/api/1/print/{}".format(settings.LABEL_API, self.short_id()) + ) resp.raise_for_status() class Meta: - ordering = ('path',) + ordering = ("path",) class ItemImage(models.Model): - item = models.ForeignKey(Item, related_name='images') + item = models.ForeignKey(Item, related_name="images") image = models.ImageField() def __str__(self): - return '{}'.format(self.image.name) + return "{}".format(self.image.name) class Label(models.Model): id = models.CharField(max_length=64, primary_key=True) - item = models.ForeignKey(Item, related_name='labels') - style = models.CharField(max_length=32, choices=( - ('basic_99012_v1', 'Basic Dymo 89x36mm label'), - ), default='basic_99012_v1') + item = models.ForeignKey(Item, related_name="labels") + style = models.CharField( + max_length=32, + choices=(("basic_99012_v1", "Basic Dymo 89x36mm label"),), + default="basic_99012_v1", + ) created = models.DateTimeField(auto_now_add=True, blank=True) def __str__(self): - return '{}'.format(self.id) + return "{}".format(self.id) def print(self): - resp = requests.post( - '{}/api/1/print/{}'.format(settings.LABEL_API, self.id)) + resp = requests.post("{}/api/1/print/{}".format(settings.LABEL_API, self.id)) resp.raise_for_status() diff --git a/storage/serializers.py b/storage/serializers.py index d760e05..334f0cc 100644 --- a/storage/serializers.py +++ b/storage/serializers.py @@ -5,16 +5,37 @@ from rest_framework_hstore.serializers import HStoreSerializer class ItemSerializer(HStoreSerializer): - categories = serializers.SlugRelatedField(queryset=Category.objects, many=True, slug_field='name') - owner = serializers.SlugRelatedField(queryset=User.objects, slug_field='username') - taken_by = serializers.SlugRelatedField(queryset=User.objects, slug_field='username') + categories = serializers.SlugRelatedField( + queryset=Category.objects, many=True, slug_field="name" + ) + owner = serializers.SlugRelatedField(queryset=User.objects, slug_field="username") + taken_by = serializers.SlugRelatedField( + queryset=User.objects, slug_field="username" + ) + class Meta: model = Item - fields = ('uuid', 'short_id', 'name', 'description', 'props', 'state', 'parent', 'labels', 'owner', 'taken_by', 'taken_on', 'taken_until', 'categories') + fields = ( + "uuid", + "short_id", + "name", + "description", + "props", + "state", + "parent", + "labels", + "owner", + "taken_by", + "taken_on", + "taken_until", + "categories", + ) + class LabelSerializer(serializers.ModelSerializer): item = ItemSerializer(required=False) - item_id = serializers.PrimaryKeyRelatedField(queryset=Item.objects, source='item') + item_id = serializers.PrimaryKeyRelatedField(queryset=Item.objects, source="item") + class Meta: model = Label - fields = ('id', 'item', 'item_id', 'style') + fields = ("id", "item", "item_id", "style") diff --git a/storage/urls.py b/storage/urls.py index 1481483..9e2eca5 100644 --- a/storage/urls.py +++ b/storage/urls.py @@ -1,16 +1,21 @@ from django.conf.urls import include, url from storage.views import ( - index, search, item_display, label_lookup, apitoken, ItemSelectView, - PropSelectView + index, + search, + item_display, + label_lookup, + apitoken, + ItemSelectView, + PropSelectView, ) urlpatterns = [ - url(r'^$', index), - url(r'^search$', search), - url(r'^apitoken$', apitoken), - url(r'^item/(?P.*)$', item_display, name='item-display'), - url(r'^autocomplete.json$', ItemSelectView.as_view(), name='item-complete'), - url(r'^autocomplete_prop.json$', PropSelectView.as_view(), name='prop-complete'), - url(r'^(?P[^/]*)$', label_lookup, name='label-lookup'), - url('', include('social_django.urls', namespace='social')), + url(r"^$", index), + url(r"^search$", search), + url(r"^apitoken$", apitoken), + url(r"^item/(?P.*)$", item_display, name="item-display"), + url(r"^autocomplete.json$", ItemSelectView.as_view(), name="item-complete"), + url(r"^autocomplete_prop.json$", PropSelectView.as_view(), name="prop-complete"), + url(r"^(?P[^/]*)$", label_lookup, name="label-lookup"), + url("", include("social_django.urls", namespace="social")), ] diff --git a/storage/views.py b/storage/views.py index b827f77..7d2e13f 100644 --- a/storage/views.py +++ b/storage/views.py @@ -13,29 +13,30 @@ from storage.models import Item, Label from django.contrib.auth.decorators import login_required from rest_framework.authtoken.models import Token + def apply_smart_search(query, objects): general_term = [] filters = {} for prop in shlex.split(query): - if ':' not in prop: + if ":" not in prop: general_term.append(prop) else: - key, value = prop.split(':', 1) - if key in ['owner', 'taken_by']: - filters[key + '__username'] = value + key, value = prop.split(":", 1) + if key in ["owner", "taken_by"]: + filters[key + "__username"] = value elif hasattr(Item, key): - filters[key + '__search'] = value - elif key == 'ancestor': + filters[key + "__search"] = value + elif key == "ancestor": objects = Item.objects.get(pk=value).get_children() - elif key == 'prop' or value: - if key == 'prop': - key, _, value = value.partition(':') + elif key == "prop" or value: + if key == "prop": + key, _, value = value.partition(":") if not value: - filters['props__isnull'] = {key: False} + filters["props__isnull"] = {key: False} else: - filters['props__contains'] = {key: value} + filters["props__contains"] = {key: value} else: # "Whatever:" general_term.append(prop) @@ -44,35 +45,39 @@ def apply_smart_search(query, objects): if not general_term: return objects - general_term = ' '.join(general_term) + general_term = " ".join(general_term) - objects = objects.annotate( - search=SearchVector('name', 'description', 'props', config='simple'), - similarity=TrigramSimilarity('name', general_term) - ).filter( - Q(similarity__gte=0.15) | Q(search__contains=general_term) - ).order_by('-similarity') + objects = ( + objects.annotate( + search=SearchVector("name", "description", "props", config="simple"), + similarity=TrigramSimilarity("name", general_term), + ) + .filter(Q(similarity__gte=0.15) | Q(search__contains=general_term)) + .order_by("-similarity") + ) return objects def index(request): - return render(request, 'results.html', { - 'results': Item.get_roots() - }) + return render(request, "results.html", {"results": Item.get_roots()}) def search(request): - query = request.GET.get('q', '') + query = request.GET.get("q", "") results = apply_smart_search(query, Item.objects).all() - if results and (len(results) == 1 or getattr(results[0], 'similarity', 0) == 1): + if results and (len(results) == 1 or getattr(results[0], "similarity", 0) == 1): return redirect(results[0]) - return render(request, 'results.html', { - 'query': query, - 'results': results, - }) + return render( + request, + "results.html", + { + "query": query, + "results": results, + }, + ) def item_display(request, pk): @@ -83,18 +88,22 @@ def item_display(request, pk): labels = item.labels.all() has_one_label = len(labels) == 1 - return render(request, 'item.html', { - 'title': item.name, - 'item': item, - 'categories': item.categories.all(), - 'props': sorted(item.props.items()), - 'images': item.images.all(), - 'labels': labels, - 'has_one_label': has_one_label, - 'history': LogEntry.objects.filter(object_id=item.pk), - 'ancestors': item.get_ancestors(), - 'children': item.get_children().prefetch_related('categories'), - }) + return render( + request, + "item.html", + { + "title": item.name, + "item": item, + "categories": item.categories.all(), + "props": sorted(item.props.items()), + "images": item.images.all(), + "labels": labels, + "has_one_label": has_one_label, + "history": LogEntry.objects.filter(object_id=item.pk), + "ancestors": item.get_ancestors(), + "children": item.get_children().prefetch_related("categories"), + }, + ) def label_lookup(request, pk): @@ -114,49 +123,56 @@ def label_lookup(request, pk): def apitoken(request): print(Token) token, created = Token.objects.get_or_create(user=request.user) - return HttpResponse(token.key, content_type='text/plain') + return HttpResponse(token.key, content_type="text/plain") class ItemSelectView(AutoResponseView): def get(self, request, *args, **kwargs): self.widget = self.get_widget_or_404() - self.term = kwargs.get('term', request.GET.get('term', '')) + self.term = kwargs.get("term", request.GET.get("term", "")) self.object_list = apply_smart_search(self.term, Item.objects) context = self.get_context_data() - return JsonResponse({ - 'results': [ - { - 'text': obj.name, - 'path': [o.name for o in obj.get_ancestors()], - 'id': obj.pk, - } - for obj in context['object_list'] + return JsonResponse( + { + "results": [ + { + "text": obj.name, + "path": [o.name for o in obj.get_ancestors()], + "id": obj.pk, + } + for obj in context["object_list"] ], - 'more': context['page_obj'].has_next() - }) + "more": context["page_obj"].has_next(), + } + ) class PropSelectView(AutoResponseView): def get(self, request, *args, **kwargs): # self.widget = self.get_widget_or_404() - self.term = kwargs.get('term', request.GET.get('term', '')) + self.term = kwargs.get("term", request.GET.get("term", "")) # context = self.get_context_data() with connection.cursor() as c: - c.execute(""" + c.execute( + """ SELECT key, count(*) FROM (SELECT (each(props)).key FROM storage_item) AS stat WHERE key like %s GROUP BY key ORDER BY count DESC, key limit 10; - """, ['%' + self.term + '%']) + """, + ["%" + self.term + "%"], + ) props = [e[0] for e in c.fetchall()] - return JsonResponse({ - 'results': [ - { - 'text': p, - 'id': p, - } - for p in props + return JsonResponse( + { + "results": [ + { + "text": p, + "id": p, + } + for p in props ], - }) + } + ) diff --git a/storage/widgets.py b/storage/widgets.py index 82c1906..d67d714 100644 --- a/storage/widgets.py +++ b/storage/widgets.py @@ -13,9 +13,8 @@ from django.contrib.admin.widgets import AdminTextareaWidget class ItemSelectWidget(ModelSelect2Widget): - def __init__(self, *args, **kwargs): - kwargs['data_view'] = 'item-complete' + kwargs["data_view"] = "item-complete" super(ItemSelectWidget, self).__init__(*args, **kwargs) def label_from_instance(self, obj): @@ -30,21 +29,24 @@ class PropsSelectWidget(DictionaryFieldWidget): if attrs is None: attrs = {} # it's called "original" because it will be replaced by a copy - attrs['class'] = 'hstore-original-textarea' - w = HeavySelect2Widget(data_view='prop-complete', attrs={'data-tags': 'true', 'class': 'hs-key'}) + attrs["class"] = "hstore-original-textarea" + w = HeavySelect2Widget( + data_view="prop-complete", attrs={"data-tags": "true", "class": "hs-key"} + ) # get default HTML from AdminTextareaWidget html = AdminTextareaWidget.render(self, name, value, attrs) # prepare template context template_context = { - 'field_name': name, - 'STATIC_URL': settings.STATIC_URL, - 'use_svg': parse_version(get_version()) >= parse_version('1.9'), # use svg icons if django >= 1.9 - 'ajax_url': reverse('prop-complete'), - 'w': w.build_attrs(base_attrs=w.attrs) + "field_name": name, + "STATIC_URL": settings.STATIC_URL, + "use_svg": parse_version(get_version()) + >= parse_version("1.9"), # use svg icons if django >= 1.9 + "ajax_url": reverse("prop-complete"), + "w": w.build_attrs(base_attrs=w.attrs), } # get template object - template = get_template('hstore_%s_widget.html' % self.admin_style) + template = get_template("hstore_%s_widget.html" % self.admin_style) # render additional html additional_html = template.render(template_context)