django - why is this custom middleware not working - django

I am building a website using django 1.10. I have written a middleware class to restrict the number of page views for users that are not logged in. I implemented this functionality into a separate application 'pagerestrict'
settings.py [Relevant Entries]
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'pagerestrict.apps.PagerestrictConfig',
# ...
]
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',
'pagerestrict.middleware.CheckPageAccessMiddleware',
]
Middleware code (middleware.py):
from django.http import HttpResponseRedirect
from decouple import config
class CheckPageAccessMiddleware(object):
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
return self.get_response(request)
def process_request(self, request):
if not request.user.is_authenticated():
current_page_url = request.path_info.lstrip('/')
ignore_list = config('PAGE_RESTRICT_PAGE_IGNORES', cast=lambda v: [s.strip() for s in v.split(',')])
#if not current_page_url or not any(current_page_url != url for url in ignore_list):
if not current_page_url in ignore_list:
# first things first ...
if not has_attr(request, 'pages_visited'):
request.pages_visited = set()
if not has_attr(request, 'page_count'):
request.page_count = 0
if current_page_url not in request.pages_visited:
request.pages_visited.add(current_page_url)
request.page_count += 1
if request.page_count > config('PAGE_RESTRICT_PAGE_LIMIT'):
return HttpResponseRedirect(config('PAGE_RESTRICT_REDIRECT_URL'))
I tested this by going to my homepage and refreshing the browser several times - the code never seemed to be triggered. What am I missing?

New-style middleware does not call process_request and process_response by default. This functionally has been replaced with __call__ and the get_response function that is passed to __init__.
You need to call process_request() inside __call__ and process the return value if it is not None. The easiest way to do this is to use the MiddlewareMixin provided by Django. This will define the necessary __init__ and __call__ methods, and __call__ will call process_request() and process_response() if they are defined.
from django.utils.deprecation import MiddlewareMixin
class CheckPageAccessMiddleware(MiddlewareMixin):
def process_request(self, request):
if not request.user.is_authenticated():
current_page_url = request.path_info.lstrip('/')
ignore_list = config('PAGE_RESTRICT_PAGE_IGNORES', cast=lambda v: [s.strip() for s in v.split(',')])
#if not current_page_url or not any(current_page_url != url for url in ignore_list):
if not current_page_url in ignore_list:
# first things first ...
if not has_attr(request, 'pages_visited'):
request.pages_visited = set()
if not has_attr(request, 'page_count'):
request.page_count = 0
if current_page_url not in request.pages_visited:
request.pages_visited.add(current_page_url)
request.page_count += 1
if request.page_count > config('PAGE_RESTRICT_PAGE_LIMIT'):
return HttpResponseRedirect(config('PAGE_RESTRICT_REDIRECT_URL'))

