diff --git a/FAIRshakeAPI/forms.py b/FAIRshakeAPI/forms.py index 85c93be..1725ddd 100644 --- a/FAIRshakeAPI/forms.py +++ b/FAIRshakeAPI/forms.py @@ -16,7 +16,18 @@ def __init__(self, *args, **kwargs): help_text=None, initial=getattr(self.instance, child).all() if self.instance and self.instance.id else [], ) - + + def clean_slug(self): + slug = self.cleaned_data.get('slug') + try: + if self.Meta.model.objects.get(slug=slug) != self.instance: + raise forms.ValidationError( + 'Slug was already taken, please try something different.' + ) + except self.Meta.model.DoesNotExist: + pass + return slug + def save(self, *args, commit=True, **kwargs): ''' Explicitly add children for children in the reverse direction. ''' @@ -43,6 +54,7 @@ class Meta: 'image', 'tags', 'type', + 'slug', 'digital_objects', 'authors', ) @@ -57,6 +69,7 @@ class Meta: 'image', 'tags', 'type', + 'slug', 'rubrics', 'authors', ) @@ -71,6 +84,7 @@ class Meta: 'image', 'tags', 'type', + 'slug', 'license', 'metrics', 'authors', @@ -86,6 +100,7 @@ class Meta: 'image', 'tags', 'type', + 'slug', 'license', 'rationale', 'principle', diff --git a/FAIRshakeAPI/migrations/0008_auto_20180912_2050.py b/FAIRshakeAPI/migrations/0008_auto_20180912_2050.py new file mode 100644 index 0000000..5162ecc --- /dev/null +++ b/FAIRshakeAPI/migrations/0008_auto_20180912_2050.py @@ -0,0 +1,38 @@ +# Generated by Django 2.0.7 on 2018-09-12 20:50 + +import builtins +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('FAIRshakeAPI', '0007_auto_20180924_1435'), + ] + + operations = [ + migrations.AddField( + model_name='digitalobject', + name='slug', + field=models.CharField(max_length=255, default=None, null=True), + preserve_default=False, + ), + migrations.AddField( + model_name='metric', + name='slug', + field=models.CharField(max_length=255, default=None, null=True), + preserve_default=False, + ), + migrations.AddField( + model_name='project', + name='slug', + field=models.CharField(max_length=255, default=None, null=True), + preserve_default=False, + ), + migrations.AddField( + model_name='rubric', + name='slug', + field=models.CharField(max_length=255, default=None, null=True), + preserve_default=False, + ), + ] diff --git a/FAIRshakeAPI/migrations/0009_auto_20180912_2051.py b/FAIRshakeAPI/migrations/0009_auto_20180912_2051.py new file mode 100644 index 0000000..a89cbc3 --- /dev/null +++ b/FAIRshakeAPI/migrations/0009_auto_20180912_2051.py @@ -0,0 +1,36 @@ +# Generated by Django 2.0.7 on 2018-09-12 20:51 + +from django.db import migrations + +def migrate(apps, schema_editor): + Project = apps.get_model('FAIRshakeAPI', 'Project') + for project in Project.objects.all(): + project.slug = project.id + project.save() + + DigitalObject = apps.get_model('FAIRshakeAPI', 'DigitalObject') + for obj in DigitalObject.objects.all(): + obj.slug = obj.id + obj.save() + + Rubric = apps.get_model('FAIRshakeAPI', 'Rubric') + for rubric in Rubric.objects.all(): + rubric.slug = rubric.id + rubric.save() + + Metric = apps.get_model('FAIRshakeAPI', 'Metric') + for metric in Metric.objects.all(): + metric.slug = metric.id + metric.save() + +class Migration(migrations.Migration): + + dependencies = [ + ('FAIRshakeAPI', '0008_auto_20180912_2050'), + ] + + operations = [ + migrations.RunPython( + migrate + ) + ] diff --git a/FAIRshakeAPI/migrations/0010_auto_20180912_2054.py b/FAIRshakeAPI/migrations/0010_auto_20180912_2054.py new file mode 100644 index 0000000..6d2d7b2 --- /dev/null +++ b/FAIRshakeAPI/migrations/0010_auto_20180912_2054.py @@ -0,0 +1,33 @@ +# Generated by Django 2.0.7 on 2018-09-12 20:54 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('FAIRshakeAPI', '0009_auto_20180912_2051'), + ] + + operations = [ + migrations.AlterField( + model_name='digitalobject', + name='slug', + field=models.CharField(max_length=255, unique=True), + ), + migrations.AlterField( + model_name='metric', + name='slug', + field=models.CharField(max_length=255, unique=True), + ), + migrations.AlterField( + model_name='project', + name='slug', + field=models.CharField(max_length=255, unique=True), + ), + migrations.AlterField( + model_name='rubric', + name='slug', + field=models.CharField(max_length=255, unique=True), + ), + ] diff --git a/FAIRshakeAPI/models.py b/FAIRshakeAPI/models.py index 0eebca1..e9e8e79 100644 --- a/FAIRshakeAPI/models.py +++ b/FAIRshakeAPI/models.py @@ -10,6 +10,7 @@ class IdentifiableModelMixin(models.Model): title = models.CharField(max_length=255, blank=False) url = models.TextField(blank=True, null=False, default='') + slug = models.CharField(max_length=255, unique=True, blank=False, null=False) description = models.TextField(blank=True, null=False, default='') image = models.CharField(max_length=255, blank=True, null=False, default='') tags = models.CharField(max_length=255, blank=True, null=False, default='') @@ -30,7 +31,7 @@ def urls_as_list(self): def tags_as_list(self): return self.tags.split() - + def model_name(self): return self._meta.verbose_name_raw @@ -43,7 +44,7 @@ def attrs(self): 'tags': self.tags, 'type': self.type, } - + def has_permission(self, user, perm): if perm in ['list', 'retrieve', 'stats']: return True @@ -166,19 +167,19 @@ def has_permission(self, user, perm): def save(self, *args, **kwargs): if self.target is not None: - k = '#digital_object={pk}'.format(pk=self.target.pk) + k = '#digital_object={slug}'.format(slug=self.target.slug) l = cache.get(k) l = json.loads(l) if l else [] l += [k] cache.delete_many(l) if self.rubric is not None: - k = '#rubric={pk}'.format(pk=self.rubric.pk) + k = '#rubric={slug}'.format(slug=self.rubric.slug) l = cache.get(k) l = json.loads(l) if l else [] l += [k] cache.delete_many(l) if self.project is not None: - k = '#project={pk}'.format(pk=self.project.pk) + k = '#project={slug}'.format(slug=self.project.slug) l = cache.get(k) l = json.loads(l) if l else [] l += [k] diff --git a/FAIRshakeAPI/serializers.py b/FAIRshakeAPI/serializers.py index dc8a44d..2b7dcb5 100644 --- a/FAIRshakeAPI/serializers.py +++ b/FAIRshakeAPI/serializers.py @@ -20,6 +20,7 @@ def update(self, instance, validated_data): class Meta: abstract = True + lookup_field = 'slug' read_only_fields = ( 'id', diff --git a/FAIRshakeAPI/tests.py b/FAIRshakeAPI/tests.py index 21116c8..e08496f 100644 --- a/FAIRshakeAPI/tests.py +++ b/FAIRshakeAPI/tests.py @@ -156,7 +156,7 @@ def test_score_viewset_list(self): def test_project_viewset_detail(self): response = self.anonymous_client.get( reverse('project-detail', kwargs=dict( - pk=models.Project.objects.first().pk + slug=models.Project.objects.first().slug )), HTTP_ACCEPT='text/html', ) @@ -165,7 +165,7 @@ def test_project_viewset_detail(self): response = self.anonymous_client.get( reverse('project-detail', kwargs=dict( - pk=models.Project.objects.first().pk + slug=models.Project.objects.first().slug )), HTTP_ACCEPT='application/json', ) @@ -174,7 +174,7 @@ def test_project_viewset_detail(self): response = self.authenticated_client.get( reverse('project-detail', kwargs=dict( - pk=models.Project.objects.first().pk + slug=models.Project.objects.first().slug )), HTTP_ACCEPT='text/html', ) @@ -183,7 +183,7 @@ def test_project_viewset_detail(self): response = self.authenticated_client.get( reverse('project-detail', kwargs=dict( - pk=models.Project.objects.first().pk + slug=models.Project.objects.first().slug )), HTTP_ACCEPT='application/json', ) @@ -193,7 +193,7 @@ def test_project_viewset_detail(self): def test_digital_object_viewset_detail(self): response = self.anonymous_client.get( reverse('digital_object-detail', kwargs=dict( - pk=models.DigitalObject.objects.first().pk + slug=models.DigitalObject.objects.first().slug )), HTTP_ACCEPT='text/html', ) @@ -202,7 +202,7 @@ def test_digital_object_viewset_detail(self): response = self.anonymous_client.get( reverse('digital_object-detail', kwargs=dict( - pk=models.DigitalObject.objects.first().pk + slug=models.DigitalObject.objects.first().slug )), HTTP_ACCEPT='application/json', ) @@ -211,7 +211,7 @@ def test_digital_object_viewset_detail(self): response = self.authenticated_client.get( reverse('digital_object-detail', kwargs=dict( - pk=models.DigitalObject.objects.first().pk + slug=models.DigitalObject.objects.first().slug )), HTTP_ACCEPT='text/html', ) @@ -220,7 +220,7 @@ def test_digital_object_viewset_detail(self): response = self.authenticated_client.get( reverse('digital_object-detail', kwargs=dict( - pk=models.DigitalObject.objects.first().pk + slug=models.DigitalObject.objects.first().slug )), HTTP_ACCEPT='application/json', ) @@ -230,7 +230,7 @@ def test_digital_object_viewset_detail(self): def test_rubric_viewset_detail(self): response = self.anonymous_client.get( reverse('rubric-detail', kwargs=dict( - pk=models.Rubric.objects.first().pk + slug=models.Rubric.objects.first().slug )), HTTP_ACCEPT='text/html', ) @@ -239,7 +239,7 @@ def test_rubric_viewset_detail(self): response = self.anonymous_client.get( reverse('rubric-detail', kwargs=dict( - pk=models.Rubric.objects.first().pk + slug=models.Rubric.objects.first().slug )), HTTP_ACCEPT='application/json', ) @@ -248,7 +248,7 @@ def test_rubric_viewset_detail(self): response = self.authenticated_client.get( reverse('rubric-detail', kwargs=dict( - pk=models.Rubric.objects.first().pk + slug=models.Rubric.objects.first().slug )), HTTP_ACCEPT='text/html', ) @@ -257,7 +257,7 @@ def test_rubric_viewset_detail(self): response = self.authenticated_client.get( reverse('rubric-detail', kwargs=dict( - pk=models.Rubric.objects.first().pk + slug=models.Rubric.objects.first().slug )), HTTP_ACCEPT='application/json', ) @@ -267,7 +267,7 @@ def test_rubric_viewset_detail(self): def test_metric_viewset_detail(self): response = self.anonymous_client.get( reverse('metric-detail', kwargs=dict( - pk=models.Metric.objects.first().pk + slug=models.Metric.objects.first().slug )), HTTP_ACCEPT='text/html', ) @@ -276,7 +276,7 @@ def test_metric_viewset_detail(self): response = self.anonymous_client.get( reverse('metric-detail', kwargs=dict( - pk=models.Metric.objects.first().pk + slug=models.Metric.objects.first().slug )), HTTP_ACCEPT='application/json', ) @@ -285,7 +285,7 @@ def test_metric_viewset_detail(self): response = self.authenticated_client.get( reverse('metric-detail', kwargs=dict( - pk=models.Metric.objects.first().pk + slug=models.Metric.objects.first().slug )), HTTP_ACCEPT='text/html', ) @@ -294,7 +294,7 @@ def test_metric_viewset_detail(self): response = self.authenticated_client.get( reverse('metric-detail', kwargs=dict( - pk=models.Metric.objects.first().pk + slug=models.Metric.objects.first().slug )), HTTP_ACCEPT='application/json', ) @@ -304,7 +304,7 @@ def test_metric_viewset_detail(self): def test_assessment_viewset_detail(self): response = self.anonymous_client.get( reverse('assessment-detail', kwargs=dict( - pk=models.Assessment.objects.first().pk + slug=models.Assessment.objects.first().slug )), HTTP_ACCEPT='text/html', ) @@ -313,7 +313,7 @@ def test_assessment_viewset_detail(self): response = self.anonymous_client.get( reverse('assessment-detail', kwargs=dict( - pk=models.Assessment.objects.first().pk + slug=models.Assessment.objects.first().slug )), HTTP_ACCEPT='application/json', ) @@ -322,7 +322,7 @@ def test_assessment_viewset_detail(self): response = self.authenticated_client.get( reverse('assessment-detail', kwargs=dict( - pk=models.Assessment.objects.first().pk + slug=models.Assessment.objects.first().slug )), HTTP_ACCEPT='text/html', ) @@ -331,7 +331,7 @@ def test_assessment_viewset_detail(self): response = self.authenticated_client.get( reverse('assessment-detail', kwargs=dict( - pk=models.Assessment.objects.first().pk + slug=models.Assessment.objects.first().slug )), HTTP_ACCEPT='application/json', ) @@ -373,7 +373,7 @@ def test_project_create(self): def test_project_update(self): response = self.anonymous_client.put( - '/project/{pk}/'.format(pk=models.Project.objects.first().pk), + '/project/{slug}/'.format(slug=models.Project.objects.first().slug), { 'title': 'test project improved', }, @@ -384,7 +384,7 @@ def test_project_update(self): self.assertNotEqual(models.Project.objects.first().title, 'test project improved') response = self.authenticated_client.patch( - '/project/{pk}/'.format(pk=models.Project.objects.first().pk), + '/project/{slug}/'.format(slug=models.Project.objects.first().slug), { 'title': 'test project improved', }, @@ -396,7 +396,7 @@ def test_project_update(self): def test_project_partial_update(self): response = self.anonymous_client.patch( - '/project/{pk}/'.format(pk=models.Project.objects.first().pk), + '/project/{slug}/'.format(slug=models.Project.objects.first().slug), { 'description': 'test improved', }, @@ -407,7 +407,7 @@ def test_project_partial_update(self): self.assertNotEqual(models.Project.objects.first().description, 'test improved') response = self.authenticated_client.patch( - '/project/{pk}/'.format(pk=models.Project.objects.first().pk), + '/project/{slug}/'.format(slug=models.Project.objects.first().slug), { 'description': 'test improved', }, @@ -418,23 +418,23 @@ def test_project_partial_update(self): self.assertEqual(models.Project.objects.first().description, 'test improved') def test_project_destroy(self): - pk = models.Project.objects.first().pk + slug = models.Project.objects.first().slug response = self.anonymous_client.delete( - '/project/{pk}/'.format(pk=pk), + '/project/{slug}/'.format(slug=slug), HTTP_ACCEPT='application/json', ) self.assertEqual(response.status_code, 401) self.assertEqual(response['Content-Type'], 'application/json', response) - self.assertEqual(models.Project.objects.first().pk, pk) + self.assertEqual(models.Project.objects.first().slug, slug) response = self.authenticated_client.delete( - '/project/{pk}/'.format(pk=pk), + '/project/{slug}/'.format(slug=slug), HTTP_ACCEPT='application/json', ) self.assertEqual(response.status_code, 204) # self.assertEqual(response['Content-Type'], 'application/json', response) try: - models.Project.objects.get(pk=pk) + models.Project.objects.get(slug=slug) self.fail('Project was not deleted') except: pass diff --git a/FAIRshakeAPI/views.py b/FAIRshakeAPI/views.py index c6c78e0..824be3e 100644 --- a/FAIRshakeAPI/views.py +++ b/FAIRshakeAPI/views.py @@ -31,6 +31,8 @@ def get_template_context(self, data, renderer_context): return view.get_template_context(request, context) class CustomModelViewSet(viewsets.ModelViewSet): + lookup_field = 'slug' + renderer_classes = [ renderers.JSONRenderer, CustomTemplateHTMLRenderer, @@ -132,7 +134,7 @@ def get_template_context(self, request, context): detail=False, methods=['get', 'post'], renderer_classes=[CustomTemplateHTMLRenderer], ) - def add(self, request, pk=None, **kwargs): + def add(self, request, slug=None, **kwargs): self.check_permissions(request) if request.method == 'GET': return response.Response() @@ -141,7 +143,7 @@ def add(self, request, pk=None, **kwargs): if instance: return callback_or_redirect(request, self.get_model_name()+'-detail', - pk=instance.id, + slug=instance.slug, ) return response.Response() @@ -150,7 +152,7 @@ def add(self, request, pk=None, **kwargs): methods=['get', 'post'], renderer_classes=[CustomTemplateHTMLRenderer], ) - def modify(self, request, pk=None): + def modify(self, request, slug=None): item = self.get_object() if request.method == 'GET': return response.Response() @@ -159,7 +161,7 @@ def modify(self, request, pk=None): if instance: return callback_or_redirect(request, self.get_model_name()+'-detail', - pk=pk, + slug=instance.slug, ) return response.Response() @@ -167,7 +169,7 @@ def modify(self, request, pk=None): detail=True, methods=['get'], ) - def remove(self, request, pk=None): + def remove(self, request, slug=None): item = self.get_object() self.check_object_permissions(request, item) item.delete() @@ -198,7 +200,7 @@ class ProjectViewSet(CustomModelViewSet): methods=['get'], renderer_classes=[CustomTemplateHTMLRenderer], ) - def stats(self, request, pk=None): + def stats(self, request, slug=None): item = self.get_object() self.check_object_permissions(request, item) return response.Response() @@ -222,6 +224,7 @@ class RubricViewSet(CustomModelViewSet): filter_class = filters.RubricFilterSet class AssessmentViewSet(CustomModelViewSet): + lookup_field = 'pk' model = models.Assessment form = forms.AssessmentForm serializer_class = serializers.AssessmentSerializer diff --git a/FAIRshakeHub/templates/fairshake/assessment/add.html b/FAIRshakeHub/templates/fairshake/assessment/add.html index 9eedcfc..267bdc9 100644 --- a/FAIRshakeHub/templates/fairshake/assessment/add.html +++ b/FAIRshakeHub/templates/fairshake/assessment/add.html @@ -1,12 +1,18 @@ {% load bootstrap %} {% load filters %} -
+ {{ model|unslugify }} - of {{ item.target.title }} - with {{ item.rubric.title }} + of {{ item.target.title }} + with {{ item.rubric.title }} {% if item.project %} - for {{ item.project.title }} + for {{ item.project.title }} {% endif %}   diff --git a/FAIRshakeHub/templates/fairshake/assessment/element.html b/FAIRshakeHub/templates/fairshake/assessment/element.html index 54c63f2..8d2b93b 100644 --- a/FAIRshakeHub/templates/fairshake/assessment/element.html +++ b/FAIRshakeHub/templates/fairshake/assessment/element.html @@ -4,18 +4,18 @@
{{ model|unslugify }} - of {{ item.target.title }} - with {{ item.rubric.title }} + of {{ item.target.title }} + with {{ item.rubric.title }} {% if item.project %} - for {{ item.project.title }} + for {{ item.project.title }} {% endif %}   - + + {{ model|unslugify }} - of {{ item.target.title }} - with {{ item.rubric.title }} + of {{ item.target.title }} + with {{ item.rubric.title }} {% if item.project %} - for {{ item.project.title }} + for {{ item.project.title }} {% endif %} {% csrf_token %} diff --git a/FAIRshakeHub/templates/fairshake/generic/element.html b/FAIRshakeHub/templates/fairshake/generic/element.html index af2d1db..68e44e4 100644 --- a/FAIRshakeHub/templates/fairshake/generic/element.html +++ b/FAIRshakeHub/templates/fairshake/generic/element.html @@ -3,7 +3,7 @@
@@ -61,7 +61,7 @@ {% has_permission item 'modify' as edit_perm %} {% if edit_perm %}
- +
- +
diff --git a/FAIRshakeHub/templates/fairshake/generic/modify.html b/FAIRshakeHub/templates/fairshake/generic/modify.html index abae5be..55a1db9 100644 --- a/FAIRshakeHub/templates/fairshake/generic/modify.html +++ b/FAIRshakeHub/templates/fairshake/generic/modify.html @@ -6,6 +6,6 @@ {{ form|bootstrap }} diff --git a/FAIRshakeHub/templates/fairshake/generic/retrieve.html b/FAIRshakeHub/templates/fairshake/generic/retrieve.html index d9f4351..ed1acee 100644 --- a/FAIRshakeHub/templates/fairshake/generic/retrieve.html +++ b/FAIRshakeHub/templates/fairshake/generic/retrieve.html @@ -57,12 +57,12 @@

{{ item.title }}

{% if model == "project" %} View Analytics @@ -71,7 +71,7 @@

{{ item.title }}

{% if model == "digital_object" and user.is_authenticated %}