Skip to content
Advertisement

Python/Django routing messed up when clicking a button

I am working through a basic django tutorial and I have become stuck. When the favorites button is clicked I am trying to redirect the user to reviews/favorite, which will then redirect to reviews/review_id, which is where they started. Instead, whenever I click the favorite button on the page it redirects me to reviews/reviews/review_id which fails.

Interestingly enough if I remove the /reviews/ part from the form action in single-review.html then it routes me to /favorite, which fails because it doesnt exist, but when I put it back to reviews/favorite I go back to reviews/reviews/favorite as the routing. Anyone know what I am doing wrong?

Views.py

from django.http.response import HttpResponse
from django.shortcuts import render
from django.http import HttpResponseRedirect
from django import forms
from django.views import View
from django.views.generic.base import TemplateView
from django.views.generic import ListView
from django.views.generic.detail import DetailView
from django.views.generic.edit import FormView, CreateView

from .forms import ReviewForm
from .models import Review

# Create your views here.

# uses createview to handle all the data processing and then save data to database. Removes need for class ReviewForm in forms.py unless you want to customize
class ReviewView(CreateView):
    model = Review
    form_class = ReviewForm
    template_name = "reviews/review.html"
    success_url = "/thank-you"

class ThankYouView(TemplateView):
    template_name = "reviews/thank-you.html"

    # method allows you to set items to the context dictionary and them import that data into the thank-you.html file via the message variable
    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["message"] = "This Works!"
        return context

class ReviewsListView(ListView):
    template_name = "reviews/review-list.html"
    model = Review  # do not instantiate when pointing to model
    context_object_name = "reviews"

class SingleReviewView(DetailView):
    template_name = "reviews/single-review.html"
    model = Review


class AddFavoriteView(View):
    def post(self, request):
        print(request)
        review_id = request.POST["review_id"]
        request.session["favorite_review"] = review_id
        return HttpResponseRedirect("reviews/" + review_id)

Models.py

from django.db import models

# Create your models here.

class Review(models.Model):
    user_name=models.CharField(max_length=100)
    review_text=models.TextField()
    rating = models.IntegerField()

Forms.py

from .models import Review

class ReviewForm(forms.ModelForm):
    class Meta:
        model = Review
        fields = "__all__"
        labels = {
            "user_name": "Your Name",
            "review_text": "Your Feedback",
            "rating": "Your Rating"
        }
        error_messages = {
            "user_name": {
              "required": "Your name must not be empty!",
              "max_length": "Please enter a shorter name!"
            }
        }

Urls.py in /reviews

from django.urls import path

from . import views

urlpatterns = [
    path("", views.ReviewView.as_view()),
    path("thank-you", views.ThankYouView.as_view()),
    path("reviews", views.ReviewsListView.as_view()),
    path("reviews/favorite", views.AddFavoriteView.as_view()),
    path("reviews/<int:pk>", views.SingleReviewView.as_view()),

Urls.py in project folder

from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static
from django.conf import settings

urlpatterns = [
    path('admin/', admin.site.urls),
    path("", include("reviews.urls")),
    path("profiles/", include("profiles.urls"))
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Settings.py

"""
"""
Django settings for feedback project.

Generated by 'django-admin startproject' using Django 4.0.1.

For more information on this file, see
https://docs.djangoproject.com/en/4.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.0/ref/settings/
"""

from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent


# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure--#-h(v7)m6p4rr&fs#7&caa9!$w4!#xzhkioljoy34v3@o!%@%'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = []


# Application definition

INSTALLED_APPS = [
    'reviews',
    'profiles',
    'feedback',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'feedback.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'feedback.wsgi.application'


# Database
# https://docs.djangoproject.com/en/4.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}


# Password validation
# https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]


# Internationalization
# https://docs.djangoproject.com/en/4.0/topics/i18n/

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_TZ = True


# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.0/howto/static-files/

STATIC_URL = 'static/'
#STATICFILE_DIRS = BASE_DIR / "uploads" / "image"

# Default primary key field type
# https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

#path for files to be stored
MEDIA_ROOT = BASE_DIR / "uploads"
MEDIA_URL = BASE_DIR = "/user-image/" #you can define the URL to whatever you want between '/ /'

# SESSION_COOKIE_AGE = 2592000 #sets how long cookies will last

Single Review HTML Page

{% extends "reviews/base.html" %}

{% block title %}Review Detail{% endblock %}

{% block content %}
    <h1>{{ review.user_name }}</h1>
    <p>Rating: {{ review.rating }} </p>
    <p>{{ review.review_text }}</p>

    <form action='/reviews/favorite' method='POST'>
        {% csrf_token %}
        <input type="hidden" name="review_id" value="{{ review.id }}" />
        <button>Favorite</button>
    </form>
{% endblock %}

Advertisement

Answer

You should use a leading slash in the HTTP redirect response:

class AddFavoriteView(View):
    def post(self, request):
        print(request)
        review_id = request.session['favorite_review'] = request.POST['review_id']
        return HttpResponseRedirect(f'/reviews/{review_id}')

If you give the path a name, like:

path('reviews/<int:pk>', views.SingleReviewView.as_view(), name='review-details'),

then you can use redirect(…) [Django-doc] to determine the URL and return the proper HTTP redirect response:

from django.shortcuts import redirect

class AddFavoriteView(View):
    def post(self, request):
        print(request)
        review_id = request.session['favorite_review'] = request.POST['review_id']
        return redirect('review-details', pk=review_id)
Advertisement