From 02e889ef8c3d69523ce7957b4fe8b4308dee6422 Mon Sep 17 00:00:00 2001 From: "S. Andrew Sheppard" Date: Thu, 8 Sep 2022 01:01:49 -0500 Subject: [PATCH] cleanup - simplify user-specific config JSON to only include permissions (see wq/wq#54) - include related_name in form config for ForeignKeys - detect SlugRelatedField when mapping _id columns - remove wq.db.default_settings - remove wq.db.patterns.identify - remove remaining Mustache template support - remove wq.db.rest.context_processors - remove wq.db.rest.auth.context_processors - remove url compatibility with include() --- .github/workflows/test.yml | 2 +- default_settings.py | 65 ---- patterns/base/serializers.py | 6 - patterns/identify/__init__.py | 3 - patterns/identify/filters.py | 83 ---- patterns/identify/management/__init__.py | 0 .../identify/management/commands/__init__.py | 0 .../management/commands/make_authorities.py | 14 - patterns/identify/migrations/0001_initial.py | 49 --- patterns/identify/migrations/__init__.py | 0 patterns/identify/models.py | 214 ----------- patterns/identify/rest.py | 8 - patterns/identify/serializers.py | 72 ---- patterns/identify/views.py | 17 - patterns/models.py | 8 - patterns/rest.py | 8 - patterns/serializers.py | 3 - patterns/views.py | 6 - rest/auth/context_processors.py | 4 - rest/auth/views.py | 2 +- rest/context_processors.py | 130 ------- rest/management/commands/dump_config.py | 2 +- rest/models.py | 4 +- rest/renderers.py | 2 +- rest/routers.py | 66 +--- rest/serializers.py | 8 +- rest/views.py | 3 +- tests/gis_app/models.py | 4 +- tests/patterns_app/migrations/0001_initial.py | 259 +++++++++---- tests/patterns_app/models.py | 5 +- tests/patterns_app/rest.py | 5 +- tests/patterns_app/views.py | 21 - tests/settings.py | 47 ++- tests/templates/auth_context.html | 5 - tests/templates/child_edit.html | 14 - tests/templates/child_list.html | 10 - tests/templates/choicemodel_edit.html | 11 - tests/templates/item_detail.html | 5 - tests/templates/item_edit.html | 14 - tests/templates/item_list.html | 8 - tests/templates/itemtype_list.html | 5 - tests/templates/login.html | 0 tests/templates/parent_list.html | 7 - tests/templates/partials/csrf.html | 1 - tests/templates/partials/paginator.html | 15 - tests/templates/rest_context.html | 26 -- tests/templates/rootmodel_detail.html | 17 - tests/templates/rootmodel_list.html | 5 - tests/templates/script_context.html | 7 - tests/templates/slugmodel_detail.html | 1 - tests/templates/slugrefchild_edit.html | 14 - tests/templates/slugrefparent_edit.html | 14 - tests/templates/usermanagedmodel_detail.html | 5 - tests/templates/usermanagedmodel_list.html | 8 - tests/test_auth.py | 12 - tests/test_config.py | 9 +- tests/test_custompatterns.py | 1 + tests/test_identify.py | 362 ------------------ tests/test_router.py | 10 +- tests/test_template.py | 224 ----------- tests/urls.py | 25 +- 61 files changed, 269 insertions(+), 1686 deletions(-) delete mode 100644 default_settings.py delete mode 100644 patterns/identify/__init__.py delete mode 100644 patterns/identify/filters.py delete mode 100644 patterns/identify/management/__init__.py delete mode 100644 patterns/identify/management/commands/__init__.py delete mode 100644 patterns/identify/management/commands/make_authorities.py delete mode 100644 patterns/identify/migrations/0001_initial.py delete mode 100644 patterns/identify/migrations/__init__.py delete mode 100644 patterns/identify/models.py delete mode 100644 patterns/identify/rest.py delete mode 100644 patterns/identify/serializers.py delete mode 100644 patterns/identify/views.py delete mode 100644 patterns/rest.py delete mode 100644 patterns/views.py delete mode 100644 rest/auth/context_processors.py delete mode 100644 rest/context_processors.py delete mode 100644 tests/patterns_app/views.py delete mode 100644 tests/templates/auth_context.html delete mode 100644 tests/templates/child_edit.html delete mode 100644 tests/templates/child_list.html delete mode 100644 tests/templates/choicemodel_edit.html delete mode 100644 tests/templates/item_detail.html delete mode 100644 tests/templates/item_edit.html delete mode 100644 tests/templates/item_list.html delete mode 100644 tests/templates/itemtype_list.html delete mode 100644 tests/templates/login.html delete mode 100644 tests/templates/parent_list.html delete mode 100644 tests/templates/partials/csrf.html delete mode 100644 tests/templates/partials/paginator.html delete mode 100644 tests/templates/rest_context.html delete mode 100644 tests/templates/rootmodel_detail.html delete mode 100644 tests/templates/rootmodel_list.html delete mode 100644 tests/templates/script_context.html delete mode 100644 tests/templates/slugmodel_detail.html delete mode 100644 tests/templates/slugrefchild_edit.html delete mode 100644 tests/templates/slugrefparent_edit.html delete mode 100644 tests/templates/usermanagedmodel_detail.html delete mode 100644 tests/templates/usermanagedmodel_list.html delete mode 100644 tests/test_identify.py delete mode 100644 tests/test_template.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 145cc4da..b533ad8b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -47,7 +47,7 @@ jobs: python -m pip install flake8 pytest wheel python -m pip install django==${{ matrix.django-version }} python -m pip install djangorestframework==${{ matrix.drf-version }} - python -m pip install django-mustache + python -m pip install pystache python -m pip install . - name: Lint with flake8 run: | diff --git a/default_settings.py b/default_settings.py deleted file mode 100644 index 34098d4a..00000000 --- a/default_settings.py +++ /dev/null @@ -1,65 +0,0 @@ -# Django settings -context_processors = ( - 'django.template.context_processors.csrf', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - 'wq.db.rest.auth.context_processors.is_authenticated', - 'wq.db.rest.context_processors.version', - 'wq.db.rest.context_processors.router_info', - 'wq.db.rest.context_processors.wq_config', - 'wq.db.rest.context_processors.script_tags', -) - -TEMPLATES = [ - { - 'BACKEND': 'django_mustache.Mustache', - 'DIRS': tuple(), - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': context_processors, - } - }, - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': context_processors, - } - } -] - -# Django Rest Framework settings -REST_FRAMEWORK = { - - 'DEFAULT_RENDERER_CLASSES': ( - 'wq.db.rest.renderers.HTMLRenderer', - 'wq.db.rest.renderers.JSONRenderer', - 'wq.db.rest.renderers.GeoJSONRenderer', - ), - - 'DEFAULT_PAGINATION_CLASS': 'wq.db.rest.pagination.Pagination', - 'PAGE_SIZE': 50, - - 'DEFAULT_PERMISSION_CLASSES': ( - 'wq.db.rest.permissions.ModelPermissions', - ), - - 'DEFAULT_FILTER_BACKENDS': ( - 'wq.db.rest.filters.FilterBackend', - ), - - 'DEFAULT_CONTENT_NEGOTIATION_CLASS': - 'wq.db.rest.negotiation.ContentNegotiation' -} - -# wq.db settings -ANONYMOUS_PERMISSIONS = tuple() -SRID = 4326 - -# Deprecated, do not use -# FIXME: Remove in wq.db 2.0 -SESSION_COOKIE_HTTPONLY = False -SOCIAL_AUTH_PIPELINE = None -DEFAULT_AUTH_GROUP = None -DISAMBIGUATE = None diff --git a/patterns/base/serializers.py b/patterns/base/serializers.py index 21769431..bbee809d 100644 --- a/patterns/base/serializers.py +++ b/patterns/base/serializers.py @@ -1,7 +1,6 @@ from wq.db.rest.serializers import ModelSerializer from rest_framework import serializers from natural_keys import NaturalKeySerializer, NaturalKeyModelSerializer -from django.conf import settings class AttachmentListSerializer(serializers.ListSerializer): @@ -16,11 +15,6 @@ def to_representation(self, data): if self.parent.instance and not self.parent.instance.pk: data = self.default_attachments(initial) - if not getattr(settings, 'WQ_APP_TEMPLATE', None): - # FIXME: remove in 2.0 - for i, row in enumerate(data): - row['@index'] = i - return data def default_attachments(self, initial): diff --git a/patterns/identify/__init__.py b/patterns/identify/__init__.py deleted file mode 100644 index b899ea50..00000000 --- a/patterns/identify/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# identify -# - Allows multiple global identifiers to be applied to arbitrary models -# - Usage: extend IdentifiedModel to get unique identifiers diff --git a/patterns/identify/filters.py b/patterns/identify/filters.py deleted file mode 100644 index bdc305a5..00000000 --- a/patterns/identify/filters.py +++ /dev/null @@ -1,83 +0,0 @@ -from rest_framework.filters import BaseFilterBackend -from .models import Identifier - - -class IdentifierFilterBackend(BaseFilterBackend): - ignore_extra = True - exclude_apps = [] - view_kwarg = 'ids' - - @property - def filter_options(self): - if hasattr(self, '_filter_options'): - return self._filter_options - - slugs = self.view.kwargs[self.view_kwarg].split('/') - id_map, unresolved = Identifier.objects.resolve( - slugs, exclude_apps=self.exclude_apps - ) - options = {} - if unresolved: - options['extra'] = [] - for key, items in unresolved.items(): - items = [ - item for item in items - if self.can_filter(item.content_type.model) - ] - if len(items) > 1: - raise Exception( - "Could not resolve %s to a single item!" % key - ) - elif len(items) == 1: - if id_map is None: - id_map = {} - id_map[key] = items[0] - else: - options['extra'].append(key) - - if id_map: - for slug, ident in id_map.items(): - ctype = ident.content_type.model - if ctype not in options: - options[ctype] = [] - options[ctype].append(ident) - - self._filter_options = options - return options - - def filter_queryset(self, request, queryset, view): - self.request = request - self.view = view - - # Filter by identifiers in url - for name, idents in self.filter_options.items(): - if name == "extra": - continue - ids = [ident.object_id for ident in idents] - fn = self.get_filter_by(name) - if fn: - queryset = fn(queryset, ids) - else: - raise Exception("Don't know how to filter by %s" % name) - - # Filter by any extra slugs in url - if 'extra' in self.filter_options: - fn = getattr(self.view, 'filter_by_extra', self.filter_by_extra) - queryset = fn(queryset, self.filter_options['extra']) - - return queryset - - def get_filter_by(self, name): - fn = getattr(self, 'filter_by_%s' % name, None) - if not fn: - fn = getattr(self.view, 'filter_by_%s' % name, None) - return fn - - def can_filter(self, name): - return self.get_filter_by(name) is not None - - def filter_by_extra(self, queryset, extra): - if self.ignore_extra: - return queryset - else: - raise Exception("Extra URL options found: %s" % extra) diff --git a/patterns/identify/management/__init__.py b/patterns/identify/management/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/patterns/identify/management/commands/__init__.py b/patterns/identify/management/commands/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/patterns/identify/management/commands/make_authorities.py b/patterns/identify/management/commands/make_authorities.py deleted file mode 100644 index 1ebfcf57..00000000 --- a/patterns/identify/management/commands/make_authorities.py +++ /dev/null @@ -1,14 +0,0 @@ -from django.core.management.base import BaseCommand -from wq.db.patterns.identify.models import Authority - - -class Command(BaseCommand): - def handle(self, *args, **options): - Authority.objects.get_or_create( - name="This Site", - ) - Authority.objects.get_or_create( - name="Wikipedia", - homepage="https://wikipedia.org", - object_url="https://wikipedia.org/wiki/%s", - ) diff --git a/patterns/identify/migrations/0001_initial.py b/patterns/identify/migrations/0001_initial.py deleted file mode 100644 index cba109ae..00000000 --- a/patterns/identify/migrations/0001_initial.py +++ /dev/null @@ -1,49 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by Django 1.9.1 on 2016-01-12 03:18 -from __future__ import unicode_literals - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('contenttypes', '0002_remove_content_type_name'), - ] - - operations = [ - migrations.CreateModel( - name='Authority', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('homepage', models.URLField(blank=True, null=True)), - ('object_url', models.URLField(blank=True, null=True)), - ], - options={ - 'abstract': False, - 'verbose_name_plural': 'authorities', - 'db_table': 'wq_identifiertype', - }, - ), - migrations.CreateModel( - name='Identifier', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(db_index=True, max_length=255)), - ('slug', models.SlugField(blank=True)), - ('is_primary', models.BooleanField(default=False)), - ('object_id', models.PositiveIntegerField()), - ('authority', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='identify.Authority')), - ('content_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), - ], - options={ - 'abstract': False, - 'ordering': ['-is_primary', 'authority', 'name'], - 'db_table': 'wq_identifier', - }, - ), - ] diff --git a/patterns/identify/migrations/__init__.py b/patterns/identify/migrations/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/patterns/identify/models.py b/patterns/identify/models.py deleted file mode 100644 index 210773a2..00000000 --- a/patterns/identify/models.py +++ /dev/null @@ -1,214 +0,0 @@ -from django.db import models, connections -from django.contrib.contenttypes.models import ContentType -from django.contrib.contenttypes.fields import ( - GenericForeignKey, GenericRelation -) -from natural_keys.models import NaturalKeyModelManager, NaturalKeyModel -from ..base.models import LabelModel -from django.template.defaultfilters import slugify -from django.conf import settings -INSTALLED = ('wq.db.patterns.identify' in settings.INSTALLED_APPS) - - -WQ_IDENTIFIER_ORDER = getattr( - settings, "WQ_IDENTIFIER_ORDER", - ["-is_primary", "authority", "name"] -) - - -def find_unique_slug(name, queryset): - if not name: - maxobj = queryset.order_by('-pk').first() - name = str(maxobj.pk + 1 if maxobj else 1) - if len(name) > 45: - name = name[:45] - slug = slugify(name) - exists = queryset.filter(slug=slug) - num = '' - while exists.count() > 0: - slug = slugify('%s %s' % (name, num)) - exists = queryset.filter(slug=slug) - if num == '': - num = 1 - else: - num += 1 - return slug - - -class IdentifierManager(models.Manager): - def filter_by_identifier(self, identifier): - return self.filter( - models.Q(slug=identifier) | models.Q(name=identifier) - ).order_by('-is_primary') - - def resolve(self, identifiers, exclude_apps=[]): - resolved = None - unresolved = None - for identifier in identifiers: - ids = self.filter_by_identifier(identifier) - if exclude_apps: - ids = ids.exclude(content_type__app_label__in=exclude_apps) - if connections[self.db].features.can_distinct_on_fields: - items = ids.order_by( - 'content_type', 'object_id' - ).distinct('content_type', 'object_id').count() - else: - distinct_ids = set() - for ident in ids: - distinct_ids.add((ident.content_type_id, ident.object_id)) - items = len(distinct_ids) - if items == 1: - if resolved is None: - resolved = {} - resolved[identifier] = ids[0] - else: - if unresolved is None: - unresolved = {} - unresolved[identifier] = ids - return resolved, unresolved - - # Default implementation of get_or_create doesn't work well with generics - def get_or_create(self, defaults={}, **kwargs): - try: - return self.get(**kwargs), False - except self.model.DoesNotExist: - kwargs.update(defaults) - return self.create(**kwargs), True - - -class Identifier(models.Model): - name = models.CharField(max_length=255, db_index=True) - slug = models.SlugField(blank=True) - authority = models.ForeignKey( - 'Authority', models.CASCADE, blank=True, null=True, - ) - is_primary = models.BooleanField(default=False) - - # Identifer can contain a pointer to any model - content_type = models.ForeignKey(ContentType, models.CASCADE) - object_id = models.PositiveIntegerField() - content_object = GenericForeignKey() - - objects = IdentifierManager() - - @property - def url(self): - if (not self.authority or not self.authority.object_url): - return None - else: - return self.authority.object_url % self.slug - - def save(self, *args, **kwargs): - if not self.slug: - model = self.content_type.model_class() - queryset = model.objects.exclude(pk=self.object_id) - self.slug = find_unique_slug(self.name, queryset) - - if self.is_primary and not self.pk: - exist = self.content_object.primary_identifier - if exist: - # Primary identifier already exists for this object, update - # instead of adding another - self.pk = exist.pk - kwargs.pop('force_insert', None) - kwargs['force_update'] = True - - super(Identifier, self).save(*args, **kwargs) - - if self.is_primary: - obj = self.content_object - if self.name != obj.name or self.slug != obj.slug: - obj.name = self.name - obj.slug = self.slug - obj.save() - - def __str__(self): - return self.name - - class Meta: - db_table = 'wq_identifier' - ordering = WQ_IDENTIFIER_ORDER - abstract = not INSTALLED - - -class IdentifiedModelManager(NaturalKeyModelManager): - def get_by_identifier(self, identifier, auto_create=False): - searches = [ - {'slug': identifier}, - {'name': identifier}, - {'identifiers__slug': identifier}, - {'identifiers__name': identifier}, - ] - - obj = None - for search in searches: - try: - obj = self.get(**search) - break - except (ValueError, self.model.DoesNotExist): - pass - - if obj is None: - if auto_create: - obj = self.create_by_natural_key(identifier) - else: - name = self.model._meta.object_name - raise self.model.DoesNotExist( - '%s "%s" does not exist' % (name, identifier) - ) - - return obj - - def get_by_natural_key(self, identifier): - return self.get_by_identifier(identifier) - - def create_by_natural_key(self, identifier, **kwargs): - defaults = kwargs.get('defaults') or {} - return self.create(name=identifier, **defaults) - - -class IdentifiedModel(NaturalKeyModel, LabelModel): - name = models.CharField( - max_length=255, blank=True, db_index=True - ) - slug = models.CharField(max_length=255, blank=True, unique=True) - - identifiers = GenericRelation(Identifier) - objects = IdentifiedModelManager() - - def save(self, *args, **kwargs): - if not self.slug: - self.slug = find_unique_slug(self.name, type(self).objects) - if not self.name: - self.name = self.slug - super(IdentifiedModel, self).save() - ident, is_new = self.identifiers.get_or_create( - is_primary=True, - defaults={ - 'name': self.name, - 'slug': self.slug, - } - ) - if not is_new and (ident.name != self.name or ident.slug != self.slug): - ident.name = self.name - ident.slug = self.slug - ident.save() - - @property - def primary_identifier(self): - return self.identifiers.filter(is_primary=True).first() - - class Meta: - ordering = ['name'] - abstract = True - - -class Authority(LabelModel): - name = models.CharField(max_length=255) - homepage = models.URLField(null=True, blank=True) - object_url = models.URLField(null=True, blank=True) - - class Meta: - verbose_name_plural = 'authorities' - db_table = 'wq_identifiertype' - abstract = not INSTALLED diff --git a/patterns/identify/rest.py b/patterns/identify/rest.py deleted file mode 100644 index e1d1bc1b..00000000 --- a/patterns/identify/rest.py +++ /dev/null @@ -1,8 +0,0 @@ -from wq.db import rest -from .models import Authority - - -rest.router.register_model( - Authority, - fields="__all__", -) diff --git a/patterns/identify/serializers.py b/patterns/identify/serializers.py deleted file mode 100644 index e98015f8..00000000 --- a/patterns/identify/serializers.py +++ /dev/null @@ -1,72 +0,0 @@ -from rest_framework import serializers -from rest_framework.validators import UniqueTogetherValidator -from wq.db.patterns.base import serializers as base -from .models import Identifier - - -class IdentifierListSerializer(base.TypedAttachmentListSerializer): - def to_internal_value(self, data): - data = super(IdentifierListSerializer, self).to_internal_value(data) - primary = [ - ident for ident in data - if ident and ident.get('is_primary', None) - ] - if not any(primary) and len(data) > 0: - for ident in data: - if ident: - ident['is_primary'] = True - break - return data - - -class IdentifierSerializer(base.TypedAttachmentSerializer): - url = serializers.ReadOnlyField() - - class Meta(base.TypedAttachmentSerializer.Meta): - model = Identifier - list_serializer_class = IdentifierListSerializer - - # patterns-specific - type_field = 'authority_id' - - -class IdentifiedModelValidator(UniqueTogetherValidator): - def enforce_required_fields(self, attrs, serializer=None): - pass - - def filter_queryset(self, attrs, queryset, serializer=None): - for field in self.fields: - attrs.setdefault(field, None) - if getattr(self, 'requires_context', None): - # DRF 3.11+ - return super(IdentifiedModelValidator, self).filter_queryset( - attrs, queryset, serializer - ) - else: - # DRF 3.10 and earlier - return super(IdentifiedModelValidator, self).filter_queryset( - attrs, queryset - ) - - -class IdentifiedModelSerializer(base.AttachedModelSerializer): - name = serializers.CharField(max_length=255) - slug = serializers.SlugField(required=False) - identifiers = IdentifierSerializer(many=True) - - def get_unique_together_validators(self): - return [IdentifiedModelValidator( - queryset=self.Meta.model.objects.all(), - fields=['slug'] - )] - - def save(self, *args, **kwargs): - super(IdentifiedModelSerializer, self).save(*args, **kwargs) - # Fetch instance from DB in case identifier changed slug while saving - self.instance = self.Meta.model.objects.get(pk=self.instance.pk) - return self.instance - - class Meta: - wq_config = { - 'lookup': 'slug', - } diff --git a/patterns/identify/views.py b/patterns/identify/views.py deleted file mode 100644 index a70946ea..00000000 --- a/patterns/identify/views.py +++ /dev/null @@ -1,17 +0,0 @@ -from django.http import Http404 -from wq.db.rest.views import ModelViewSet - - -class IdentifiedModelViewSet(ModelViewSet): - def get_object(self): - try: - obj = super(ModelViewSet, self).get_object() - except Http404 as notfound: - # Allow retrieval via non-primary identifiers - slug = self.kwargs.get(self.lookup_url_kwarg or self.lookup_field) - try: - obj = self.model.objects.get_by_identifier(slug) - except self.model.DoesNotExist: - raise notfound - # TODO: automatically redirect to primary identifier? - return obj diff --git a/patterns/models.py b/patterns/models.py index 432eb485..e098dc9c 100644 --- a/patterns/models.py +++ b/patterns/models.py @@ -1,15 +1,7 @@ from .base.models import ( LabelModel, ) -from .identify.models import ( - IdentifiedModel, - Authority, - Identifier, -) __all__ = ( 'LabelModel', - 'IdentifiedModel', - 'Authority', - 'Identifier', ) diff --git a/patterns/rest.py b/patterns/rest.py deleted file mode 100644 index f545727f..00000000 --- a/patterns/rest.py +++ /dev/null @@ -1,8 +0,0 @@ -from .serializers import ( - IdentifiedModelSerializer, -) - - -__all__ = ( - 'IdentifiedModelSerializer', -) diff --git a/patterns/serializers.py b/patterns/serializers.py index 88d0fa33..b4896e93 100644 --- a/patterns/serializers.py +++ b/patterns/serializers.py @@ -6,8 +6,6 @@ NaturalKeyAttachedModelSerializer, ) -from .identify.serializers import IdentifiedModelSerializer - __all__ = ( 'AttachmentSerializer', @@ -15,5 +13,4 @@ 'AttachedModelSerializer', 'NaturalKeyModelSerializer', 'NaturalKeyAttachedModelSerializer', - 'IdentifiedModelSerializer', ) diff --git a/patterns/views.py b/patterns/views.py deleted file mode 100644 index 53dcb794..00000000 --- a/patterns/views.py +++ /dev/null @@ -1,6 +0,0 @@ -from .identify.views import IdentifiedModelViewSet - - -__all__ = ( - 'IdentifiedModelViewSet', -) diff --git a/rest/auth/context_processors.py b/rest/auth/context_processors.py deleted file mode 100644 index 18f500d8..00000000 --- a/rest/auth/context_processors.py +++ /dev/null @@ -1,4 +0,0 @@ -def is_authenticated(request): - return { - 'is_authenticated': bool(request.user.is_authenticated) - } diff --git a/rest/auth/views.py b/rest/auth/views.py index 1c2498d8..c6618a32 100644 --- a/rest/auth/views.py +++ b/rest/auth/views.py @@ -14,7 +14,7 @@ def user_info(self, request): user_dict['id'] = get_object_id(request.user) return Response({ 'user': user_dict, - 'config': rest.router.get_config(request.user), + 'config': rest.router.get_user_config(request.user), 'csrftoken': csrf.get_token(request), }) diff --git a/rest/context_processors.py b/rest/context_processors.py deleted file mode 100644 index 6f85e696..00000000 --- a/rest/context_processors.py +++ /dev/null @@ -1,130 +0,0 @@ -from . import router -from django.utils.safestring import mark_safe -from django.urls import reverse -from django.conf import settings -from urllib.parse import quote -from html.parser import HTMLParser - - -def version(request): - return {'version': router.version} - - -def get_base_url(): - base_url = reverse('wq:config-list').replace('/config/', '') - if base_url != router.get_base_url(): - # FIXME: raise ImproperlyConfigured in 2.0? - pass - return base_url - - -def get_wq_path(request): - base_url = get_base_url() - if base_url and not request.path.startswith(base_url): - return None - path = request.path.replace(base_url + "/", "") - return path - - -def router_info(request): - base_url = get_base_url() - full_path = request.path - path = get_wq_path(request) - if not path: - return {} - if request.GET: - path += "?" + request.GET.urlencode() - - info = { - 'base_url': base_url, - 'path': path, - 'path_enc': quote(path), - 'params': request.GET, - 'full_path': full_path, - 'full_path_enc': quote(full_path), - 'prev_path': '', # Referer? - } - - return { - 'rt': base_url, - 'svc': base_url, - 'router_info': info, - 'pages_info': info, # FIXME: Remove in 2.0 - } - - -# FIXME: Remove in 2.0 -def pages_info(request): - return router_info(request) - - -def wq_config(request): - path = get_wq_path(request) - if not path: - return {} - parts = path.split('/') - user = request.user if request.user.is_authenticated else None - wq_conf = router.get_config(user=user) - page_conf = None - root_conf = None - - for name, conf in wq_conf['pages'].items(): - if parts[0] == conf['url']: - page_conf = conf - elif conf['url'] == "": - root_conf = conf - - if not page_conf and root_conf and len(parts) == 1: - page_conf = root_conf - - return { - 'wq_config': wq_conf, - 'page_config': page_conf, - } - - -_script_tags = None - - -def script_tags(request): - global _script_tags - if _script_tags is None: - _script_tags = parse_script_tags() - return {'script_tags': mark_safe(_script_tags)} - - -def parse_script_tags(): - filename = getattr(settings, 'WQ_SCRIPT_FILE', None) - if not filename: - return '' - - parser = ScriptTagParser() - with open(filename) as f: - parser.feed(f.read()) - - return '\n'.join(parser.output) - - -class ScriptTagParser(HTMLParser): - in_script = None - output = [] - - def handle_starttag(self, tag, attrs): - if tag == 'script': - if attrs: - attrs = dict(attrs) - self.output.append( - "') - self.in_script = False diff --git a/rest/management/commands/dump_config.py b/rest/management/commands/dump_config.py index c6ef3896..37c9e5df 100644 --- a/rest/management/commands/dump_config.py +++ b/rest/management/commands/dump_config.py @@ -25,7 +25,7 @@ def handle(self, **options): def dump_config(f, format='json', **kwargs): text = json.dumps( - rest.router.get_config(), + rest.router.config, cls=encoders.JSONEncoder, indent=4, ) diff --git a/rest/models.py b/rest/models.py index 4173698f..cfa3469b 100644 --- a/rest/models.py +++ b/rest/models.py @@ -74,10 +74,10 @@ def get_children(self, include_rels=False): else: return set(child[0] for child in children) - def get_config(self, user=None): + def get_config(self): from . import router # avoid circular import cls = self.model_class() - return router.get_model_config(cls, user) + return router.get_model_config(cls) def is_registered(self): from . import router # avoid circular import diff --git a/rest/renderers.py b/rest/renderers.py index c1e16886..7ed31054 100644 --- a/rest/renderers.py +++ b/rest/renderers.py @@ -1,10 +1,10 @@ from rest_framework.renderers import TemplateHTMLRenderer, JSONRenderer from django.conf import settings -from wq.db.default_settings import SRID import re APP_TEMPLATES = {} +SRID = 4326 def load_app_template(template_name): diff --git a/rest/routers.py b/rest/routers.py index 65ebea06..c9b02761 100644 --- a/rest/routers.py +++ b/rest/routers.py @@ -15,8 +15,6 @@ from .renderers import JSONRenderer, ESMRenderer from .exceptions import ImproperlyConfigured -import inspect - class ModelRouter(DefaultRouter): _models = set() @@ -160,11 +158,6 @@ def get_class(self, classes, model_class, default=lambda model: None): if real_model in classes: return classes[real_model] else: - if real_model not in self._config: - # FIXME: Probably can just use default instead of erroring - raise ImproperlyConfigured( - "%s is not registered!" % real_model - ) return default(real_model) def get_serializer_for_model(self, model_class, serializer_depth=None): @@ -349,27 +342,27 @@ def base_config(self): self._base_config.update(self._extra_config) return self._base_config - def get_config(self, user=None): - if user is None or not user.is_authenticated: - return self.base_config + @property + def config(self): + return self.base_config + def get_user_config(self, user): # Add user-specific permissions to configuration config = { - key: val.copy() if hasattr(val, 'copy') else val - for key, val in self.base_config.items() + "pages": {} } - for page, info in config['pages'].items(): + for page, info in self.config['pages'].items(): if not info.get('list', False): continue try: ct = get_ct(self._page_models[page]) except (RuntimeError, DatabaseError): continue - info = info.copy() + perms = {} for perm in ('add', 'change', 'delete'): if has_perm(user, ct, perm): - info['can_' + perm] = True - config['pages'][page] = info + perms['can_' + perm] = True + config['pages'][page] = perms return config @@ -391,18 +384,16 @@ def list(self, request, *args, **kwargs): def get_page(self, page): return self._extra_pages[page] - def get_page_config(self, name, user=None): - config = self.get_config(user) - return config['pages'].get(name, None) + def get_page_config(self, name): + return self.config['pages'].get(name, None) - def get_model_config(self, model, user=None): + def get_model_config(self, model): if model in self._page_models: model = self._page_models[model] # First, check models registered with API if model in self._page_names: - config = self.get_config(user) - return config['pages'][self._page_names[model]] + return self.config['pages'][self._page_names[model]] # Then check config cache directly (in case model was configured but # not fully registered as part of API) @@ -418,30 +409,14 @@ class ConfigView(SimpleViewSet): renderer_classes = [JSONRenderer, ESMRenderer] def list(this, request, *args, **kwargs): - if request.accepted_renderer.format == 'js': - config = self.base_config - else: - config = self.get_config(request.user) - return Response(config) + return Response(self.config) return ConfigView - def get_index(self, user): - config = self.get_config(user) - - def page_sort(page): - is_list = page.get('list', False) - return (not is_list, page['name']) - - pages = sorted(config['pages'].values(), key=page_sort) - return { - 'pages': pages - } - def get_index_view(self): class IndexView(SimpleViewSet): def list(this, request, *args, **kwargs): - return Response(self.get_index(request.user)) + return Response({}) return IndexView def get_multi_view(self): @@ -452,11 +427,10 @@ def list(this, request, *args, **kwargs): return MultipleListView def get_multi(self, request, urls): - conf = self.get_config(request.user) conf_by_url = { conf['url']: (page, conf) for page, conf - in self.get_config(request.user)['pages'].items() + in self.config['pages'].items() } result = {} for listurl in urls: @@ -603,14 +577,6 @@ def version(self): @property def urls(self): urls = super(ModelRouter, self).urls - try: - # FIXME: Remove in 2.0 - caller = inspect.stack()[1] - code_context = getattr(caller, 'code_context', caller[4]) - if 'include' in code_context[0]: - return urls, 'wq' - except Exception: - pass return urls, 'wq', 'wq' diff --git a/rest/serializers.py b/rest/serializers.py index 0313bbef..418632de 100644 --- a/rest/serializers.py +++ b/rest/serializers.py @@ -15,7 +15,6 @@ from rest_framework.utils import model_meta from html_json_forms.serializers import parse_json_form, JSONFormSerializer -from .model_tools import get_object_id from .exceptions import ImproperlyConfigured @@ -295,7 +294,12 @@ def get_wq_field_info(self, name, field, model=None): if hasattr(field, 'queryset'): fk = self.get_wq_foreignkey_info(field.queryset.model) if fk: + model = model or self.Meta.model + source = model._meta.get_field(name) info['wq:ForeignKey'] = fk + info[ + 'wq:related_name' + ] = source.remote_field.get_accessor_name() elif isinstance(field, serializers.ManyRelatedField): # ManyToMany field to related model @@ -510,7 +514,7 @@ def update_id_fields(self, fields): auto_related_field = ( serializers.BaseSerializer, serializers.ManyRelatedField, - LookupRelatedField, + serializers.SlugRelatedField, ) if not isinstance(default_field, auto_related_field): continue diff --git a/rest/views.py b/rest/views.py index 280d07e2..4ebdb992 100644 --- a/rest/views.py +++ b/rest/views.py @@ -3,7 +3,6 @@ from rest_framework.decorators import action from rest_framework import status, viewsets from .model_tools import get_ct, get_object_id, get_by_identifier -from django.core.exceptions import FieldDoesNotExist from django.db.models import ProtectedError @@ -147,7 +146,7 @@ def update(self, request, *args, **kwargs): def postsave(self, request, response): ct = get_ct(self.model) - conf = ct.get_config(request.user) + conf = ct.get_config() # Redirect to new page postsave = conf.get('postsave', ct.identifier + '_detail') diff --git a/tests/gis_app/models.py b/tests/gis_app/models.py index d326a40f..46fc2836 100644 --- a/tests/gis_app/models.py +++ b/tests/gis_app/models.py @@ -7,11 +7,11 @@ class GeometryModel(LabelModel): name = models.CharField(max_length=255) - geometry = models.GeometryField(srid=settings.SRID) + geometry = models.GeometryField(srid=4326) class PointModel(LabelModel): name = models.CharField(max_length=255) - geometry = models.PointField(srid=settings.SRID) + geometry = models.PointField(srid=4326) else: GeometryModel = None diff --git a/tests/patterns_app/migrations/0001_initial.py b/tests/patterns_app/migrations/0001_initial.py index e5ac0854..36a28d88 100644 --- a/tests/patterns_app/migrations/0001_initial.py +++ b/tests/patterns_app/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.1.7 on 2019-03-21 07:55 +# Generated by Django 4.1 on 2022-09-08 02:59 from django.db import migrations, models import django.db.models.deletion @@ -8,124 +8,241 @@ class Migration(migrations.Migration): initial = True - dependencies = [ - ] + dependencies = [] operations = [ migrations.CreateModel( - name='Attribute', + name="Attribute", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=10)), - ('is_active', models.BooleanField()), - ('category', models.CharField(blank=True, max_length=10)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=10)), + ("is_active", models.BooleanField()), + ("category", models.CharField(blank=True, max_length=10)), ], ), migrations.CreateModel( - name='Campaign', + name="Campaign", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), ], ), migrations.CreateModel( - name='CustomAttachment', + name="CustomPatternModel", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=10)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=10)), ], ), migrations.CreateModel( - name='CustomPatternModel', + name="CustomType", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=10)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=10)), ], ), migrations.CreateModel( - name='CustomType', + name="CustomTypedPatternModel", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=10)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=10)), ], ), migrations.CreateModel( - name='CustomTypedAttachment', + name="Entity", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(blank=True, max_length=10, null=True)), - ('value', models.FloatField(blank=True, null=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "campaign", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="patterns_app.campaign", + ), + ), ], + options={ + "verbose_name_plural": "entities", + }, ), migrations.CreateModel( - name='CustomTypedPatternModel', + name="IdentifiedModel", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=10)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("slug", models.SlugField()), + ("name", models.CharField(max_length=255)), ], + options={ + "abstract": False, + }, ), migrations.CreateModel( - name='Entity', + name="Value", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('campaign', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='patterns_app.Campaign')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "attribute", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="patterns_app.attribute", + ), + ), + ( + "entity", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="values", + to="patterns_app.entity", + ), + ), ], - options={ - 'verbose_name_plural': 'entities', - }, ), migrations.CreateModel( - name='FilterableModel', + name="FilterableModel", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=10)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=10)), + ( + "parent", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="patterns_app.identifiedmodel", + ), + ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.CreateModel( - name='IdentifiedModel', + name="CustomTypedAttachment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(blank=True, db_index=True, max_length=255)), - ('slug', models.CharField(blank=True, max_length=255, unique=True)), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(blank=True, max_length=10, null=True)), + ("value", models.FloatField(blank=True, null=True)), + ( + "parent", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="attachments", + to="patterns_app.customtypedpatternmodel", + ), + ), + ( + "type", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="patterns_app.customtype", + ), + ), ], - options={ - 'abstract': False, - 'ordering': ['name'], - }, ), migrations.CreateModel( - name='Value', + name="CustomAttachment", fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('attribute', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='patterns_app.Attribute')), - ('entity', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='values', to='patterns_app.Entity')), + ( + "id", + models.AutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=10)), + ( + "parent", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="attachments", + to="patterns_app.custompatternmodel", + ), + ), ], ), migrations.AddField( - model_name='filterablemodel', - name='parent', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='patterns_app.IdentifiedModel'), - ), - migrations.AddField( - model_name='customtypedattachment', - name='parent', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='patterns_app.CustomTypedPatternModel'), - ), - migrations.AddField( - model_name='customtypedattachment', - name='type', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='patterns_app.CustomType'), - ), - migrations.AddField( - model_name='customattachment', - name='parent', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='attachments', to='patterns_app.CustomPatternModel'), - ), - migrations.AddField( - model_name='attribute', - name='campaign', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='patterns_app.Campaign'), + model_name="attribute", + name="campaign", + field=models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, to="patterns_app.campaign" + ), ), ] diff --git a/tests/patterns_app/models.py b/tests/patterns_app/models.py index 0ed3b11b..f021d851 100644 --- a/tests/patterns_app/models.py +++ b/tests/patterns_app/models.py @@ -2,8 +2,9 @@ from wq.db.patterns import models as patterns -class IdentifiedModel(patterns.IdentifiedModel): - pass +class IdentifiedModel(patterns.LabelModel): + slug = models.SlugField() + name = models.CharField(max_length=255) class FilterableModel(patterns.LabelModel): diff --git a/tests/patterns_app/rest.py b/tests/patterns_app/rest.py index 06dce1aa..7e945e9a 100644 --- a/tests/patterns_app/rest.py +++ b/tests/patterns_app/rest.py @@ -1,6 +1,4 @@ from wq.db import rest -from wq.db.patterns import rest as patterns -from wq.db.patterns.identify.views import IdentifiedModelViewSet from .models import ( IdentifiedModel, CustomPatternModel, CustomTypedPatternModel, CustomType, @@ -11,9 +9,8 @@ ) rest.router.register_model( IdentifiedModel, - serializer=patterns.IdentifiedModelSerializer, + lookup="slug", fields='__all__', - viewset=IdentifiedModelViewSet, ) rest.router.register_model( CustomPatternModel, diff --git a/tests/patterns_app/views.py b/tests/patterns_app/views.py deleted file mode 100644 index e0e11f7f..00000000 --- a/tests/patterns_app/views.py +++ /dev/null @@ -1,21 +0,0 @@ -from rest_framework.generics import ListAPIView -from wq.db.patterns.identify.filters import IdentifierFilterBackend -from .models import FilterableModel -from wq.db.rest.serializers import ModelSerializer - - -class FilterableView(ListAPIView): - serializer_class = ModelSerializer.for_model( - FilterableModel, - include_fields='__all__', - ) - filter_backends = [IdentifierFilterBackend] - queryset = FilterableModel.objects.all() - - router = None - action = None - - def filter_by_identifiedmodel(self, queryset, ids): - return queryset.filter( - parent_id__in=ids, - ) diff --git a/tests/settings.py b/tests/settings.py index dec3c0a8..10dfdb8a 100644 --- a/tests/settings.py +++ b/tests/settings.py @@ -1,10 +1,42 @@ import os -from wq.db.default_settings import ( # NOQA - TEMPLATES, - REST_FRAMEWORK, - SRID, -) + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': ( + 'django.template.context_processors.csrf', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + )} + } +] + +REST_FRAMEWORK = { + + 'DEFAULT_RENDERER_CLASSES': ( + 'wq.db.rest.renderers.HTMLRenderer', + 'wq.db.rest.renderers.JSONRenderer', + 'wq.db.rest.renderers.GeoJSONRenderer', + ), + + 'DEFAULT_PAGINATION_CLASS': 'wq.db.rest.pagination.Pagination', + 'PAGE_SIZE': 50, + + 'DEFAULT_PERMISSION_CLASSES': ( + 'wq.db.rest.permissions.ModelPermissions', + ), + + 'DEFAULT_FILTER_BACKENDS': ( + 'wq.db.rest.filters.FilterBackend', + ), + + 'DEFAULT_CONTENT_NEGOTIATION_CLASS': + 'wq.db.rest.negotiation.ContentNegotiation' +} SECRET_KEY = '1234' @@ -17,7 +49,6 @@ 'django.contrib.auth', 'wq.db.rest', 'wq.db.rest.auth', - 'wq.db.patterns.identify', 'tests.rest_app', 'tests.conflict_app', 'tests.patterns_app', @@ -73,8 +104,6 @@ BASE_DIR = os.path.dirname(__file__) MEDIA_ROOT = os.path.join(BASE_DIR, "media") VERSION_TXT = os.path.join(BASE_DIR, "version.txt") -WQ_SCRIPT_FILE = os.path.join(BASE_DIR, "index.html") - -TEMPLATES[0]['DIRS'] += (os.path.join(BASE_DIR, "templates"),) +WQ_APP_TEMPLATE = os.path.join(BASE_DIR, "index.html") DEBUG = True diff --git a/tests/templates/auth_context.html b/tests/templates/auth_context.html deleted file mode 100644 index c539b9c1..00000000 --- a/tests/templates/auth_context.html +++ /dev/null @@ -1,5 +0,0 @@ -
- {{#is_authenticated}} -

{{#user}}{{username}}{{/user}}

- {{/is_authenticated}} -
diff --git a/tests/templates/child_edit.html b/tests/templates/child_edit.html deleted file mode 100644 index f0f561de..00000000 --- a/tests/templates/child_edit.html +++ /dev/null @@ -1,14 +0,0 @@ -
- - - -
diff --git a/tests/templates/child_list.html b/tests/templates/child_list.html deleted file mode 100644 index 9ad8c2a1..00000000 --- a/tests/templates/child_list.html +++ /dev/null @@ -1,10 +0,0 @@ -

{{count}} Records

-{{>paginator}} -{{#parent_id}} -

Childs for {{parent_label}}

-{{/parent_id}} - diff --git a/tests/templates/choicemodel_edit.html b/tests/templates/choicemodel_edit.html deleted file mode 100644 index e5765cab..00000000 --- a/tests/templates/choicemodel_edit.html +++ /dev/null @@ -1,11 +0,0 @@ -
- -
- Choice - {{#choice_choices}} - - - {{/choice_choices}} -
- -
diff --git a/tests/templates/item_detail.html b/tests/templates/item_detail.html deleted file mode 100644 index 977540d2..00000000 --- a/tests/templates/item_detail.html +++ /dev/null @@ -1,5 +0,0 @@ -

{{label}}

-{{type_label}} -{{#page_config.can_change}} -Edit -{{/page_config.can_change}} diff --git a/tests/templates/item_edit.html b/tests/templates/item_edit.html deleted file mode 100644 index 3cf4fc83..00000000 --- a/tests/templates/item_edit.html +++ /dev/null @@ -1,14 +0,0 @@ -
- - - -
diff --git a/tests/templates/item_list.html b/tests/templates/item_list.html deleted file mode 100644 index 43e97e9a..00000000 --- a/tests/templates/item_list.html +++ /dev/null @@ -1,8 +0,0 @@ -{{#parent_id}} -

{{parent_label}} Items

-{{/parent_id}} - diff --git a/tests/templates/itemtype_list.html b/tests/templates/itemtype_list.html deleted file mode 100644 index 0cc204c2..00000000 --- a/tests/templates/itemtype_list.html +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/tests/templates/login.html b/tests/templates/login.html deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/templates/parent_list.html b/tests/templates/parent_list.html deleted file mode 100644 index 4e951c14..00000000 --- a/tests/templates/parent_list.html +++ /dev/null @@ -1,7 +0,0 @@ -

{{count}} Records

-{{>paginator}} - diff --git a/tests/templates/partials/csrf.html b/tests/templates/partials/csrf.html deleted file mode 100644 index 978afba8..00000000 --- a/tests/templates/partials/csrf.html +++ /dev/null @@ -1 +0,0 @@ - diff --git a/tests/templates/partials/paginator.html b/tests/templates/partials/paginator.html deleted file mode 100644 index 56099e89..00000000 --- a/tests/templates/partials/paginator.html +++ /dev/null @@ -1,15 +0,0 @@ -{{#multiple}} -
- {{#previous}} - - Prev {{per_page}} - - {{/previous}} -

Page {{page}} of {{pages}}

- {{#next}} - - Next {{per_page}} - - {{/next}} -
-{{/multiple}} diff --git a/tests/templates/rest_context.html b/tests/templates/rest_context.html deleted file mode 100644 index d848cbc4..00000000 --- a/tests/templates/rest_context.html +++ /dev/null @@ -1,26 +0,0 @@ -

- {{#pages_info}}{{full_path}}{{/pages_info}} -

-

- {{#pages_info}}{{path}}{{/pages_info}} -

-

- {{rt}}/ -

-

- {{svc}}/ -

-

- {{version}} -

-

- {{>csrf}} -

-

- {{#page_config}}{{url}}{{/page_config}} -

-

- {{#wq_config.pages.item.can_change}} - Can Edit Items - {{/wq_config.pages.item.can_change}} -

diff --git a/tests/templates/rootmodel_detail.html b/tests/templates/rootmodel_detail.html deleted file mode 100644 index 3446a4ce..00000000 --- a/tests/templates/rootmodel_detail.html +++ /dev/null @@ -1,17 +0,0 @@ -

{{label}}

-

{{description}}

-

OneToOneModel

-{{#onetoonemodel}} -

{{label}}

-{{/onetoonemodel}} -

ExtraModels

- -

-{{#page_config.can_change}} - Edit -{{/page_config.can_change}} -

diff --git a/tests/templates/rootmodel_list.html b/tests/templates/rootmodel_list.html deleted file mode 100644 index 3b7cda13..00000000 --- a/tests/templates/rootmodel_list.html +++ /dev/null @@ -1,5 +0,0 @@ - diff --git a/tests/templates/script_context.html b/tests/templates/script_context.html deleted file mode 100644 index 0d2d16b0..00000000 --- a/tests/templates/script_context.html +++ /dev/null @@ -1,7 +0,0 @@ - - - {{{ script_tags }}} - - - - diff --git a/tests/templates/slugmodel_detail.html b/tests/templates/slugmodel_detail.html deleted file mode 100644 index 270c9b77..00000000 --- a/tests/templates/slugmodel_detail.html +++ /dev/null @@ -1 +0,0 @@ -

{{label}}

diff --git a/tests/templates/slugrefchild_edit.html b/tests/templates/slugrefchild_edit.html deleted file mode 100644 index 8c6f7ed5..00000000 --- a/tests/templates/slugrefchild_edit.html +++ /dev/null @@ -1,14 +0,0 @@ -
- - - -
diff --git a/tests/templates/slugrefparent_edit.html b/tests/templates/slugrefparent_edit.html deleted file mode 100644 index 9d43dc69..00000000 --- a/tests/templates/slugrefparent_edit.html +++ /dev/null @@ -1,14 +0,0 @@ -
- - - -
diff --git a/tests/templates/usermanagedmodel_detail.html b/tests/templates/usermanagedmodel_detail.html deleted file mode 100644 index 30543e00..00000000 --- a/tests/templates/usermanagedmodel_detail.html +++ /dev/null @@ -1,5 +0,0 @@ -

Object #{{id}}

-{{#user}} -

Created by {{user_label}}

-

{{password}}

-{{/user}} diff --git a/tests/templates/usermanagedmodel_list.html b/tests/templates/usermanagedmodel_list.html deleted file mode 100644 index f1cd06e2..00000000 --- a/tests/templates/usermanagedmodel_list.html +++ /dev/null @@ -1,8 +0,0 @@ -{{#parent_id}} -

{{parent_label}}'s Items

-{{/parent_id}} - diff --git a/tests/test_auth.py b/tests/test_auth.py index 14405c87..decd6c48 100644 --- a/tests/test_auth.py +++ b/tests/test_auth.py @@ -18,15 +18,3 @@ def test_auth_login_info(self): response = self.client.get('/login.json') result = json.loads(response.content.decode('utf-8')) self.assertTrue("user" in result) - - def test_auth_context_processors(self): - response = self.client.get('/auth_context') - result = response.content.decode('utf-8') - self.assertHTMLEqual( - result, - """ -
-

testuser

-
- """ - ) diff --git a/tests/test_config.py b/tests/test_config.py index d579d1db..7284383a 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -29,13 +29,6 @@ def test_rest_config_json(self): # Extra config self.assertIn("debug", result) - def test_rest_index_json(self): - from wq.db.rest import router - result = router.get_index(None) - self.assertIn("pages", result) - self.assertIn("list", result["pages"][0]) - self.assertNotIn("list", result["pages"][-1]) - def test_rest_config_meta(self): conf = self.get_config('item') self.assertEqual({ @@ -168,6 +161,7 @@ def test_rest_config_json_rels(self): 'label': 'Parent', 'type': 'select one', 'wq:ForeignKey': 'parent', + 'wq:related_name': 'children', 'bind': {'required': True}, }, self.get_field(cconf, 'parent')) @@ -178,6 +172,7 @@ def test_rest_config_json_override(self): 'label': 'Type', 'type': 'select one', 'wq:ForeignKey': 'itemtype', + 'wq:related_name': 'item_set', 'filter': {'active': ['1', '{{#id}}0{{/id}}{{^id}}1{{/id}}']}, 'bind': {'required': True}, }, self.get_field(iconf, 'type')) diff --git a/tests/test_custompatterns.py b/tests/test_custompatterns.py index eb891108..78d2cbc6 100644 --- a/tests/test_custompatterns.py +++ b/tests/test_custompatterns.py @@ -82,6 +82,7 @@ def test_customtypedpattern_config(self): 'label': 'Type', 'type': 'select one', 'wq:ForeignKey': 'customtype', + 'wq:related_name': 'customtypedattachment_set', 'bind': {'required': True}, }], 'initial': { diff --git a/tests/test_identify.py b/tests/test_identify.py deleted file mode 100644 index 1a078682..00000000 --- a/tests/test_identify.py +++ /dev/null @@ -1,362 +0,0 @@ -from .base import APITestCase -from rest_framework import status -from django.contrib.auth.models import User -from tests.patterns_app.models import IdentifiedModel -from wq.db.patterns.models import Authority - - -def ident_by_auth(idents): - return { - ident['authority_id']: ident for ident in idents - } - - -class IdentifyTestCase(APITestCase): - def setUp(self): - self.auth = Authority.objects.create( - name="Example", - homepage="http://example.com/", - object_url="http://example.com/pages/%s", - ) - - def test_identify_find(self): - instance = IdentifiedModel.objects.find("Test 1") - self.assertEqual(instance.name, "Test 1") - self.assertEqual(instance.slug, "test-1") - self.assertEqual(instance.primary_identifier.name, "Test 1") - self.assertEqual(instance.primary_identifier.slug, "test-1") - - instance2 = IdentifiedModel.objects.find("Test 1") - self.assertEqual(instance, instance2) - - def test_identify_auth_url(self): - instance = IdentifiedModel.objects.find("Test 2") - instance.identifiers.create( - authority=self.auth, - name="Test 2", - slug="test2" - ) - - ident = instance.primary_identifier - self.assertIsNone(ident.url) - - ident = instance.identifiers.get(authority=self.auth) - self.assertEqual(ident.url, "http://example.com/pages/test2") - - def test_identify_order(self): - auth2 = Authority.objects.create( - name="Example.org", - homepage="http://example.org/", - object_url="http://example.org/content.php?id=%s", - ) - instance = IdentifiedModel.objects.find("Test 3") - instance.identifiers.create( - authority=auth2, - name="Test 3", - slug="123", - ) - instance.identifiers.create( - authority=self.auth, - name="Test 3", - slug="test3" - ) - idents = list(instance.identifiers.all()) - self.assertIsNone(idents[0].authority) - self.assertTrue(idents[0].is_primary) - self.assertEqual(idents[1].authority, self.auth) - self.assertEqual(idents[2].authority, auth2) - - def test_identify_update(self): - """ - Updating identifier slug should update object and vice-versa - """ - instance = IdentifiedModel.objects.create(name="Test 4") - ident = instance.primary_identifier - self.assertIsNotNone(ident) - self.assertEqual(ident.slug, 'test-4') - ident.slug = 'test-4-update' - ident.name = 'Test 4 Update' - ident.save() - instance = IdentifiedModel.objects.get(pk=instance.pk) - self.assertEqual(instance.slug, 'test-4-update') - self.assertEqual(instance.name, 'Test 4 Update') - instance.slug = "test-4-update-2" - instance.name = "Test 4 Update 2" - instance.save() - self.assertEqual(instance.primary_identifier.slug, 'test-4-update-2') - self.assertEqual(instance.primary_identifier.name, 'Test 4 Update 2') - - def test_identify_autocreate(self): - """ - Deleting identifier and re-saving should create a new one. - """ - instance = IdentifiedModel.objects.create( - name="Test 1", - ) - - instance.identifiers.all().delete() - self.assertIsNone(instance.primary_identifier) - instance.save() - self.assertIsNotNone(instance.primary_identifier) - - self.assertEqual(instance.primary_identifier.slug, "test-1") - - -class IdentifyRestTestCase(APITestCase): - def setUp(self): - self.user = User.objects.create(username='testuser', is_superuser=True) - self.auth = Authority.objects.create( - name="Example", - homepage="http://example.com/", - object_url="http://example.com/pages/%s", - ) - self.instance = IdentifiedModel.objects.find("Test 1") - self.instance.identifiers.create( - authority=self.auth, - name="Test 1", - slug="test1" - ) - self.client.force_authenticate(user=self.user) - - def test_identify_config(self): - response = self.client.get('/config.json') - self.maxDiff = None - self.assertEqual([ - { - 'name': 'name', - 'label': 'Name', - 'type': 'string', - 'wq:length': 255, - 'bind': {'required': True}, - }, { - 'name': 'slug', - 'label': 'Slug', - 'type': 'string', - }, { - 'name': 'identifiers', - 'label': 'Identifiers', - 'type': 'repeat', - 'bind': {'required': True}, - 'children': [{ - 'name': 'name', - 'label': 'Name', - 'type': 'string', - 'bind': {'required': True}, - 'wq:length': 255, - }, { - 'name': 'slug', - 'label': 'Slug', - 'type': 'string', - 'wq:length': 50, - }, { - 'name': 'authority', - 'label': 'Authority', - 'type': 'select one', - 'wq:ForeignKey': 'authority', - }, { - 'name': 'is_primary', - 'label': 'Is Primary', - 'type': 'select one', - 'choices': [ - {'name': True, 'label': 'Yes'}, - {'name': False, 'label': 'No'}, - ], - }], - 'initial': { - 'type_field': 'authority', - 'filter': {}, - } - }, - ], response.data['pages']['identifiedmodel']['form']) - - def test_identify_detail_nested_identifiers(self): - response = self.client.get('/identifiedmodels/test-1.json') - self.assertIn('identifiers', response.data) - self.assertEqual(response.data['identifiers'][0]['slug'], 'test-1') - - def test_identify_list_nested_identifiers(self): - response = self.client.get('/identifiedmodels.json') - self.assertIn('identifiers', response.data['list'][0]) - - def test_identify_post(self): - form = { - 'name': 'Test 2', - - 'identifiers[0][name]': "Test 2", - - 'identifiers[1][name]': "Test 2", - 'identifiers[1][authority_id]': self.auth.pk, - 'identifiers[1][slug]': "test2", - } - - response = self.client.post('/identifiedmodels.json', form) - self.assertEqual( - response.status_code, status.HTTP_201_CREATED, response.data - ) - self.assertEqual(response.data['name'], "Test 2") - self.assertEqual(response.data['id'], "test-2") - - self.assertIn("identifiers", response.data) - self.assertEqual(len(response.data["identifiers"]), 2) - - idents = ident_by_auth(response.data["identifiers"]) - - self.assertEqual(idents[None]["slug"], "test-2") - self.assertEqual(idents[self.auth.pk]["slug"], "test2") - - def test_identify_put(self): - ident = self.instance.identifiers.get(authority=self.auth) - form = { - 'name': 'Test 1 - Updated', - - 'identifiers[0][id]': self.instance.primary_identifier.pk, - 'identifiers[0][name]': "Test 1 - Updated", - 'identifiers[0][slug]': "test-1-updated", - - 'identifiers[1][id]': ident.pk, - 'identifiers[1][name]': "Test 1B", - 'identifiers[1][slug]': "test1b", - 'identifiers[1][type]': self.auth.pk, - } - url = ( - '/identifiedmodels/%s.json' % self.instance.slug - ) - - response = self.client.put(url, form) - self.assertEqual( - response.status_code, status.HTTP_200_OK, response.data - ) - self.instance = IdentifiedModel.objects.get(pk=self.instance.pk) - self.assertEqual(self.instance.name, "Test 1 - Updated") - self.assertEqual(response.data['id'], "test-1-updated") - - self.assertIn("identifiers", response.data) - self.assertEqual(len(response.data["identifiers"]), 2) - - idents = ident_by_auth(response.data["identifiers"]) - - self.assertEqual(idents[None]["slug"], "test-1-updated") - self.assertEqual(idents[self.auth.pk]["slug"], "test1b") - self.assertEqual( - idents[self.auth.pk]["url"], "http://example.com/pages/test1b" - ) - - def test_identify_post_duplicate_auto_slug(self): - form = { - 'name': 'Test 3', - } - response = self.client.post('/identifiedmodels.json', form) - self.assertEqual( - response.status_code, status.HTTP_201_CREATED, response.data - ) - self.assertEqual(response.data['id'], 'test-3') - - response = self.client.post('/identifiedmodels.json', form) - self.assertEqual( - response.status_code, status.HTTP_201_CREATED, response.data - ) - self.assertEqual(response.data['id'], 'test-3-1') - - def test_identify_post_duplicate_auto_ident_slug(self): - form = { - 'name': 'Test 4', - 'identifiers[0][name]': 'Test 4 Ident', - } - response = self.client.post('/identifiedmodels.json', form) - self.assertEqual( - response.status_code, status.HTTP_201_CREATED, response.data - ) - self.assertEqual(response.data['id'], 'test-4-ident', response.data) - - response = self.client.post('/identifiedmodels.json', form) - self.assertEqual( - response.status_code, status.HTTP_201_CREATED, response.data - ) - self.assertEqual(response.data['id'], 'test-4-ident-1') - - def test_identify_post_duplicate_explicit_slug(self): - form = { - 'name': 'Test 5', - 'slug': 'test-5', - } - response = self.client.post('/identifiedmodels.json', form) - self.assertEqual( - response.status_code, status.HTTP_201_CREATED, response.data - ) - - response = self.client.post('/identifiedmodels.json', form) - self.assertEqual( - response.status_code, status.HTTP_400_BAD_REQUEST, response.data - ) - self.assertEqual( - response.data['non_field_errors'], - ["The fields slug must make a unique set."], - ) - - def test_identify_post_duplicate_explicit_ident_slug(self): - # FIXME: Handle this case - return - form = { - 'name': 'Test 6', - 'identifiers[0][name]': 'Test 6', - 'identifiers[0][slug]': 'test-6', - } - response = self.client.post('/identifiedmodels.json', form) - self.assertEqual( - response.status_code, status.HTTP_201_CREATED, response.data - ) - - response = self.client.post('/identifiedmodels.json', form) - self.assertEqual( - response.status_code, status.HTTP_400_BAD_REQUEST, response.data - ) - - def test_identify_alt_ident(self): - form = { - 'name': 'Test 7', - 'identifiers[0][name]': "Test 7", - - 'identifiers[1][name]': "Test 7 Alt", - 'identifiers[1][authority_id]': self.auth.pk, - } - - response = self.client.post('/identifiedmodels.json', form) - self.assertEqual( - response.status_code, status.HTTP_201_CREATED, response.data - ) - - response = self.client.get('/identifiedmodels/test-7-alt.json', form) - self.assertEqual( - response.status_code, status.HTTP_200_OK, response.data - ) - self.assertEqual(response.data['id'], 'test-7') - - def test_filter_backend(self): - self.instance.filterablemodel_set.create( - pk=1, - name="Filter 1", - ) - self.instance.filterablemodel_set.create( - pk=2, - name="Filter 2", - ) - instance2 = IdentifiedModel.objects.find("Test 2") - instance2.filterablemodel_set.create( - pk=3, - name="Filter 3", - ) - instance3 = IdentifiedModel.objects.find("Test 3") - instance3.filterablemodel_set.create( - pk=4, - name="Filter 4", - ) - - response = self.client.get("/filterable/test1/test-3?format=json") - self.assertEqual( - status.HTTP_200_OK, response.status_code - ) - self.assertIn('list', response.data) - self.assertEqual(3, len(response.data['list'])) - self.assertEqual(1, response.data['list'][0]['id']) - self.assertEqual(2, response.data['list'][1]['id']) - self.assertEqual(4, response.data['list'][2]['id']) diff --git a/tests/test_router.py b/tests/test_router.py index 0841fac7..dc58d81c 100644 --- a/tests/test_router.py +++ b/tests/test_router.py @@ -1,9 +1,5 @@ from .base import APITestCase from django.core.exceptions import ImproperlyConfigured -try: - from django.urls import include -except ImportError: - from django.conf.urls import include class RestRouterTestCase(APITestCase): @@ -40,7 +36,7 @@ def test_rest_model_conflict(self): Item, name="conflictitem", url="conflictitems", fields="__all__" ) self.assertIn(Item, rest.router._models) - self.assertIn("conflictitem", rest.router.get_config()['pages']) + self.assertIn("conflictitem", rest.router.config['pages']) def test_rest_old_config(self): from wq.db import rest @@ -72,7 +68,3 @@ def test_rest_old_config(self): ) self.assertNotIn(TestModel, rest.router._models) - - def test_rest_include(self): - from wq.db import rest - include(rest.router.urls) diff --git a/tests/test_template.py b/tests/test_template.py deleted file mode 100644 index 84d6fd43..00000000 --- a/tests/test_template.py +++ /dev/null @@ -1,224 +0,0 @@ -from .base import APITestCase -from rest_framework import status -from tests.rest_app.models import ( - RootModel, OneToOneModel, ForeignKeyModel, ExtraModel, UserManagedModel, - Parent, Child, ItemType, Item, SlugModel, SlugRefParent, ChoiceModel, -) -from django.contrib.auth.models import User -from django.conf import settings - - -class TemplateTestCase(APITestCase): - def setUp(self): - instance = RootModel.objects.create( - slug='instance', - description="Test" - ) - for cls in OneToOneModel, ForeignKeyModel, ExtraModel: - cls.objects.create( - root=instance, - ) - user = User.objects.create(username="testuser", is_superuser=True) - self.client.force_authenticate(user) - UserManagedModel.objects.create(id=1, user=user) - parent = Parent.objects.create(name="Test", pk=1) - parent.children.create(name="Test 1") - parent.children.create(name="Test 2") - itype = ItemType.objects.create(name="Test", pk=1) - itype.item_set.create(name="Test 1") - itype.item_set.create(name="Test 2") - SlugModel.objects.create( - code="test", - name="Test", - ) - - def assertHTMLEqual(self, expected_html, html, auto_replace=True): - if settings.WITH_NONROOT and auto_replace: - html = html.replace('/wqsite/', '/') - super().assertHTMLEqual(expected_html, html) - - def check_html(self, url, expected_html): - response = self.client.get(url) - self.assertTrue(status.is_success(response.status_code), response.data) - html = response.content.decode('utf-8') - self.assertHTMLEqual(expected_html, html) - - # Test url="" use case - def test_template_list_at_root(self): - self.check_html("/", """ - - """) - - def test_template_detail_at_root(self): - instance = RootModel.objects.get(slug='instance') - self.check_html("/instance", """ -

instance

-

Test

-

OneToOneModel

-

- - onetoonemodel for instance - -

-

ExtraModels

- -

Edit

- """.format( - onetoone_pk=instance.onetoonemodel.pk, - extra_pk=instance.extramodels.all()[0].pk, - )) - - def test_template_filter_by_parent(self): - childs = Parent.objects.get(pk=1).children.order_by('pk') - self.check_html('/parents/1/children', """ -

2 Records

-

Childs for Test

- - """.format( - c1_pk=childs[0].pk, - c2_pk=childs[1].pk, - )) - - items = ItemType.objects.get(pk=1).item_set.order_by('pk') - self.check_html('/itemtypes/1/items', """ -

Test Items

- - """.format( - i1_pk=items[0].pk, - i2_pk=items[1].pk, - )) - - def test_template_detail_user_serializer(self): - self.check_html('/usermanagedmodels/1', """ -

Object #1

-

Created by testuser

-

- """) - - def test_template_custom_lookup(self): - self.check_html('/slugmodels/test', "

Test

") - - def test_template_default_per_page(self): - parent = Parent.objects.get(pk=1) - parent.name = "Test 1" - parent.save() - for i in range(2, 101): - Parent.objects.create( - id=i, - name="Test %s" % i, - ) - - html = """ -

100 Records

-
-

Page 1 of 2

- Next 50 -
- - """ - self.check_html("/parents/", html) - - def test_template_custom_per_page(self): - for i in range(3, 102): - child = Child.objects.create( - name="Test %s" % i, - parent_id=1, - ) - - self.check_html("/children/?page=2", """ -

101 Records

-
- Prev 100 -

Page 2 of 2

-
- - """.format(pk=child.pk)) - - def test_template_limit(self): - for i in range(3, 101): - child = Child.objects.create( - name="Test %s" % i, - parent_id=1, - ) - html = """ -

100 Records

-
-

Page 1 of 10

- Next 10 -
- - """ - self.check_html("/children/?limit=10", html) - - def test_template_context_processors(self): - response = self.client.get('/rest_context') - html = response.content.decode('utf-8') - token = html.split('value="')[1].split('"')[0] - self.assertTrue(len(token) >= 32) - if settings.WITH_NONROOT: - base_url = '/wqsite' - else: - base_url = '' - self.assertHTMLEqual(""" -

{base_url}/rest_context

-

rest_context

-

{base_url}/

-

{base_url}/

-

0.0.0

-

- -

-

rest_context

-

Can Edit Items

- """.format(csrf=token, base_url=base_url), html, auto_replace=False) - - def test_template_page_config(self): - item = Item.objects.get(name="Test 1") - self.check_html('/items/%s' % item.pk, """ -

Test 1

- Test - Edit - """.format(pk=item.pk)) - - def test_script_tags(self): - self.check_html('/script_context', """ - - - - - - - - - - """) diff --git a/tests/urls.py b/tests/urls.py index d7b57bb8..a77becb9 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -1,11 +1,5 @@ -try: - from django.urls import path -except ImportError: - from django.conf.urls import url - path = None - +from django.urls import path from wq.db import rest -from tests.patterns_app.views import FilterableView from .settings import WITH_NONROOT @@ -15,17 +9,6 @@ base_url = '' -if path: - urlpatterns = [ - path(base_url + 'filterable/', FilterableView.as_view()), - path(base_url, rest.router.urls), - ] -else: - # FIXME: Remove in 2.0 - urlpatterns = [ - url( - r'^' + base_url + 'filterable/(?P.+)', - FilterableView.as_view() - ), - url(r'^' + base_url, rest.router.urls), - ] +urlpatterns = [ + path(base_url, rest.router.urls), +]