-
-
Notifications
You must be signed in to change notification settings - Fork 782
/
fields.py
125 lines (104 loc) · 4.2 KB
/
fields.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
from django.core.exceptions import ObjectDoesNotExist
from django.db.models.fields import NOT_PROVIDED
from django.db.models.manager import Manager
from . import widgets
class Field:
"""
Field represent mapping between `object` field and representation of
this field.
:param attribute: A string of either an instance attribute or callable off
the object.
:param column_name: Lets you provide a name for the column that represents
this field in the export.
:param widget: Defines a widget that will be used to represent this
field's data in the export.
:param readonly: A Boolean which defines if this field will be ignored
during import.
:param default: This value will be returned by
:meth:`~import_export.fields.Field.clean` if this field's widget did
not return an adequate value.
:param saves_null_values: Controls whether null values are saved on the object
"""
empty_values = [None, '']
def __init__(self, attribute=None, column_name=None, widget=None,
default=NOT_PROVIDED, readonly=False, saves_null_values=True):
self.attribute = attribute
self.default = default
self.column_name = column_name
if not widget:
widget = widgets.Widget()
self.widget = widget
self.readonly = readonly
self.saves_null_values = saves_null_values
def __repr__(self):
"""
Displays the module, class and name of the field.
"""
path = '%s.%s' % (self.__class__.__module__, self.__class__.__name__)
column_name = getattr(self, 'column_name', None)
if column_name is not None:
return '<%s: %s>' % (path, column_name)
return '<%s>' % path
def clean(self, data, **kwargs):
"""
Translates the value stored in the imported datasource to an
appropriate Python object and returns it.
"""
try:
value = data[self.column_name]
except KeyError:
raise KeyError("Column '%s' not found in dataset. Available "
"columns are: %s" % (self.column_name, list(data)))
# If ValueError is raised here, import_obj() will handle it
value = self.widget.clean(value, row=data, **kwargs)
if value in self.empty_values and self.default != NOT_PROVIDED:
if callable(self.default):
return self.default()
return self.default
return value
def get_value(self, obj):
"""
Returns the value of the object's attribute.
"""
if self.attribute is None:
return None
attrs = self.attribute.split('__')
value = obj
for attr in attrs:
try:
value = getattr(value, attr, None)
except (ValueError, ObjectDoesNotExist):
# needs to have a primary key value before a many-to-many
# relationship can be used.
return None
if value is None:
return None
# RelatedManager and ManyRelatedManager classes are callable in
# Django >= 1.7 but we don't want to call them
if callable(value) and not isinstance(value, Manager):
value = value()
return value
def save(self, obj, data, is_m2m=False, **kwargs):
"""
If this field is not declared readonly, the object's attribute will
be set to the value returned by :meth:`~import_export.fields.Field.clean`.
"""
if not self.readonly:
attrs = self.attribute.split('__')
for attr in attrs[:-1]:
obj = getattr(obj, attr, None)
cleaned = self.clean(data, **kwargs)
if cleaned is not None or self.saves_null_values:
if not is_m2m:
setattr(obj, attrs[-1], cleaned)
else:
getattr(obj, attrs[-1]).set(cleaned)
def export(self, obj):
"""
Returns value from the provided object converted to export
representation.
"""
value = self.get_value(obj)
if value is None:
return ""
return self.widget.render(value, obj)