Update model and serializers to 1.0 schema

The proof of concept/prototype was built using a database schema that
was closer to what the stable release of ARA used instead of the one
that had been worked on already for 1.0.

This updates the models and API serializers to reflect that and be more
accurate.

Change-Id: I963823e84e2d2ef9f6262cd04de8fd31ffc7d5b4
This commit is contained in:
David Moreau Simard 2018-03-08 18:22:22 -05:00
parent 4e6df8ba44
commit e6a39c9663
No known key found for this signature in database
GPG Key ID: 33A07694CBB71ECC
6 changed files with 485 additions and 102 deletions

View File

@ -1,10 +1,8 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.8 on 2018-02-27 12:25
from __future__ import unicode_literals
# Generated by Django 2.0.3 on 2018-03-10 16:49
from django.db import migrations, models
import django.db.models.deletion
import uuid
import django.utils.timezone
class Migration(migrations.Migration):
@ -18,22 +16,37 @@ class Migration(migrations.Migration):
migrations.CreateModel(
name='File',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('content', models.FileField(upload_to='files/%Y/%m/%d/')),
('id', models.BigAutoField(editable=False, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('path', models.CharField(max_length=255)),
('is_playbook', models.BooleanField(default=False)),
],
options={
'abstract': False,
'db_table': 'files',
},
),
migrations.CreateModel(
name='FileContent',
fields=[
('id', models.BigAutoField(editable=False, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('sha1', models.CharField(max_length=40, unique=True)),
('contents', models.BinaryField(max_length=4294967295)),
],
options={
'db_table': 'file_contents',
},
),
migrations.CreateModel(
name='Host',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('start', models.DateTimeField(auto_now_add=True, verbose_name='started')),
('ended', models.DateTimeField(auto_now=True, verbose_name='ended')),
('id', models.BigAutoField(editable=False, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('name', models.CharField(max_length=255)),
('facts', models.TextField(blank=True, null=True)),
('facts', models.BinaryField(max_length=4294967295)),
('changed', models.IntegerField(default=0)),
('failed', models.IntegerField(default=0)),
('ok', models.IntegerField(default=0)),
@ -41,100 +54,127 @@ class Migration(migrations.Migration):
('unreachable', models.IntegerField(default=0)),
],
options={
'abstract': False,
'db_table': 'hosts',
},
),
migrations.CreateModel(
name='Play',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('start', models.DateTimeField(auto_now_add=True, verbose_name='started')),
('ended', models.DateTimeField(auto_now=True, verbose_name='ended')),
('name', models.TextField(blank=True, null=True)),
('id', models.BigAutoField(editable=False, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('started', models.DateTimeField(default=django.utils.timezone.now)),
('ended', models.DateTimeField(blank=True, null=True)),
('name', models.TextField()),
],
options={
'abstract': False,
'db_table': 'plays',
},
),
migrations.CreateModel(
name='Playbook',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('start', models.DateTimeField(auto_now_add=True, verbose_name='started')),
('ended', models.DateTimeField(auto_now=True, verbose_name='ended')),
('id', models.BigAutoField(editable=False, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('started', models.DateTimeField(default=django.utils.timezone.now)),
('ended', models.DateTimeField(blank=True, null=True)),
('path', models.CharField(max_length=255)),
('ansible_version', models.CharField(max_length=255)),
('parameters', models.TextField(blank=True, null=True)),
('parameters', models.BinaryField(max_length=4294967295)),
('completed', models.BooleanField(default=False)),
],
options={
'abstract': False,
'db_table': 'playbooks',
},
),
migrations.CreateModel(
name='Record',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('start', models.DateTimeField(auto_now_add=True, verbose_name='started')),
('ended', models.DateTimeField(auto_now=True, verbose_name='ended')),
('id', models.BigAutoField(editable=False, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('key', models.CharField(max_length=255)),
('value', models.TextField(blank=True, null=True)),
('type', models.CharField(max_length=255)),
('playbook_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='records', to='api.Playbook')),
('playbook', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='records', to='api.Playbook')),
],
options={
'abstract': False,
'db_table': 'records',
},
),
migrations.CreateModel(
name='Result',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('start', models.DateTimeField(auto_now_add=True, verbose_name='started')),
('ended', models.DateTimeField(auto_now=True, verbose_name='ended')),
('status', models.CharField(blank=True, choices=[('ok', 'ok'), ('failed', 'failed'), ('skipped', 'skipped'), ('unreachable', 'unreachable')], max_length=11, null=True)),
('id', models.BigAutoField(editable=False, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('started', models.DateTimeField(default=django.utils.timezone.now)),
('ended', models.DateTimeField(blank=True, null=True)),
('status', models.CharField(choices=[('ok', 'ok'), ('failed', 'failed'), ('skipped', 'skipped'), ('unreachable', 'unreachable'), ('unknown', 'unknown')], default='unknown', max_length=25)),
('changed', models.BooleanField(default=False)),
('failed', models.BooleanField(default=False)),
('skipped', models.BooleanField(default=False)),
('unreachable', models.BooleanField(default=False)),
('ignore_errors', models.BooleanField(default=False)),
('result', models.TextField(blank=True, null=True)),
('playbook_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='results', to='api.Playbook')),
('result', models.BinaryField(max_length=4294967295)),
('playbook', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='results', to='api.Playbook')),
],
options={
'abstract': False,
'db_table': 'results',
},
),
migrations.CreateModel(
name='Task',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('start', models.DateTimeField(auto_now_add=True, verbose_name='started')),
('ended', models.DateTimeField(auto_now=True, verbose_name='ended')),
('name', models.TextField(blank=True, null=True)),
('action', models.TextField(blank=True, null=True)),
('id', models.BigAutoField(editable=False, primary_key=True, serialize=False)),
('created', models.DateTimeField(auto_now_add=True)),
('updated', models.DateTimeField(auto_now=True)),
('started', models.DateTimeField(default=django.utils.timezone.now)),
('ended', models.DateTimeField(blank=True, null=True)),
('name', models.TextField()),
('action', models.TextField()),
('lineno', models.IntegerField()),
('tags', models.TextField(blank=True, null=True)),
('tags', models.BinaryField(max_length=4294967295)),
('handler', models.BooleanField()),
('playbook_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tasks', to='api.Playbook')),
('file', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='tasks', to='api.File')),
('play', models.ForeignKey(on_delete=django.db.models.deletion.DO_NOTHING, related_name='tasks', to='api.Play')),
('playbook', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='tasks', to='api.Playbook')),
],
options={
'abstract': False,
'db_table': 'tasks',
},
),
migrations.AddField(
model_name='play',
name='playbook_id',
name='playbook',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='plays', to='api.Playbook'),
),
migrations.AddField(
model_name='host',
name='playbook_id',
name='playbook',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='hosts', to='api.Playbook'),
),
migrations.AddField(
model_name='file',
name='playbook_id',
name='content',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='files', to='api.FileContent'),
),
migrations.AddField(
model_name='file',
name='playbook',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='files', to='api.Playbook'),
),
migrations.AlterUniqueTogether(
name='record',
unique_together={('key', 'playbook')},
),
migrations.AlterUniqueTogether(
name='host',
unique_together={('name', 'playbook')},
),
migrations.AlterUniqueTogether(
name='file',
unique_together={('path', 'playbook')},
),
]

