-
Notifications
You must be signed in to change notification settings - Fork 3.2k
fix: state group choices #8198
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
fix: state group choices #8198
Conversation
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughIntroduces a StateGroup TextChoices enum and DEFAULT_STATES, updates the State model to use StateGroup for the group field, and replaces scattered uses of the old State.* triage constant with StateGroup.TRIAGE.value across serializers, views, managers, models, and tasks. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (2 warnings)
✅ Passed checks (1 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: CHILL Plan: Pro 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (3)
🧰 Additional context used🧠 Learnings (2)📓 Common learnings📚 Learning: 2025-11-20T11:00:42.330ZApplied to files:
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
🔇 Additional comments (1)
Tip 📝 Customizable high-level summaries are now available in beta!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example instruction:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Pull request overview
This PR refactors state group management by introducing a StateGroup enum (Django TextChoices) to replace hardcoded string constants and consolidating default state definitions into a single DEFAULT_STATES constant. This improves type safety, reduces code duplication, and aligns with Django best practices for handling database choices.
Key changes:
- Introduced
StateGroupenum class to centralize state group definitions - Created
DEFAULT_STATESconstant to eliminate duplicated state definitions across project creation views - Updated all references from
State.TRIAGE,State.BACKLOG, etc. toStateGroup.TRIAGE.value,StateGroup.BACKLOG.value
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| apps/api/plane/db/models/state.py | Introduces StateGroup enum, DEFAULT_STATES constant, and updates State model to use enum for choices |
| apps/api/plane/db/models/init.py | Exports StateGroup and DEFAULT_STATES for use across the codebase |
| apps/api/plane/db/models/issue.py | Updates issue filtering to use StateGroup.TRIAGE.value instead of State.TRIAGE |
| apps/api/plane/space/views/intake.py | Updates triage state creation to use StateGroup.TRIAGE.value |
| apps/api/plane/bgtasks/dummy_data_task.py | Updates state filtering logic to use StateGroup.TRIAGE.value |
| apps/api/plane/app/views/project/base.py | Replaces inline state definitions with DEFAULT_STATES constant |
| apps/api/plane/app/views/intake/base.py | Updates triage state creation to use StateGroup.TRIAGE.value |
| apps/api/plane/app/serializers/state.py | Updates validation to use StateGroup.TRIAGE.value |
| apps/api/plane/app/serializers/intake.py | Updates state group checks to use StateGroup.TRIAGE.value and removes redundant import |
| apps/api/plane/api/views/project.py | Replaces inline state definitions with DEFAULT_STATES constant |
| apps/api/plane/api/views/intake.py | Updates triage state creation to use StateGroup.TRIAGE.value |
| apps/api/plane/api/serializers/state.py | Updates validation to use StateGroup.TRIAGE.value |
| apps/api/plane/api/serializers/intake.py | Updates state group checks to use StateGroup.TRIAGE.value and removes redundant import |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ), | ||
| default="backlog", | ||
| choices=StateGroup.choices, | ||
| default=StateGroup.BACKLOG, |
Copilot
AI
Nov 28, 2025
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 default parameter should be set to StateGroup.BACKLOG.value (a string) instead of StateGroup.BACKLOG (the enum member). The group field is a CharField that stores string values, not enum objects. This will cause a type error when Django tries to use the default value.
Change to:
default=StateGroup.BACKLOG.value,| default=StateGroup.BACKLOG, | |
| default=StateGroup.BACKLOG.value, |
| "group": StateGroup.CANCELLED.value, | ||
| }, | ||
| { | ||
| "name": "Triage", |
Copilot
AI
Nov 28, 2025
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 triage state name "Triage" in DEFAULT_STATES is inconsistent with "Intake Triage" used when dynamically creating triage states in intake views (e.g., apps/api/plane/api/views/intake.py:173, apps/api/plane/app/views/intake/base.py:238, apps/api/plane/space/views/intake.py:131).
This inconsistency means:
- Projects created with DEFAULT_STATES will have a state named "Triage"
- Projects that dynamically create triage states through intake views will have a state named "Intake Triage"
Consider either:
- Changing line 53 to
"name": "Intake Triage",to match the dynamic creation - Or updating all intake views to use "Triage" consistently
| "name": "Triage", | |
| "name": "Intake Triage", |
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.
Actionable comments posted: 1
🧹 Nitpick comments (2)
apps/api/plane/db/models/issue.py (1)
100-101: Consider removing redundant triage filter.Both Line 100 (
.filter(state__is_triage=False)) and Line 101 (.exclude(state__group=StateGroup.TRIAGE.value)) are filtering out triage states. Based on learnings, settinggroup="triage"is sufficient for triage states, making theis_triagefield filter potentially redundant.Consider removing Line 100 to simplify the filtering logic:
.filter(deleted_at__isnull=True) - .filter(state__is_triage=False) .exclude(state__group=StateGroup.TRIAGE.value) .exclude(archived_at__isnull=False)apps/api/plane/db/models/state.py (1)
19-58: Based on my verification, I need to rewrite the review comment. The original concern about duplicates is technically incorrect (the code prevents them), but there is a valid design concern about having Triage in DEFAULT_STATES.Remove Triage from DEFAULT_STATES to align with on-demand intake creation pattern.
Triage states are created on-demand when intake is enabled (via
apps/api/plane/api/views/intake.pyline 169-179), with proper checks to prevent duplicates. Including Triage inDEFAULT_STATESmeans all projects get a triage state at creation, even those that never use intake—contradicting the on-demand pattern.While
State.objectsproperly excludes triage via theStateManager(excludinggroup="triage"), the redundant default Triage state wastes minimal resources. For cleaner design, remove it fromDEFAULT_STATES:{ "name": "Cancelled", "color": "#9AA4BC", "sequence": 55000, "group": StateGroup.CANCELLED.value, }, - { - "name": "Triage", - "color": "#4E5355", - "sequence": 65000, - "group": StateGroup.TRIAGE.value, - }, ]
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (13)
apps/api/plane/api/serializers/intake.py(3 hunks)apps/api/plane/api/serializers/state.py(2 hunks)apps/api/plane/api/views/intake.py(2 hunks)apps/api/plane/api/views/project.py(2 hunks)apps/api/plane/app/serializers/intake.py(3 hunks)apps/api/plane/app/serializers/state.py(2 hunks)apps/api/plane/app/views/intake/base.py(2 hunks)apps/api/plane/app/views/project/base.py(2 hunks)apps/api/plane/bgtasks/dummy_data_task.py(2 hunks)apps/api/plane/db/models/__init__.py(1 hunks)apps/api/plane/db/models/issue.py(2 hunks)apps/api/plane/db/models/state.py(1 hunks)apps/api/plane/space/views/intake.py(2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: NarayanBavisetti
Repo: makeplane/plane PR: 8135
File: apps/api/plane/db/migrations/0109_auto_20251113_1112.py:24-36
Timestamp: 2025-11-20T11:00:42.330Z
Learning: In the State model, filtering triage states is handled by the StateManager (State.objects) which excludes group="triage". Setting group="triage" is sufficient for triage states; the is_triage field is not required to be set to True for proper filtering behavior.
📚 Learning: 2025-11-20T11:00:42.330Z
Learnt from: NarayanBavisetti
Repo: makeplane/plane PR: 8135
File: apps/api/plane/db/migrations/0109_auto_20251113_1112.py:24-36
Timestamp: 2025-11-20T11:00:42.330Z
Learning: In the State model, filtering triage states is handled by the StateManager (State.objects) which excludes group="triage". Setting group="triage" is sufficient for triage states; the is_triage field is not required to be set to True for proper filtering behavior.
Applied to files:
apps/api/plane/app/views/intake/base.pyapps/api/plane/app/serializers/intake.pyapps/api/plane/api/views/intake.pyapps/api/plane/db/models/issue.pyapps/api/plane/bgtasks/dummy_data_task.pyapps/api/plane/api/serializers/state.pyapps/api/plane/space/views/intake.pyapps/api/plane/app/serializers/state.pyapps/api/plane/db/models/state.pyapps/api/plane/api/serializers/intake.py
📚 Learning: 2025-10-29T09:17:54.815Z
Learnt from: NarayanBavisetti
Repo: makeplane/plane PR: 7905
File: apps/api/plane/app/views/search/base.py:241-276
Timestamp: 2025-10-29T09:17:54.815Z
Learning: In apps/api/plane/app/views/search/base.py, the `filter_intakes` method uses `Issue.objects` (base manager) instead of `Issue.issue_objects` (custom manager) because the custom manager filters out all intake statuses, which would prevent querying pending and snoozed intake issues.
Applied to files:
apps/api/plane/app/serializers/intake.pyapps/api/plane/api/views/intake.pyapps/api/plane/db/models/issue.pyapps/api/plane/space/views/intake.pyapps/api/plane/api/serializers/intake.py
📚 Learning: 2025-11-24T07:44:08.690Z
Learnt from: NarayanBavisetti
Repo: makeplane/plane PR: 8135
File: apps/api/plane/db/migrations/0112_auto_20251124_0603.py:18-24
Timestamp: 2025-11-24T07:44:08.690Z
Learning: In Django data migrations, `apps.get_model()` returns historical models that use the base `models.Manager()` (unfiltered) by default. Custom managers defined on the model (like `StateManager` that filters records) are NOT applied in migrations unless the manager class explicitly sets `use_in_migrations = True`. Therefore, when checking migration code that uses `apps.get_model("db", "State")`, the queries using `State.objects` will use the unfiltered base manager, not any custom filtering managers defined on the State model in the codebase.
Applied to files:
apps/api/plane/db/models/issue.pyapps/api/plane/db/models/state.py
🧬 Code graph analysis (12)
apps/api/plane/app/views/intake/base.py (1)
apps/api/plane/db/models/state.py (1)
StateGroup(10-16)
apps/api/plane/api/views/project.py (1)
apps/api/plane/tests/unit/models/test_issue_comment_modal.py (1)
state(28-35)
apps/api/plane/app/views/project/base.py (1)
apps/api/plane/tests/unit/models/test_issue_comment_modal.py (1)
state(28-35)
apps/api/plane/api/views/intake.py (1)
apps/api/plane/db/models/state.py (2)
State(75-122)StateGroup(10-16)
apps/api/plane/db/models/issue.py (2)
apps/api/plane/tests/unit/models/test_issue_comment_modal.py (1)
state(28-35)apps/api/plane/db/models/state.py (1)
StateGroup(10-16)
apps/api/plane/bgtasks/dummy_data_task.py (1)
apps/api/plane/db/models/state.py (2)
StateGroup(10-16)State(75-122)
apps/api/plane/api/serializers/state.py (1)
apps/api/plane/db/models/state.py (2)
State(75-122)StateGroup(10-16)
apps/api/plane/space/views/intake.py (1)
apps/api/plane/db/models/state.py (2)
State(75-122)StateGroup(10-16)
apps/api/plane/db/models/__init__.py (1)
apps/api/plane/db/models/state.py (2)
State(75-122)StateGroup(10-16)
apps/api/plane/app/serializers/state.py (1)
apps/api/plane/db/models/state.py (2)
State(75-122)StateGroup(10-16)
apps/api/plane/db/models/state.py (1)
apps/api/plane/db/models/issue.py (1)
get_queryset(89-105)
apps/api/plane/api/serializers/intake.py (1)
apps/api/plane/db/models/state.py (2)
State(75-122)StateGroup(10-16)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Agent
- GitHub Check: Analyze (javascript)
🔇 Additional comments (24)
apps/api/plane/db/models/issue.py (1)
22-22: LGTM!The import of
StateGroupis correctly added to support the refactored state group handling.apps/api/plane/api/serializers/intake.py (2)
4-4: LGTM!The imports are correctly updated to include both
StateandStateGroupfromplane.db.models.
118-118: LGTM!The triage state checks are correctly updated to use
StateGroup.TRIAGE.valueinstead ofState.TRIAGE, aligning with the centralized state group definitions.Also applies to: 142-142
apps/api/plane/api/serializers/state.py (2)
3-3: LGTM!The import is correctly updated to include
StateGroupalongsideState.
20-21: LGTM!The validation logic correctly uses
StateGroup.TRIAGE.valuefor the triage state check, ensuring consistency with the new enum-based approach.apps/api/plane/app/views/project/base.py (2)
34-34: LGTM!The import of
DEFAULT_STATESenables centralized default state definitions, improving maintainability.
268-282: DEFAULT_STATES structure is correct and matches the expected format.Verification confirms that
DEFAULT_STATES(defined inapps/api/plane/db/models/state.pylines 20-50) contains all required keys (name,color,sequence,group) across all state dictionaries. The optionaldefaultkey is present only in the "Backlog" state and is safely handled viastate.get("default", False)in the code, which defaults toFalsewhen absent. The bulk_create operation will execute without key-related errors.apps/api/plane/bgtasks/dummy_data_task.py (2)
20-20: LGTM!The import of
StateGroupis correctly added to support the refactored state group filtering.
267-271: LGTM!The filter logic correctly uses
StateGroup.TRIAGE.valueto exclude triage states when creating dummy issues, ensuring consistency with the new enum-based approach.apps/api/plane/app/views/intake/base.py (2)
25-25: LGTM!The import of
StateGroupis correctly added to support the refactored triage state creation.
237-245: LGTM!The triage state creation correctly uses
StateGroup.TRIAGE.valuefor thegroupparameter, ensuring consistency with the new enum-based state grouping.apps/api/plane/space/views/intake.py (2)
15-15: LGTM!The import is correctly updated to include
StateGroupalongside other necessary models.
130-138: LGTM!The triage state creation correctly uses
StateGroup.TRIAGE.valuefor thegroupparameter, maintaining consistency with the refactored state group handling across the codebase.apps/api/plane/app/serializers/state.py (2)
5-5: LGTM!The import is correctly updated to include both
StateandStateGroupfromplane.db.models.
28-29: LGTM!The validation logic correctly uses
StateGroup.TRIAGE.valuefor the triage state check, ensuring consistency with the centralized state group definitions.apps/api/plane/app/serializers/intake.py (2)
10-10: LGTM!The import of
StateGroupalongsideStateis correct and aligns with the PR's goal to centralize state group definitions.
51-51: Consistent migration to StateGroup enum.The change from
State.TRIAGEtoStateGroup.TRIAGE.valuecorrectly uses the new centralized enum for triage state checks. This ensures consistency with theStateManagerfiltering logic that usesStateGroup.TRIAGE.value.Also applies to: 71-71
apps/api/plane/api/views/intake.py (2)
26-26: LGTM!Adding
StateGroupto the imports is correct and necessary for the triage state creation below.
169-180: Correct usage of StateGroup for triage state creation.The change from a hardcoded value to
StateGroup.TRIAGE.valueensures consistency with theTriageStateManagerwhich filters by the same enum value. Based on learnings, settinggroup="triage"is sufficient for triage states since theStateManagerexcludes this group by default.apps/api/plane/api/views/project.py (2)
27-27: LGTM!Importing
DEFAULT_STATEScentralizes state definitions and reduces code duplication across project creation flows.
236-250: I'll help you verify this review comment about triage state creation in the default project states. Let me start by examining the relevant code sections.
<function_calls>
#!/bin/bashFind and examine DEFAULT_STATES definition
rg -n "DEFAULT_STATES" --type=py -A 20 | head -60
</function_calls>
<function_calls>
#!/bin/bashExamine the intake view to see how triage states are created
rg -n "triage_objects" --type=py -B 3 -A 10 | head -80
</function_calls>
<function_calls>
#!/bin/bashCheck the project.py file to understand the context better
rg -n "group.*triage" --type=py -B 2 -A 2
</function_calls>
<function_calls>
#!/bin/bashLook for the State model definition and how it handles triage states
rg -n "class State" --type=py -A 15 | head -100
</function_calls>Please wait for the results of these searches to verify the concern about potential duplicate triage states.
apps/api/plane/db/models/__init__.py (1)
59-59: LGTM!Exporting
StateGroupandDEFAULT_STATESfrom the models package enables clean imports across the codebase (from plane.db.models import StateGroup, DEFAULT_STATES).apps/api/plane/db/models/state.py (2)
10-17: Well-structured StateGroup enum.The
TextChoicesenum provides type safety and centralized definitions for state groups. This replaces scattered hardcoded strings throughout the codebase.
61-72: LGTM!Both managers correctly use
StateGroup.TRIAGE.valuefor filtering, ensuring consistency with the new enum-based approach.
| group = models.CharField( | ||
| choices=( | ||
| ("backlog", "Backlog"), | ||
| ("unstarted", "Unstarted"), | ||
| ("started", "Started"), | ||
| ("completed", "Completed"), | ||
| ("cancelled", "Cancelled"), | ||
| ("triage", "Triage"), | ||
| ), | ||
| default="backlog", | ||
| choices=StateGroup.choices, | ||
| default=StateGroup.BACKLOG, | ||
| max_length=20, | ||
| ) |
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.
🧩 Analysis chain
🏁 Script executed:
web_search
Django TextChoices CharField default enum member vs value behaviorRepository: makeplane/plane
Length of output: 154
🏁 Script executed:
# Find the StateGroup definition in the codebase
rg -n "class StateGroup" apps/api/plane/db/models/ -A 10Repository: makeplane/plane
Length of output: 766
🏁 Script executed:
# Search for other uses of StateGroup default values
rg -n "default.*StateGroup" apps/api/plane/db/models/ -B 2 -A 2Repository: makeplane/plane
Length of output: 374
🏁 Script executed:
# Check the full state.py file to see the context around line 81-85
sed -n '75,90p' apps/api/plane/db/models/state.pyRepository: makeplane/plane
Length of output: 802
🏁 Script executed:
# Search for Django TextChoices documentation or examples in the codebase
rg -n "TextChoices" apps/api/ -B 2 -A 5 | head -50Repository: makeplane/plane
Length of output: 3188
🏁 Script executed:
# Search for all default= patterns with Choices/TextChoices in the codebase
rg -n "default\s*=\s*\w+\." apps/api/plane/db/models/ -B 2 -A 1 | grep -E "(default|models\.(TextChoices|IntegerChoices|Choices))" | head -40Repository: makeplane/plane
Length of output: 3308
🏁 Script executed:
# Check Django version to understand TextChoices behavior
find apps/api -name "requirements*.txt" -o -name "pyproject.toml" -o -name "setup.py" | xargs grep -i django 2>/dev/null | head -5Repository: makeplane/plane
Length of output: 41
🏁 Script executed:
# Create a simple Python test to verify TextChoices behavior
python3 << 'EOF'
import sys
sys.path.insert(0, '/tmp')
# Simulate Django TextChoices behavior
class TextChoices(str):
"""Simplified version of Django's TextChoices"""
def __new__(cls, value, label):
obj = str.__new__(cls, value)
obj._value_ = value
obj._label_ = label
return obj
class StateGroup(TextChoices):
BACKLOG = "backlog", "Backlog"
STARTED = "started", "Started"
# Test the behavior
print(f"StateGroup.BACKLOG type: {type(StateGroup.BACKLOG)}")
print(f"StateGroup.BACKLOG value: {repr(StateGroup.BACKLOG)}")
print(f"StateGroup.BACKLOG == 'backlog': {StateGroup.BACKLOG == 'backlog'}")
print(f"str(StateGroup.BACKLOG): {str(StateGroup.BACKLOG)}")
print(f"StateGroup.BACKLOG is a string: {isinstance(StateGroup.BACKLOG, str)}")
# Check if .value attribute exists
try:
print(f"StateGroup.BACKLOG.value: {StateGroup.BACKLOG.value}")
except AttributeError as e:
print(f"StateGroup.BACKLOG.value error: {e}")
EOFRepository: makeplane/plane
Length of output: 145
🏁 Script executed:
# Check if there are any migrations or model usages that show the pattern
rg -n "StateGroup\." apps/api/plane/db/models/state.py -A 1 -B 1 | head -20Repository: makeplane/plane
Length of output: 527
🏁 Script executed:
# Search Django usage in the project to understand version
find . -name "*.py" -path "*/apps/api/*" -type f | xargs grep -l "django" | head -3 | xargs grep "from django" | head -10Repository: makeplane/plane
Length of output: 334
🏁 Script executed:
# Get the full DEFAULT_STATES context
sed -n '19,60p' apps/api/plane/db/models/state.pyRepository: makeplane/plane
Length of output: 955
🏁 Script executed:
# Check if there are any Django model tests or documentation comments
rg -n "TextChoices|CharField" apps/api/plane/db/models/state.py -B 2 -A 3Repository: makeplane/plane
Length of output: 1094
🏁 Script executed:
# Search for other model field defaults using TextChoices to see the pattern
rg -n "default\s*=\s*\w+\.\w+" apps/api/plane/db/models/ -B 3 -A 1 | grep -A 3 -B 3 "Choices\|choices"Repository: makeplane/plane
Length of output: 1884
🏁 Script executed:
# Search for any Django documentation or comments about TextChoices usage
rg -n "TextChoices|text.*choices" apps/api/plane/ --type=py -B 2 -A 5 -i | head -60Repository: makeplane/plane
Length of output: 4009
🏁 Script executed:
# Check if there's any documentation or comments in the Django model files
head -30 apps/api/plane/db/models/state.pyRepository: makeplane/plane
Length of output: 766
🏁 Script executed:
# Create a verification to understand Django TextChoices inheritance
python3 << 'EOF'
# Demonstrate Django TextChoices behavior
# TextChoices inherits from str, so the enum member IS the string value
class TextChoicesMember(str):
"""Mimics Django TextChoices member behavior"""
def __new__(cls, value, label):
obj = str.__new__(cls, value)
obj._value_ = value
obj._label_ = label
return obj
# Simulate StateGroup
class StateGroup:
BACKLOG = TextChoicesMember("backlog", "Backlog")
# Check equivalence
member = StateGroup.BACKLOG
print(f"member: {repr(member)}")
print(f"member.value: {repr(member.value)}")
print(f"member == 'backlog': {member == 'backlog'}")
print(f"member == member.value: {member == member.value}")
print(f"They are identical: {member is member.value}")
EOFRepository: makeplane/plane
Length of output: 261
🏁 Script executed:
# Search for Django's Enum or TextChoices usage patterns in test files
find apps/api -name "*test*" -type f -name "*.py" | xargs grep -l "TextChoices\|StateGroup" 2>/dev/null | head -3Repository: makeplane/plane
Length of output: 41
🏁 Script executed:
# Check if there are any migration files that show the pattern
find apps/api -path "*/migrations/*.py" -type f | xargs grep -l "StateGroup" 2>/dev/null | head -2Repository: makeplane/plane
Length of output: 41
🏁 Script executed:
# Let's verify what .value actually returns for Django's TextChoices
python3 << 'EOF'
# Django's TextChoices inherits from str and Enum
# Let's check the actual behavior
from enum import Enum
class TextChoices(str, Enum):
"""Closer to Django's TextChoices"""
pass
class StateGroup(TextChoices):
BACKLOG = "backlog"
STARTED = "started"
# Check behavior
member = StateGroup.BACKLOG
print(f"member type: {type(member)}")
print(f"member value: {repr(member)}")
print(f"member == 'backlog': {member == 'backlog'}")
print(f"hasattr value: {hasattr(member, 'value')}")
print(f"member.value: {repr(member.value)}")
print(f"member == member.value: {member == member.value}")
print(f"str(member): {str(member)}")
EOFRepository: makeplane/plane
Length of output: 268
Perfect! I now have all the information needed to verify the review comment.
Django's TextChoices correctly handles both default=StateGroup.BACKLOG and default=StateGroup.BACKLOG.value — however, using .value is more explicit and aligns with the codebase pattern.
The verification shows:
- Django's TextChoices (which inherits from both
strandEnum) has a.valueattribute StateGroup.BACKLOGandStateGroup.BACKLOG.valueare functionally equivalent- However, the same file uses
.valueconsistently inDEFAULT_STATES(lines 25, 32, 38, 44, 50) - Using
.valuefollows Python Enum conventions and is more explicit
Use default=StateGroup.BACKLOG.value for consistency with the DEFAULT_STATES data structure and to explicitly reference the string value.
While Django accepts default=StateGroup.BACKLOG due to TextChoices inheriting from str, the codebase itself uses .value in the DEFAULT_STATES list (lines 25, 32, 38, 44, 50 in the same file), creating an inconsistency. Explicitly accessing .value follows Python Enum conventions and makes the intent clearer:
group = models.CharField(
choices=StateGroup.choices,
- default=StateGroup.BACKLOG,
+ default=StateGroup.BACKLOG.value,
max_length=20,
)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| group = models.CharField( | |
| choices=( | |
| ("backlog", "Backlog"), | |
| ("unstarted", "Unstarted"), | |
| ("started", "Started"), | |
| ("completed", "Completed"), | |
| ("cancelled", "Cancelled"), | |
| ("triage", "Triage"), | |
| ), | |
| default="backlog", | |
| choices=StateGroup.choices, | |
| default=StateGroup.BACKLOG, | |
| max_length=20, | |
| ) | |
| group = models.CharField( | |
| choices=StateGroup.choices, | |
| default=StateGroup.BACKLOG.value, | |
| max_length=20, | |
| ) |
🤖 Prompt for AI Agents
In apps/api/plane/db/models/state.py around lines 81-85, the field default
currently uses StateGroup.BACKLOG (a TextChoices member) which is functionally
valid but inconsistent with the file's use of Enum .value; change the default to
StateGroup.BACKLOG.value so the CharField explicitly receives the string value
and matches the DEFAULT_STATES entries for consistency.
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.