From 4e503b22864b347dbc3cb22a2dc30c5ed62b21ca Mon Sep 17 00:00:00 2001 From: Vitalii Boiko Date: Tue, 15 May 2018 11:31:43 -0500 Subject: [PATCH 1/6] initial commit models added basic view added --- .gitignore | 2 + config/settings/base.py | 2 +- hhcodingtask/synthetic/__init__.py | 0 hhcodingtask/synthetic/admin.py | 3 + hhcodingtask/synthetic/apps.py | 6 ++ hhcodingtask/synthetic/migrations/__init__.py | 0 hhcodingtask/synthetic/models.py | 23 +++++++ hhcodingtask/synthetic/tests.py | 3 + hhcodingtask/synthetic/urls.py | 11 ++++ hhcodingtask/synthetic/views.py | 61 +++++++++++++++++++ 10 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 hhcodingtask/synthetic/__init__.py create mode 100644 hhcodingtask/synthetic/admin.py create mode 100644 hhcodingtask/synthetic/apps.py create mode 100644 hhcodingtask/synthetic/migrations/__init__.py create mode 100644 hhcodingtask/synthetic/models.py create mode 100644 hhcodingtask/synthetic/tests.py create mode 100644 hhcodingtask/synthetic/urls.py create mode 100644 hhcodingtask/synthetic/views.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d04a2dd --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.py[cod] + diff --git a/config/settings/base.py b/config/settings/base.py index 8227c34..b5e97e0 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -71,7 +71,7 @@ ] LOCAL_APPS = [ 'hhcodingtask.users.apps.UsersConfig', - # Your stuff: custom apps go here + 'hhcodingtask.synthetic.apps.SyntheticConfig' ] # https://docs.djangoproject.com/en/dev/ref/settings/#installed-apps INSTALLED_APPS = DJANGO_APPS + THIRD_PARTY_APPS + LOCAL_APPS diff --git a/hhcodingtask/synthetic/__init__.py b/hhcodingtask/synthetic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hhcodingtask/synthetic/admin.py b/hhcodingtask/synthetic/admin.py new file mode 100644 index 0000000..8c38f3f --- /dev/null +++ b/hhcodingtask/synthetic/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/hhcodingtask/synthetic/apps.py b/hhcodingtask/synthetic/apps.py new file mode 100644 index 0000000..b8b23dc --- /dev/null +++ b/hhcodingtask/synthetic/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class SyntheticConfig(AppConfig): + name = 'hhcodingtask.synthetic' + verbose_name = 'Synthethic' diff --git a/hhcodingtask/synthetic/migrations/__init__.py b/hhcodingtask/synthetic/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hhcodingtask/synthetic/models.py b/hhcodingtask/synthetic/models.py new file mode 100644 index 0000000..d43391e --- /dev/null +++ b/hhcodingtask/synthetic/models.py @@ -0,0 +1,23 @@ +from django.db import models + +from schematics.models import Model as SchematicsModel +from schematics.types import StringType, DecimalType, DateTimeType + +from datetime import datetime + + +class AnyData(SchematicsModel): + city = StringType() + temperature = DecimalType() + taken_at = DateTimeType(default=datetime.now) + + +class GenericModel(models.Model): + any_data = JSONField() + + def __unicode__(self): + return unicode(self.id) + + def to_dict(self): + return AnyData(raw_data=self.any_data).to_native() + diff --git a/hhcodingtask/synthetic/tests.py b/hhcodingtask/synthetic/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/hhcodingtask/synthetic/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/hhcodingtask/synthetic/urls.py b/hhcodingtask/synthetic/urls.py new file mode 100644 index 0000000..aa7388d --- /dev/null +++ b/hhcodingtask/synthetic/urls.py @@ -0,0 +1,11 @@ +from django.conf.urls import url + +from . import views + +app_name = 'synthetic' + +urlpatterns = [ + url(regex=r"^$", view=views.GenericView.as_view(), name="synthetic_list"), + url(regex=r"^update/(?P\d+)/$", view=views.GenericDetailView.as_view(), + name="synthetic_update"), +] diff --git a/hhcodingtask/synthetic/views.py b/hhcodingtask/synthetic/views.py new file mode 100644 index 0000000..3b64ff4 --- /dev/null +++ b/hhcodingtask/synthetic/views.py @@ -0,0 +1,61 @@ +from django.http import JsonResponse, Http404 +from django.shortcuts import render +from django.views.generic import View + +from .models import GenericModel +from schematics.exceptions import ModelValidationError, ModelConversionError + + +class GenericView(View): + http_method_names = ['post', 'get'] + + def post(self): + data = json.loads(request.body.decode()) + try: + data_obj = AnyData(raw_data=data) + data_obj.validate() + kwargs = data_obj.to_native() + result_obj = GenericModel.objects.create(**kwargs) + + return_data = AnyData(result_obj.to_dict()) + return JsonResponse(data=return_data, status=201) + except (ModelValidationError, ModelConversionError) as exc: + return JsonResponse(exc.messages, status=400) + + def get(self): + qs = GenericModel.objects.all() + items = [AnyData(i.to_dict()).to_native() for i in qs] + return_data = {'items': items, 'total': len(items)} + return JsonResponse(data=return_data) + + +class GenericDetailView(View): + + def get_or_404(self, pk): + try: + GenericModel.objects.get(pk=pk) + except GenericModel.DoesNotExist + raise Http404 + + def delete(self, request, pk): + obj = self.get_or_404(pk=pk) + obj.delete() + return JsonResponse(data={}, status=204) + + def patch(self, request, pk): + data = json.loads(request.body.decode()) + try: + data_obj = AnyData(raw_data=data) + kwargs = data_obj.to_native() + + obj = self.get_or_404(pk=pk) + obj.update(**kwargs) + + return_data = AnyData(obj.to_dict()).to_native() + return JsonResponse(data=data, status=202) + except ModelConversionError as exc: + data = {field: msg for field, value in exc.messages.iteritems()} + return JsonResponse(data=data, status=400) + except ModelValidationError as exc: + return JsonResponse(exc.messages, status=400) + From c24447928ac8dea9c7dd29cfed27cea07da2e37e Mon Sep 17 00:00:00 2001 From: Vitalii Boiko Date: Tue, 15 May 2018 14:50:00 -0500 Subject: [PATCH 2/6] code covered with unit tests --- hhcodingtask/synthetic/admin.py | 41 +++++++- hhcodingtask/synthetic/models.py | 12 ++- hhcodingtask/synthetic/tests/__init__.py | 0 hhcodingtask/synthetic/tests/test_generic.py | 101 +++++++++++++++++++ hhcodingtask/synthetic/tests/test_urls.py | 13 +++ hhcodingtask/synthetic/urls.py | 6 +- hhcodingtask/synthetic/views.py | 57 ++++++----- 7 files changed, 198 insertions(+), 32 deletions(-) create mode 100644 hhcodingtask/synthetic/tests/__init__.py create mode 100644 hhcodingtask/synthetic/tests/test_generic.py create mode 100644 hhcodingtask/synthetic/tests/test_urls.py diff --git a/hhcodingtask/synthetic/admin.py b/hhcodingtask/synthetic/admin.py index 8c38f3f..a3ebcc0 100644 --- a/hhcodingtask/synthetic/admin.py +++ b/hhcodingtask/synthetic/admin.py @@ -1,3 +1,42 @@ +from django import forms from django.contrib import admin +from .models import GenericModel, AnyData -# Register your models here. + +class GenericChangeForm(forms.ModelForm): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + for field, kind in AnyData().fields.items(): + self.fields[field] = forms.CharField() + # TODO: add date type, populate with instance's data + if self.instance.pk: + pass + + def clean(self): + try: + data_obj = AnyData(raw_data=self.cleaned_data) + data_obj.validate() + except (ModelValidationError, ModelConversionError) as exc: + result = {} + for field, messages in exc.messages.items(): + raise forms.ValidationError(str(exc.messages[field].to_primitive())) + return self.cleaned_data + + class Meta: + model = GenericModel + #fields = "__all__" + exclude = ['any_data'] + + +@admin.register(GenericModel) +class GenericModelAdmin(admin.ModelAdmin): + form = GenericChangeForm + + fieldsets = ( + (None, { + 'fields': ('city',), + }), + ) + + # TODO: define get_list_display, make fieldsets dynamic, etc \ No newline at end of file diff --git a/hhcodingtask/synthetic/models.py b/hhcodingtask/synthetic/models.py index d43391e..a7bdcdc 100644 --- a/hhcodingtask/synthetic/models.py +++ b/hhcodingtask/synthetic/models.py @@ -1,4 +1,7 @@ +import json + from django.db import models +from jsonfield import JSONField from schematics.models import Model as SchematicsModel from schematics.types import StringType, DecimalType, DateTimeType @@ -7,8 +10,8 @@ class AnyData(SchematicsModel): - city = StringType() - temperature = DecimalType() + city = StringType(required=True) + temperature = DecimalType(required=True) taken_at = DateTimeType(default=datetime.now) @@ -19,5 +22,6 @@ def __unicode__(self): return unicode(self.id) def to_dict(self): - return AnyData(raw_data=self.any_data).to_native() - + result = AnyData(raw_data=json.loads(self.any_data)).to_native() + result['id'] = self.pk + return result diff --git a/hhcodingtask/synthetic/tests/__init__.py b/hhcodingtask/synthetic/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/hhcodingtask/synthetic/tests/test_generic.py b/hhcodingtask/synthetic/tests/test_generic.py new file mode 100644 index 0000000..6418b21 --- /dev/null +++ b/hhcodingtask/synthetic/tests/test_generic.py @@ -0,0 +1,101 @@ +import json + +from django.test import TestCase, Client +from rest_framework import status + +class TestCreateGenericObject(TestCase): + + def setUp(self): + super().setUp() + self.client = Client() + + def test_create_fails(self): + data = {'city': 'bad city'} + response = self.client.post('/synthetic/', content_type='application/json', + data=json.dumps(data)) + assert response.status_code == status.HTTP_400_BAD_REQUEST + + def test_create(self): + data = {'city': 'London', 'temperature': 18} + response = self.client.post('/synthetic/', content_type='application/json', + data=json.dumps(data)) + + assert response.status_code == status.HTTP_201_CREATED + + +class TestUpdateGenericObject(TestCase): + + def setUp(self): + super().setUp() + self.client = Client() + + data = {'city': 'London', 'temperature': 18} + response = self.client.post('/synthetic/', content_type='application/json', + data=json.dumps(data)) + + self.obj = response.json() + + def test_update_succeeds(self): + pk = self.obj['id'] + data = {'city': 'London', 'temperature': 18} + response = self.client.patch('/synthetic/detail/{}/'.format(pk), + content_type='application/json', data=json.dumps(data)) + + assert response.status_code == status.HTTP_202_ACCEPTED + + def test_update_fails(self): + data = {'city': 'nowhere'} + + pk = self.obj['id'] + response = self.client.patch('/synthetic/detail/{}/'.format(pk), + content_type='application/json', data=json.dumps(data)) + + #import rpdb;rpdb.set_trace() + assert response.status_code == status.HTTP_400_BAD_REQUEST + + +class TestListGenericModel(TestCase): + + def setUp(self): + super().setUp() + self.client = Client() + + data = {'city': 'London', 'temperature': 18} + response = self.client.post('/synthetic/', content_type='application/json', + data=json.dumps(data)) + + self.obj = response.json() + + def test_list(self): + response = self.client.get('/synthetic/') + obj = response.json() + + assert response.status_code == status.HTTP_200_OK + assert int(obj['total'] == 1) + + for pk in obj['items'].keys(): + assert pk == '1' + item = obj['items'][pk] + assert item['city'] + assert item['temperature'] + assert item['taken_at'] + + +class TestDeleteGenericObject(TestCase): + + def setUp(self): + super().setUp() + self.client = Client() + self.data = {'city': 'London', 'temperature': 18} + + response = self.client.post('/synthetic/', content_type='application/json', + data=json.dumps(self.data)) + self.obj = response.json() + + def test_delete(self): + response = self.client.delete('/synthetic/detail/{}/'.format(self.obj['id'])) + assert response.status_code == status.HTTP_204_NO_CONTENT + + response = self.client.get('/synthetic/') + obj = response.json() + assert self.obj['id'] not in obj diff --git a/hhcodingtask/synthetic/tests/test_urls.py b/hhcodingtask/synthetic/tests/test_urls.py new file mode 100644 index 0000000..6ca78b2 --- /dev/null +++ b/hhcodingtask/synthetic/tests/test_urls.py @@ -0,0 +1,13 @@ +from django.urls import reverse, resolve + +from test_plus.test import TestCase + + +class TestSyntheticURLs(TestCase): + """Test URL patterns for users app.""" + + def test_list_reverse(self): + self.assertEqual(reverse("synthetic:list"), "/synthetic/") + + def test_update_reverse(self): + self.assertEqual(reverse("synthetic:detail", kwargs={'pk': 8}), "/synthetic/detail/8/") diff --git a/hhcodingtask/synthetic/urls.py b/hhcodingtask/synthetic/urls.py index aa7388d..81852dd 100644 --- a/hhcodingtask/synthetic/urls.py +++ b/hhcodingtask/synthetic/urls.py @@ -5,7 +5,7 @@ app_name = 'synthetic' urlpatterns = [ - url(regex=r"^$", view=views.GenericView.as_view(), name="synthetic_list"), - url(regex=r"^update/(?P\d+)/$", view=views.GenericDetailView.as_view(), - name="synthetic_update"), + url(regex=r"^$", view=views.GenericView.as_view(), name="list"), + url(regex=r"^detail/(?P\d+)/$", view=views.GenericDetailView.as_view(), + name="detail"), ] diff --git a/hhcodingtask/synthetic/views.py b/hhcodingtask/synthetic/views.py index 3b64ff4..47a3aac 100644 --- a/hhcodingtask/synthetic/views.py +++ b/hhcodingtask/synthetic/views.py @@ -1,30 +1,40 @@ +import json + from django.http import JsonResponse, Http404 from django.shortcuts import render from django.views.generic import View -from .models import GenericModel +from .models import GenericModel, AnyData from schematics.exceptions import ModelValidationError, ModelConversionError class GenericView(View): http_method_names = ['post', 'get'] - def post(self): + def post(self, request): data = json.loads(request.body.decode()) try: data_obj = AnyData(raw_data=data) data_obj.validate() - kwargs = data_obj.to_native() - result_obj = GenericModel.objects.create(**kwargs) + response = data_obj.serialize() + result_obj = GenericModel.objects.create( + any_data=json.dumps(response)) - return_data = AnyData(result_obj.to_dict()) - return JsonResponse(data=return_data, status=201) + response['id'] = result_obj.pk + return JsonResponse(data=response, status=201) except (ModelValidationError, ModelConversionError) as exc: - return JsonResponse(exc.messages, status=400) + result = {} + for field, messages in exc.messages.items(): + result[field] = exc.messages[field].to_primitive() + return JsonResponse(result, status=400) - def get(self): + def get(self, request): qs = GenericModel.objects.all() - items = [AnyData(i.to_dict()).to_native() for i in qs] + items = {} + for bits in qs.values_list('pk', 'any_data'): + pk = bits[0] + data = AnyData(raw_data=json.loads(bits[1])).to_native() + items[pk] = data return_data = {'items': items, 'total': len(items)} return JsonResponse(data=return_data) @@ -33,8 +43,8 @@ class GenericDetailView(View): def get_or_404(self, pk): try: - GenericModel.objects.get(pk=pk) - except GenericModel.DoesNotExist + return GenericModel.objects.get(pk=pk) + except GenericModel.DoesNotExist: raise Http404 def delete(self, request, pk): @@ -45,17 +55,16 @@ def delete(self, request, pk): def patch(self, request, pk): data = json.loads(request.body.decode()) try: - data_obj = AnyData(raw_data=data) - kwargs = data_obj.to_native() - obj = self.get_or_404(pk=pk) - obj.update(**kwargs) - - return_data = AnyData(obj.to_dict()).to_native() - return JsonResponse(data=data, status=202) - except ModelConversionError as exc: - data = {field: msg for field, value in exc.messages.iteritems()} - return JsonResponse(data=data, status=400) - except ModelValidationError as exc: - return JsonResponse(exc.messages, status=400) - + data_obj = AnyData(raw_data=data) + data_obj.validate() + response = data_obj.serialize() + obj.any_data = json.dumps(response) + obj.save() + response['id'] = obj.pk + return JsonResponse(data=response, status=202) + except (ModelValidationError, ModelConversionError) as exc: + result = {} + for field, messages in exc.messages.items(): + result[field] = exc.messages[field].to_primitive() + return JsonResponse(result, status=400) From 4145b0db29fb74255d03f71ceccd2d3621e78fe0 Mon Sep 17 00:00:00 2001 From: Vitalii Boiko Date: Tue, 15 May 2018 15:36:39 -0500 Subject: [PATCH 3/6] web form added --- .gitignore | 1 + config/urls.py | 1 + hhcodingtask/static/js/synthetic.js | 108 +++++++++++++++++++++ hhcodingtask/templates/synthetic/form.html | 45 +++++++++ 4 files changed, 155 insertions(+) create mode 100644 hhcodingtask/static/js/synthetic.js create mode 100644 hhcodingtask/templates/synthetic/form.html diff --git a/.gitignore b/.gitignore index d04a2dd..00f1783 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ +syntax: glob *.py[cod] diff --git a/config/urls.py b/config/urls.py index 15cfe0b..b1c9d9f 100644 --- a/config/urls.py +++ b/config/urls.py @@ -20,6 +20,7 @@ include("hhcodingtask.users.urls", namespace="users"), ), url(r"^accounts/", include("allauth.urls")), + url(r"^synthetic/", include("hhcodingtask.synthetic.urls", namespace="synthetic")), # Your stuff: custom urls includes go here ] + static( settings.MEDIA_URL, document_root=settings.MEDIA_ROOT diff --git a/hhcodingtask/static/js/synthetic.js b/hhcodingtask/static/js/synthetic.js new file mode 100644 index 0000000..41d47bc --- /dev/null +++ b/hhcodingtask/static/js/synthetic.js @@ -0,0 +1,108 @@ +(function($) { + + $.synthetic = function(options) { + if (typeof(options) == "undefined" || options == null) { + options = {}; + }; + + var API = { + options: $.extend({ + rpc_url: "", + onSuccess: function(data, status) {}, + onFailure: function(data) {}, + onProgress: function(evt) {}, + errorElementID: '' + }, + options), + get_json: function(data, callback, error_callback) { + $.ajax({ + url: API.options.rpc_url, + cache: false, + dataType: 'json', + data: data, + beforeSend: function(xhr) { + // set auth token here + }, + success: function(data, status) { + if (data && data.hasOwnProperty('error')) { + if (!error_callback) { + API.error_happened(); + } else { + error_callback(data); + } + return; + } else { + $('#failure').hide(); + } + if (callback) callback(data, status); + } + }).fail(function(jqxhr, textStatus, error) { + var err = textStatus + ', ' + error; + if (error != '') API.error_happened(err); + }); + }, + post_json: function(url, form_data, headers, onSuccess, onFailure, onProgress) { + // ``form_data`` -- instance of ``FormData`` + $.ajax({ + url: url, + type: 'POST', + cache: false, + dataType: 'json', + data: form_data, + processData: false, + contentType: false, + timeout: 480000, + headers: headers, + success: function(data, status, jqXHR) { + if (data && data.hasOwnProperty('error')) { + onFailure(NaN, NaN, data); + return; + } + onSuccess(data, status); + }, + beforeSend: function(xhr) { + // set auth header here + // xhr.setRequestHeader("authorization", "Token ..."); + }, + xhr: function() { + var req = $.ajaxSettings.xhr(); + if (req) { + if (typeof req.upload == "object") { + req.upload.addEventListener("progress", function(evt) { + onProgress(evt); + }); + } + } + return req; + } + }).fail(function(xhr, status, msg) { + var e = msg; + try { + e = $.parseJSON(xhr.responseText); + } catch (e) {}; + onFailure(xhr, status, e); + }); + }, + error_happened: function(msg) { + var message = "Error loading content."; + if (msg != undefined) { + message = msg; + } + $('#' + API.options.errorElementID).empty().append('' + message + ''); + }, + get_objects_list: function() { + API.get_json(); + }, + create_object: function(city, temperature){ + var form_data = new FormData(); + form_data.append('city', city); + form_data.append('temperature', temperature); + API.post_json(API.options.rpc_url, JSON.stringify(form_data), {}, function(data, status){}, function(xhr, status, msg){}, function(evt){}); + } + }; + return { + list: API.get_objects_list, + create: API.create_object + }; + }; +})(jQuery); diff --git a/hhcodingtask/templates/synthetic/form.html b/hhcodingtask/templates/synthetic/form.html new file mode 100644 index 0000000..06a306e --- /dev/null +++ b/hhcodingtask/templates/synthetic/form.html @@ -0,0 +1,45 @@ +{% load static %} + + + + + + + + +
+ + + + + + + + + + + +
CityTemperature +Taken At
 
+ + + + From 4acce6cfe0e4a6bc9b79dc8a1ed9f08e2857e726 Mon Sep 17 00:00:00 2001 From: Vitalii Boiko Date: Tue, 15 May 2018 15:37:12 -0500 Subject: [PATCH 4/6] missing files added --- config/settings/local.py | 1 + hhcodingtask/synthetic/admin.py | 8 +------- hhcodingtask/synthetic/urls.py | 2 ++ hhcodingtask/synthetic/views.py | 6 +++++- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/config/settings/local.py b/config/settings/local.py index d1704d3..92981c2 100644 --- a/config/settings/local.py +++ b/config/settings/local.py @@ -12,6 +12,7 @@ "localhost", "0.0.0.0", "127.0.0.1", + "31.28.168.164" ] # DATABASES diff --git a/hhcodingtask/synthetic/admin.py b/hhcodingtask/synthetic/admin.py index a3ebcc0..8488368 100644 --- a/hhcodingtask/synthetic/admin.py +++ b/hhcodingtask/synthetic/admin.py @@ -33,10 +33,4 @@ class Meta: class GenericModelAdmin(admin.ModelAdmin): form = GenericChangeForm - fieldsets = ( - (None, { - 'fields': ('city',), - }), - ) - - # TODO: define get_list_display, make fieldsets dynamic, etc \ No newline at end of file + # TODO: define get_list_display, make fieldsets dynamic, etc diff --git a/hhcodingtask/synthetic/urls.py b/hhcodingtask/synthetic/urls.py index 81852dd..66c7179 100644 --- a/hhcodingtask/synthetic/urls.py +++ b/hhcodingtask/synthetic/urls.py @@ -1,4 +1,5 @@ from django.conf.urls import url +from django.urls import path from . import views @@ -6,6 +7,7 @@ urlpatterns = [ url(regex=r"^$", view=views.GenericView.as_view(), name="list"), + url(regex=r"^form/", view=views.GenericFormView.as_view()), url(regex=r"^detail/(?P\d+)/$", view=views.GenericDetailView.as_view(), name="detail"), ] diff --git a/hhcodingtask/synthetic/views.py b/hhcodingtask/synthetic/views.py index 47a3aac..72b2809 100644 --- a/hhcodingtask/synthetic/views.py +++ b/hhcodingtask/synthetic/views.py @@ -2,7 +2,7 @@ from django.http import JsonResponse, Http404 from django.shortcuts import render -from django.views.generic import View +from django.views.generic import View, TemplateView from .models import GenericModel, AnyData from schematics.exceptions import ModelValidationError, ModelConversionError @@ -68,3 +68,7 @@ def patch(self, request, pk): for field, messages in exc.messages.items(): result[field] = exc.messages[field].to_primitive() return JsonResponse(result, status=400) + + +class GenericFormView(TemplateView): + template_name = 'synthetic/form.html' From 08228ddaf31c962beb2e8cd790bacc37457c62b3 Mon Sep 17 00:00:00 2001 From: Vitalii Boiko Date: Tue, 15 May 2018 15:57:55 -0500 Subject: [PATCH 5/6] UI for creating and listing objects added --- hhcodingtask/static/js/synthetic.js | 25 ++++++---------------- hhcodingtask/synthetic/views.py | 10 +++++++-- hhcodingtask/templates/synthetic/form.html | 16 +++++++++----- 3 files changed, 26 insertions(+), 25 deletions(-) diff --git a/hhcodingtask/static/js/synthetic.js b/hhcodingtask/static/js/synthetic.js index 41d47bc..658e672 100644 --- a/hhcodingtask/static/js/synthetic.js +++ b/hhcodingtask/static/js/synthetic.js @@ -14,26 +14,15 @@ errorElementID: '' }, options), - get_json: function(data, callback, error_callback) { + get_json: function(callback, error_callback) { $.ajax({ url: API.options.rpc_url, cache: false, dataType: 'json', - data: data, beforeSend: function(xhr) { // set auth token here }, success: function(data, status) { - if (data && data.hasOwnProperty('error')) { - if (!error_callback) { - API.error_happened(); - } else { - error_callback(data); - } - return; - } else { - $('#failure').hide(); - } if (callback) callback(data, status); } }).fail(function(jqxhr, textStatus, error) { @@ -91,13 +80,13 @@ $('#' + API.options.errorElementID).empty().append('' + message + ''); }, get_objects_list: function() { - API.get_json(); + API.get_json(API.options.onSuccess, API.options.onFailure); }, - create_object: function(city, temperature){ - var form_data = new FormData(); - form_data.append('city', city); - form_data.append('temperature', temperature); - API.post_json(API.options.rpc_url, JSON.stringify(form_data), {}, function(data, status){}, function(xhr, status, msg){}, function(evt){}); + create_object: function(city, temperature, callback){ + var data = JSON.stringify({'city': city, 'temperature': temperature}); + API.post_json(API.options.rpc_url, data, {}, function(data, status){ + if(callback) callback(data); + }, function(xhr, status, msg){}, function(evt){}); } }; return { diff --git a/hhcodingtask/synthetic/views.py b/hhcodingtask/synthetic/views.py index 72b2809..61d8afc 100644 --- a/hhcodingtask/synthetic/views.py +++ b/hhcodingtask/synthetic/views.py @@ -3,12 +3,18 @@ from django.http import JsonResponse, Http404 from django.shortcuts import render from django.views.generic import View, TemplateView +from django.utils.decorators import method_decorator +from django.views.decorators.csrf import csrf_exempt from .models import GenericModel, AnyData from schematics.exceptions import ModelValidationError, ModelConversionError +class CSRFExemptMixin(View): + @method_decorator(csrf_exempt) + def dispatch(self, *args, **kwargs): + return super(CSRFExemptMixin, self).dispatch(*args, **kwargs) -class GenericView(View): +class GenericView(CSRFExemptMixin): http_method_names = ['post', 'get'] def post(self, request): @@ -39,7 +45,7 @@ def get(self, request): return JsonResponse(data=return_data) -class GenericDetailView(View): +class GenericDetailView(CSRFExemptMixin): def get_or_404(self, pk): try: diff --git a/hhcodingtask/templates/synthetic/form.html b/hhcodingtask/templates/synthetic/form.html index 06a306e..7379e84 100644 --- a/hhcodingtask/templates/synthetic/form.html +++ b/hhcodingtask/templates/synthetic/form.html @@ -4,12 +4,16 @@