View File

@ -1,104 +1,251 @@
import uuid
# Copyright (c) 2018 Red Hat, Inc.
#
# This file is part of ARA Records Ansible.
#
# ARA is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# ARA is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with ARA. If not, see <http://www.gnu.org/licenses/>.
import logging
from django.db import models
from django.utils import timezone
logger = logging.getLogger('ara_backend.models')
class Base(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
id = models.BigAutoField(primary_key=True, editable=False)
created = models.DateTimeField(auto_now_add=True, editable=False)
updated = models.DateTimeField(auto_now=True, editable=False)
@property
def age(self):
"""
Calculates duration between created and updated.
"""
return self.updated - self.created
class Meta:
abstract = True
class DateMixin(Base):
start = models.DateTimeField(auto_now_add=True, verbose_name='started')
ended = models.DateTimeField(auto_now=True, verbose_name='ended')
class DurationMixin(models.Model):
started = models.DateTimeField(default=timezone.now)
ended = models.DateTimeField(blank=True, null=True)
@property
def duration(self):
"""
Calculates duration between started and ended or between started and
updated if we do not yet have an end.
"""
if self.ended is None:
if self.started is None:
return timezone.timedelta(seconds=0)
else:
return self.updated - self.started
return self.ended - self.started
class Meta:
abstract = True
class Playbook(DateMixin):
class Playbook(Base, DurationMixin):
"""
The 'playbook' table represents a single execution of the ansible or
ansible-playbook commands. All the data for that execution is tied back
to this one playbook.
"""
class Meta:
db_table = 'playbooks'
path = models.CharField(max_length=255)
ansible_version = models.CharField(max_length=255)
parameters = models.TextField(null=True, blank=True)
parameters = models.BinaryField(max_length=(2 ** 32) - 1)
completed = models.BooleanField(default=False)
def __str__(self):
return '<Playbook %s>' % self.id
return '<Playbook %s:%s>' % (self.id, self.path)
__repr__ = __str__
class FileContent(Base):
class Meta:
db_table = 'file_contents'
sha1 = models.CharField(max_length=40, unique=True)
contents = models.BinaryField(max_length=(2 ** 32) - 1)
def __str__(self):
return '<FileContent %s:%s>' % (self.id, self.sha1)
__repr__ = __str__
class File(Base):
content = models.FileField(upload_to='files/%Y/%m/%d/')
class Meta:
db_table = 'files'
unique_together = ('path', 'playbook',)
path = models.CharField(max_length=255)
is_playbook = models.BooleanField(default=False)
playbook_id = models.ForeignKey(Playbook, on_delete=models.CASCADE, related_name='files')
content = models.ForeignKey(FileContent,
on_delete=models.CASCADE,
related_name='files')
playbook = models.ForeignKey(Playbook,
on_delete=models.CASCADE,
related_name='files')
def __str__(self):
return '<File %s:%s>' % (self.id, self.path)
__repr__ = __str__
class Record(DateMixin):
class Record(Base):
class Meta:
db_table = 'records'
unique_together = ('key', 'playbook',)
key = models.CharField(max_length=255)
value = models.TextField(null=True, blank=True)
type = models.CharField(max_length=255)
playbook_id = models.ForeignKey(Playbook, on_delete=models.CASCADE, related_name='records')
playbook = models.ForeignKey(Playbook,
on_delete=models.CASCADE,
related_name='records')
def __str__(self):
return '<Record %s>' % self.id
return '<Record %s:%s>' % (self.id, self.key)
__repr__ = __str__
class Host(DateMixin):
class Host(Base):
class Meta:
db_table = 'hosts'
unique_together = ('name', 'playbook',)
name = models.CharField(max_length=255)
facts = models.TextField(null=True, blank=True)
facts = models.BinaryField(max_length=(2 ** 32) - 1)
changed = models.IntegerField(default=0)
failed = models.IntegerField(default=0)
ok = models.IntegerField(default=0)
skipped = models.IntegerField(default=0)
unreachable = models.IntegerField(default=0)
playbook_id = models.ForeignKey(Playbook, on_delete=models.CASCADE, related_name='hosts')
playbook = models.ForeignKey(Playbook,
on_delete=models.CASCADE,
related_name='hosts')
def __str__(self):
return '<Host %s:%s>' % (self.name, self.id)
return '<Host %s:%s>' % (self.id, self.name)
__repr__ = __str__
OK = "ok"
FAILED = "failed"
SKIPPED = "skipped"
UNREACHABLE = "unreachable"
class Play(Base, DurationMixin):
class Meta:
db_table = 'plays'
RESULT_STATUS = (
(OK, "ok"),
(FAILED, "failed"),
(SKIPPED, "skipped"),
(UNREACHABLE, "unreachable")
)
name = models.TextField()
playbook = models.ForeignKey(Playbook,
on_delete=models.CASCADE,
related_name='plays')
class Play(DateMixin):
name = models.TextField(null=True, blank=True)
playbook_id = models.ForeignKey(Playbook, on_delete=models.CASCADE, related_name='plays')
@property
def offset_from_playbook(self):
return self.started - self.playbook.started
def __str__(self):
return '<Play %s:%s>' % (self.name, self.id)
__repr__ = __str__
class Task(DateMixin):
name = models.TextField(null=True, blank=True)
action = models.TextField(null=True, blank=True)
class Task(Base, DurationMixin):
class Meta:
db_table = 'tasks'
name = models.TextField()
action = models.TextField()
lineno = models.IntegerField()
tags = models.TextField(null=True, blank=True)
tags = models.BinaryField(max_length=(2 ** 32) - 1)
handler = models.BooleanField()
playbook_id = models.ForeignKey(Playbook, on_delete=models.CASCADE, related_name='tasks')
playbook = models.ForeignKey(Playbook,
on_delete=models.CASCADE,
related_name='tasks')
file = models.ForeignKey(File,
on_delete=models.DO_NOTHING,
related_name='tasks')
play = models.ForeignKey(Play,
on_delete=models.DO_NOTHING,
related_name='tasks')
@property
def offset_from_playbook(self):
return self.started - self.playbook.started
@property
def offset_from_play(self):
return self.started - self.play.started
def __str__(self):
return '<Task %s:%s>' % (self.name, self.id)
__repr__ = __str__
class Result(DateMixin):
status = models.CharField(max_length=11, choices=RESULT_STATUS, null=True, blank=True)
class Result(Base, DurationMixin):
class Meta:
db_table = 'results'
# Ansible statuses
OK = 'ok'
FAILED = 'failed'
SKIPPED = 'skipped'
UNREACHABLE = 'unreachable'
# ARA specific statuses (derived or assumed)
CHANGED = 'changed'
IGNORED = 'ignored'
UNKNOWN = 'unknown'
RESULT_STATUS = (
(OK, 'ok'),
(FAILED, 'failed'),
(SKIPPED, 'skipped'),
(UNREACHABLE, 'unreachable'),
(UNKNOWN, 'unknown')
)
status = models.CharField(max_length=25,
choices=RESULT_STATUS,
default=UNKNOWN)
changed = models.BooleanField(default=False)
failed = models.BooleanField(default=False)
skipped = models.BooleanField(default=False)
unreachable = models.BooleanField(default=False)
ignore_errors = models.BooleanField(default=False)
result = models.TextField(null=True, blank=True)
playbook_id = models.ForeignKey(Playbook, on_delete=models.CASCADE, related_name='results')
result = models.BinaryField(max_length=(2 ** 32) - 1)
playbook = models.ForeignKey(Playbook,
on_delete=models.CASCADE,
related_name='results')
@property
def derived_status(self):
if self.status == self.OK and self.changed:
return self.CHANGED
elif self.status == self.FAILED and self.ignore_errors:
return self.IGNORED
elif self.status not in [
self.OK, self.FAILED, self.SKIPPED, self.UNREACHABLE
]:
return self.UNKNOWN
else:
return self.status
def __str__(self):
return '<Result %s>' % self.id
return '<Result %s:%s>' % (self.id, self.derived_status)
__repr__ = __str__

View File

@ -1,24 +1,207 @@
# -*- coding: utf-8 -*-
import hashlib
import json
import logging
import zlib
from api import models
from django.utils import timezone
from rest_framework import serializers
from rest_framework.exceptions import ValidationError
DATE_FORMAT = "(iso-8601: 2016-05-06T17:20:25.749489-04:00)"
DURATION_FORMAT = "([DD] [HH:[MM:]]ss[.uuuuuu])"
logger = logging.getLogger('api.serializers')
class PlaybookSerializer(serializers.ModelSerializer):
class CompressedTextField(serializers.CharField):
def to_representation(self, obj):
return zlib.decompress(obj).decode('utf8')
def to_internal_value(self, data):
return zlib.compress(data.encode('utf8'))
class CompressedObjectField(serializers.JSONField):
def to_representation(self, obj):
return json.loads(zlib.decompress(obj).decode('utf8'))
def to_internal_value(self, data):
return zlib.compress(data.encode('utf8'))
class SHA1Field(serializers.CharField):
def to_representation(self, obj):
return json.loads(lzma.decompress(obj).decode('utf8'))
def to_internal_value(self, data):
return lzma.compress(data.encode('utf8'))
class BaseSerializer(serializers.ModelSerializer):
"""
Serializer for the data in the model base
"""
created = serializers.DateTimeField(
read_only=True, help_text='Date of creation %s' % DATE_FORMAT
)
updated = serializers.DateTimeField(
read_only=True, help_text='Date of last update %s' % DATE_FORMAT
)
age = serializers.DurationField(
read_only=True,
help_text='Duration since the creation %s' % DURATION_FORMAT
)
class Meta:
abstract = True
class DurationSerializer(serializers.ModelSerializer):
"""
Serializer for duration-based fields
"""
started = serializers.DateTimeField(
initial=timezone.now().isoformat(),
help_text='Date this item started %s' % DATE_FORMAT
)
ended = serializers.DateTimeField(
required=False,
help_text='Date this item ended %s' % DATE_FORMAT
)
duration = serializers.DurationField(
read_only=True,
help_text="Duration between 'started' and 'ended' %s" % DURATION_FORMAT
)
def validate(self, data):
"""
Check that the start is before the end.
"""
if 'ended' in data and (data['started'] > data['ended']):
raise serializers.ValidationError(
"'Ended' must be before 'started'"
)
return data
class Meta:
abstract = True
class PlaybookSerializer(BaseSerializer, DurationSerializer):
class Meta:
model = models.Playbook
fields = '__all__'
plays = serializers.HyperlinkedRelatedField(
many=True,
read_only=True,
view_name='plays',
help_text='Plays associated to this playbook'
)
# tasks = serializers.HyperlinkedRelatedField(
# many=True,
# read_only=True,
# view_name='tasks',
# help_text='Tasks associated to this playbook'
# )
# hosts = serializers.HyperlinkedRelatedField(
# many=True,
# read_only=True,
# view_name='hosts',
# help_text='Hosts associated to this playbook'
# )
# results = serializers.HyperlinkedRelatedField(
# many=True,
# read_only=True,
# view_name='results',
# help_text='Results associated to this playbook'
# )
# records = serializers.HyperlinkedRelatedField(
# many=True,
# read_only=True,
# view_name='records',
# help_text='Records associated to this playbook'
# )
# files = serializers.HyperlinkedRelatedField(
# many=True,
# read_only=True,
# view_name='files',
# help_text='Records associated to this playbook'
# )
class FileSerializer(serializers.ModelSerializer):
playbook = PlaybookSerializer(source='playbook_id', read_only=True)
parameters = CompressedObjectField(
initial={},
help_text='A JSON dictionary containing Ansible command parameters'
)
path = serializers.CharField(help_text='Path to the playbook file')
ansible_version = serializers.CharField(
help_text='Version of Ansible used to run this playbook'
)
completed = serializers.BooleanField(
help_text='If the completion of the execution has been acknowledged'
)
class PlaySerializer(BaseSerializer, DurationSerializer):
class Meta:
model = models.File
model = models.Play
fields = '__all__'
class RecordSerializer(serializers.ModelSerializer):
playbook = PlaybookSerializer(source='playbook_id', read_only=True)
class TaskSerializer(BaseSerializer, DurationSerializer):
class Meta:
model = models.Task
fields = '__all__'
class HostSerializer(BaseSerializer):
class Meta:
model = models.Host
fields = '__all__'
class ResultSerializer(BaseSerializer, DurationSerializer):
class Meta:
model = models.Result
fields = '__all__'
class RecordSerializer(BaseSerializer):
class Meta:
model = models.Record
fields = '__all__'
class FileContentSerializer(BaseSerializer):
class Meta:
model = models.FileContent
fields = ('contents', 'sha1')
contents = CompressedTextField(help_text='Contents of the file')
sha1 = serializers.CharField(read_only=True, help_text='sha1 of the file')
def create(self, validated_data):
sha1 = hashlib.sha1(validated_data['contents']).hexdigest()
validated_data['sha1'] = sha1
obj, created = models.FileContent.objects.get_or_create(
**validated_data
)
return obj
class FileSerializer(BaseSerializer):
path = serializers.CharField(help_text='Path to the file')
content = FileContentSerializer()
def create(self, validated_data):
contents = validated_data.pop('content')['contents']
obj, created = models.FileContent.objects.get_or_create(
contents=contents,
sha1=hashlib.sha1(contents).hexdigest()
)
validated_data['content'] = obj
return models.File.objects.create(**validated_data)
class Meta:
model = models.File
fields = ('id', 'path', 'content', 'playbook')

View File

@ -20,15 +20,23 @@ from rest_framework.routers import DefaultRouter
from api import views
REST_FRAMEWORK = {
# Use URL-based versioning
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
'DEFAULT_VERSION': 'v1',
'ALLOWED_VERSIONS': {'v1'},
}
router = DefaultRouter()
router.register(r'playbooks', views.PlaybookViewSet, base_name='playbooks')
router.register(r'plays', views.PlayViewSet, base_name='plays')
router.register(r'tasks', views.TaskViewSet, base_name='tasks')
router.register(r'hosts', views.HostViewSet, base_name='hosts')
router.register(r'results', views.ResultViewSet, base_name='results')
router.register(r'records', views.RecordViewSet, base_name='records')
router.register(r'files', views.FileViewSet, base_name='files')
router.register(r'playbooks', views.PlaybookViewSet)
router.register(r'plays', views.PlayViewSet)
router.register(r'tasks', views.TaskViewSet)
router.register(r'hosts', views.HostViewSet)
router.register(r'results', views.ResultViewSet)
router.register(r'records', views.RecordViewSet)
router.register(r'files', views.FileViewSet)
# router.register(r'filecontent', views.FileContentViewSet)
urlpatterns = [
url(r'^', include(router.urls)),
url(r'^(?P<version>[v1]+)/', include(router.urls)),
]

View File

@ -50,6 +50,11 @@ class RecordViewSet(viewsets.ModelViewSet):
serializer_class = serializers.RecordSerializer
# class FileContentViewSet(viewsets.ModelViewSet):
# queryset = models.FileContent.objects.all()
# serializer_class = serializers.FileContentSerializer
class FileViewSet(viewsets.ModelViewSet):
queryset = models.File.objects.all()
serializer_class = serializers.FileSerializer

View File

@ -7,7 +7,7 @@ admin.site.site_header = 'Administration'
admin.site.index_title = 'Administration Ara'
routes = [
url(r'^api/v1/', include('api.urls')),
url(r'^api/', include('api.urls')),
url(r'^admin/', admin.site.urls),
]
urlpatterns = routes + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)