I finally got this to work. It was based on the Middleware topic on the now defunct Stack Overflow Documentation site. (Attribution details can be found on the contributor page. Reference topic ID: 1721.)
from django.http import HttpResponseRedirect
from decouple import config
class CheckPageAccessMiddleware(object):
def __init__(self, next_layer=None):
"""We allow next_layer to be None because old-style middlewares
won't accept any argument.
"""
self.get_response = next_layer
def process_request(self, request):
"""Let's handle old-style request processing here, as usual."""
# Do something with request
# Probably return None
# Or return an HttpResponse in some cases
if not request.user.is_authenticated():
current_page_url = request.path_info.lstrip('/')
print ('Current page url: {0}'.format(current_page_url))
root_url = '' # root url == ('')
restrict_redirect_page = config('PAGE_RESTRICT_REDIRECT_URL')[1:] # chop of leading '/'
ignore_list = [root_url, restrict_redirect_page] + \
config('PAGE_RESTRICT_PAGE_IGNORES', cast=lambda v: [s.strip() for s in v.split(',')])
print('ignore list: %s' % ",".join([x for x in ignore_list]))
#if not current_page_url or not any(current_page_url != url for url in ignore_list):
if not current_page_url in ignore_list:
# first things first ...
if 'pages_visited' not in request.session:
request.session['pages_visited'] = ''
if 'page_count' not in request.session:
request.session['page_count'] = 0
pages_visited = [x.strip() for x in request.session['pages_visited'].split(',')]
if current_page_url not in pages_visited:
pages_visited.append(current_page_url)
request.session['pages_visited'] = ",".join(x.strip() for x in pages_visited)
request.session['page_count'] += 1
print('Request page count: {0}'.format(request.session['page_count']))
if request.session['page_count'] > config('PAGE_RESTRICT_PAGE_LIMIT', cast=int):
return HttpResponseRedirect(config('PAGE_RESTRICT_REDIRECT_URL'))
return None
def process_response(self, request, response):
"""Let's handle old-style response processing here, as usual."""
# Do something with response, possibly using request.
return response
def __call__(self, request):
"""Handle new-style middleware here."""
response = self.process_request(request)
if response is None:
# If process_request returned None, we must call the next middleware or
# the view. Note that here, we are sure that self.get_response is not
# None because this method is executed only in new-style middlewares.
response = self.get_response(request)
response = self.process_response(request, response)
return response

You can change import like this:
try:
from django.utils.deprecation import MiddlewareMixin
except ImportError:
MiddlewareMixin = object

Here is an example to show how to update the middleware when migrating to
Django ver > 1.10:
Upgrading pre-Django 1.10-style middleware
If it does not work:
You should also temporarily remove all default MIDDLEWARE class,
keeping only your custom MIDDLEWARE to make sure the previous
layers(default MIDDLEWARE) did not break the flow:
MIDDLEWARE = (
# 'django.middleware.common.CommonMiddleware',
'app.middleware.MyMiddlewareClass',
)
If you remove django.middleware.common.CommonMiddleware then it works. That could be ALLOWED_HOSTS problem.
- Replace
ALLOWED_HOSTS= ['localhost:8001']
- With
ALLOWED_HOSTS= ['localhost']

New-style middleware does not call process_request and process_response by default.
This functionally has been replaced with call and the get_response function that
is passed to init.NYou need to call process_request() inside call and
process the return value if it is not None.
So you can write the code that you want to put into process_request() method to call() as below:
from django.http import HttpResponseRedirect
from decouple import config
class CheckPageAccessMiddleware(object):
def __init__(self, next_layer=None):
self.get_response = next_layer
def __call__(self, request):
if not request.user.is_authenticated():
current_page_url = request.path_info.lstrip('/')
ignore_list = config('PAGE_RESTRICT_PAGE_IGNORES', cast=lambda v: [s.strip() for s in v.split(',')])
#if not current_page_url or not any(current_page_url != url for url in ignore_list):
if not current_page_url in ignore_list:
# first things first ...
if not has_attr(request, 'pages_visited'):
request.pages_visited = set()
if not has_attr(request, 'page_count'):
request.page_count = 0
if current_page_url not in request.pages_visited:
request.pages_visited.add(current_page_url)
request.page_count += 1
if request.page_count > config('PAGE_RESTRICT_PAGE_LIMIT'):
return HttpResponseRedirect(config('PAGE_RESTRICT_REDIRECT_URL'))

Related

Middleware Setup

I am trying to setup a message middleware for my marketing app,but the method process_request is not returning anything.
it is running without errors still though not returning message requests.
my middleware.py---
from .models import MarketingMessage
class DisplayMarketing():
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
def process_request(request):
print("something")
try:
request.session['marketing_message'] = MarketingMessage.objects.all()[0].message
except:
request.session['marketing_message'] = False
response = self.get_response(request)
return response
my settings.py
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',
'marketing.middleware.DisplayMarketing',
]
if you use middleware mixin process_request will automatically called.
try
from django.utils.deprecation import MiddlewareMixin
class DisplayMarketing(MiddlewareMixin):
def __init__(self, get_response):
self.get_response = get_response
def process_request(self, request):
print("something")
try:
request.session['marketing_message'] = "bossk"
except:
request.session['marketing_message'] = False
or, other wise you have call process_request manually
from .models import MarketingMessage
class DisplayMarketing():
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
def process_request(request):
print("something")
try:
request.session['marketing_message'] ="bossk"
except:
request.session['marketing_message'] = False
response = self.get_response(process_request(request))
return response

