|
3 | 3 | django-postgresql-dag to alternate formats.
|
4 | 4 | """
|
5 | 5 |
|
6 |
| -from itertools import chain |
7 |
| - |
8 | 6 | import networkx as nx
|
9 | 7 | import pandas as pd
|
10 |
| -from django.core.exceptions import FieldDoesNotExist |
11 |
| -from django.db.models import Case, When |
12 |
| -from django.db.models.fields import DateTimeField, UUIDField |
13 |
| -from django.db.models.fields.files import FileField, ImageField |
14 |
| -from django.db.models.fields.related import ManyToManyField |
15 |
| - |
16 |
| -from .exceptions import (GraphModelsCannotBeParsedException, |
17 |
| - IncorrectUsageException) |
18 |
| - |
19 |
| - |
20 |
| -def _ordered_filter(queryset, field_names, values): |
21 |
| - """ |
22 |
| - Filters the provided queryset for 'field_name__in values' for each given field_name in [field_names] |
23 |
| - orders results in the same order as provided values |
24 |
| -
|
25 |
| - For instance |
26 |
| - _ordered_filter(self.__class__.objects, "pk", pks) |
27 |
| - returns a queryset of the current class, with instances where the 'pk' field matches an pk in pks |
28 |
| -
|
29 |
| - """ |
30 |
| - if not isinstance(field_names, list): |
31 |
| - field_names = [field_names] |
32 |
| - case = [] |
33 |
| - for pos, value in enumerate(values): |
34 |
| - when_condition = {field_names[0]: value, "then": pos} |
35 |
| - case.append(When(**when_condition)) |
36 |
| - order_by = Case(*case) |
37 |
| - filter_condition = {field_name + "__in": values for field_name in field_names} |
38 |
| - return queryset.filter(**filter_condition).order_by(order_by) |
39 |
| - |
40 |
| - |
41 |
| -def get_instance_characteristics(instance): |
42 |
| - """ |
43 |
| - Returns a tuple of the node & edge model classes and the instance_type |
44 |
| - for the provided instance |
45 |
| - """ |
46 |
| - try: |
47 |
| - # Assume a queryset of nodes was provided |
48 |
| - _NodeModel = instance._meta.model |
49 |
| - _EdgeModel = instance._meta.model._meta.get_field("parents").through |
50 |
| - instance_type = "node" |
51 |
| - except FieldDoesNotExist: |
52 |
| - try: |
53 |
| - # Assume a queryset of edges was provided |
54 |
| - _EdgeModel = instance._meta.model |
55 |
| - _NodeModel = instance._meta.model._meta.get_field("parent").related_model |
56 |
| - instance_type = "edge" |
57 |
| - except FieldDoesNotExist: |
58 |
| - raise GraphModelsCannotBeParsedException |
59 |
| - return (_NodeModel, _EdgeModel, instance_type) |
60 |
| - |
61 |
| - |
62 |
| -def get_queryset_characteristics(queryset): |
63 |
| - """ |
64 |
| - Returns a tuple of the node & edge model classes and the queryset type |
65 |
| - for the provided queryset |
66 |
| - """ |
67 |
| - try: |
68 |
| - # Assume a queryset of nodes was provided |
69 |
| - _NodeModel = queryset.model |
70 |
| - _EdgeModel = queryset.model._meta.get_field("parents").through |
71 |
| - queryset_type = "nodes_queryset" |
72 |
| - except FieldDoesNotExist: |
73 |
| - try: |
74 |
| - # Assume a queryset of edges was provided |
75 |
| - _EdgeModel = queryset.model |
76 |
| - _NodeModel = queryset.model._meta.get_field("parent").related_model |
77 |
| - queryset_type = "edges_queryset" |
78 |
| - except FieldDoesNotExist: |
79 |
| - raise GraphModelsCannotBeParsedException |
80 |
| - return (_NodeModel, _EdgeModel, queryset_type) |
81 |
| - |
82 |
| - |
83 |
| -def model_to_dict(instance, fields=None, date_strf=None): |
84 |
| - """ |
85 |
| - Returns a dictionary of {field_name: field_value} for a given model instance |
86 |
| - e.g.: model_to_dict(myqueryset.first(), fields=["id",]) |
87 |
| -
|
88 |
| - For DateTimeFields, a formatting string can be provided |
89 |
| -
|
90 |
| - Adapted from: https://ziwon.github.io/post/using_custom_model_to_dict_in_django/ |
91 |
| - """ |
92 |
| - |
93 |
| - if not fields: |
94 |
| - raise IncorrectUsageException("fields list must be provided") |
95 |
| - |
96 |
| - opts = instance._meta |
97 |
| - data = {} |
98 |
| - __fields = list(map(lambda a: a.split("__")[0], fields or [])) |
99 |
| - |
100 |
| - for f in chain(opts.concrete_fields, opts.private_fields, opts.many_to_many): |
101 |
| - is_editable = getattr(f, "editable", False) |
102 |
| - |
103 |
| - if fields and f.name not in __fields: |
104 |
| - continue |
105 |
| - |
106 |
| - if isinstance(f, DateTimeField): |
107 |
| - dt = f.value_from_object(instance) |
108 |
| - # Format based on format string provided, otherwise return a timestamp |
109 |
| - data[f.name] = dt.strftime(date_strf) if date_strf else dt.timestamp() |
110 |
| - |
111 |
| - elif isinstance(f, ImageField): |
112 |
| - image = f.value_from_object(instance) |
113 |
| - data[f.name] = image.url if image else None |
114 |
| - |
115 |
| - elif isinstance(f, FileField): |
116 |
| - file = f.value_from_object(instance) |
117 |
| - data[f.name] = file.url if file else None |
118 |
| - |
119 |
| - elif isinstance(f, ManyToManyField): |
120 |
| - if instance.pk is None: |
121 |
| - data[f.name] = [] |
122 |
| - else: |
123 |
| - qs = f.value_from_object(instance) |
124 |
| - if qs._result_cache is not None: |
125 |
| - data[f.name] = [item.pk for item in qs] |
126 |
| - else: |
127 |
| - try: |
128 |
| - m2m_field = list( |
129 |
| - filter(lambda a: f.name in a and a.find("__") != -1, fields) |
130 |
| - )[0] |
131 |
| - key = m2m_field[len(f.name) + 2 :] |
132 |
| - data[f.name] = list(qs.values_list(key, flat=True)) |
133 |
| - except IndexError: |
134 |
| - data[f.name] = list(qs.values_list("pk", flat=True)) |
135 |
| - |
136 |
| - if isinstance(f, UUIDField): |
137 |
| - uuid = f.value_from_object(instance) |
138 |
| - data[f.name] = str(uuid) if uuid else None |
139 |
| - |
140 |
| - # ToDo: Process other model fields |
141 |
| - |
142 |
| - elif is_editable: |
143 |
| - data[f.name] = f.value_from_object(instance) |
144 |
| - |
145 |
| - funcs = set(__fields) - set(list(data.keys())) |
146 |
| - for func in funcs: |
147 |
| - obj = getattr(instance, func) |
148 |
| - if inspect.ismethod(obj): |
149 |
| - data[func] = obj() |
150 |
| - else: |
151 |
| - data[func] = obj |
152 |
| - return data |
153 |
| - |
154 |
| - |
155 |
| -def edges_from_nodes_queryset(nodes_queryset): |
156 |
| - """Given an Edge Model and a QuerySet or RawQuerySet of nodes, |
157 |
| - returns a queryset of the associated edges""" |
158 |
| - _NodeModel, _EdgeModel, queryset_type = get_queryset_characteristics(nodes_queryset) |
159 |
| - |
160 |
| - if queryset_type == "nodes_queryset": |
161 |
| - return _ordered_filter(_EdgeModel.objects, ["parent", "child"], nodes_queryset) |
162 |
| - raise IncorrectQuerysetTypeException |
163 |
| - |
164 |
| - |
165 |
| -def nodes_from_edges_queryset(edges_queryset): |
166 |
| - """Given a Node Model and a QuerySet or RawQuerySet of edges, |
167 |
| - returns a queryset of the associated nodes""" |
168 |
| - _NodeModel, _EdgeModel, queryset_type = get_queryset_characteristics(edges_queryset) |
169 |
| - |
170 |
| - if queryset_type == "edges_queryset": |
171 |
| - |
172 |
| - nodes_list = ( |
173 |
| - _ordered_filter( |
174 |
| - _NodeModel.objects, |
175 |
| - [ |
176 |
| - f"{_NodeModel.__name__}_child", |
177 |
| - ], |
178 |
| - edges_queryset, |
179 |
| - ) |
180 |
| - | _ordered_filter( |
181 |
| - _NodeModel.objects, |
182 |
| - [ |
183 |
| - f"{_NodeModel.__name__}_parent", |
184 |
| - ], |
185 |
| - edges_queryset, |
186 |
| - ) |
187 |
| - ).values_list("pk") |
188 | 8 |
|
189 |
| - return _NodeModel.objects.filter(pk__in=nodes_list) |
190 |
| - raise IncorrectQuerysetTypeException |
| 9 | +from .utils import (_ordered_filter, get_queryset_characteristics, |
| 10 | + model_to_dict, edges_from_nodes_queryset, |
| 11 | + nodes_from_edges_queryset) |
191 | 12 |
|
192 | 13 |
|
193 | 14 | def nx_from_queryset(
|
|
0 commit comments