Skip to content

Commit

Permalink
Adds doc strings for lots of functions.
Browse files Browse the repository at this point in the history
  • Loading branch information
coordt committed Dec 22, 2021
1 parent f9a4684 commit 076debb
Show file tree
Hide file tree
Showing 26 changed files with 380 additions and 155 deletions.
15 changes: 11 additions & 4 deletions categories/admin.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Admin interface classes."""
from django import forms
from django.contrib import admin
from django.utils.translation import ugettext_lazy as _
Expand All @@ -15,10 +16,9 @@ def __init__(self, level_indicator="---", *args, **kwargs):
self.level_indicator = level_indicator
super(NullTreeNodeChoiceField, self).__init__(*args, **kwargs)

def label_from_instance(self, obj):
def label_from_instance(self, obj) -> str:
"""
Creates labels which represent the tree level of each node when
generating option labels.
Creates labels which represent the tree level of each node when generating option labels.
"""
return "%s %s" % (self.level_indicator * getattr(obj, obj._mptt_meta.level_attr), obj)

Expand All @@ -27,22 +27,29 @@ def label_from_instance(self, obj):
from .models import CategoryRelation

class InlineCategoryRelation(GenericCollectionTabularInline):
"""The inline admin panel for category relations."""

model = CategoryRelation


class CategoryAdminForm(CategoryBaseAdminForm):
"""The form for a category in the admin."""

class Meta:
model = Category
fields = "__all__"

def clean_alternate_title(self):
def clean_alternate_title(self) -> str:
"""Return either the name or alternate title for the category."""
if self.instance is None or not self.cleaned_data["alternate_title"]:
return self.cleaned_data["name"]
else:
return self.cleaned_data["alternate_title"]


class CategoryAdmin(CategoryBaseAdmin):
"""Admin for categories."""

form = CategoryAdminForm
list_display = ("name", "alternate_title", "active")
fieldsets = (
Expand Down
6 changes: 5 additions & 1 deletion categories/apps.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
"""Django application setup."""
from django.apps import AppConfig


class CategoriesConfig(AppConfig):
"""Application configuration for categories."""

name = "categories"
verbose_name = "Categories"

Expand All @@ -12,6 +15,7 @@ def __init__(self, *args, **kwargs):
class_prepared.connect(handle_class_prepared)

def ready(self):
"""Migrate the app after it is ready."""
from django.db.models.signals import post_migrate

from .migration import migrate_app
Expand All @@ -21,7 +25,7 @@ def ready(self):

def handle_class_prepared(sender, **kwargs):
"""
See if this class needs registering of fields
See if this class needs registering of fields.
"""
from .registration import registry
from .settings import FK_REGISTRY, M2M_REGISTRY
Expand Down
51 changes: 27 additions & 24 deletions categories/base.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
"""
This is the base class on which to build a hierarchical category-like model
with customizable metadata and its own name space.
"""
import sys
This is the base class on which to build a hierarchical category-like model.
It provides customizable metadata and its own name space.
"""
from django import forms
from django.contrib import admin
from django.db import models
Expand All @@ -17,31 +16,24 @@
from .editor.tree_editor import TreeEditor
from .settings import ALLOW_SLUG_CHANGE, SLUG_TRANSLITERATOR

if sys.version_info[0] < 3: # Remove this after dropping support of Python 2
from django.utils.encoding import python_2_unicode_compatible
else:

def python_2_unicode_compatible(x):
return x


class CategoryManager(models.Manager):
"""
A manager that adds an "active()" method for all active categories
A manager that adds an "active()" method for all active categories.
"""

def active(self):
"""
Only categories that are active
Only categories that are active.
"""
return self.get_queryset().filter(active=True)


@python_2_unicode_compatible
class CategoryBase(MPTTModel):
"""
This base model includes the absolute bare bones fields and methods. One
could simply subclass this model and do nothing else and it should work.
This base model includes the absolute bare-bones fields and methods.
One could simply subclass this model, do nothing else, and it should work.
"""

