-
Notifications
You must be signed in to change notification settings - Fork 0
REST API
API root is located at /apps/api/
Will return the API Root with links to the other endpoints of the api.
As of right now everything is in development
Located at /api/productiondata/
(Could be changed to /api/prod-data/
)
Accepts lists on this form:
[
{
"time": "YYYY-MM-DDThh:mm:ss",
"startlat": ddd.ddddddddddddd,
"startlong": ddd.ddddddddddddd,
"endlat": ddd.ddddddddddddd,
"endlong": ddd.ddddddddddddd,
# The fields below are optional
"dry_spreader_active": <True/False>,
"plow_active": <True/False>,
"wet_spreader_active": <True/False>,
"brush_active": <True/False>
"material_type_code": <Integer>
},
...
]
Exclude "dry_spreader_active", "plow_active", "wet_spreader_active", "brush_active" or "material_type_code" fields to set them to null.
For "material_type_code" the values correspond to these materials:
- 1: Tørrsand
- 2: Saltblandet sand
- 3: Fastsan
- 4: Tørt salt (NaCl
- 5: Befuktet Salt (NaCl)
- 6: Saltslurry (NaCl)
- 7: Saltløsning (NaCl)
- 11: Befuktet salt og saltslurry
The response will echo the inserted data.
Python script:
import requests
r = requests.post('url', json=list_of_data, auth=('username', 'password'))
print(r.status_code, r.text)
from django.test import TestCase
from api.models import ProductionData
class ProductionDataTest(TestCase):
"""
Test module for ProductionData.
"""
def setUp(self):
ProductionData.objects.create(
time=timezone.now(), startlat=60.7758584, startlong=20.756444,
endlat=60.45454, endlong=20.57575, plow_active=True
)
def test_prod_data(self):
"""
Test input of one ProductionData object
"""
prod_data = ProductionData.objects.all()
# Check that there is one and only one item in prod-data table
self.assertEqual(len(prod_data), 1)
In backend/urls.py
include your api app's urls.py file.
urlpatterns = [
url(r'^', include('api.urls')),
...
]
This should be near the top of urlpatterns, because it is prioritized by order.
In your api/urls.py
file, the urlpatterns router include should be set to api/
urlpatterns = [
url(r'api/', include(router.urls))
]
Override the create method to set the many flag in the serializer to True
Example:
from rest_framework import viewsets, status
from rest_framework.response import Response
from api.models import RoadSegment
from api.serializers import RoadSegmentSerializer
class RoadSegmentViewSet(viewsets.ModelViewSet):
queryset = RoadSegment.objects.all()
serializer_class = RoadSegmentSerializer
def create(self, request, *args, **kwargs):
many = False
# Check if the incoming data is a list
# If it is a list set the many flag to True
if isinstance(request.data, list):
many = True
# Instantiates the serializer
serializer = self.get_serializer(data=request.data, many=many)
# Checks if the serializer is valid and takes the necessary actions
if serializer.is_valid():
serializer.save()
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED,
headers=headers)
# If not valid return error
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Example of POST request:
import requests
r = requests.post(url, json=roads)
print(r.text)
Use the json field to pass the data (for lists).
See the Django and Django Rest Framework documentation for more informantion
api/urls.py
is the file where the api is routed. In the file there is a section where the router is declared and the different viewsets are registered and a urlpatterns variable that just places the api router on /api/
. In the routing section, to add a new endpoint, just add new line with router.register(r'<endpoint>', <viewset>)
If is replaced with users, /api/users
will be the endpoint for the viewset.
It is fairly easy to change the name of an endpoint.
Using a ModelViewSet
list, create, retrieve, update and destroy actions are provided and do need to be declared, but can be overridden.
There are others like ReadOnlyModelViewSet
(pretty self explanatory what it does).
The queryset variable is what set of data from the database to use with the serializer. In case of a basic viewset for a database model set it to queryset = <modelname>.objects.all()
.
We may have to use other, more complex queries for some of the functionality.
Selects which serializer to use for serializing and deserializing the data from/to the database. See the Serializer section for more information.
Some viewsets, like ReadOnlyModelViewSet
have inbuilt permissions.
You can set your own permissions by doing this:
permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
permission_classes takes a tuple. IsAuthenticatedOrReadOnly is a built in permission class and there are a few others, but you can make your own permission classes. (See below)
Make an api/permissions.py
file.
Example:
from rest_framework import permissions
class IsAdminOrReadOnly(permissions.BasePermission):
"""
Custom permission to only allow admins to create and edit.
Read only for normal users and unauthorized users.
"""
def has_permission(self, request, view):
if request.method in permissions.SAFE_METHODS:
return True
else:
return request.user.is_staff
Description of class in docstring.
Checks if user is flagged as is_staff
.
The permissions.SAFE_METHODS is a tuple containing the request methods GET, OPTIONS and HEAD.
A serializer allow complex data such as model instances to be converted to native Python data types that can be rendered into html or json. It also provide deserialization, allowing data to be converted back to complex data.
A HyperlinkedModelSerializer is a ModelSerializer that generates a url for each row in a database table. Say you are making a serializer for the built in User model. If you use a HyperlinkedModelSerializer add a 'url' field to the fields metaoption and do a GET request to the api user endpoint. It will list all the users and also give a url to each specific user that can used for another GET request.
The Meta class has many different options that can be set.
The model option specifies which model this serializer uses (database model). model = User
The fields option selects which fields from the model the serializer uses when serializing/deserializing. fields = ('id', 'username')
Similar to serializers the models supports metadata.
The abstract option sets the model to be abstract. This can be used to make a template model with base fields if you want several of your models to have some of the same fields.
Example:
class BaseModel(models.Model):
id = models.AutoField(primary_key=True)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
Usage:
class ProductionData(BaseModel):
This will include the three fields of BaseModel in ProductionData. The id field is the primary key and will auto increment. It will (should) also make it so the created field is set to the date at which the row was created and the updated field should be set to the current datetime every time the row is updated. Fields with auto_now_add=True
and auto_now=True
also have editable=False
by default
-
Product related
-
Development process and guidelines
-
Sprints
-
Testing
-
-
Old notes