Django Success URL Gets Appended To Middleware Redirect URL Instead of Redirecting To Success URL

I have a form with a username field on it located at /accounts/choose-username/.
views.py
from django.views.generic import FormView
from .forms import ChooseUsernameForm
from django.contrib.auth import get_user_model
User = get_user_model()
class ChooseUsernameView(FormView):
template_name = 'registration/choose-username.html'
form_class = ChooseUsernameForm
success_url = 'accounts/profile/' # <-------------
def form_valid(self, form):
"""If the form is valid, redirect to the supplied URL."""
print(form.cleaned_data['username'])
print(self.request.user)
user = User.objects.get(email=self.request.user)
user.username = form.cleaned_data['username']
user.save()
return super().form_valid(form)
middleware.py
from django.shortcuts import redirect, reverse
class SimpleMiddleware:
def __init__(self, get_response):
self.get_response = get_response
# One-time configuration and initialization.
def __call__(self, request):
# Code to be executed for each request before
# the view (and later middleware) are called.
response = self.get_response(request)
if request.user.is_authenticated:
if request.user.username is None:
print(request.path)
if not request.path == reverse('choose_username'):
return redirect(reverse('choose_username'))
return response
When I submit the form, my url is accounts/choose-username/accounts/profile/
I want it to be accounts/profile/
MIDDLEWARE settings.py looks like this:
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'accounts.middleware.SimpleMiddleware', # <---
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
My Question
Why does my success URL get appended to my accounts/choose-username/ URL? How can I fix it?
As this—accounts/profile/—doesn't start with a slash (/), so it is a relative path. Browsers automatically append the relative path to the current page's url.
You need to use absolute path (add slash in the beginning).
success_url = '/accounts/profile/'

Django: Having 2 cookie sessions but app gets logged out when the admin app gets logged in (viceversa)