parent = TreeForeignKey(
Expand All @@ -61,9 +53,15 @@ class CategoryBase(MPTTModel):

def save(self, *args, **kwargs):
"""
Save the category.
While you can activate an item without activating its descendants,
It doesn't make sense that you can deactivate an item and have its
decendants remain active.
Args:
args: generic args
kwargs: generic keyword arguments
"""
if not self.slug:
self.slug = slugify(SLUG_TRANSLITERATOR(self.name))[:50]
Expand Down Expand Up @@ -95,14 +93,16 @@ class MPTTMeta:


class CategoryBaseAdminForm(forms.ModelForm):
"""Base admin form for categories."""

def clean_slug(self):
if not self.cleaned_data.get("slug", None):
if self.instance is None or not ALLOW_SLUG_CHANGE:
self.cleaned_data["slug"] = slugify(SLUG_TRANSLITERATOR(self.cleaned_data["name"]))
"""Prune and transliterate the slug."""
if not self.cleaned_data.get("slug", None) and (self.instance is None or not ALLOW_SLUG_CHANGE):
self.cleaned_data["slug"] = slugify(SLUG_TRANSLITERATOR(self.cleaned_data["name"]))
return self.cleaned_data["slug"][:50]

def clean(self):

"""Clean the data passed from the admin interface."""
super(CategoryBaseAdminForm, self).clean()

if not self.is_valid():
Expand Down Expand Up @@ -141,6 +141,8 @@ def clean(self):


class CategoryBaseAdmin(TreeEditor, admin.ModelAdmin):
"""Base admin class for categories."""

form = CategoryBaseAdminForm
list_display = ("name", "active")
search_fields = ("name",)
Expand All @@ -149,14 +151,15 @@ class CategoryBaseAdmin(TreeEditor, admin.ModelAdmin):
actions = ["activate", "deactivate"]

def get_actions(self, request):
"""Get available actions for the admin interface."""
actions = super(CategoryBaseAdmin, self).get_actions(request)
if "delete_selected" in actions:
del actions["delete_selected"]
return actions

def deactivate(self, request, queryset):
def deactivate(self, request, queryset): # NOQA: queryset is not used.
"""
Set active to False for selected items
Set active to False for selected items.
"""
selected_cats = self.model.objects.filter(pk__in=[int(x) for x in request.POST.getlist("_selected_action")])

Expand All @@ -168,9 +171,9 @@ def deactivate(self, request, queryset):

deactivate.short_description = _("Deactivate selected categories and their children")

def activate(self, request, queryset):
def activate(self, request, queryset): # NOQA: queryset is not used.
"""
Set active to True for selected items
Set active to True for selected items.
"""
selected_cats = self.model.objects.filter(pk__in=[int(x) for x in request.POST.getlist("_selected_action")])

Expand Down
2 changes: 1 addition & 1 deletion categories/editor/models.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
# Placeholder for Django
"""Placeholder for Django."""
1 change: 1 addition & 0 deletions categories/editor/settings.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Settings management for the editor."""
import django
from django.conf import settings

Expand Down
15 changes: 10 additions & 5 deletions categories/editor/templatetags/admin_tree_list_tags.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
"""Template tags used to render the tree editor."""
import django
from django.contrib.admin.templatetags.admin_list import _boolean_icon, result_headers
from django.contrib.admin.utils import lookup_field
Expand All @@ -19,13 +20,14 @@


def get_empty_value_display(cl):
"""Get the value to display when empty."""
if hasattr(cl.model_admin, "get_empty_value_display"):
return cl.model_admin.get_empty_value_display()
else:
# Django < 1.9
from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE

return EMPTY_CHANGELIST_VALUE
# Django < 1.9
from django.contrib.admin.views.main import EMPTY_CHANGELIST_VALUE

return EMPTY_CHANGELIST_VALUE


def items_for_tree_result(cl, result, form):
Expand Down Expand Up @@ -130,10 +132,13 @@ def items_for_tree_result(cl, result, form):


class TreeList(list):
"""A list subclass for tree result."""

pass


def tree_results(cl):
"""Generates a list of results for the tree."""
if cl.formset:
for res, form in zip(cl.result_list, cl.formset.forms):
result = TreeList(items_for_tree_result(cl, res, form))
Expand All @@ -158,7 +163,7 @@ def tree_results(cl):

def result_tree_list(cl):
"""
Displays the headers and data list together
Displays the headers and data list together.
"""
import django

Expand Down
67 changes: 50 additions & 17 deletions categories/editor/tree_editor.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
"""Classes for representing tree structures in Django's admin."""
from typing import Any

import django
from django.contrib import admin
from django.contrib.admin.options import IncorrectLookupParameters
Expand All @@ -12,9 +15,9 @@

class TreeEditorQuerySet(QuerySet):
"""
The TreeEditorQuerySet is a special query set used only in the TreeEditor
ChangeList page. The only difference to a regular QuerySet is that it
will enforce:
A special query set used only in the TreeEditor ChangeList page.
The only difference to a regular QuerySet is that it will enforce:
(a) The result is ordered in correct tree order so that
the TreeAdmin works all right.
Expand All @@ -25,6 +28,7 @@ class TreeEditorQuerySet(QuerySet):
"""

def iterator(self):
"""Iterates through the items in thee query set."""
qs = self
# Reaching into the bowels of query sets to find out whether the qs is
# actually filtered and we need to do the INCLUDE_ANCESTORS dance at all.
Expand Down Expand Up @@ -54,36 +58,59 @@ def iterator(self):
# def __getitem__(self, index):
# return self # Don't even try to slice

def get(self, *args, **kwargs):
def get(self, *args, **kwargs) -> Any:
"""
Quick and dirty hack to fix change_view and delete_view; they use
self.queryset(request).get(...) to get the object they should work
with. Our modifications to the queryset when INCLUDE_ANCESTORS is
enabled make get() fail often with a MultipleObjectsReturned
exception.
Quick and dirty hack to fix change_view and delete_view.
They use ``self.queryset(request).get(...)`` to get the object they should work
with. Our modifications to the queryset when ``INCLUDE_ANCESTORS`` is enabled make ``get()``
fail often with a ``MultipleObjectsReturned`` exception.
Args:
args: generic arguments
kwargs: generic keyword arguments
Returns:
The object they should work with.
"""
return self.model._default_manager.get(*args, **kwargs)


class TreeChangeList(ChangeList):
"""A change list for a tree."""

def _get_default_ordering(self):
if django.VERSION[0] == 1 and django.VERSION[1] < 4:
return "", "" # ('tree_id', 'lft')
else:
return []

def get_ordering(self, request=None, queryset=None):
"""
Return ordering information for the change list.
Always returns empty/default ordering.
Args:
request: The incoming request.
queryset: The current queryset
Returns:
Either a tuple of empty strings or an empty list.
"""
if django.VERSION[0] == 1 and django.VERSION[1] < 4:
return "", "" # ('tree_id', 'lft')
else:
return []

def get_queryset(self, *args, **kwargs):
qs = super(TreeChangeList, self).get_queryset(*args, **kwargs).order_by("tree_id", "lft")
return qs
"""Return a queryset."""
return super(TreeChangeList, self).get_queryset(*args, **kwargs).order_by("tree_id", "lft")


class TreeEditor(admin.ModelAdmin):
"""A tree editor view for Django's admin."""

list_per_page = 999999999 # We can't have pagination
list_max_show_all = 200 # new in django 1.4

Expand Down Expand Up @@ -120,7 +147,7 @@ def get_changelist(self, request, **kwargs):
return TreeChangeList

def old_changelist_view(self, request, extra_context=None):
"The 'change list' admin view for this model."
"""The 'change list' admin view for this model."""
from django.contrib.admin.views.main import ERROR_FLAG
from django.core.exceptions import PermissionDenied
from django.utils.encoding import force_text
Expand Down Expand Up @@ -302,8 +329,7 @@ def old_changelist_view(self, request, extra_context=None):

def changelist_view(self, request, extra_context=None, *args, **kwargs):
"""
Handle the changelist view, the django view for the model instances
change list/actions page.
Handle the changelist view, the django view for the model instances change list/actions page.
"""
extra_context = extra_context or {}
extra_context["EDITOR_MEDIA_PATH"] = settings.MEDIA_PATH
Expand All @@ -312,10 +338,17 @@ def changelist_view(self, request, extra_context=None, *args, **kwargs):
# FIXME
return self.old_changelist_view(request, extra_context)

def get_queryset(self, request):
def get_queryset(self, request) -> TreeEditorQuerySet:
"""
Returns a QuerySet of all model instances that can be edited by the
admin site. This is used by changelist_view.
Returns a QuerySet of all model instances that can be edited by the admin site.
This is used by changelist_view.
Args:
request: the incoming request.
Returns:
A QuerySet of editable model instances
"""
qs = self.model._default_manager.get_queryset()
qs.__class__ = TreeEditorQuerySet
Expand Down

0 comments on commit 076debb

Please sign in to comment.