Skip to content
Open
Show file tree
Hide file tree
Changes from 101 commits
Commits
Show all changes
190 commits
Select commit Hold shift + click to select a range
e46d0f1
WIP
aleontiev Feb 28, 2017
4fc0a93
WIP
aleontiev Feb 28, 2017
5f44421
Merge branch 'master' of github.com:AltSchool/dynamic-rest into featu…
aleontiev Mar 1, 2017
f949a2b
processed data
aleontiev Mar 2, 2017
8503299
Improve design for details view
lynnetye Mar 6, 2017
60601bb
Provide interface for uploading file
lynnetye Mar 6, 2017
7349d99
Improve styling
lynnetye Mar 7, 2017
4aa3406
working CSV upload support
aleontiev Mar 8, 2017
d507e25
add csv upload test
aleontiev Mar 9, 2017
e96353a
Merge pull request #155 from AltSchool/feature/drest-admin-view-csv
aleontiev Mar 9, 2017
1c4e075
Merge branch 'integration/admin-view' of https://github.com/AltSchool…
lynnetye Mar 9, 2017
219eb0f
add disable envelope to list serializer
aleontiev Mar 9, 2017
75b8842
Merge branch 'integration/admin-view' of https://github.com/AltSchool…
lynnetye Mar 9, 2017
04bc4b1
Merge branch 'feature/drest-admin-view' into integration/admin-view
aleontiev Mar 9, 2017
f990be4
Merge branch 'integration/admin-view' of https://github.com/AltSchool…
lynnetye Mar 9, 2017
4310071
Consolidate templates
lynnetye Mar 17, 2017
f8e09d6
relationship support for getter/setter
aleontiev Mar 21, 2017
fe7dc7b
passing tests with self links, catch missing AdminRenderer
aleontiev Mar 21, 2017
50224ba
added changelog
aleontiev Mar 21, 2017
e65e98e
fix typo
aleontiev Mar 21, 2017
3d72a08
change renderer order
aleontiev Mar 21, 2017
2f9d550
optional get on results
aleontiev Mar 21, 2017
7d15baa
add vertical/horizontal field copies
aleontiev Mar 21, 2017
34f5401
remove 1.7 from tox
aleontiev Mar 21, 2017
3ded4a5
refactor field code locations
aleontiev Mar 21, 2017
dd976ca
Remove sidebar buttons and pluralize collection in breadcrumbs
lynnetye Mar 21, 2017
9177c5d
test groups.csv
aleontiev Mar 21, 2017
4ff6a8b
attempt to fix SUF for py3
aleontiev Mar 21, 2017
857ece3
py3 fix
aleontiev Mar 22, 2017
0f9b7bd
add back django 1.7
aleontiev Mar 22, 2017
6217256
Merge pull request #158 from AltSchool/feature/admin-view/relationships
aleontiev Mar 23, 2017
6d016b3
Fix merge conflicts
lynnetye Mar 23, 2017
38950f5
Update test
lynnetye Mar 24, 2017
8e65aac
relationship hyperlinks + natural key
aleontiev Mar 28, 2017
a3fa079
revert change to test locationviewset (pagination)
aleontiev Mar 28, 2017
317e647
remove unused queryset resolution logic in related field
aleontiev Mar 28, 2017
7bb24fe
Display .csv bulk import errors
lynnetye Mar 29, 2017
57b66d4
tweak get_url usage
aleontiev Mar 29, 2017
ac109c5
add DRF 3.1 compat for Hyperlink
aleontiev Mar 29, 2017
305b8db
Merge pull request #160 from AltSchool/lt/drest-admin-view
aleontiev Mar 29, 2017
76e2caa
Merge branch 'integration/admin-view' of github.com:AltSchool/dynamic…
aleontiev Mar 29, 2017
0fce86c
Merge pull request #159 from AltSchool/feature/admin-view/relationships
aleontiev Mar 30, 2017
c609b99
import DynamicComputedField
aleontiev Mar 30, 2017
944fdf7
Update API directory display
lynnetye Apr 3, 2017
484ee31
Update color palette
lynnetye Apr 10, 2017
f5f887c
Make responsive
lynnetye Apr 10, 2017
f8e4b39
Merge pull request #165 from AltSchool/lt/drest-admin-view-design
aleontiev Apr 10, 2017
a3271b5
Merge branch 'master' of github.com:AltSchool/dynamic-rest into integ…
aleontiev Apr 13, 2017
1d879a6
improve view name / fix directory vs detail / fix header/data order m…
aleontiev Apr 13, 2017
262aeae
tweak breadcrumbs (remove ref to root, change style)
aleontiev Apr 13, 2017
b0d3f4f
more robust natural key logic
aleontiev Apr 13, 2017
792b4be
visual tweaks
aleontiev Apr 27, 2017
1d42e8f
..
aleontiev May 8, 2017
fef7cf3
rename directory.html -> root.html
aleontiev May 8, 2017
49c4958
fix is_root logic
aleontiev May 8, 2017
0bc9dab
add get breadcrumbs / renderer
aleontiev May 8, 2017
5221a57
request.path
aleontiev May 8, 2017
ce6b05e
response status code to prevent failure on error pages
aleontiev May 8, 2017
3494536
add ROOT_REQUIRES_AUTHENTICATION
aleontiev May 8, 2017
77a2bf5
fix as_id_to_name with uuid
aleontiev May 8, 2017
5613c7c
only show active routes in root view
aleontiev May 8, 2017
3672448
is_prefix_of
aleontiev May 8, 2017
75170e9
get_url no request
aleontiev May 8, 2017
4d1e163
print
aleontiev May 8, 2017
b8e8dee
WIP breadcrumbs
aleontiev May 9, 2017
ada9e78
Merge branch 'master' of github.com:AltSchool/dynamic-rest into integ…
aleontiev May 9, 2017
76b2433
respect serializer field ordering, respect http_method_names in admin…
aleontiev May 18, 2017
c58a093
Merge branch 'integration/admin-view' of github.com:AltSchool/dynamic…
aleontiev May 18, 2017
3e6f26f
Merge branch 'master' of github.com:AltSchool/dynamic-rest into integ…
aleontiev Aug 31, 2017
2da8b4a
Merge branch 'integration/admin-view' of github.com:AltSchool/dynamic…
aleontiev Aug 31, 2017
304e765
better support for nested source -- nested updates/creates/filters
aleontiev Sep 1, 2017
6a42075
better generic error handling + relationships
aleontiev Sep 3, 2017
d582d84
fix get model field
aleontiev Sep 3, 2017
f63e760
spike on CSS
aleontiev Sep 5, 2017
52fa899
add custom body
aleontiev Sep 6, 2017
bd6ceb3
stackables
aleontiev Sep 7, 2017
0038ed1
..
aleontiev Sep 7, 2017
68a0296
..
aleontiev Sep 8, 2017
bd59684
..
aleontiev Sep 8, 2017
bc5de10
..
aleontiev Sep 8, 2017
cd6d561
choices
aleontiev Sep 8, 2017
70c94a8
sd
aleontiev Sep 8, 2017
4d13e9e
choices field processing/dynamic value with classes
aleontiev Sep 8, 2017
a6d0443
get_class WIP
aleontiev Sep 8, 2017
ffbb477
..
aleontiev Sep 10, 2017
1b33e82
smaller width
aleontiev Sep 11, 2017
9809908
add alert, modal styling
aleontiev Sep 11, 2017
0686aa6
..
aleontiev Sep 11, 2017
f5980cb
..
aleontiev Sep 11, 2017
0da5e09
fix class for search
aleontiev Sep 11, 2017
e7e3258
hide those pages
aleontiev Sep 11, 2017
633d4f0
better pagination
aleontiev Sep 12, 2017
bc1f8de
..
aleontiev Sep 12, 2017
e56fd1a
add Dynamic*Fields
aleontiev Sep 13, 2017
c107d0c
rename natural_key -> name_field
aleontiev Sep 13, 2017
aade1b9
allowClear on select2
aleontiev Sep 13, 2017
9fac917
..
aleontiev Sep 13, 2017
ede1e5c
..
aleontiev Sep 13, 2017
c8491d8
fix name field
aleontiev Sep 13, 2017
084b64a
remove print
aleontiev Sep 14, 2017
1e8372c
early drf compat
aleontiev Sep 14, 2017
ce3092b
refactor rewrite
aleontiev Sep 15, 2017
d8ff5b3
refactor/shorten
aleontiev Sep 15, 2017
38ad948
use super() instead of object to call __new__ super
aleontiev Sep 15, 2017
fcb1b72
super(Meta, cls)
aleontiev Sep 15, 2017
0d80037
fix call to super
aleontiev Sep 15, 2017
b7b5820
change meta superclass
aleontiev Sep 15, 2017
5e3ed75
drop py33
aleontiev Sep 15, 2017
0e5f2d3
no test sugar
aleontiev Sep 15, 2017
5edddbb
..
aleontiev Sep 15, 2017
b85d702
3.3.6
aleontiev Sep 15, 2017
e3c6ea9
add dj.yml and fix tox invocation
aleontiev Sep 15, 2017
4610db5
use latest minor versions
aleontiev Sep 15, 2017
cfe282b
add CHANGELOG
aleontiev Sep 15, 2017
9227cc7
remove 3.3
aleontiev Sep 15, 2017
59a808a
fix meta
aleontiev Sep 15, 2017
c2acc0d
docs, rename rewrite -> resolve
aleontiev Sep 15, 2017
d76b232
clearer exceptions
aleontiev Sep 15, 2017
8a8fdc2
improve docs and UI
aleontiev Sep 15, 2017
650ab0f
fix carserializer name field
aleontiev Sep 15, 2017
27677fe
..
aleontiev Sep 15, 2017
6f417cd
fix sorting filter exception
aleontiev Sep 16, 2017
45e4f37
add needs_prefetch
aleontiev Sep 19, 2017
e5be6af
remove is_admin, change to gui (passed-in from view)
aleontiev Sep 19, 2017
6020e4e
..
aleontiev Sep 19, 2017
210ffa0
improve list field labeling
aleontiev Sep 19, 2017
794e03f
add labels and help text tooltips
aleontiev Sep 19, 2017
1873b61
..
aleontiev Sep 20, 2017
6230cc2
allowClear fix for many
aleontiev Sep 20, 2017
83fee9d
e.args[0]
aleontiev Sep 20, 2017
5c7e033
error
aleontiev Sep 21, 2017
c07feda
fix view
aleontiev Sep 21, 2017
b707e7a
remove unused variable, allow API exceptions to pass through serializ…
aleontiev Sep 21, 2017
c42fcff
remove fitlers
aleontiev Sep 21, 2017
4c95095
..
aleontiev Sep 22, 2017
781d0af
remove print
aleontiev Sep 22, 2017
bc09b1d
use field.get_related for magic
aleontiev Sep 25, 2017
d9aa86e
..
aleontiev Sep 27, 2017
181779c
fix related filtering
aleontiev Sep 28, 2017
7a08130
use query name
aleontiev Sep 28, 2017
b6a67d8
fix rel case
aleontiev Sep 28, 2017
d0901bb
add changelog reference + post/put support for ViewSetTestCase
aleontiev Sep 28, 2017
ce9b90b
add docs
aleontiev Sep 28, 2017
6148a4f
update changelog
aleontiev Sep 28, 2017
b4a61de
changelog format
aleontiev Sep 28, 2017
90b7055
better CL format
aleontiev Sep 28, 2017
9ae19ca
add missing `
aleontiev Sep 28, 2017
4869d4d
add **
aleontiev Sep 28, 2017
49ddee8
readme + change blueprint
aleontiev Sep 29, 2017
285cf81
update benchmarks
aleontiev Sep 29, 2017
ef35175
import from compat
aleontiev Sep 29, 2017
c75498c
fix as_hyperlink
aleontiev Sep 29, 2017
d75c38a
update readme
aleontiev Sep 29, 2017
7ceac0c
README
aleontiev Sep 29, 2017
c1302fd
add disclaimer
aleontiev Sep 29, 2017
e88d2e5
change benchmarks
aleontiev Sep 29, 2017
1e9b281
readme
aleontiev Sep 29, 2017
242740d
change integration test cond
aleontiev Sep 29, 2017
b0dfcda
remove vertical/
aleontiev Sep 29, 2017
2c11cac
README
aleontiev Sep 29, 2017
307d68a
remove browsable API test for user view
aleontiev Sep 29, 2017
f5ac480
better test view
aleontiev Sep 29, 2017
324b85c
deepcopy
aleontiev Sep 29, 2017
882b813
WIP 3.1/3.2 admin renderer
aleontiev Sep 29, 2017
ce81975
change DHTMLFR
aleontiev Sep 29, 2017
c0f429b
make is_superuser, not is_staff
aleontiev Oct 3, 2017
e50beca
fix tests
aleontiev Oct 4, 2017
adee8ae
invalid PK fix
aleontiev Oct 4, 2017
508301d
..
aleontiev Oct 5, 2017
955a78c
add DCreatorField
aleontiev Oct 5, 2017
2c30b25
Merge branch 'integration/admin-view'
aleontiev Oct 10, 2017
865f437
require < 3.6.4 (breaking change)
aleontiev Oct 10, 2017
1eba4b6
;
aleontiev Oct 10, 2017
87e5c56
..
aleontiev Oct 10, 2017
b7d86e3
..
aleontiev Oct 11, 2017
be68d37
..
aleontiev Oct 13, 2017
c2ce5c1
..
aleontiev Oct 17, 2017
85dc6e6
..
aleontiev Oct 19, 2017
5415e10
..
aleontiev Oct 19, 2017
4c39477
..
aleontiev Oct 20, 2017
e03decc
..
aleontiev Oct 23, 2017
151f724
..
aleontiev Oct 23, 2017
5a311a1
!
aleontiev Oct 23, 2017
a8aab6b
..
aleontiev Oct 24, 2017
5ab2d2f
....
aleontiev Oct 24, 2017
c86bc2a
as;da
aleontiev Oct 24, 2017
fb9cb43
..asdll
aleontiev Oct 24, 2017
6f6d359
sdasd
aleontiev Oct 25, 2017
a5c3b74
Merge pull request #196 from aleontiev/master
notsoluckycharm Nov 2, 2017
533c671
rollback to debug
notsoluckycharm Nov 6, 2017
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
1.6.3

- Added `ENABLE_SELF_LINKS` option, default False.
When enabled, links will include reference to the current resource.

- Made `serializer_class` optional on `DynamicRelationField`.
`DynamicRelationField` will attempt to infer `serializer_class` from the
`source` using `DynamicRouter.get_canonical_serializer`.

- Added `getter`/`setter` support to `DynamicRelationField`, allowing
for custom relationship getting and setting. This can be useful for simplifying
complex "through"-relations.
39 changes: 39 additions & 0 deletions dynamic_rest/compat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# flake8: noqa
from __future__ import absolute_import

from django import VERSION

DJANGO110 = VERSION >= (1, 10)

try:
from django.urls import (
NoReverseMatch,
RegexURLPattern,
RegexURLResolver,
ResolverMatch,
Resolver404,
get_script_prefix,
reverse,
reverse_lazy,
resolve
)
except ImportError:
from django.core.urlresolvers import ( # Will be removed in Django 2.0
NoReverseMatch,
RegexURLPattern,
RegexURLResolver,
ResolverMatch,
Resolver404,
get_script_prefix,
reverse,
reverse_lazy,
resolve
)


def set_many(instance, field, value):
if DJANGO110:
field = getattr(instance, field)
field.set(value)
else:
setattr(instance, field, value)
21 changes: 21 additions & 0 deletions dynamic_rest/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from django.test.signals import setting_changed

DYNAMIC_REST = {
'ADMIN_TEMPLATE': 'dynamic_rest/admin.html',

# DEBUG: enable/disable internal debugging
'DEBUG': False,

Expand All @@ -12,6 +14,9 @@
# ENABLE_LINKS: enable/disable relationship links
'ENABLE_LINKS': True,

# ENABLE_SELF_LINKS: enable/disable links to self
'ENABLE_SELF_LINKS': False,

# ENABLE_SERIALIZER_CACHE: enable/disable caching of related serializers
'ENABLE_SERIALIZER_CACHE': True,

Expand All @@ -32,6 +37,14 @@
# Can be overriden at the viewset level.
'MAX_PAGE_SIZE': None,

# LOGIN_URL: the login URL, defaults to reverse-URL lookup
'LOGIN_URL': '',

# LOGOUT_URL: the logout URL, defaults to reverse-URL lookup
'LOGOUT_URL': '',

'LOGIN_TEMPLATE': 'dynamic_rest/login.html',

# PAGE_QUERY_PARAM: global setting for the pagination query parameter.
# Can be overriden at the viewset level.
'PAGE_QUERY_PARAM': 'page',
Expand All @@ -52,6 +65,14 @@
# through the dynamic router. If a resource doesn't have a canonical
# path registered, links will default back to being resource-relative urls
'ENABLE_HOST_RELATIVE_LINKS': True,

# Whether or not the root API view requires authentication
'ROOT_REQUIRES_AUTHENTICATION': False,

# Name of the root API view
'ROOT_VIEW_NAME': 'API',

'ROOT_DESCRIPTION': '',
}


Expand Down
12 changes: 10 additions & 2 deletions dynamic_rest/fields/__init__.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,10 @@
from dynamic_rest.fields.fields import * # noqa
from dynamic_rest.fields.generic import * # noqa
# flake8: noqa
from .base import (
DynamicField,
CountField,
DynamicComputedField
)
from .relation import DynamicRelationField
from .generic import DynamicGenericRelationField
from .choices import DynamicChoicesField
from .model import *
135 changes: 135 additions & 0 deletions dynamic_rest/fields/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
from rest_framework import fields
from dynamic_rest.meta import get_model_field


class DynamicField(fields.Field):

"""
Generic field base to capture additional custom field attributes.
"""

def __init__(
self,
requires=None,
deferred=None,
field_type=None,
immutable=False,
get_class=None,
**kwargs
):
"""
Arguments:
deferred: Whether or not this field is deferred.
Deferred fields are not included in the response,
unless explicitly requested.
field_type: Field data type, if not inferrable from model.
requires: List of fields that this field depends on.
Processed by the view layer during queryset build time.
"""
self.requires = requires
self.deferred = deferred
self.field_type = field_type
self.immutable = immutable
self.kwargs = kwargs
super(DynamicField, self).__init__(**kwargs)

def to_representation(self, value):
return value

def to_internal_value(self, value):
return value

@property
def parent_model(self):
if not hasattr(self, '_parent_model'):
self._parent_model = getattr(self.parent.Meta, 'model', None)
return self._parent_model

@property
def model_field(self):
if not hasattr(self, '_model_field'):
try:
self._model_field = get_model_field(
self.parent_model, self.source
)
except:
self._model_field = None
return self._model_field


class DynamicComputedField(DynamicField):
def __init__(self, *args, **kwargs):
kwargs['read_only'] = True
super(DynamicComputedField, self).__init__(*args, **kwargs)


class CountField(DynamicComputedField):

"""
Computed field that counts the number of elements in another field.
"""

def __init__(self, serializer_source, *args, **kwargs):
"""
Arguments:
serializer_source: A serializer field.
unique: Whether or not to perform a count of distinct elements.
"""
self.field_type = int
# Use `serializer_source`, which indicates a field at the API level,
# instead of `source`, which indicates a field at the model level.
self.serializer_source = serializer_source
# Set `source` to an empty value rather than the field name to avoid
# an attempt to look up this field.
kwargs['source'] = ''
self.unique = kwargs.pop('unique', True)
return super(CountField, self).__init__(*args, **kwargs)

def get_attribute(self, obj):
source = self.serializer_source
if source not in self.parent.fields:
return None
value = self.parent.fields[source].get_attribute(obj)
data = self.parent.fields[source].to_representation(value)

# How to count None is undefined... let the consumer decide.
if data is None:
return None

# Check data type. Technically len() works on dicts, strings, but
# since this is a "count" field, we'll limit to list, set, tuple.
if not isinstance(data, (list, set, tuple)):
raise TypeError(
"'%s' is %s. Must be list, set or tuple to be countable." % (
source, type(data)
)
)

if self.unique:
# Try to create unique set. This may fail if `data` contains
# non-hashable elements (like dicts).
try:
data = set(data)
except TypeError:
pass

return len(data)


class WithRelationalFieldMixin(object):
"""Mostly code shared by DynamicRelationField and
DynamicGenericRelationField.
"""

def _get_request_fields_from_parent(self):
"""Get request fields from the parent serializer."""
if not self.parent:
return None

if not getattr(self.parent, 'request_fields'):
return None

if not isinstance(self.parent.request_fields, dict):
return None

return self.parent.request_fields.get(self.field_name)
22 changes: 22 additions & 0 deletions dynamic_rest/fields/choices.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from .base import DynamicField
from rest_framework.serializers import ChoiceField
from dynamic_rest.utils import DynamicValue


class DynamicChoicesField(
ChoiceField,
DynamicField
):
def __init__(self, *args, **kwargs):
self.class_choices = kwargs.pop(
'class_choices', {}
)
super(DynamicChoicesField, self).__init__(*args, **kwargs)

def to_representation(self, value):
return DynamicValue(
super(DynamicChoicesField, self).to_representation(value),
self.choices.get(value, value),
classes=self.class_choices.get(value, None),
display_name=True
)
17 changes: 0 additions & 17 deletions dynamic_rest/fields/common.py

This file was deleted.

6 changes: 3 additions & 3 deletions dynamic_rest/fields/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

from rest_framework.exceptions import ValidationError

from dynamic_rest.fields.common import WithRelationalFieldMixin
from dynamic_rest.fields.fields import DynamicField
from dynamic_rest.routers import DynamicRouter
from .base import WithRelationalFieldMixin, DynamicField
from dynamic_rest.tagged import TaggedDict


Expand Down Expand Up @@ -64,6 +62,7 @@ def get_pk_object(self, type_key, id_value):
}

def get_serializer_class_for_instance(self, instance):
from dynamic_rest.routers import DynamicRouter
return DynamicRouter.get_canonical_serializer(
resource_key=None,
instance=instance
Expand Down Expand Up @@ -118,6 +117,7 @@ def to_internal_value(self, data):
model_name = data.get('type', None)
model_id = data.get('id', None)
if model_name and model_id:
from dynamic_rest.routers import DynamicRouter
serializer_class = DynamicRouter.get_canonical_serializer(
resource_key=None,
resource_name=model_name
Expand Down
45 changes: 45 additions & 0 deletions dynamic_rest/fields/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import sys
from .base import DynamicField
from rest_framework import serializers

for cls_name in (
'BooleanField',
'CharField',
'DateField',
'DateTimeField',
'DecimalField',
'DictField',
'EmailField',
'FileField',
'FilePathField',
'FloatField',
'HiddenField',
'IPAddressField',
'ImageField',
'IntegerField',
'JSONField',
'ListField',
'RegexField',
'SlugField',
'TimeField',
'URLField',
'UUIDField',
):
cls = getattr(serializers, cls_name, None)
if not cls:
continue

new_name = 'Dynamic%s' % cls_name
new_cls = type(
new_name,
(cls, DynamicField),
{}
)
setattr(sys.modules[__name__], new_name, new_cls)


class DynamicMethodField(
serializers.SerializerMethodField,
DynamicField
):
pass
Loading