207 lines
6.2 KiB
Markdown
207 lines
6.2 KiB
Markdown
# Survey 404 Error - URL Format Issue
|
|
|
|
## Problem Description
|
|
|
|
When trying to access a survey URL, you may encounter a 404 (Page Not Found) error:
|
|
|
|
```
|
|
Page not found (404)
|
|
Request Method: GET
|
|
Request URL: http://localhost:8000/surveys/H8d9tlVs0BgeAp1XA4NczXoiCcqAaN0r_lc0Eb63U1Y/
|
|
```
|
|
|
|
## Root Cause
|
|
|
|
The survey URL is missing the required `/s/` prefix in the path. The correct URL format includes `/s/` between `/surveys/` and the access token.
|
|
|
|
### Incorrect URL (404 Error)
|
|
```
|
|
http://localhost:8000/surveys/H8d9tlVs0BgeAp1XA4NczXoiCcqAaN0r_lc0Eb63U1Y/
|
|
```
|
|
|
|
### Correct URL
|
|
```
|
|
http://localhost:8000/surveys/s/H8d9tlVs0BgeAp1XA4NczXoiCcqAaN0r_lc0Eb63U1Y/
|
|
```
|
|
|
|
## Why the `/s/` Prefix is Required
|
|
|
|
The `/s/` prefix is necessary to avoid URL routing conflicts with other survey-related paths:
|
|
|
|
1. **UI Views** (authenticated):
|
|
- `/surveys/instances/` - Survey instance list
|
|
- `/surveys/instances/<id>/` - Instance detail
|
|
- `/surveys/templates/` - Template list
|
|
- `/surveys/templates/<id>/` - Template detail
|
|
|
|
2. **API Endpoints**:
|
|
- `/surveys/api/templates/` - Template API
|
|
- `/surveys/api/instances/` - Instance API
|
|
- `/surveys/public/<token>/` - Public API
|
|
|
|
3. **Public Survey Forms** (token-based, no auth):
|
|
- `/surveys/s/<token>/` - Survey form
|
|
- `/surveys/s/<token>/thank-you/` - Thank you page
|
|
|
|
Without the `/s/` prefix, a catch-all pattern like `<str:token>/` would conflict with reserved words like "instances", "templates", "public", etc., causing routing conflicts.
|
|
|
|
## Solution
|
|
|
|
Use the correct URL format when accessing surveys:
|
|
|
|
### Method 1: Use Generated URLs
|
|
The survey model's `get_survey_url()` method automatically generates the correct URL format:
|
|
|
|
```python
|
|
survey = SurveyInstance.objects.get(id=...)
|
|
survey_url = survey.get_survey_url()
|
|
# Returns: /surveys/s/H8d9tlVs0BgeAp1XA4NczXoiCcqAaN0r_lc0Eb63U1Y/
|
|
```
|
|
|
|
### Method 2: Manual URL Construction
|
|
When constructing URLs manually, always include the `/s/` prefix:
|
|
|
|
```python
|
|
from django.urls import reverse
|
|
from apps.surveys.models import SurveyInstance
|
|
|
|
survey = SurveyInstance.objects.get(id=...)
|
|
survey_url = reverse('surveys:survey_form', kwargs={'token': survey.access_token})
|
|
# Returns: /surveys/s/H8d9tlVs0BgeAp1XA4NczXoiCcqAaN0r_lc0Eb63U1Y/
|
|
```
|
|
|
|
### Method 3: Email Templates
|
|
When sending survey links via email, use the `survey_url` field from the serializer:
|
|
|
|
```python
|
|
# In email template
|
|
{{ survey.survey_url }}
|
|
# Displays: http://localhost:8000/surveys/s/H8d9tlVs0BgeAp1XA4NczXoiCcqAaN0r_lc0Eb63U1Y/
|
|
```
|
|
|
|
## URL Structure Overview
|
|
|
|
```
|
|
/surveys/
|
|
├── instances/ # UI: Instance list
|
|
├── instances/<uuid>/ # UI: Instance detail
|
|
├── templates/ # UI: Template list
|
|
├── templates/<uuid>/ # UI: Template detail
|
|
├── public/<token>/ # API: Public survey data
|
|
├── api/ # API: Authenticated endpoints
|
|
│ ├── templates/
|
|
│ ├── questions/
|
|
│ ├── instances/
|
|
│ └── responses/
|
|
├── s/<token>/ # Public: Survey form (no auth)
|
|
└── s/<token>/thank-you/ # Public: Thank you page
|
|
```
|
|
|
|
## Common Mistakes
|
|
|
|
### ❌ Wrong
|
|
```python
|
|
# Missing /s/ prefix
|
|
url = f"/surveys/{survey.access_token}/"
|
|
```
|
|
|
|
```python
|
|
# Wrong reverse name
|
|
url = reverse('surveys:survey_form_direct', kwargs={'token': survey.access_token})
|
|
```
|
|
|
|
### ✅ Correct
|
|
```python
|
|
# Including /s/ prefix
|
|
url = survey.get_survey_url()
|
|
```
|
|
|
|
```python
|
|
# Using correct reverse name
|
|
url = reverse('surveys:survey_form', kwargs={'token': survey.access_token})
|
|
```
|
|
|
|
## Testing Survey Access
|
|
|
|
### Check if Survey Exists
|
|
```python
|
|
from apps.surveys.models import SurveyInstance
|
|
from django.utils import timezone
|
|
|
|
try:
|
|
survey = SurveyInstance.objects.get(
|
|
access_token="H8d9tlVs0BgeAp1XA4NczXoiCcqAaN0r_lc0Eb63U1Y",
|
|
status__in=['pending', 'sent', 'in_progress'],
|
|
token_expires_at__gt=timezone.now()
|
|
)
|
|
print(f"Survey found: {survey.survey_template.name}")
|
|
print(f"URL: {survey.get_survey_url()}")
|
|
except SurveyInstance.DoesNotExist:
|
|
print("Survey not found, expired, or invalid token")
|
|
```
|
|
|
|
### Verify Survey URL
|
|
```python
|
|
from django.test import Client
|
|
|
|
client = Client()
|
|
response = client.get('/surveys/s/H8d9tlVs0BgeAp1XA4NczXoiCcqAaN0r_lc0Eb63U1Y/')
|
|
print(f"Status: {response.status_code}") # Should be 200
|
|
if response.status_code == 200:
|
|
print("Survey accessible")
|
|
elif response.status_code == 404:
|
|
print("Survey not found or expired")
|
|
```
|
|
|
|
## URL Configuration
|
|
|
|
The survey URLs are defined in `apps/surveys/urls.py`:
|
|
|
|
```python
|
|
urlpatterns = [
|
|
# Public survey pages (no auth required)
|
|
path('invalid/', public_views.invalid_token, name='invalid_token'),
|
|
|
|
# UI Views (authenticated)
|
|
path('instances/', ui_views.survey_instance_list, name='instance_list'),
|
|
path('templates/', ui_views.survey_template_list, name='template_list'),
|
|
|
|
# Public API endpoints
|
|
path('public/<str:token>/', PublicSurveyViewSet.as_view({'get': 'retrieve'}), name='public-survey'),
|
|
|
|
# Authenticated API endpoints
|
|
path('', include(router.urls)),
|
|
|
|
# Public survey token access (requires /s/ prefix)
|
|
path('s/<str:token>/', public_views.survey_form, name='survey_form'),
|
|
path('s/<str:token>/thank-you/', public_views.thank_you, name='thank_you'),
|
|
]
|
|
```
|
|
|
|
## Security Considerations
|
|
|
|
The `/s/` prefix also serves as a security measure:
|
|
|
|
1. **Separation of Concerns**: Public survey forms are clearly separated from administrative and API endpoints
|
|
2. **URL Clarity**: The `/s/` prefix makes it immediately clear that this is a public, token-based survey access
|
|
3. **Prevents Ambiguity**: Avoids confusion between different types of survey-related URLs
|
|
|
|
## Summary
|
|
|
|
**Key Points:**
|
|
- Survey URLs MUST include the `/s/` prefix
|
|
- Format: `/surveys/s/<access_token>/`
|
|
- Use `survey.get_survey_url()` method to generate correct URLs
|
|
- The `/s/` prefix prevents URL routing conflicts
|
|
- Survey access requires valid, non-expired token and appropriate status
|
|
|
|
**Quick Reference:**
|
|
- Survey Form: `/surveys/s/<token>/`
|
|
- Thank You: `/surveys/s/<token>/thank-you/`
|
|
- Invalid Token: `/surveys/invalid/`
|
|
|
|
For more information, see:
|
|
- `apps/surveys/urls.py` - URL patterns
|
|
- `apps/surveys/models.py` - `SurveyInstance.get_survey_url()` method
|
|
- `apps/surveys/public_views.py` - Public survey views
|