-
Notifications
You must be signed in to change notification settings - Fork 1
BSTRelatedColumn
#1500
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
base: is_fk
Are you sure you want to change the base?
BSTRelatedColumn
#1500
Conversation
Also tweaked some of the BSTColumn doc string. Files checked in: DataRepo/models/utilities.py DataRepo/views/models/bst_list_view/column/field.py DataRepo/views/models/bst_list_view/column/related_field.py
Details: - Checked for path being None in is_key_field in the model utilities. - Fixed an errant reference to self.name in the create sorter/filterer methods. - Added the ability of selecting the only non-ID field from a model when selecting the display field. - Renamed display_field to display_field_path to disambiguate it from the 'field' instance attribute added to BSTColumn. - Fixed an issue where field_path was being set via kwargs instead of args. - Set a default of None for the display_field_path arg. Files checked in: DataRepo/models/utilities.py DataRepo/views/models/bst_list_view/column/related_field.py DataRepo/tests/views/models/bst_list_view/column/test_related_field.py
…warning class (DeveloperWarning) which I applied to all warnings under the bst_list_view category. Files checked in: DataRepo/tests/tracebase_test_case.py DataRepo/tests/views/models/bst_list_view/column/filterer/test_annotation.py DataRepo/tests/views/models/bst_list_view/column/filterer/test_field.py DataRepo/tests/views/models/bst_list_view/column/sorter/test_annotation.py DataRepo/tests/views/models/bst_list_view/column/sorter/test_field.py DataRepo/tests/views/models/bst_list_view/column/test_related_field.py DataRepo/utils/exceptions.py DataRepo/views/models/bst_list_view/column/annotation.py DataRepo/views/models/bst_list_view/column/filterer/base.py DataRepo/views/models/bst_list_view/column/related_field.py DataRepo/views/models/bst_list_view/column/sorter/base.py
@@ -354,7 +354,6 @@ def field_path_to_model_path( | |||
|
|||
|
|||
def is_key_field( | |||
model: Optional[Type[Model]], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. Moved model
to the second argument and gave it a default of None
, since it is optional, which makes calling this method more simplistic if you already have a Field
instance.
if path is None: | ||
raise ValueError("path must not be None") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. I only recently realized that type hinting is not something that's enforced. So here, I check. I find this error more helpful than what you get without it. It doesn't reference the variable. It just says NoneType
doesn't have attribute is_relation
.
def assertNotWarns(unexpected_warning=UserWarning): | ||
def assertNotWarns(unexpected_warning=Warning): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. Been learning more about Warning
s. UserWarning
is the default warning class. They all are derived from Warning
, which is a subclass of Exception
.
@@ -2,6 +2,7 @@ | |||
from django.test import override_settings | |||
|
|||
from DataRepo.tests.tracebase_test_case import TracebaseTestCase | |||
from DataRepo.utils.exceptions import DeveloperWarning |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. I created a class named DeveloperWarning
. The way I used it was to only issue these warnings in debug mode, so they won't go into the production log.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I edited a bunch of previously implemented tests to check for this instead of UserWarning
. You can skip down to my next comment.
) | ||
|
||
|
||
@override_settings(DEBUG=True) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. Review the tests if you want...
@@ -25,17 +25,15 @@ class BSTColumn(BSTBaseColumn): | |||
1. Annotations are not supported. See BSTAnnotColumn. | |||
2. Related columns are only partially supported, as they will not be linked. See BSTRelatedColumn. | |||
3. Many-related columns are not supported. See BSTManyRelatedColumn. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. Edited the docstring to move the stuff specific to the new BSTRelatedColumn
class into the new class.
sorter = kwargs.get("sorter") | ||
filterer = kwargs.get("filterer") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. I realized that I didn't need to do this in the constructors of derived classes because the BSTBaseColumn
calls the create_sorter
and create_filterer
methods that the derived classes define.
"filterer": filterer, | ||
} | ||
) | ||
self.field = field_path_to_field(self.model, self.field_path) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. I made a field
instance member, because it's useful in derived classes and takes less code downstream.
} | ||
) | ||
self.field = field_path_to_field(self.model, self.field_path) | ||
self.is_fk = is_key_field(self.field) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. I realized that is_fk
should be supported in the BSTColumn
class, because a BSTRelatecColumn
(and the eventual BSTManyRelatedColumn
may or may not be used for foreign key fields, and this class can in fact process foreign key fields. It just doesn't support search/sort on them because the querysets end up rending the model objects in string context using the model's __str__
method. So the user sees something different than the value that would be applied to search/sort. The DB treated them as their literal pk
value. So if a user were to say, sort by such a foreign key field, the sort will appear random, because they will see (for example) the model record's name
, but ordered by its primary key.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rest of the changes in this file are trivial, due to the new instance member being accessed via self
.
@@ -10,6 +10,8 @@ | |||
from django.utils.functional import classproperty | |||
from django.utils.safestring import mark_safe | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. More DeveloeprWarning
changes you can skip...
@@ -0,0 +1,197 @@ | |||
from __future__ import annotations |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. This is the crux of this PR.
f"display_field_path '{display_field_path}' is only allowed to differ from field_path '{field_path}' " | ||
"when the field is a foreign key." | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. The conditional block below attempts to select a default display_field_path
. The display_field_path
is what makes search/sort possible when the field_path
ends in a foreign key. Instead of rendering the foreign key as a model object in string context (which can potentially differ from any one single field in the model object), it renders the field value from the display_field_path
, so that what the user sees is what search/sort will operate on.
If setting a default display_field_path
fails, search and sort are disabled.
"searchable or sortable unless a display_field_path is supplied.", | ||
DeveloperWarning, | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. The methods below are the same as in the base class, but the BSTSorter
and BSTFilterer
objects are created using the display_field_path
instead of the field_path
(when possible).
…xplain how a default is chosen. Files checked in: DataRepo/views/models/bst_list_view/column/related_field.py
Files checked in: DataRepo/views/models/bst_list_view/column/annotation.py DataRepo/views/models/bst_list_view/column/base.py DataRepo/views/models/bst_list_view/column/field.py DataRepo/views/models/bst_list_view/column/related_field.py
from DataRepo.views.models.bst_list_view.column.related_field import ( | ||
BSTRelatedColumn, | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. These create some temporary test models...
@@ -128,22 +131,18 @@ def __init__( | |||
|
|||
super().__init__(name, **kwargs) | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. Simplified these methods a little, added more precise type hinting, and used kwargs
for more robust support for the constructors they call.
… into keyword arguments. Files checked in: DataRepo/views/models/bst_list_view/column/annotation.py DataRepo/views/models/bst_list_view/column/base.py DataRepo/views/models/bst_list_view/column/field.py DataRepo/views/models/bst_list_view/column/related_field.py
def create_sorter(self, sorter: Optional[str] = None) -> BSTBaseSorter: | ||
def create_sorter(self, field=None, **kwargs) -> BSTBaseSorter: | ||
"""Derived classes must define this method to set self.sorter""" | ||
pass | ||
|
||
@abstractmethod | ||
def create_filterer(self, filterer: Optional[str] = None) -> BSTBaseFilterer: | ||
def create_filterer(self, field=None, **kwargs) -> BSTBaseFilterer: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. The filterer and sorter arguments were moved into kwargs
. They were intended to be the "client" sorter/filterer settings. The server version of these arguments is "private", so from the perspective of these BSTColumn classes, they are just referred to as "sorter" and "filterer". The "field" argument is positional in the BSTSorter
and BSTFilterer
classes, but those values are autimatically set in these Column
classes, so I made them into kwargs
.
Data Format Name | ||
Data Type Name | ||
Data Format | ||
Data Type |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. The BSTColumn
class basically only supports fields directly on the root model. Those fields can be related fields (i.e. foreign keys), but they are not supported for search and sort, because for that, they need a "display field", which is what BSTRelatedColumn
provides. So in this class, without that support, just displays the related model objects as they render in string context, but since searching and sorting on those fields uses their numeric primary keys, it automatically disables search and sort, because what the user sees is not what search/sort is based on.
So in this doc string, data_type__name
is not supported. I changed this example to refer to only the foreign key field (thus the removal of "Name" from the header, and moved that example into BSTRelatedColumn
.
len(path_tail) == 2 | ||
and path_tail[1] == "name" | ||
and is_unique_field(self.field) | ||
): | ||
return underscored_to_title(path_tail[0]) | ||
|
||
# Default is to use the last 2 elements of the path | ||
return underscored_to_title("_".join(path_tail)) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
C. As mentioned in base.py
, I simplified these methods.
Summary Change Description
Added
BSTRelatedColumn
and madefield
an instance member ofBSTColumn
.BSTColumn
doc string.path
beingNone
inis_key_field
in the model utilities.DeveloperWarning
) which I applied to all warnings under thebst_list_view
category.Affected Issues/Pull Requests
is_fk
BSTManyRelatedColumn
#1501Reviewer Notes/Requests
See comments in-line.
Checklist
This pull request will be merged once the following requirements are met. The
author and/or reviewers should uncheck any unmet requirements:
CHANGELOG.md
Unreleased section