From 89ab60b395dbef7c778a3ed0c913e5fbfffce3a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laura=20Kl=C3=BCnder?= Date: Fri, 5 May 2017 16:21:48 +0200 Subject: [PATCH] remove name field from most mapdata models --- src/c3nav/editor/urls.py | 2 +- src/c3nav/editor/views.py | 11 ++-- src/c3nav/mapdata/api.py | 24 +++----- .../migrations/0055_auto_20170505_1334.py | 57 +++++++++++++++++++ src/c3nav/mapdata/models/base.py | 4 +- src/c3nav/mapdata/models/geometry.py | 8 +-- src/c3nav/mapdata/models/locations.py | 14 +++-- src/c3nav/mapdata/models/source.py | 1 + src/c3nav/mapdata/search.py | 10 ++-- src/c3nav/mapdata/serializers/main.py | 4 +- 10 files changed, 96 insertions(+), 39 deletions(-) create mode 100644 src/c3nav/mapdata/migrations/0055_auto_20170505_1334.py diff --git a/src/c3nav/editor/urls.py b/src/c3nav/editor/urls.py index 6f824e3b..fdea1009 100644 --- a/src/c3nav/editor/urls.py +++ b/src/c3nav/editor/urls.py @@ -9,5 +9,5 @@ urlpatterns = [ url(r'^mapitems/(?P[^/]+)/list/$', list_mapitems, name='editor.mapitems'), url(r'^mapitems/(?P[^/]+)/list/(?P[^/]+)/$', list_mapitems, name='editor.mapitems.level'), url(r'^mapitems/(?P[^/]+)/add/$', edit_mapitem, name='editor.mapitems.add'), - url(r'^mapitems/(?P[^/]+)/edit/(?P[^/]+)/$', edit_mapitem, name='editor.mapitems.edit'), + url(r'^mapitems/(?P[^/]+)/edit/(?P[^/]+)/$', edit_mapitem, name='editor.mapitems.edit'), ] diff --git a/src/c3nav/editor/views.py b/src/c3nav/editor/views.py index 3a8cc2d7..740bdd6d 100644 --- a/src/c3nav/editor/views.py +++ b/src/c3nav/editor/views.py @@ -4,11 +4,12 @@ from django.http.response import Http404 from django.shortcuts import get_object_or_404, redirect, render from c3nav.access.apply import can_access, filter_queryset_by_access -from c3nav.mapdata.models import AreaLocation +from c3nav.mapdata.models import AreaLocation, Level from c3nav.mapdata.models.base import FEATURE_TYPES def list_mapitemtypes(request, level): + level = get_object_or_404() def get_item_count(mapitemtype): if hasattr(mapitemtype, 'level'): return filter_queryset_by_access(request, mapitemtype.objects.filter(level__name=level)).count() @@ -43,11 +44,13 @@ def list_mapitems(request, mapitem_type, level=None): return redirect('editor.mapitems', mapitem_type=mapitem_type) queryset = mapitemtype.objects.all().order_by('name') + if level is not None: + level = get_object_or_404(Level, level) if hasattr(mapitemtype, 'level'): - queryset = queryset.filter(level__name=level) + queryset = queryset.filter(level=level) elif hasattr(mapitemtype, 'levels'): - queryset = queryset.filter(levels__name=level) + queryset = queryset.filter(levels=level) queryset = filter_queryset_by_access(request, queryset) @@ -62,7 +65,7 @@ def list_mapitems(request, mapitem_type, level=None): 'has_levels': hasattr(mapitemtype, 'levels'), 'has_altitude': hasattr(mapitemtype, 'altitude'), 'has_intermediate': hasattr(mapitemtype, 'intermediate'), - 'level': level, + 'level': level.id, 'items': queryset, }) diff --git a/src/c3nav/mapdata/api.py b/src/c3nav/mapdata/api.py index 483af4a6..3467255c 100644 --- a/src/c3nav/mapdata/api.py +++ b/src/c3nav/mapdata/api.py @@ -4,6 +4,7 @@ import mimetypes import hashlib from collections import OrderedDict from django.http import Http404, HttpResponse, HttpResponseNotModified +from django.shortcuts import get_object_or_404 from rest_framework.decorators import detail_route, list_route from rest_framework.response import Response from rest_framework.viewsets import ReadOnlyModelViewSet, ViewSet @@ -48,10 +49,7 @@ class GeometryViewSet(ViewSet): level = None if 'level' in request.GET: - levels_cached = get_levels_cached() - level_name = request.GET['level'] - if level_name in levels_cached: - level = levels_cached[level_name] + level = get_object_or_404(Level, id=request.GET['level']) cache_key = '__'.join(( ','.join([str(i) for i in types]), @@ -78,7 +76,7 @@ class GeometryViewSet(ViewSet): else: queryset = queryset.none() queryset = filter_queryset_by_access(request, queryset) - queryset = queryset.order_by('name') + queryset = queryset.order_by('id') for field_name in ('level', 'crop_to_level', 'elevator'): if hasattr(mapitemtype, field_name): @@ -105,9 +103,7 @@ class LevelViewSet(CachedReadOnlyViewSetMixin, ReadOnlyModelViewSet): """ queryset = Level.objects.all() serializer_class = LevelSerializer - lookup_field = 'name' - lookup_value_regex = '[^/]+' - ordering = ('altitude',) + lookup_field = 'id' class SourceViewSet(CachedReadOnlyViewSetMixin, ReadOnlyModelViewSet): @@ -116,9 +112,7 @@ class SourceViewSet(CachedReadOnlyViewSetMixin, ReadOnlyModelViewSet): """ queryset = Source.objects.all() serializer_class = SourceSerializer - lookup_field = 'name' - lookup_value_regex = '[^/]+' - ordering = ('name',) + lookup_field = 'id' def get_queryset(self): return filter_queryset_by_access(self.request, super().get_queryset().all()) @@ -140,11 +134,11 @@ class LocationViewSet(ViewSet): List and retrieve locations """ # We don't cache this, because it depends on access_list - lookup_field = 'name' + lookup_field = 'location_id' @staticmethod def _filter(queryset): - return queryset.filter(can_search=True).order_by('name') + return queryset.filter(can_search=True).order_by('id') def list(self, request, **kwargs): etag = hashlib.sha256(json.dumps({ @@ -168,8 +162,8 @@ class LocationViewSet(ViewSet): response['Cache-Control'] = 'no-cache' return response - def retrieve(self, request, name=None, **kwargs): - location = get_location(request, name) + def retrieve(self, request, location_id=None, **kwargs): + location = get_location(request, location_id) if location is None: raise Http404 return Response(location.to_location_json()) diff --git a/src/c3nav/mapdata/migrations/0055_auto_20170505_1334.py b/src/c3nav/mapdata/migrations/0055_auto_20170505_1334.py new file mode 100644 index 00000000..5620cc54 --- /dev/null +++ b/src/c3nav/mapdata/migrations/0055_auto_20170505_1334.py @@ -0,0 +1,57 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.10.4 on 2017-05-05 13:34 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('mapdata', '0054_remove_obstacle_crop_to_level'), + ] + + operations = [ + migrations.RenameField( + model_name='arealocation', + old_name='name', + new_name='slug', + ), + migrations.RenameField( + model_name='locationgroup', + old_name='name', + new_name='slug', + ), + migrations.RemoveField( + model_name='area', + name='name', + ), + migrations.RemoveField( + model_name='building', + name='name', + ), + migrations.RemoveField( + model_name='door', + name='name', + ), + migrations.RemoveField( + model_name='hole', + name='name', + ), + migrations.RemoveField( + model_name='lineobstacle', + name='name', + ), + migrations.RemoveField( + model_name='obstacle', + name='name', + ), + migrations.RemoveField( + model_name='stair', + name='name', + ), + migrations.RemoveField( + model_name='stuffedarea', + name='name', + ), + ] diff --git a/src/c3nav/mapdata/models/base.py b/src/c3nav/mapdata/models/base.py index a907dd1a..4850092f 100644 --- a/src/c3nav/mapdata/models/base.py +++ b/src/c3nav/mapdata/models/base.py @@ -32,8 +32,6 @@ class FeatureBase(ModelBase): class Feature(models.Model, metaclass=FeatureBase): - name = models.SlugField(_('Name'), unique=True, max_length=50) - EditorForm = None @property @@ -71,7 +69,7 @@ class GeometryFeature(Feature): def get_geojson_properties(self): return OrderedDict(( ('type', self.__class__.__name__.lower()), - ('name', self.name), + ('id', self.id), )) def to_geojson(self): diff --git a/src/c3nav/mapdata/models/geometry.py b/src/c3nav/mapdata/models/geometry.py index 8e6dc41b..3fcaa00b 100644 --- a/src/c3nav/mapdata/models/geometry.py +++ b/src/c3nav/mapdata/models/geometry.py @@ -19,7 +19,7 @@ class LevelFeature(GeometryFeature): def get_geojson_properties(self): result = super().get_geojson_properties() - result['level'] = self.level.name + result['level'] = self.level.id return result @@ -34,7 +34,7 @@ class AreaFeature(GeometryFeature): def get_geojson_properties(self): result = super().get_geojson_properties() - result['area'] = self.area.name + result['area'] = self.area.id return result @@ -124,8 +124,8 @@ class Stair(AreaFeature): ('properties', OrderedDict(( ('type', 'shadow'), ('original_type', self.__class__.__name__.lower()), - ('original_name', self.name), - ('area', self.area.name), + ('original_id', self.id), + ('area', self.area.id), ))), ('geometry', format_geojson(mapping(shadow), round=False)), )) diff --git a/src/c3nav/mapdata/models/locations.py b/src/c3nav/mapdata/models/locations.py index fa1fadc1..9575ad43 100644 --- a/src/c3nav/mapdata/models/locations.py +++ b/src/c3nav/mapdata/models/locations.py @@ -25,7 +25,8 @@ class Location: def to_location_json(self): return OrderedDict(( - ('id', self.location_id), + ('id', self.id), + ('location_id', self.location_id), ('title', str(self.title)), ('subtitle', str(self.subtitle)), )) @@ -35,6 +36,7 @@ class Location: class LocationModelMixin(Location): def get_geojson_properties(self): result = super().get_geojson_properties() + result['slug'] = self.slug result['titles'] = OrderedDict(sorted(self.titles.items())) return result @@ -44,6 +46,7 @@ class LocationModelMixin(Location): class LocationGroup(LocationModelMixin, Feature): + slug = models.SlugField(_('Name'), unique=True, max_length=50) titles = JSONField() can_search = models.BooleanField(default=True, verbose_name=_('can be searched')) can_describe = models.BooleanField(default=True, verbose_name=_('can be used to describe a position')) @@ -58,14 +61,14 @@ class LocationGroup(LocationModelMixin, Feature): @cached_property def location_id(self): - return 'g:'+self.name + return 'g:'+self.slug def get_in_levels(self): last_update = get_last_mapdata_update() if last_update is None: return self._get_in_levels() - cache_key = 'c3nav__mapdata__locationgroup__in_levels__'+last_update.isoformat()+'__'+self.name, + cache_key = 'c3nav__mapdata__locationgroup__in_levels__'+last_update.isoformat()+'__'+str(self.id), in_levels = cache.get(cache_key) if not in_levels: in_levels = self._get_in_levels() @@ -115,6 +118,7 @@ class AreaLocation(LocationModelMixin, LevelFeature): ('needs_permission', _('Excluded, needs permission to include')), ) + slug = models.SlugField(_('Name'), unique=True, max_length=50) location_type = models.CharField(max_length=20, choices=LOCATION_TYPES, verbose_name=_('Location Type')) titles = JSONField() groups = models.ManyToManyField(LocationGroup, verbose_name=_('Location Groups'), blank=True) @@ -137,14 +141,14 @@ class AreaLocation(LocationModelMixin, LevelFeature): @cached_property def location_id(self): - return self.name + return self.slug def get_in_areas(self): last_update = get_last_mapdata_update() if last_update is None: return self._get_in_areas() - cache_key = 'c3nav__mapdata__location__in_areas__'+last_update.isoformat()+'__'+self.name, + cache_key = 'c3nav__mapdata__location__in_areas__'+last_update.isoformat()+'__'+str(self.id), in_areas = cache.get(cache_key) if not in_areas: in_areas = self._get_in_areas() diff --git a/src/c3nav/mapdata/models/source.py b/src/c3nav/mapdata/models/source.py index 887e01e9..f0f61c04 100644 --- a/src/c3nav/mapdata/models/source.py +++ b/src/c3nav/mapdata/models/source.py @@ -8,6 +8,7 @@ class Source(Feature): """ A map source, images of levels that can be useful as backgrounds for the map editor """ + name = models.SlugField(_('Name'), unique=True, max_length=50) bottom = models.DecimalField(_('bottom coordinate'), max_digits=6, decimal_places=2) left = models.DecimalField(_('left coordinate'), max_digits=6, decimal_places=2) top = models.DecimalField(_('top coordinate'), max_digits=6, decimal_places=2) diff --git a/src/c3nav/mapdata/search.py b/src/c3nav/mapdata/search.py index 20fdd289..b3b80861 100644 --- a/src/c3nav/mapdata/search.py +++ b/src/c3nav/mapdata/search.py @@ -8,8 +8,8 @@ from c3nav.mapdata.models.locations import PointLocation from c3nav.mapdata.utils.cache import get_levels_cached -def get_location(request, name): - match = re.match('^c:(?P[a-z0-9-_]+):(?P[0-9]+):(?P[0-9]+)$', name) +def get_location(request, location_id): + match = re.match('^c:(?P[a-z0-9-_]+):(?P[0-9]+):(?P[0-9]+)$', location_id) if match: levels = get_levels_cached() level = levels.get(match.group('level')) @@ -17,11 +17,11 @@ def get_location(request, name): return None return PointLocation(level=level, x=int(match.group('x'))/100, y=int(match.group('y'))/100, request=request) - if name.startswith('g:'): - queryset = LocationGroup.objects.filter(Q(name=name[2:], can_search=True)) + if location_id.startswith('g:'): + queryset = LocationGroup.objects.filter(Q(slug=location_id[2:], can_search=True)) return filter_queryset_by_access(request, queryset).first() - return filter_arealocations_by_access(request, AreaLocation.objects.filter(name=name, can_search=True)).first() + return filter_arealocations_by_access(request, AreaLocation.objects.filter(slug=location_id, can_search=True)).first() def filter_words(queryset, words): diff --git a/src/c3nav/mapdata/serializers/main.py b/src/c3nav/mapdata/serializers/main.py index de34862a..3907e519 100644 --- a/src/c3nav/mapdata/serializers/main.py +++ b/src/c3nav/mapdata/serializers/main.py @@ -6,10 +6,10 @@ from c3nav.mapdata.models import Level, Source class LevelSerializer(serializers.ModelSerializer): class Meta: model = Level - fields = ('name', 'altitude') + fields = ('id', 'name', 'altitude') class SourceSerializer(serializers.ModelSerializer): class Meta: model = Source - fields = ('name', 'bounds') + fields = ('id', 'name', 'bounds')