• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

django-import-export / django-import-export / 13202505604

07 Feb 2025 03:06PM CUT coverage: 100.0%. Remained the same
13202505604

Pull #1892

github

web-flow
Merge d44808f14 into 8a6586090
Pull Request #1892: Remove get_valid_export_item_pks

2282 of 2282 relevant lines covered (100.0%)

4.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

100.0
/import_export/fields.py
1
from django.core.exceptions import ObjectDoesNotExist
5✔
2
from django.db.models.fields import NOT_PROVIDED
5✔
3
from django.db.models.manager import Manager
5✔
4

5
from . import widgets
5✔
6
from .exceptions import FieldError
5✔
7

8

9
class Field:
5✔
10
    """
11
    ``Field`` represents a mapping between an ``instance`` field and a representation of
12
    the field's data.
13

14
    :param attribute: A string of either an instance attribute or callable of
15
        the instance.
16

17
    :param column_name: An optional column name for the column that represents
18
        this field in the export.
19

20
    :param widget: Defines a widget that will be used to represent this
21
        field's data in the export, or transform the value during import.
22

23
    :param readonly: A Boolean which defines if this field will be ignored
24
        during import.
25

26
    :param default: This value will be returned by
27
        :meth:`~import_export.fields.Field.clean` if this field's widget returned
28
        a value defined in :attr:`~import_export.fields.empty_values`.
29

30
    :param saves_null_values: Controls whether null values are saved on the instance.
31
      This can be used if the widget returns null, but there is a default instance
32
      value which should not be overwritten.
33

34
    :param dehydrate_method: You can provide a `dehydrate_method` as a string to use
35
        instead of the default `dehydrate_{field_name}` syntax, or you can provide
36
        a callable that will be executed with the instance as its argument.
37

38
    :param m2m_add: changes save of this field to add the values, if they do not exist,
39
        to a ManyToMany field instead of setting all values.  Only useful if field is
40
        a ManyToMany field.
41
    """
42

43
    empty_values = [None, ""]
5✔
44

45
    def __init__(
5✔
46
        self,
47
        attribute=None,
48
        column_name=None,
49
        widget=None,
50
        default=NOT_PROVIDED,
51
        readonly=False,
52
        saves_null_values=True,
53
        dehydrate_method=None,
54
        m2m_add=False,
55
    ):
56
        self.attribute = attribute
5✔
57
        self.default = default
5✔
58
        self.column_name = column_name
5✔
59
        if not widget:
5✔
60
            widget = widgets.Widget()
5✔
61
        self.widget = widget
5✔
62
        self.readonly = readonly
5✔
63
        self.saves_null_values = saves_null_values
5✔
64
        self.dehydrate_method = dehydrate_method
5✔
65
        self.m2m_add = m2m_add
5✔
66

67
    def __repr__(self):
5✔
68
        """
69
        Displays the module, class and name of the field.
70
        """
71
        path = f"{self.__class__.__module__}.{self.__class__.__name__}"
5✔
72
        if self.column_name is not None:
5✔
73
            return f"<{path}: {self.column_name}>"
5✔
74
        return "<%s>" % path
5✔
75

76
    def clean(self, row, **kwargs):
5✔
77
        """
78
        Translates the value stored in the imported datasource to an
79
        appropriate Python object and returns it.
80
        """
81
        try:
5✔
82
            value = row[self.column_name]
5✔
83
        except KeyError:
5✔
84
            raise KeyError(
5✔
85
                "Column '%s' not found in dataset. Available "
86
                "columns are: %s" % (self.column_name, list(row))
87
            )
88

89
        value = self.widget.clean(value, row=row, **kwargs)
5✔
90

91
        if value in self.empty_values and self.default != NOT_PROVIDED:
5✔
92
            if callable(self.default):
5✔
93
                return self.default()
5✔
94
            return self.default
5✔
95

96
        return value
5✔
97

98
    def get_value(self, instance):
5✔
99
        """
100
        Returns the value of the instance's attribute.
101
        """
102

103
        # The objects of a queryset can be dictionaries if the values method is used.
104
        if isinstance(instance, dict):
5✔
105
            if self.attribute not in instance:
5✔
106
                return None
5✔
107
            return instance[self.attribute]
5✔
108

109
        if self.attribute is None:
5✔
110
            return None
5✔
111

112
        attrs = self.attribute.split("__")
5✔
113
        value = instance
5✔
114

115
        for attr in attrs:
5✔
116
            try:
5✔
117
                value = getattr(value, attr, None)
5✔
118
            except (ValueError, ObjectDoesNotExist):
5✔
119
                # needs to have a primary key value before a many-to-many
120
                # relationship can be used.
121
                return None
5✔
122
            if value is None:
5✔
123
                return None
5✔
124

125
        # RelatedManager and ManyRelatedManager classes are callable in
126
        # Django >= 1.7 but we don't want to call them
127
        if callable(value) and not isinstance(value, Manager):
5✔
128
            value = value()
5✔
129
        return value
5✔
130

131
    def save(self, instance, row, is_m2m=False, **kwargs):
5✔
132
        """
133
        If this field is not declared readonly, the instance's attribute will
134
        be set to the value returned by :meth:`~import_export.fields.Field.clean`.
135
        """
136
        if not self.readonly:
5✔
137
            attrs = self.attribute.split("__")
5✔
138
            for attr in attrs[:-1]:
5✔
139
                instance = getattr(instance, attr, None)
5✔
140
            cleaned = self.clean(row, **kwargs)
5✔
141
            if cleaned is not None or self.saves_null_values:
5✔
142
                if not is_m2m:
5✔
143
                    setattr(instance, attrs[-1], cleaned)
5✔
144
                else:
145
                    if self.m2m_add:
5✔
146
                        getattr(instance, attrs[-1]).add(*cleaned)
5✔
147
                    else:
148
                        getattr(instance, attrs[-1]).set(cleaned)
5✔
149

150
    def export(self, instance, **kwargs):
5✔
151
        """
152
        Returns value from the provided instance converted to export
153
        representation.
154
        """
155
        value = self.get_value(instance)
5✔
156
        return self.widget.render(value, **kwargs)
5✔
157

158
    def get_dehydrate_method(self, field_name=None):
5✔
159
        """
160
        Returns method name to be used for dehydration of the field.
161
        Defaults to `dehydrate_{field_name}`
162
        """
163
        DEFAULT_DEHYDRATE_METHOD_PREFIX = "dehydrate_"
5✔
164

165
        if not self.dehydrate_method and not field_name:
5✔
166
            raise FieldError("Both dehydrate_method and field_name are not supplied.")
5✔
167

168
        return self.dehydrate_method or DEFAULT_DEHYDRATE_METHOD_PREFIX + field_name
5✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc