6.2 KiB
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:
-
UI Views (authenticated):
/surveys/instances/- Survey instance list/surveys/instances/<id>/- Instance detail/surveys/templates/- Template list/surveys/templates/<id>/- Template detail
-
API Endpoints:
/surveys/api/templates/- Template API/surveys/api/instances/- Instance API/surveys/public/<token>/- Public API
-
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:
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:
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:
# 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
# Missing /s/ prefix
url = f"/surveys/{survey.access_token}/"
# Wrong reverse name
url = reverse('surveys:survey_form_direct', kwargs={'token': survey.access_token})
✅ Correct
# Including /s/ prefix
url = survey.get_survey_url()
# Using correct reverse name
url = reverse('surveys:survey_form', kwargs={'token': survey.access_token})
Testing Survey Access
Check if Survey Exists
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
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:
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:
- Separation of Concerns: Public survey forms are clearly separated from administrative and API endpoints
- URL Clarity: The
/s/prefix makes it immediately clear that this is a public, token-based survey access - 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 patternsapps/surveys/models.py-SurveyInstance.get_survey_url()methodapps/surveys/public_views.py- Public survey views