forked from jrief/django-angular
-
Notifications
You must be signed in to change notification settings - Fork 0
/
crud.py
165 lines (139 loc) · 6.04 KB
/
crud.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# -*- coding: utf-8 -*-
import json
from django.core.exceptions import ValidationError
from django.core import serializers
from django.forms.models import modelform_factory
from django.views.generic import FormView
from djangular.views.mixins import JSONBaseMixin, JSONResponseException
class NgMissingParameterError(ValueError):
pass
class NgCRUDView(JSONBaseMixin, FormView):
"""
Basic view to support default angular $resource CRUD actions on server side
Subclass and override ``model`` with your model
Optional 'pk' GET parameter must be passed when object identification is required (save to update and delete)
If fields != None the serialized data will only contain field names from fields array
"""
model = None
fields = None
slug_field = 'slug'
serialize_natural_keys = False
def dispatch(self, request, *args, **kwargs):
"""
Override dispatch to call appropriate methods:
* $query - ng_query
* $get - ng_get
* $save - ng_save
* $delete and $remove - ng_delete
"""
try:
if request.method == 'GET':
if 'pk' in request.GET or self.slug_field in request.GET:
return self.ng_get(request, *args, **kwargs)
return self.ng_query(request, *args, **kwargs)
elif request.method == 'POST':
return self.ng_save(request, *args, **kwargs)
elif request.method == 'DELETE':
return self.ng_delete(request, *args, **kwargs)
except self.model.DoesNotExist as e:
return self.error_json_response(e.args[0], 404)
except NgMissingParameterError as e:
return self.error_json_response(e.args[0])
except JSONResponseException as e:
return self.error_json_response(e.args[0], e.status_code)
except ValidationError as e:
if hasattr(e, 'error_dict'):
return self.error_json_response('Form not valid', detail=e.message_dict)
else:
return self.error_json_response(e.message)
return self.error_json_response('This view can not handle method {0}'.format(request.method), 405)
def get_form_class(self):
"""
Build ModelForm from model
"""
return modelform_factory(self.model)
def build_json_response(self, data, **kwargs):
return self.json_response(self.serialize_to_json(data), separators=(',', ':'), **kwargs)
def error_json_response(self, message, status_code=400, detail=None):
response_data = {
"message": message,
"detail": detail,
}
return self.json_response(response_data, status=status_code, separators=(',', ':'))
def serialize_to_json(self, queryset):
"""
Return JSON serialized data
serialize() only works on iterables, so to serialize a single object we put it in a list
"""
object_data = []
is_queryset = False
query_fields = self.get_fields()
try:
iter(queryset)
is_queryset = True
raw_data = serializers.serialize('python', queryset, fields=query_fields, use_natural_keys=self.serialize_natural_keys)
except TypeError: # Not iterable
raw_data = serializers.serialize('python', [queryset, ], fields=query_fields, use_natural_keys=self.serialize_natural_keys)
for obj in raw_data: # Add pk to fields
obj['fields']['pk'] = obj['pk']
object_data.append(obj['fields'])
if is_queryset:
return object_data
return object_data[0] # If there's only one object
def get_form_kwargs(self):
kwargs = super(NgCRUDView, self).get_form_kwargs()
# Since angular sends data in JSON rather than as POST parameters, the default data (request.POST)
# is replaced with request.body that contains JSON encoded data
kwargs['data'] = json.loads(self.request.body.decode('utf-8'))
if 'pk' in self.request.GET or self.slug_field in self.request.GET:
kwargs['instance'] = self.get_object()
return kwargs
def get_object(self):
if 'pk' in self.request.GET:
return self.model.objects.get(pk=self.request.GET['pk'])
elif self.slug_field in self.request.GET:
return self.model.objects.get(**{self.slug_field: self.request.GET[self.slug_field]})
raise NgMissingParameterError("Attempted to get an object by 'pk' or slug field, but no identifier is present. Missing GET parameter?")
def get_fields(self):
"""
Get fields to return from a query.
Can be overridden (e.g. to use a query parameter).
"""
return self.fields
def get_queryset(self):
"""
Get query to use in ng_query
Allows for easier overriding
"""
return self.model.objects.all()
def ng_query(self, request, *args, **kwargs):
"""
Used when angular's query() method is called
Build an array of all objects, return json response
"""
return self.build_json_response(self.get_queryset())
def ng_get(self, request, *args, **kwargs):
"""
Used when angular's get() method is called
Returns a JSON response of a single object dictionary
"""
return self.build_json_response(self.get_object())
def ng_save(self, request, *args, **kwargs):
"""
Called on $save()
Use modelform to save new object or modify an existing one
"""
form = self.get_form(self.get_form_class())
if form.is_valid():
obj = form.save()
return self.build_json_response(obj)
raise ValidationError(form.errors)
def ng_delete(self, request, *args, **kwargs):
"""
Delete object and return it's data in JSON encoding
"""
if 'pk' not in request.GET:
raise NgMissingParameterError("Object id is required to delete.")
obj = self.get_object()
obj.delete()
return self.build_json_response(obj)