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
|