Coverage for apis_core/generic/abc.py: 63%
79 statements
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-22 07:51 +0000
« prev ^ index » next coverage.py v7.6.4, created at 2024-11-22 07:51 +0000
1from django.contrib.contenttypes.models import ContentType
2from django.db.models import BooleanField, CharField, TextField
3from django.urls import reverse
5from apis_core.generic.helpers import permission_fullname
8class GenericModel:
9 def __repr__(self):
10 return super().__repr__() + f" (ID: {self.id})"
12 @classmethod
13 def get_listview_url(cls):
14 ct = ContentType.objects.get_for_model(cls)
15 return reverse("apis_core:generic:list", args=[ct])
17 @classmethod
18 def get_createview_url(cls):
19 ct = ContentType.objects.get_for_model(cls)
20 return reverse("apis_core:generic:create", args=[ct])
22 @classmethod
23 def get_importview_url(cls):
24 ct = ContentType.objects.get_for_model(cls)
25 return reverse("apis_core:generic:import", args=[ct])
27 def get_edit_url(self):
28 ct = ContentType.objects.get_for_model(self)
29 return reverse("apis_core:generic:update", args=[ct, self.id])
31 def get_enrich_url(self):
32 ct = ContentType.objects.get_for_model(self)
33 return reverse("apis_core:generic:enrich", args=[ct, self.id])
35 def get_absolute_url(self):
36 ct = ContentType.objects.get_for_model(self)
37 return reverse("apis_core:generic:detail", args=[ct, self.id])
39 def get_delete_url(self):
40 ct = ContentType.objects.get_for_model(self)
41 return reverse("apis_core:generic:delete", args=[ct, self.id])
43 def get_merge_url(self, other_id):
44 ct = ContentType.objects.get_for_model(self)
45 return reverse("apis_core:generic:merge", args=[ct, self.id, other_id])
47 def get_create_success_url(self):
48 return self.get_absolute_url()
50 def get_update_success_url(self):
51 return self.get_edit_url()
53 def get_api_detail_endpoint(self):
54 ct = ContentType.objects.get_for_model(self)
55 return reverse("apis_core:generic:genericmodelapi-detail", args=[ct, self.id])
57 @classmethod
58 def get_change_permission(self):
59 return permission_fullname("change", self)
61 @classmethod
62 def get_add_permission(self):
63 return permission_fullname("add", self)
65 @classmethod
66 def get_delete_permission(self):
67 return permission_fullname("delete", self)
69 def get_merge_charfield_value(self, other: CharField, field: CharField):
70 res = getattr(self, field.name)
71 if not field.choices:
72 otherres = getattr(other, field.name, res)
73 if otherres != res:
74 res += f" ({otherres})"
75 return res
77 def get_merge_textfield_value(self, other: TextField, field: TextField):
78 res = getattr(self, field.name)
79 if getattr(other, field.name):
80 res += "\n" + f"Merged from {other}:\n" + getattr(other, field.name)
81 return res
83 def get_merge_booleanfield(self, other: BooleanField, field: BooleanField):
84 return getattr(other, field.name)
86 def get_field_value_after_merge(self, other, field):
87 """
88 This method finds the value of a field after merging `other` into `self`.
89 It first tries to find a merge method that is specific to that field
90 (merge_{fieldname}) and then tries to find a method that is specific to
91 the type of the field (merge_{fieldtype})
92 If neither of those exist, it uses the others field value if the field
93 in self is not set, otherwise it keeps the value in self.
94 """
95 fieldtype = field.get_internal_type().lower()
96 # if there is a `get_merge_{fieldname}` method in this model, use that one
97 if callable(getattr(self, f"get_merge_{field.name}_value", None)):
98 return getattr(self, f"get_merge_{field.name}_value")(other)
99 # otherwise we check if there is a method for the field type and use that one
100 elif callable(getattr(self, f"get_merge_{fieldtype}_value", None)):
101 return getattr(self, f"get_merge_{fieldtype}_value")(other, field)
102 else:
103 if not getattr(self, field.name):
104 return getattr(other, field.name)
105 return getattr(self, field.name)
107 def merge_fields(self, other):
108 """
109 This method iterates through the model fields and uses the
110 `get_field_value_after_merge` method to copy values from `other` to `self`.
111 It is called by the `merge_with` method.
112 """
113 for field in self._meta.fields:
114 newval = self.get_field_value_after_merge(other, field)
115 if newval != getattr(self, field.name):
116 setattr(self, field.name, newval)
117 self.save()