I want to have 2 sessions, one for my application (myapp.com) and one for the admin (myapp.com/admin). With this, I can have access to both in different tabs of my web client without logging in every time I want to use one of them. It is very irritating.
I have created a new session middleware to control that.
import time
from importlib import import_module
from django.conf import settings
from django.contrib.sessions.backends.base import UpdateError
from django.core.exceptions import SuspiciousOperation
from django.utils.cache import patch_vary_headers
from django.utils.deprecation import MiddlewareMixin
from django.utils.http import http_date
class AdminCookieSessionMiddleware(MiddlewareMixin):
def __init__(self, get_response=None):
self.get_response = get_response
engine = import_module(settings.SESSION_ENGINE)
self.SessionStore = engine.SessionStore
def cookie_name(self, request):
parts = request.path.split('/')
if len(parts) > 1 and parts[1].startswith('admin'):
return settings.ADMIN_SESSION_COOKIE_NAME
return settings.SESSION_COOKIE_NAME
def cookie_path(self, request):
parts = request.path.split('/')
if len(parts) > 1 and parts[1].startswith('admin'):
return settings.ADMIN_SESSION_COOKIE_PATH
return settings.SESSION_COOKIE_PATH
def process_request(self, request):
session_key = request.COOKIES.get(self.cookie_name(request))
request.session = self.SessionStore(session_key)
def process_response(self, request, response):
"""
If request.session was modified, or if the configuration is to save the
session every time, save the changes and set a session cookie or delete
the session cookie if the session has been emptied.
"""
try:
accessed = request.session.accessed
modified = request.session.modified
empty = request.session.is_empty()
except AttributeError:
pass
else:
# First check if we need to delete this cookie.
# The session should be deleted only if the session is entirely empty
if settings.SESSION_COOKIE_NAME in request.COOKIES and empty:
response.delete_cookie(
settings.SESSION_COOKIE_NAME,
path=settings.SESSION_COOKIE_PATH,
domain=settings.SESSION_COOKIE_DOMAIN,
)
else:
if accessed:
patch_vary_headers(response, ('Cookie',))
if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty:
if request.session.get_expire_at_browser_close():
max_age = None
expires = None
else:
max_age = request.session.get_expiry_age()
expires_time = time.time() + max_age
expires = http_date(expires_time)
# Save the session data and refresh the client cookie.
# Skip session save for 500 responses, refs #3881.
if response.status_code != 500:
try:
request.session.save()
except UpdateError:
raise SuspiciousOperation(
"The request's session was deleted before the "
"request completed. The user may have logged "
"out in a concurrent request, for example."
)
response.set_cookie(
self.cookie_name(request),
request.session.session_key, max_age=max_age,
expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
path=settings.SESSION_COOKIE_PATH,
secure=settings.SESSION_COOKIE_SECURE or None,
httponly=settings.SESSION_COOKIE_HTTPONLY or None,
)
return response
In my settings file:
SESSION_COOKIE_NAME='usersessionid'
ADMIN_SESSION_COOKIE_NAME='adminsessionid'
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',
'core.utils.middleware.AdminCookieSessionMiddleware'
]
However, I have still the problem that if logging in one of the apps, i got automatically logged out of the other.
I was tracing the sessionkey and sometimes it is the same for both coockies. is that ok? If not, what should the problem be?
OK, I could fix it, therefore I post the answer so I can help somebody trying to do that as well. I copy the code from the django SessionMiddleware and added a cookie_name function. Inside I created the logic that should apply for my needs.
Remember to replace settings.SESSION_COOKIE_NAME for cookie_name.
import time
from importlib import import_module
from django.conf import settings
from django.contrib.sessions.backends.base import UpdateError
from django.core.exceptions import SuspiciousOperation
from django.utils.cache import patch_vary_headers
from django.utils.deprecation import MiddlewareMixin
from django.utils.http import http_date
class MySessionMiddleware(MiddlewareMixin):
def __init__(self, get_response=None):
self.get_response = get_response
engine = import_module(settings.SESSION_ENGINE)
self.SessionStore = engine.SessionStore
def cookie_name(self, request):
parts = request.path.split('/')
if settings.DEBUG:
if len(parts)>1 and parts[1].startswith('admin'):
return settings.ADMIN_SESSION_COOKIE_NAME
else:
if len(parts)>2 and parts[2].startswith('admin'):
return settings.ADMIN_SESSION_COOKIE_NAME
return settings.SESSION_COOKIE_NAME
def process_request(self, request):
session_key = request.COOKIES.get(self.cookie_name(request))
request.session = self.SessionStore(session_key)
def process_response(self, request, response):
"""
If request.session was modified, or if the configuration is to save the
session every time, save the changes and set a session cookie or delete
the session cookie if the session has been emptied.
"""
try:
accessed = request.session.accessed
modified = request.session.modified
empty = request.session.is_empty()
except AttributeError:
pass
else:
# First check if we need to delete this cookie.
# The session should be deleted only if the session is entirely empty
cookie_name = self.cookie_name(request)
if cookie_name in request.COOKIES and empty:
response.delete_cookie(
cookie_name,
path=settings.SESSION_COOKIE_PATH,
domain=settings.SESSION_COOKIE_DOMAIN,
)
else:
if accessed:
patch_vary_headers(response, ('Cookie',))
if (modified or settings.SESSION_SAVE_EVERY_REQUEST) and not empty:
if request.session.get_expire_at_browser_close():
max_age = None
expires = None
else:
max_age = request.session.get_expiry_age()
expires_time = time.time() + max_age
expires = http_date(expires_time)
# Save the session data and refresh the client cookie.
# Skip session save for 500 responses, refs #3881.
if response.status_code != 500:
try:
request.session.save()
except UpdateError:
raise SuspiciousOperation(
"The request's session was deleted before the "
"request completed. The user may have logged "
"out in a concurrent request, for example."
)
response.set_cookie(
cookie_name,
request.session.session_key, max_age=max_age,
expires=expires, domain=settings.SESSION_COOKIE_DOMAIN,
path=settings.SESSION_COOKIE_PATH,
secure=settings.SESSION_COOKIE_SECURE or None,
httponly=settings.SESSION_COOKIE_HTTPONLY or None,
)
return response
And this is very important: in settings, in the MIDDELWARE list, you must add it but also remove the SessionMiddleware per default. Insert it in the same position where the normal SessionMiddleware was. The idea is to put it before 'django.contrib.auth.middleware.AuthenticationMiddleware', otherwise, you will get an error.
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
#'django.contrib.sessions.middleware.SessionMiddleware',
'core.utils.middleware.MySessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

missing 1 required positional argument: 'get_response'

So I am using Django 1.11. I used to use Django 1.9 and I remembered writing this piece of login middleware.
import re
from django.conf import settings
from django.shortcuts import redirect
EXEMPT_URLS = [re.compile(settings.LOGIN_URL.lstrip('/'))]
if hasattr(settings, 'LOGIN_EXEMPT_URLS'):
EXEMPT_URLS += [re.compile(url) for url in settings.LOGIN_EXEMPT_URLS]
class LoginRequired:
def __init__(self, get_response):
self.get_response = get_response(request)
def __call__(self, request):
response = self.get_response(request)
return response
def process_view(self, request, view_func, view_args, view_kwargs):
assert hasattr(request, 'user')
path = request.path_info.lstrip('/')
if not request.user.is_authenticated():
if not any(url.match(path) for url in EXEMPT_URLS):
return redirect(settings.LOGIN_URL)
However, I think something changed but I'm not sure what. I get the error:__init__() missing 1 required positional argument: 'get_response'
Any ideas?
You have written a new style middleware which will work in Django 1.10+. It will not work with old style middleware.
Make sure that you have defined MIDDLEWARE instead of MIDDLEWARE_CLASSES in your settings, so that Django treats your middleware as new-style middleware.

Django: NoReverseMatch Error production_id: None

Having an issue where I would fill out the form and when I click to save the input, it would show the info submitted into the query but my production_id value would return as None.
Here is the error:
Environment:
Request Method: POST
Request URL: http://192.168.33.10:8000/podfunnel/episodeinfo/
Django Version: 1.9
Python Version: 2.7.6
Installed Applications:
('producer',
'django.contrib.admin',
'django.contrib.sites',
'registration',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'storages',
'django_extensions',
'randomslugfield',
'adminsortable2',
'crispy_forms')
Installed Middleware:
('django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'django.middleware.security.SecurityMiddleware')
Traceback:
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response
149. response = self.process_exception_by_middleware(e, request)
File "/usr/local/lib/python2.7/dist-packages/django/core/handlers/base.py" in get_response
147. response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/views/generic/base.py" in view
68. return self.dispatch(request, *args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/contrib/auth/mixins.py" in dispatch
56. return super(LoginRequiredMixin, self).dispatch(request, *args, **kwargs)
File "/usr/local/lib/python2.7/dist-packages/django/views/generic/base.py" in dispatch
88. return handler(request, *args, **kwargs)
File "/home/vagrant/fullcast_project/producer/views/pod_funnel.py" in post
601. return HttpResponseRedirect(reverse('podfunnel:episodeimagefiles', kwargs={'production_id':production_id}))
File "/usr/local/lib/python2.7/dist-packages/django/core/urlresolvers.py" in reverse
600. return force_text(iri_to_uri(resolver._reverse_with_prefix(view, prefix, *args, **kwargs)))
File "/usr/local/lib/python2.7/dist-packages/django/core/urlresolvers.py" in _reverse_with_prefix
508. (lookup_view_s, args, kwargs, len(patterns), patterns))
Exception Type: NoReverseMatch at /podfunnel/episodeinfo/
Exception Value: Reverse for 'episodeimagefiles' with arguments '()' and keyword arguments '{'production_id': None}' not found. 1 pattern(s) tried: [u'podfunnel/episodeimagefiles/(?P<production_id>[0-9]+)/$']
Here is my pod_funnel.py view:
from django.http import HttpResponseRedirect, Http404, HttpResponseForbidden
from django.shortcuts import render, get_object_or_404
from django.views.generic import View, RedirectView, TemplateView
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from .forms.client_setup import ClientSetupForm
from .forms.podcast_setup import PodcastSetupForm
from .forms.episode_info import EpisodeInfoForm
from .forms.image_files import EpisodeImageFilesForm
from .forms.wordpress_info import EpisodeWordpressInfoForm
from .forms.chapter_marks import EpisodeChapterMarksForm
from .forms.show_links import ShowLinksForm
from .forms.tweetables import TweetablesForm
from .forms.clicktotweet import ClickToTweetForm
from .forms.schedule import ScheduleForm
from .forms.wordpress_account import WordpressAccountForm
from .forms.wordpress_account_setup import WordpressAccountSetupForm
from .forms.wordpress_account_sortable import WordpressAccountSortableForm
from .forms.soundcloud_account import SoundcloudAccountForm
from .forms.twitter_account import TwitterAccountForm
from producer.helpers import get_podfunnel_client_and_podcast_for_user
from producer.helpers.soundcloud_api import SoundcloudAPI
from producer.helpers.twitter import TwitterAPI
from django.conf import settings
from producer.models import Client, Production, ChapterMark, ProductionLink, ProductionTweet, Podcast, WordpressConfig, Credentials, WordPressSortableSection, \
TwitterConfig, SoundcloudConfig
from django.core.urlresolvers import reverse
from producer.tasks.auphonic import update_or_create_preset_for_podcast
class EpisodeInfoView(LoginRequiredMixin, View):
form_class = EpisodeInfoForm
template_name = 'pod_funnel/forms_episode_info.html'
def get(self, request, *args, **kwargs):
initial_values = {}
user = request.user
# Lets get client and podcast for the user already. if not existent raise 404
client, podcast = get_podfunnel_client_and_podcast_for_user(user)
if client is None or podcast is None:
raise Http404
# See if a production_id is passed on the kwargs, if so, retrieve and fill current data.
# if not just provide empty form since will be new.
production_id = kwargs.get('production_id', None)
if production_id:
production = get_object_or_404(Production, id=production_id)
# Ensure this production belongs to this user, if not Unauthorized, 403
if production.podcast_id != podcast.id:
return HttpResponseForbidden()
initial_values['production_id'] = production.id
initial_values['episode_number'] = production.episode_number
initial_values['episode_title'] = production.episode_title
initial_values['episode_guest_first_name'] = production.episode_guest_first_name
initial_values['episode_guest_last_name'] = production.episode_guest_last_name
initial_values['episode_guest_twitter_name'] = production.episode_guest_twitter_name
initial_values['episode_summary'] = production.episode_summary
form = self.form_class(initial=initial_values)
return render(request, self.template_name, {'form': form})
def post(self, request, *args, **kwargs):
form = self.form_class(request.POST)
client, podcast = get_podfunnel_client_and_podcast_for_user(request.user)
if form.is_valid():
# lets get the data
production_id = form.cleaned_data.get('production_id')
episode_number = form.cleaned_data.get('episode_number')
episode_title = form.cleaned_data.get('episode_title')
episode_guest_first_name = form.cleaned_data.get('episode_guest_first_name')
episode_guest_last_name = form.cleaned_data.get('episode_guest_last_name')
episode_guest_twitter_name = form.cleaned_data.get('episode_guest_twitter_name')
episode_summary = form.cleaned_data.get('episode_summary')
#if a production existed, we update, if not we create
if production_id is not None:
production = Production.objects.get(id=production_id)
else:
production = Production(podcast=podcast)
production.episode_number = episode_number
production.episode_title = episode_title
production.episode_guest_first_name = episode_guest_first_name
production.episode_guest_last_name = episode_guest_last_name
production.episode_guest_twitter_name = episode_guest_twitter_name
production.episode_summary = episode_summary
production.save()
return HttpResponseRedirect(reverse('podfunnel:episodeimagefiles', kwargs={'production_id':production_id}))
return render(request, self.template_name, {'form': form})
episode_info.py form:
from django import forms
class EpisodeInfoForm(forms.Form):
production_id = forms.IntegerField(widget=forms.Field.hidden_widget, required=False)
episode_number = forms.IntegerField(widget=forms.NumberInput, required=True)
episode_title = forms.CharField(max_length=255, required=True)
episode_guest_first_name = forms.CharField(max_length=128)
episode_guest_last_name = forms.CharField(max_length=128)
episode_guest_twitter_name = forms.CharField(max_length=64)
episode_summary = forms.CharField(widget=forms.Textarea)
And url.py:
from django.conf.urls import url
from django.views.generic import TemplateView
import producer.views.pod_funnel as views
urlpatterns = [
url(r'^dashboard/', views.dashboard, name="dashboard"),
url(r'^clientsetup/', views.ClientSetupView.as_view(), name="clientsetup"),
url(r'^podcastsetup/', views.PodcastSetupView.as_view(), name="podcastsetup"),
url(r'^episodeinfo/$', views.EpisodeInfoView.as_view(), name="episodeinfo"),
url(r'^episodeinfo/(?P<production_id>[0-9]+)/$', views.EpisodeInfoView.as_view(), name="episodeinfo_edit"),
url(r'^episodeimagefiles/(?P<production_id>[0-9]+)/$', views.EpisodeImageFilesView.as_view(), name="episodeimagefiles"),
Any suggestion would be appreciated.
It looks like production_id can be None in your view, in which case you can't use it when you call reverse. It would be better to use production.id instead. You have just saved the production in your view, so production.id will be set.
return HttpResponseRedirect(reverse('podfunnel:episodeimagefiles', kwargs={'production_id':production.id}))
Note that you can simplify this line by using the redirect shortcut. Add the import,
from django.shortcuts import redirect
then change the line to
return redirect('podfunnel:episodeimagefiles', production_id=production.id)
You can't always redirect to episodeimagefiles if you didn't provide appropriate initial value for production_id:
# See if a production_id is passed on the kwargs, if so, retrieve and fill current data.
# if not just provide empty form since will be new.
production_id = kwargs.get('production_id', None) <-- here you set production_id variable to None if no `production_id` in kwargs
Look at your exception:
Exception Value: Reverse for 'episodeimagefiles' with arguments '()' and keyword arguments '{'production_id': None}' not found. 1 pattern(s) tried: [u'podfunnel/episodeimagefiles/(?P<production_id>[0-9]+)/$']
It means you passed None value for production_id variable, but episodeimagefiles pattern required some int value to resolve url, so it raises NoReverseMatch exception.
Your form is valid in EpisodeInfoView.post because you set required=False for production_id attribute in your form:
class EpisodeInfoForm(forms.Form):
production_id = forms.IntegerField(widget=forms.Field.hidden_widget, required=False)
I guess, if you debug your generated form before submit it, you can see something like <input type="hidden" name="production_id" value="None" />