|
1 | | -import django |
2 | | -from django.db import models |
3 | | -from django.core.exceptions import ObjectDoesNotExist |
| 1 | +""" |
| 2 | +This is just a pure wrapper / alias module around django_serializable_model to |
| 3 | +still be able to import it with the original, unintended name of serializable. |
| 4 | +See https://github.com/agilgur5/django-serializable-model/issues/2 |
4 | 5 |
|
| 6 | +In the first major/breaking release, v1.0.0, this file should be deleted and |
| 7 | +the module removed from `setup.py`. |
| 8 | +""" |
5 | 9 |
|
6 | | -class _SerializableQuerySet(models.query.QuerySet): |
7 | | - """Implements the serialize method on a QuerySet""" |
8 | | - def serialize(self, *args): |
9 | | - serialized = [] |
10 | | - for elem in self: |
11 | | - serialized.append(elem.serialize(*args)) |
12 | | - return serialized |
13 | | - |
14 | | - |
15 | | -class SerializableManager(models.Manager): |
16 | | - """Implements table-level serialization via SerializableQuerySet""" |
17 | | - # backward compatibility for Django < 1.10 |
18 | | - if django.VERSION < (1, 10): |
19 | | - # when queried from a related Model, use this Manager |
20 | | - use_for_related_fields = True |
21 | | - |
22 | | - def get_queryset(self): |
23 | | - return _SerializableQuerySet(self.model) |
24 | | - |
25 | | - # backward compatibility for Django < 1.6 |
26 | | - if django.VERSION < (1, 6): |
27 | | - get_query_set = get_queryset |
28 | | - |
29 | | - def get_queryset_compat(self): |
30 | | - get_queryset = (self.get_query_set |
31 | | - if hasattr(self, 'get_query_set') |
32 | | - else self.get_queryset) |
33 | | - return get_queryset() |
34 | | - |
35 | | - # implement serialize on the Manager itself (on .objects, before .all()) |
36 | | - def serialize(self, *args): |
37 | | - return self.get_queryset_compat().serialize(*args) |
38 | | - |
39 | | - |
40 | | -class SerializableModel(models.Model): |
41 | | - """ |
42 | | - Abstract Model that implements recursive serializability of models to |
43 | | - dictionaries, both at the row and table level, with some overriding allowed |
44 | | - """ |
45 | | - objects = SerializableManager() |
46 | | - |
47 | | - class Meta: |
48 | | - abstract = True |
49 | | - # when queried from a related Model, use this Manager |
50 | | - base_manager_name = 'SerializableManager' |
51 | | - |
52 | | - def serialize(self, *args, **kwargs): |
53 | | - """ |
54 | | - Serializes the Model object with model_to_dict_custom and kwargs, and |
55 | | - proceeds to recursively serialize related objects as requested in args |
56 | | - """ |
57 | | - serialized = model_to_dict_custom(self, **kwargs) |
58 | | - args = list(args) # convert tuple to list |
59 | | - |
60 | | - # iterate and recurse through all arguments |
61 | | - index = 0 |
62 | | - length = len(args) |
63 | | - while index < length: |
64 | | - # split the current element |
65 | | - field_with_joins = args[index] |
66 | | - field, join = _split_joins(field_with_joins) |
67 | | - all_joins = [join] if join else [] # empty string to empty array |
68 | | - |
69 | | - # delete it from the list |
70 | | - del args[index] |
71 | | - length -= 1 |
72 | | - |
73 | | - # get all joins for this field from the arguments |
74 | | - arg_joins = [_split_joins(arg, only_join=True) |
75 | | - for arg in args if arg.startswith(field)] |
76 | | - all_joins += arg_joins # combine all joins on this field |
77 | | - |
78 | | - # recurse if related object actually exists |
79 | | - try: |
80 | | - serialized[field] = getattr(self, field).serialize(*all_joins) |
81 | | - except (AttributeError, ObjectDoesNotExist): |
82 | | - pass |
83 | | - |
84 | | - # shrink length and remove all args that were recursed over |
85 | | - length -= len(arg_joins) |
86 | | - args = [arg for arg in args if not arg.startswith(field)] |
87 | | - |
88 | | - return serialized |
89 | | - |
90 | | - |
91 | | -def model_to_dict_custom(instance, fields=None, exclude=None, editable=True): |
92 | | - """ |
93 | | - Custom model_to_dict function that differs by including all uneditable |
94 | | - fields and excluding all M2M fields by default |
95 | | - Also sets all ForeignKey fields to name + _id, similar to .values() |
96 | | - """ |
97 | | - # avoid circular import |
98 | | - from django.db.models.fields.related import ForeignKey |
99 | | - opts = instance._meta |
100 | | - data = {} |
101 | | - for f in opts.fields: |
102 | | - # skip uneditable fields if editable kwarg is False |
103 | | - if not editable and not f.ediable: |
104 | | - continue |
105 | | - # whitelisted fields only if fields kwarg is passed |
106 | | - if fields and f.name not in fields: |
107 | | - continue |
108 | | - # blacklist fields from exclude kwarg |
109 | | - if exclude and f.name in exclude: |
110 | | - continue |
111 | | - else: |
112 | | - if isinstance(f, ForeignKey): |
113 | | - data[f.name + '_id'] = f.value_from_object(instance) |
114 | | - else: |
115 | | - data[f.name] = f.value_from_object(instance) |
116 | | - return data |
117 | | - |
118 | | - |
119 | | -def _split_joins(join_string, only_join=False): |
120 | | - """ |
121 | | - Split a string into the field and it's joins, separated by __ as per |
122 | | - Django convention |
123 | | - """ |
124 | | - split = join_string.split('__') |
125 | | - field = split.pop(0) # the first field |
126 | | - join = '__'.join(split) # the rest of the fields |
127 | | - |
128 | | - # return single join or tuple based on kwarg |
129 | | - if only_join: |
130 | | - return join |
131 | | - return field, join |
| 10 | +from django_serializable_model import * # noqa F403, F401 |
0 commit comments