File: showfields.py

package info (click to toggle)
django-polymorphic 4.1.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 892 kB
  • sloc: python: 6,784; javascript: 263; makefile: 137
file content (176 lines) | stat: -rw-r--r-- 6,074 bytes parent folder | download
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
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
import re

from django.db import models

RE_DEFERRED = re.compile("_Deferred_.*")


class ShowFieldBase:
    """base class for the ShowField... model mixins, does the work"""

    # cause nicer multiline PolymorphicQuery output
    polymorphic_query_multiline_output = True

    polymorphic_showfield_type = False
    polymorphic_showfield_content = False
    polymorphic_showfield_deferred = False

    # these may be overridden by the user
    polymorphic_showfield_max_line_width = None
    polymorphic_showfield_max_field_width = 20
    polymorphic_showfield_old_format = False

    def __repr__(self):
        return self.__str__()

    def _showfields_get_content(self, field_name, field_type=type(None)):
        "helper for __unicode__"
        content = getattr(self, field_name)
        if self.polymorphic_showfield_old_format:
            out = ": "
        else:
            out = " "
        if issubclass(field_type, models.ForeignKey):
            if content is None:
                out += "None"
            else:
                out += content.__class__.__name__
        elif issubclass(field_type, models.ManyToManyField):
            out += f"{content.count()}"
        elif isinstance(content, int):
            out += str(content)
        elif content is None:
            out += "None"
        else:
            txt = str(content)
            max_len = self.polymorphic_showfield_max_field_width
            if len(txt) > max_len:
                txt = f"{txt[: max_len - 2]}.."
            out += f'"{txt}"'
        return out

    def _showfields_add_regular_fields(self, parts):
        "helper for __unicode__"
        done_fields = set()
        for field in self._meta.fields + self._meta.many_to_many:
            if field.name in self.polymorphic_internal_model_fields or "_ptr" in field.name:
                continue
            if field.name in done_fields:
                continue  # work around django diamond inheritance problem
            done_fields.add(field.name)

            out = field.name

            # if this is the standard primary key named "id", print it as we did with older versions of django_polymorphic
            if field.primary_key and field.name == "id" and type(field) == models.AutoField:
                out += f" {getattr(self, field.name)}"

            # otherwise, display it just like all other fields (with correct type, shortened content etc.)
            else:
                if self.polymorphic_showfield_type:
                    out += f" ({type(field).__name__}"
                    if field.primary_key:
                        out += "/pk"
                    out += ")"

                if self.polymorphic_showfield_content:
                    out += self._showfields_get_content(field.name, type(field))

            parts.append((False, out, ","))

    def _showfields_add_dynamic_fields(self, field_list, title, parts):
        "helper for __unicode__"
        parts.append((True, f"- {title}", ":"))
        for field_name in field_list:
            out = field_name
            content = getattr(self, field_name)
            if self.polymorphic_showfield_type:
                out += f" ({type(content).__name__})"
            if self.polymorphic_showfield_content:
                out += self._showfields_get_content(field_name)

            parts.append((False, out, ","))

    def __str__(self):
        # create list ("parts") containing one tuple for each title/field:
        # ( bool: new section , item-text , separator to use after item )

        # start with model name
        parts = [(True, RE_DEFERRED.sub("", self.__class__.__name__), ":")]

        # add all regular fields
        self._showfields_add_regular_fields(parts)

        # add annotate fields
        if hasattr(self, "polymorphic_annotate_names"):
            self._showfields_add_dynamic_fields(self.polymorphic_annotate_names, "Ann", parts)

        # add extra() select fields
        if hasattr(self, "polymorphic_extra_select_names"):
            self._showfields_add_dynamic_fields(
                self.polymorphic_extra_select_names, "Extra", parts
            )

        if self.polymorphic_showfield_deferred:
            fields = self.get_deferred_fields()
            if fields:
                fields_str = ",".join(sorted(fields))
                parts.append((False, f"deferred[{fields_str}]", ""))

        # format result

        indent = len(self.__class__.__name__) + 5
        indentstr = "".rjust(indent)
        out = ""
        xpos = 0
        possible_line_break_pos = None

        for i in range(len(parts)):
            new_section, p, separator = parts[i]
            final = i == len(parts) - 1
            if not final:
                next_new_section, _, _ = parts[i + 1]

            if (
                self.polymorphic_showfield_max_line_width
                and xpos + len(p) > self.polymorphic_showfield_max_line_width
                and possible_line_break_pos is not None
            ):
                rest = out[possible_line_break_pos:]
                out = out[:possible_line_break_pos]
                out += f"\n{indentstr}{rest}"
                xpos = indent + len(rest)

            out += p
            xpos += len(p)

            if not final:
                if not next_new_section:
                    out += separator
                    xpos += len(separator)
                out += " "
                xpos += 1

            if not new_section:
                possible_line_break_pos = len(out)

        return f"<{out}>"


class ShowFieldType(ShowFieldBase):
    """model mixin that shows the object's class and it's field types"""

    polymorphic_showfield_type = True


class ShowFieldContent(ShowFieldBase):
    """model mixin that shows the object's class, it's fields and field contents"""

    polymorphic_showfield_content = True


class ShowFieldTypeAndContent(ShowFieldBase):
    """model mixin, like ShowFieldContent, but also show field types"""

    polymorphic_showfield_type = True
    polymorphic_showfield_content = True