Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce reliance on get_absolute_url #244

Draft
wants to merge 13 commits into
base: main
Choose a base branch
from
104 changes: 37 additions & 67 deletions cbv/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,29 @@
from django.urls import reverse


class DjangoURLService:
def class_detail(
self, class_name: str, module_name: str, version_number: str
) -> str:
return reverse(
"klass-detail",
kwargs={
"version": version_number,
"module": module_name,
"klass": class_name,
},
)

def module_detail(self, module_name: str, version_number: str) -> str:
return reverse(
"module-detail",
kwargs={"version": version_number, "module": module_name},
)

def version_detail(self, version_number: str) -> str:
return reverse("version-detail", kwargs={"version": version_number})


class ProjectVersionManager(models.Manager):
def get_by_natural_key(self, name: str, version_number: str) -> "ProjectVersion":
return self.get(
Expand Down Expand Up @@ -38,12 +61,7 @@ def natural_key(self) -> tuple[str, str]:
return ("Django", self.version_number)

def get_absolute_url(self) -> str:
return reverse(
"version-detail",
kwargs={
"version": self.version_number,
},
)
return DjangoURLService().version_detail(self.version_number)

def generate_sortable_version_number(self) -> str:
return "".join(part.zfill(2) for part in self.version_number.split("."))
Expand Down Expand Up @@ -98,16 +116,12 @@ def natural_key(self) -> tuple[str, str, str]:
natural_key.dependencies = ["cbv.ProjectVersion"]

def get_absolute_url(self) -> str:
return reverse(
"module-detail",
kwargs={
"version": self.project_version.version_number,
"module": self.name,
},
return DjangoURLService().module_detail(
module_name=self.name, version_number=self.project_version.version_number
)


class KlassManager(models.Manager):
class KlassQuerySet(models.QuerySet):
def get_by_natural_key(
self, klass_name: str, module_name: str, project_name: str, version_number: str
) -> "Klass":
Expand All @@ -133,6 +147,11 @@ def get_latest_for_name(self, klass_name: str) -> "Klass":
else:
return obj

def get_latest_version(self, module_name: str, class_name: str) -> "Klass":
return self.filter(module__name=module_name, name=class_name).order_by(
"-module__project_version__sortable_version_number"
)[0]


# TODO: quite a few of the methods on here should probably be denormed.
class Klass(models.Model):
Expand All @@ -146,7 +165,7 @@ class Klass(models.Model):
# because docs urls differ between Django versions
docs_url = models.URLField(max_length=255, default="")

objects = KlassManager()
objects = KlassQuerySet.as_manager()

class Meta:
unique_together = ("module", "name")
Expand All @@ -167,26 +186,11 @@ def is_secondary(self) -> bool:
)

def get_absolute_url(self) -> str:
return reverse(
"klass-detail",
kwargs={
"version": self.module.project_version.version_number,
"module": self.module.name,
"klass": self.name,
},
)

def get_latest_version_url(self) -> str:
latest = (
self._meta.model.objects.filter(
module__name=self.module.name,
name=self.name,
)
.select_related("module__project_version")
.order_by("-module__project_version__sortable_version_number")
.first()
return DjangoURLService().class_detail(
class_name=self.name,
module_name=self.module.name,
version_number=self.module.project_version.version_number,
)
return latest.get_absolute_url()

def get_source_url(self) -> str:
url = "https://github.com/django/django/blob/"
Expand Down Expand Up @@ -257,40 +261,6 @@ def get_attributes(self) -> models.QuerySet["KlassAttribute"]:
self._attributes = attrs
return self._attributes

def get_prepared_attributes(self) -> models.QuerySet["KlassAttribute"]:
attributes = self.get_attributes()
# Make a dictionary of attributes based on name
attribute_names: dict[str, list[KlassAttribute]] = {}
for attr in attributes:
try:
attribute_names[attr.name] += [attr]
except KeyError:
attribute_names[attr.name] = [attr]

ancestors = self.get_all_ancestors()

# Find overridden attributes
for name, attrs in attribute_names.items():
# Skip if we have only one attribute.
if len(attrs) == 1:
continue

# Sort the attributes by ancestors.
def _key(a: KlassAttribute) -> int:
try:
# If ancestor, return the index (>= 0)
return ancestors.index(a.klass)
except ValueError: # Raised by .index if item is not in list.
# else a.klass == self, so return -1
return -1

sorted_attrs = sorted(attrs, key=_key)

# Mark overriden KlassAttributes
for a in sorted_attrs[1:]:
a.overridden = True
return attributes

def basic_yuml_data(self, first: bool = False) -> list[str]:
self._basic_yuml_data: list[str]
if hasattr(self, "_basic_yuml_data"):
Expand Down
15 changes: 13 additions & 2 deletions cbv/queries.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import attrs

from cbv import models
from cbv.models import DjangoURLService


@attrs.frozen
Expand Down Expand Up @@ -40,13 +41,18 @@ def _to_module_data(
active_module: models.Module | None,
active_klass: models.Klass | None,
) -> "NavData.Module":
url_service = DjangoURLService()
return NavData.Module(
source_name=module.source_name(),
short_name=module.short_name(),
classes=[
NavData.Klass(
name=klass.name,
url=klass.get_absolute_url(),
url=url_service.class_detail(
class_name=klass.name,
module_name=klass.module.name,
version_number=klass.module.project_version.version_number,
),
active=klass == active_klass,
)
for klass in module.klass_set.all()
Expand All @@ -60,6 +66,7 @@ def make_version_switcher(
klass: models.Klass | None = None,
) -> VersionSwitcher:
other_versions = models.ProjectVersion.objects.exclude(pk=project_version.pk)
url_service = DjangoURLService()
if klass:
other_versions_of_klass = models.Klass.objects.filter(
name=klass.name,
Expand All @@ -75,7 +82,11 @@ def make_version_switcher(
except KeyError:
url = other_version.get_absolute_url()
else:
url = other_klass.get_absolute_url()
url = url_service.class_detail(
class_name=other_klass.name,
module_name=other_klass.module.name,
version_number=other_version.version_number,
)

versions.append(
VersionSwitcher.OtherVersion(
Expand Down
56 changes: 27 additions & 29 deletions cbv/templates/cbv/klass_detail.html
Original file line number Diff line number Diff line change
Expand Up @@ -95,39 +95,37 @@ <h2>Descendants</h2>
</div>

<div class="row">
{% for attribute in attributes %}
{% if forloop.first %}
<div class="span12">
<h2>Attributes</h2>
<table class="table table-striped table-bordered table-condensed">
<thead>
<tr>
<th>&nbsp;</th>
<th>Defined in</th>
</tr>
</thead>
<tbody>
{% endif %}
{% if attributes %}
<div class="span12">
<h2>Attributes</h2>
<table class="table table-striped table-bordered table-condensed">
<thead>
<tr>
<td>
<code class="attribute{%if attribute.overridden %} overridden{% endif %}">
{{ attribute.name }} = {{ attribute.value }}
</code>
</td>
<td>
{% if attribute.klass == klass %}
{{ attribute.klass.name }}
{% else %}
<a href="{{ attribute.klass.get_absolute_url }}">{{ attribute.klass.name }}</a>
{% endif %}
</td>
<th>&nbsp;</th>
<th>Defined in</th>
</tr>
{% if forloop.last %}
</thead>
<tbody>
{% for attribute in attributes %}
<tr>
<td>
<code class="attribute{%if attribute.overridden %} overridden{% endif %}">
{{ attribute.name }} = {{ attribute.value }}
</code>
</td>
<td>
{% if attribute.klass == klass %}
{{ attribute.klass.name }}
{% else %}
<a href="{{ attribute.klass.get_absolute_url }}">{{ attribute.klass.name }}</a>
{% endif %}
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endif %}
{% endfor %}
</div>
{% endif %}
</div>
<div class="row">
{% for method in methods %}
Expand Down
Loading
Loading