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

django-import-export / django-import-export / 17400817337

02 Sep 2025 10:32AM UTC coverage: 100.0%. Remained the same
17400817337

push

github

web-flow
[pre-commit.ci] pre-commit autoupdate (#2105)

updates:
- [github.com/adamchainz/django-upgrade: 1.25.0 → 1.27.0](https://github.com/adamchainz/django-upgrade/compare/1.25.0...1.27.0)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>

2303 of 2303 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/declarative.py
1
import logging
5✔
2
import warnings
5✔
3
from collections import OrderedDict
5✔
4

5
from django.apps import apps
5✔
6
from django.core.exceptions import FieldDoesNotExist
5✔
7
from django.db.models.fields.related import ForeignObjectRel
5✔
8

9
from import_export.options import ResourceOptions
5✔
10

11
from .fields import Field
5✔
12
from .instance_loaders import ModelInstanceLoader
5✔
13
from .utils import get_related_model
5✔
14

15
logger = logging.getLogger(__name__)
5✔
16

17

18
class DeclarativeMetaclass(type):
5✔
19
    def __new__(cls, name, bases, attrs):
5✔
20
        def _load_meta_options(base_, meta_):
5✔
21
            options = getattr(base_, "Meta", None)
5✔
22

23
            for option in [
5✔
24
                option
25
                for option in dir(options)
26
                if not option.startswith("_") and hasattr(options, option)
27
            ]:
28
                option_value = getattr(options, option)
5✔
29
                if option == "model" and isinstance(option_value, str):
5✔
30
                    option_value = apps.get_model(option_value)
5✔
31

32
                setattr(meta_, option, option_value)
5✔
33

34
        declared_fields = []
5✔
35
        meta = ResourceOptions()
5✔
36

37
        # If this class is subclassing another Resource, add that Resource's
38
        # fields. Note that we loop over the bases in *reverse*. This is
39
        # necessary in order to preserve the correct order of fields.
40
        for base in bases[::-1]:
5✔
41
            if hasattr(base, "fields"):
5✔
42
                declared_fields = list(base.fields.items()) + declared_fields
5✔
43
                # Collect the Meta options
44
                # #1363 If there are any parent classes, set those options first
45
                for parent in base.__bases__:
5✔
46
                    _load_meta_options(parent, meta)
5✔
47
                _load_meta_options(base, meta)
5✔
48

49
        # Add direct fields
50
        for field_name, obj in attrs.copy().items():
5✔
51
            if isinstance(obj, Field):
5✔
52
                field = attrs.pop(field_name)
5✔
53
                if not field.column_name:
5✔
54
                    field.column_name = field_name
5✔
55
                declared_fields.append((field_name, field))
5✔
56

57
        attrs["fields"] = OrderedDict(declared_fields)
5✔
58
        new_class = super().__new__(cls, name, bases, attrs)
5✔
59
        # add direct fields
60
        _load_meta_options(new_class, meta)
5✔
61
        new_class._meta = meta
5✔
62

63
        return new_class
5✔
64

65

66
class ModelDeclarativeMetaclass(DeclarativeMetaclass):
5✔
67
    def __new__(cls, name, bases, attrs):
5✔
68
        # Save the names of fields declared on this class
69
        class_fields = {name for name, obj in attrs.items() if isinstance(obj, Field)}
5✔
70
        new_class = super().__new__(cls, name, bases, attrs)
5✔
71

72
        opts = new_class._meta
5✔
73

74
        if not opts.instance_loader_class:
5✔
75
            opts.instance_loader_class = ModelInstanceLoader
5✔
76

77
        if opts.model:
5✔
78
            model_opts = opts.model._meta
5✔
79

80
            # #1693 check the fields explicitly declared as attributes of the Resource
81
            # class.
82
            # if 'fields' property is defined, declared fields can only be included
83
            # if they appear in the 'fields' iterable.
84
            declared_fields = {}
5✔
85
            for field_name, field in new_class.fields.items():
5✔
86
                column_name = field.column_name
5✔
87
                if (
5✔
88
                    opts.fields is not None
89
                    and field_name not in opts.fields
90
                    and column_name not in opts.fields
91
                ):
92
                    # #2017 warn only if the unlisted field is
93
                    # part of the current class
94
                    if field_name in class_fields:
5✔
95
                        warnings.warn(
5✔
96
                            f"{name}: ignoring field '{field_name}' because "
97
                            "not declared in 'fields' whitelist",
98
                            stacklevel=2,
99
                        )
100
                    continue
5✔
101
                declared_fields[field_name] = field
5✔
102

103
            field_list = []
5✔
104
            for f in sorted(model_opts.fields + model_opts.many_to_many):
5✔
105
                if opts.fields is not None and f.name not in opts.fields:
5✔
106
                    continue
5✔
107
                if opts.exclude and f.name in opts.exclude:
5✔
108
                    continue
5✔
109

110
                if f.name in declared_fields:
5✔
111
                    # If model field is declared in `ModelResource`,
112
                    # remove it from `declared_fields`
113
                    # to keep exact order of model fields
114
                    field = declared_fields.pop(f.name)
5✔
115
                else:
116
                    field = new_class.field_from_django_field(f.name, f, readonly=False)
5✔
117

118
                field_list.append(
5✔
119
                    (
120
                        f.name,
121
                        field,
122
                    )
123
                )
124

125
            # Order as model fields first then declared fields by default
126
            new_class.fields = OrderedDict([*field_list, *declared_fields.items()])
5✔
127

128
            # add fields that follow relationships
129
            if opts.fields is not None:
5✔
130
                field_list = []
5✔
131
                for field_name in opts.fields:
5✔
132
                    if field_name in declared_fields:
5✔
133
                        continue
5✔
134
                    if field_name.find("__") == -1:
5✔
135
                        continue
5✔
136

137
                    model = opts.model
5✔
138
                    attrs = field_name.split("__")
5✔
139
                    for i, attr in enumerate(attrs):
5✔
140
                        verbose_path = ".".join(
5✔
141
                            [opts.model.__name__] + attrs[0 : i + 1]
142
                        )
143

144
                        try:
5✔
145
                            f = model._meta.get_field(attr)
5✔
146
                        except FieldDoesNotExist as e:
5✔
147
                            logger.debug(e, exc_info=e)
5✔
148
                            raise FieldDoesNotExist(
5✔
149
                                "%s: %s has no field named '%s'"
150
                                % (verbose_path, model.__name__, attr)
151
                            )
152

153
                        if i < len(attrs) - 1:
5✔
154
                            # We're not at the last attribute yet, so check
155
                            # that we're looking at a relation, and move on to
156
                            # the next model.
157
                            if isinstance(f, ForeignObjectRel):
5✔
158
                                model = get_related_model(f)
5✔
159
                            else:
160
                                if get_related_model(f) is None:
5✔
161
                                    raise KeyError(
5✔
162
                                        "%s is not a relation" % verbose_path
163
                                    )
164
                                model = get_related_model(f)
5✔
165

166
                    if isinstance(f, ForeignObjectRel):
5✔
167
                        f = f.field
5✔
168

169
                    field = new_class.field_from_django_field(
5✔
170
                        field_name, f, readonly=True
171
                    )
172
                    field_list.append((field_name, field))
5✔
173

174
                new_class.fields.update(OrderedDict(field_list))
5✔
175

176
        return new_class
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