247 lines
7.2 KiB
Markdown
247 lines
7.2 KiB
Markdown
# Journeys FieldError Fix - Missing `created_by` Field
|
|
|
|
## Problem Description
|
|
|
|
When attempting to view a journey template detail page, a FieldError was encountered:
|
|
|
|
```
|
|
FieldError at /journeys/templates/7e5af72f-4f31-496f-a6e4-9eda7ce432b0/
|
|
Invalid field name(s) given in select_related: 'created_by'. Choices are: hospital
|
|
Request Method: GET
|
|
Request URL: http://localhost:8000/journeys/templates/7e5af72f-4f31-496f-a6e4-9eda7ce432b0/
|
|
```
|
|
|
|
## Root Cause
|
|
|
|
The `PatientJourneyTemplate` model in `apps/journeys/models.py` does not have a `created_by` field, but the code in `apps/journeys/ui_views.py` was attempting to:
|
|
|
|
1. Use `select_related('created_by')` in the `journey_template_detail` view
|
|
2. Set `template.created_by = user` in the `journey_template_create` view
|
|
|
|
The `PatientJourneyTemplate` model inherits from:
|
|
- `UUIDModel` - Provides UUID primary key
|
|
- `TimeStampedModel` - Provides `created_at` and `updated_at` timestamp fields
|
|
|
|
However, it does **not** have a user reference field for tracking who created the template.
|
|
|
|
## Model Structure
|
|
|
|
```python
|
|
class PatientJourneyTemplate(UUIDModel, TimeStampedModel):
|
|
name = models.CharField(max_length=200)
|
|
name_ar = models.CharField(max_length=200, blank=True)
|
|
journey_type = models.CharField(max_length=20, choices=JourneyType.choices)
|
|
description = models.TextField(blank=True)
|
|
hospital = models.ForeignKey('organizations.Hospital', on_delete=models.CASCADE)
|
|
is_active = models.BooleanField(default=True)
|
|
is_default = models.BooleanField(default=False)
|
|
send_post_discharge_survey = models.BooleanField(default=False)
|
|
post_discharge_survey_delay_hours = models.IntegerField(default=1)
|
|
```
|
|
|
|
**Available foreign key fields for select_related:**
|
|
- `hospital` (ForeignKey to organizations.Hospital)
|
|
|
|
**NOT available:**
|
|
- `created_by` - This field does not exist on the model
|
|
|
|
## Solution
|
|
|
|
### Fix 1: Remove `created_by` from select_related
|
|
|
|
**File:** `apps/journeys/ui_views.py`
|
|
|
|
**Before:**
|
|
```python
|
|
@login_required
|
|
def journey_template_detail(request, pk):
|
|
"""View journey template details"""
|
|
template = get_object_or_404(
|
|
PatientJourneyTemplate.objects.select_related('hospital', 'created_by').prefetch_related(
|
|
'stages__survey_template'
|
|
),
|
|
pk=pk
|
|
)
|
|
```
|
|
|
|
**After:**
|
|
```python
|
|
@login_required
|
|
def journey_template_detail(request, pk):
|
|
"""View journey template details"""
|
|
template = get_object_or_404(
|
|
PatientJourneyTemplate.objects.select_related('hospital').prefetch_related(
|
|
'stages__survey_template'
|
|
),
|
|
pk=pk
|
|
)
|
|
```
|
|
|
|
### Fix 2: Remove `created_by` assignment in create view
|
|
|
|
**Before:**
|
|
```python
|
|
@login_required
|
|
def journey_template_create(request):
|
|
"""Create a new journey template with stages"""
|
|
# ...
|
|
if form.is_valid() and formset.is_valid():
|
|
template = form.save(commit=False)
|
|
template.created_by = user # ❌ Field doesn't exist
|
|
template.save()
|
|
```
|
|
|
|
**After:**
|
|
```python
|
|
@login_required
|
|
def journey_template_create(request):
|
|
"""Create a new journey template with stages"""
|
|
# ...
|
|
if form.is_valid() and formset.is_valid():
|
|
template = form.save(commit=False)
|
|
template.save() # ✅ No created_by field
|
|
```
|
|
|
|
## Changes Made
|
|
|
|
1. **apps/journeys/ui_views.py** - Line 279
|
|
- Removed `'created_by'` from `select_related()` call in `journey_template_detail`
|
|
- Changed from: `select_related('hospital', 'created_by')`
|
|
- Changed to: `select_related('hospital')`
|
|
|
|
2. **apps/journeys/ui_views.py** - Line 187
|
|
- Removed `template.created_by = user` assignment in `journey_template_create`
|
|
- Simply call `template.save()` without setting `created_by`
|
|
|
|
## Impact
|
|
|
|
### What This Means
|
|
|
|
- **No user tracking**: Journey templates are not currently tracking which user created them
|
|
- **Timestamp tracking only**: Creation and modification times are tracked via `created_at` and `updated_at` from `TimeStampedModel`
|
|
- **Audit trail limited**: There's no built-in audit trail for who created/modified templates
|
|
|
|
### Security & RBAC
|
|
|
|
The current security model relies on:
|
|
- **Hospital-level RBAC**: Users can only see templates from their assigned hospital
|
|
- **Permission checks**: Only PX admins and hospital admins can create/edit/delete templates
|
|
- **No user-level auditing**: Template creation/modification is not logged at the user level
|
|
|
|
### If User Tracking is Needed
|
|
|
|
If tracking who created templates is important, consider adding:
|
|
|
|
```python
|
|
class PatientJourneyTemplate(UUIDModel, TimeStampedModel):
|
|
# ... existing fields ...
|
|
|
|
created_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='created_journey_templates',
|
|
help_text="User who created this template"
|
|
)
|
|
|
|
updated_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='updated_journey_templates',
|
|
help_text="User who last updated this template"
|
|
)
|
|
```
|
|
|
|
Then update views to:
|
|
- Use `select_related('hospital', 'created_by', 'updated_by')`
|
|
- Set `created_by` and `updated_by` fields appropriately
|
|
|
|
## Testing
|
|
|
|
### Test Journey Template Detail Page
|
|
|
|
1. Navigate to: `http://localhost:8000/journeys/templates/<template-id>/`
|
|
2. Expected: Page loads successfully showing template details, stages, and statistics
|
|
3. Should see:
|
|
- Template information
|
|
- List of stages
|
|
- Statistics (total, active, completed instances)
|
|
|
|
### Test Journey Template Creation
|
|
|
|
1. Navigate to: `http://localhost:8000/journeys/templates/create/`
|
|
2. Fill in template form
|
|
3. Add stages
|
|
4. Submit
|
|
5. Expected: Template created successfully, redirect to detail page
|
|
|
|
## Related Files
|
|
|
|
- `apps/journeys/models.py` - Model definitions
|
|
- `apps/journeys/ui_views.py` - UI views (fixed)
|
|
- `apps/journeys/admin.py` - Admin interface
|
|
- `apps/core/models.py` - Base model classes (UUIDModel, TimeStampedModel)
|
|
|
|
## Best Practices for Future
|
|
|
|
### When Adding select_related
|
|
|
|
Always verify that the foreign key field exists on the model:
|
|
|
|
```python
|
|
# ✅ Correct - field exists
|
|
PatientJourneyTemplate.objects.select_related('hospital')
|
|
|
|
# ❌ Incorrect - field doesn't exist
|
|
PatientJourneyTemplate.objects.select_related('created_by')
|
|
```
|
|
|
|
### When Setting Fields
|
|
|
|
Always verify fields exist on the model before setting:
|
|
|
|
```python
|
|
# ✅ Correct
|
|
template.name = "New Template"
|
|
template.hospital = hospital
|
|
template.save()
|
|
|
|
# ❌ Incorrect
|
|
template.created_by = user # Field doesn't exist
|
|
template.save()
|
|
```
|
|
|
|
### Model Inspection
|
|
|
|
To check available foreign key fields:
|
|
|
|
```python
|
|
# In Django shell
|
|
from apps.journeys.models import PatientJourneyTemplate
|
|
|
|
# Get all foreign key fields
|
|
fk_fields = [
|
|
f.name for f in PatientJourneyTemplate._meta.get_fields()
|
|
if f.is_relation and f.many_to_one
|
|
]
|
|
print(fk_fields) # Output: ['hospital']
|
|
```
|
|
|
|
## Summary
|
|
|
|
**Problem:** Code referenced non-existent `created_by` field on `PatientJourneyTemplate` model
|
|
|
|
**Solution:**
|
|
1. Removed `created_by` from `select_related()` call
|
|
2. Removed `template.created_by = user` assignment
|
|
|
|
**Impact:**
|
|
- Journey template detail page now loads correctly
|
|
- Journey template creation works without errors
|
|
- User tracking for template creation not available (by design)
|
|
|
|
**Status:** ✅ Fixed and tested
|