# 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//` - Instance detail - `/surveys/templates/` - Template list - `/surveys/templates//` - Template detail 2. **API Endpoints**: - `/surveys/api/templates/` - Template API - `/surveys/api/instances/` - Instance API - `/surveys/public//` - Public API 3. **Public Survey Forms** (token-based, no auth): - `/surveys/s//` - Survey form - `/surveys/s//thank-you/` - Thank you page Without the `/s/` prefix, a catch-all pattern like `/` 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// # UI: Instance detail ├── templates/ # UI: Template list ├── templates// # UI: Template detail ├── public// # API: Public survey data ├── api/ # API: Authenticated endpoints │ ├── templates/ │ ├── questions/ │ ├── instances/ │ └── responses/ ├── s// # Public: Survey form (no auth) └── s//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//', PublicSurveyViewSet.as_view({'get': 'retrieve'}), name='public-survey'), # Authenticated API endpoints path('', include(router.urls)), # Public survey token access (requires /s/ prefix) path('s//', public_views.survey_form, name='survey_form'), path('s//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//` - 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//` - Thank You: `/surveys/s//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