summaryrefslogtreecommitdiff
path: root/django
diff options
context:
space:
mode:
authorGary Wilson Jr <gary.wilson@gmail.com>2008-08-10 21:10:47 +0000
committerGary Wilson Jr <gary.wilson@gmail.com>2008-08-10 21:10:47 +0000
commitef48a3e69c02438db32f20531f5c679e8315d528 (patch)
treea60f57ad406aaa674ee8c742947872d2efc12cf4 /django
parent94e8f4fb358fe39a67456f23f92eacd6f83e302d (diff)
Fixed #7830 -- Removed all of the remaining, deprecated, non-oldforms features:
* Support for representing files as strings was removed. Use `django.core.files.base.ContentFile` instead. * Support for representing uploaded files as dictionaries was removed. Use `django.core.files.uploadedfile.SimpleUploadedFile` instead. * The `filename`, `file_name`, `file_size`, and `chuck` properties of `UploadedFile` were removed. Use the `name`, `name`, `size`, and `chunks` properties instead, respectively. * The `get_FIELD_filename`, `get_FIELD_url`, `get_FIELD_size`, and `save_FIELD_file` methods for Models with `FileField` fields were removed. Instead, use the `path`, `url`, and `size` attributes and `save` method on the field itself, respectively. * The `get_FIELD_width` and `get_FIELD_height` methods for Models with `ImageField` fields were removed. Use the `width` and `height` attributes on the field itself instead. * The dispatcher `connect`, `disconnect`, `send`, and `sendExact` functions were removed. Use the signal object's own `connect`, `disconnect`, `send`, and `send` methods instead, respectively. * The `form_for_model` and `form_for_instance` functions were removed. Use a `ModelForm` subclass instead. * Support for importing `django.newforms` was removed. Use `django.forms` instead. * Support for importing `django.utils.images` was removed. Use `django.core.files.images` instead. * Support for the `follow` argument in the `create_object` and `update_object` generic views was removed. Use the `django.forms` package and the new `form_class` argument instead. git-svn-id: http://code.djangoproject.com/svn/django/trunk@8291 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django')
-rw-r--r--django/core/files/storage.py13
-rw-r--r--django/core/files/uploadedfile.py71
-rw-r--r--django/db/models/base.py38
-rw-r--r--django/db/models/fields/files.py28
-rw-r--r--django/dispatch/dispatcher.py45
-rw-r--r--django/forms/fields.py19
-rw-r--r--django/forms/models.py89
-rw-r--r--django/newforms/__init__.py7
-rw-r--r--django/utils/images.py5
-rw-r--r--django/views/generic/create_update.py24
10 files changed, 30 insertions, 309 deletions
diff --git a/django/core/files/storage.py b/django/core/files/storage.py
index a0732e9003..fcad86fda0 100644
--- a/django/core/files/storage.py
+++ b/django/core/files/storage.py
@@ -36,19 +36,6 @@ class Storage(object):
Saves new content to the file specified by name. The content should be a
proper File object, ready to be read from the beginning.
"""
- # Check for old-style usage. Warn here first since there are multiple
- # locations where we need to support both new and old usage.
- if isinstance(content, basestring):
- import warnings
- warnings.warn(
- message = "Representing files as strings is deprecated." \
- "Use django.core.files.base.ContentFile instead.",
- category = DeprecationWarning,
- stacklevel = 2
- )
- from django.core.files.base import ContentFile
- content = ContentFile(content)
-
# Get the proper name for the file, as it will actually be saved.
if name is None:
name = content.name
diff --git a/django/core/files/uploadedfile.py b/django/core/files/uploadedfile.py
index a5a12930e3..afbcdba0f0 100644
--- a/django/core/files/uploadedfile.py
+++ b/django/core/files/uploadedfile.py
@@ -3,7 +3,6 @@ Classes representing uploaded files.
"""
import os
-import warnings
try:
from cStringIO import StringIO
except ImportError:
@@ -11,34 +10,10 @@ except ImportError:
from django.conf import settings
from django.core.files.base import File
-
from django.core.files import temp as tempfile
-__all__ = ('UploadedFile', 'TemporaryUploadedFile', 'InMemoryUploadedFile', 'SimpleUploadedFile')
-
-# Because we fooled around with it a bunch, UploadedFile has a bunch
-# of deprecated properties. This little shortcut helps define 'em
-# without too much code duplication.
-def deprecated_property(old, new, readonly=False):
- def issue_warning():
- warnings.warn(
- message = "UploadedFile.%s is deprecated; use UploadedFile.%s instead." % (old, new),
- category = DeprecationWarning,
- stacklevel = 3
- )
-
- def getter(self):
- issue_warning()
- return getattr(self, new)
-
- def setter(self, value):
- issue_warning()
- setattr(self, new, value)
-
- if readonly:
- return property(getter)
- else:
- return property(getter, setter)
+__all__ = ('UploadedFile', 'TemporaryUploadedFile', 'InMemoryUploadedFile',
+ 'SimpleUploadedFile')
class UploadedFile(File):
"""
@@ -77,21 +52,6 @@ class UploadedFile(File):
name = property(_get_name, _set_name)
- # Deprecated properties
- filename = deprecated_property(old="filename", new="name")
- file_name = deprecated_property(old="file_name", new="name")
- file_size = deprecated_property(old="file_size", new="size")
- chunk = deprecated_property(old="chunk", new="chunks", readonly=True)
-
- def _get_data(self):
- warnings.warn(
- message = "UploadedFile.data is deprecated; use UploadedFile.read() instead.",
- category = DeprecationWarning,
- stacklevel = 2
- )
- return self.read()
- data = property(_get_data)
-
# Abstract methods; subclasses *must* define read() and probably should
# define open/close.
def read(self, num_bytes=None):
@@ -103,27 +63,6 @@ class UploadedFile(File):
def close(self):
pass
- # Backwards-compatible support for uploaded-files-as-dictionaries.
- def __getitem__(self, key):
- warnings.warn(
- message = "The dictionary access of uploaded file objects is deprecated. Use the new object interface instead.",
- category = DeprecationWarning,
- stacklevel = 2
- )
- backwards_translate = {
- 'filename': 'name',
- 'content-type': 'content_type',
- }
-
- if key == 'content':
- return self.read()
- elif key == 'filename':
- return self.name
- elif key == 'content-type':
- return self.content_type
- else:
- return getattr(self, key)
-
class TemporaryUploadedFile(UploadedFile):
"""
A file uploaded to a temporary location (i.e. stream-to-disk).
@@ -140,7 +79,7 @@ class TemporaryUploadedFile(UploadedFile):
Returns the full path of this file.
"""
return self._file.name
-
+
# Most methods on this object get proxied to NamedTemporaryFile.
# We can't directly subclass because NamedTemporaryFile is actually a
# factory function
@@ -159,9 +98,9 @@ class TemporaryUploadedFile(UploadedFile):
# Still sets self._file.close_called and calls self._file.file.close()
# before the exception
return
- else:
+ else:
raise e
-
+
class InMemoryUploadedFile(UploadedFile):
"""
A file uploaded into memory (i.e. stream-to-memory).
diff --git a/django/db/models/base.py b/django/db/models/base.py
index ed061e6634..f05699434c 100644
--- a/django/db/models/base.py
+++ b/django/db/models/base.py
@@ -3,7 +3,6 @@ import types
import sys
import os
from itertools import izip
-from warnings import warn
try:
set
except NameError:
@@ -477,43 +476,6 @@ class Model(object):
setattr(self, cachename, obj)
return getattr(self, cachename)
- def _get_FIELD_filename(self, field):
- warn("instance.get_%s_filename() is deprecated. Use instance.%s.path instead." % \
- (field.attname, field.attname), DeprecationWarning, stacklevel=3)
- try:
- return getattr(self, field.attname).path
- except ValueError:
- return ''
-
- def _get_FIELD_url(self, field):
- warn("instance.get_%s_url() is deprecated. Use instance.%s.url instead." % \
- (field.attname, field.attname), DeprecationWarning, stacklevel=3)
- try:
- return getattr(self, field.attname).url
- except ValueError:
- return ''
-
- def _get_FIELD_size(self, field):
- warn("instance.get_%s_size() is deprecated. Use instance.%s.size instead." % \
- (field.attname, field.attname), DeprecationWarning, stacklevel=3)
- return getattr(self, field.attname).size
-
- def _save_FIELD_file(self, field, filename, content, save=True):
- warn("instance.save_%s_file() is deprecated. Use instance.%s.save() instead." % \
- (field.attname, field.attname), DeprecationWarning, stacklevel=3)
- return getattr(self, field.attname).save(filename, content, save)
-
- _save_FIELD_file.alters_data = True
-
- def _get_FIELD_width(self, field):
- warn("instance.get_%s_width() is deprecated. Use instance.%s.width instead." % \
- (field.attname, field.attname), DeprecationWarning, stacklevel=3)
- return getattr(self, field.attname).width()
-
- def _get_FIELD_height(self, field):
- warn("instance.get_%s_height() is deprecated. Use instance.%s.height instead." % \
- (field.attname, field.attname), DeprecationWarning, stacklevel=3)
- return getattr(self, field.attname).height()
############################################
diff --git a/django/db/models/fields/files.py b/django/db/models/fields/files.py
index 76639596b5..935dee6162 100644
--- a/django/db/models/fields/files.py
+++ b/django/db/models/fields/files.py
@@ -192,10 +192,6 @@ class FileField(Field):
def contribute_to_class(self, cls, name):
super(FileField, self).contribute_to_class(cls, name)
setattr(cls, self.name, FileDescriptor(self))
- setattr(cls, 'get_%s_filename' % self.name, curry(cls._get_FIELD_filename, field=self))
- setattr(cls, 'get_%s_url' % self.name, curry(cls._get_FIELD_url, field=self))
- setattr(cls, 'get_%s_size' % self.name, curry(cls._get_FIELD_size, field=self))
- setattr(cls, 'save_%s_file' % self.name, lambda instance, name, content, save=True: instance._save_FIELD_file(self, name, content, save))
signals.post_delete.connect(self.delete_file, sender=cls)
def delete_file(self, instance, sender, **kwargs):
@@ -262,26 +258,15 @@ class FileField(Field):
class ImageFieldFile(ImageFile, FieldFile):
def save(self, name, content, save=True):
-
- if not hasattr(content, 'read'):
- import warnings
- warnings.warn(
- message = "Representing files as strings is deprecated." \
- "Use django.core.files.base.ContentFile instead.",
- category = DeprecationWarning,
- stacklevel = 2
- )
- content = ContentFile(content)
-
# Repopulate the image dimension cache.
self._dimensions_cache = get_image_dimensions(content)
-
+
# Update width/height fields, if needed
if self.field.width_field:
setattr(self.instance, self.field.width_field, self.width)
if self.field.height_field:
setattr(self.instance, self.field.height_field, self.height)
-
+
super(ImageFieldFile, self).save(name, content, save)
def delete(self, save=True):
@@ -300,15 +285,6 @@ class ImageField(FileField):
def get_manipulator_field_objs(self):
return [oldforms.ImageUploadField, oldforms.HiddenField]
- def contribute_to_class(self, cls, name):
- super(ImageField, self).contribute_to_class(cls, name)
- # Add get_BLAH_width and get_BLAH_height methods, but only if the
- # image field doesn't have width and height cache fields.
- if not self.width_field:
- setattr(cls, 'get_%s_width' % self.name, curry(cls._get_FIELD_width, field=self))
- if not self.height_field:
- setattr(cls, 'get_%s_height' % self.name, curry(cls._get_FIELD_height, field=self))
-
def formfield(self, **kwargs):
defaults = {'form_class': forms.ImageField}
defaults.update(kwargs)
diff --git a/django/dispatch/dispatcher.py b/django/dispatch/dispatcher.py
index ad54fa42bb..ff659d3005 100644
--- a/django/dispatch/dispatcher.py
+++ b/django/dispatch/dispatcher.py
@@ -1,5 +1,4 @@
import weakref
-import warnings
try:
set
except NameError:
@@ -197,47 +196,3 @@ class Signal(object):
for idx, (r_key, _) in enumerate(self.receivers):
if r_key == key:
del self.receivers[idx]
-
-def connect(receiver, signal, sender=None, weak=True):
- """
- For backward compatibility only. See Signal.connect()
- """
- warnings.warn(
- category = DeprecationWarning,
- message = "dispatcher.connect() is deprecated; use Signal.connect() instead.",
- stacklevel = 2
- )
- return signal.connect(receiver, sender, weak)
-
-def disconnect(receiver, signal, sender=None, weak=True):
- """
- For backward compatibility only. See Signal.disconnect()
- """
- warnings.warn(
- category = DeprecationWarning,
- message = "dispatcher.disconnect() is deprecated; use Signal.disconnect() instead.",
- stacklevel = 2
- )
- signal.disconnect(receiver, sender, weak)
-
-def send(signal, sender=None, **named):
- """
- For backward compatibility only. See Signal.send()
- """
- warnings.warn(
- category = DeprecationWarning,
- message = "dispatcher.send() is deprecated; use Signal.send() instead.",
- stacklevel = 2
- )
- return signal.send(sender=sender, **named)
-
-def sendExact(signal, sender, **named ):
- """
- This function is deprecated, as it now has the same meaning as send.
- """
- warnings.warn(
- category = DeprecationWarning,
- message = "dispatcher.sendExact() is deprecated; use Signal.send() instead.",
- stacklevel = 2
- )
- return signal.send(sender=sender, **named)
diff --git a/django/forms/fields.py b/django/forms/fields.py
index 47ae5e11b2..f3e5528f23 100644
--- a/django/forms/fields.py
+++ b/django/forms/fields.py
@@ -442,16 +442,7 @@ class FileField(Field):
elif not data and initial:
return initial
- if isinstance(data, dict):
- # We warn once, then support both ways below.
- import warnings
- warnings.warn(
- message = "Representing uploaded files as dictionaries is deprecated. Use django.core.files.uploadedfile.SimpleUploadedFile instead.",
- category = DeprecationWarning,
- stacklevel = 2
- )
- data = UploadedFile(data['filename'], data['content'])
-
+ # UploadedFile objects should have name and size attributes.
try:
file_name = data.name
file_size = data.size
@@ -507,10 +498,10 @@ class ImageField(FileField):
# but it must be called immediately after the constructor
trial_image = Image.open(file)
trial_image.verify()
- except ImportError:
+ except ImportError:
# Under PyPy, it is possible to import PIL. However, the underlying
- # _imaging C module isn't available, so an ImportError will be
- # raised. Catch and re-raise.
+ # _imaging C module isn't available, so an ImportError will be
+ # raised. Catch and re-raise.
raise
except Exception: # Python Imaging Library doesn't recognize it as an image
raise ValidationError(self.error_messages['invalid_image'])
@@ -643,7 +634,7 @@ class ChoiceField(Field):
if value == smart_unicode(k):
return True
return False
-
+
class MultipleChoiceField(ChoiceField):
hidden_widget = MultipleHiddenInput
widget = SelectMultiple
diff --git a/django/forms/models.py b/django/forms/models.py
index 787da8ca40..193dabef7d 100644
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -3,8 +3,6 @@ Helper functions for creating Form classes from Django models
and database field objects.
"""
-from warnings import warn
-
from django.utils.translation import ugettext_lazy as _
from django.utils.encoding import smart_unicode
from django.utils.datastructures import SortedDict
@@ -18,8 +16,8 @@ from formsets import BaseFormSet, formset_factory, DELETION_FIELD_NAME
__all__ = (
'ModelForm', 'BaseModelForm', 'model_to_dict', 'fields_for_model',
- 'save_instance', 'form_for_model', 'form_for_instance', 'form_for_fields',
- 'ModelChoiceField', 'ModelMultipleChoiceField',
+ 'save_instance', 'form_for_fields', 'ModelChoiceField',
+ 'ModelMultipleChoiceField',
)
def save_instance(form, instance, fields=None, fail_message='saved',
@@ -74,65 +72,6 @@ def make_instance_save(instance, fields, fail_message):
return save_instance(self, instance, fields, fail_message, commit)
return save
-def form_for_model(model, form=BaseForm, fields=None,
- formfield_callback=lambda f: f.formfield()):
- """
- Returns a Form class for the given Django model class.
-
- Provide ``form`` if you want to use a custom BaseForm subclass.
-
- Provide ``formfield_callback`` if you want to define different logic for
- determining the formfield for a given database field. It's a callable that
- takes a database Field instance and returns a form Field instance.
- """
- warn("form_for_model is deprecated. Use ModelForm instead.",
- PendingDeprecationWarning, stacklevel=3)
- opts = model._meta
- field_list = []
- for f in opts.fields + opts.many_to_many:
- if not f.editable:
- continue
- if fields and not f.name in fields:
- continue
- formfield = formfield_callback(f)
- if formfield:
- field_list.append((f.name, formfield))
- base_fields = SortedDict(field_list)
- return type(opts.object_name + 'Form', (form,),
- {'base_fields': base_fields, '_model': model,
- 'save': make_model_save(model, fields, 'created')})
-
-def form_for_instance(instance, form=BaseForm, fields=None,
- formfield_callback=lambda f, **kwargs: f.formfield(**kwargs)):
- """
- Returns a Form class for the given Django model instance.
-
- Provide ``form`` if you want to use a custom BaseForm subclass.
-
- Provide ``formfield_callback`` if you want to define different logic for
- determining the formfield for a given database field. It's a callable that
- takes a database Field instance, plus **kwargs, and returns a form Field
- instance with the given kwargs (i.e. 'initial').
- """
- warn("form_for_instance is deprecated. Use ModelForm instead.",
- PendingDeprecationWarning, stacklevel=3)
- model = instance.__class__
- opts = model._meta
- field_list = []
- for f in opts.fields + opts.many_to_many:
- if not f.editable:
- continue
- if fields and not f.name in fields:
- continue
- current_value = f.value_from_object(instance)
- formfield = formfield_callback(f, initial=current_value)
- if formfield:
- field_list.append((f.name, formfield))
- base_fields = SortedDict(field_list)
- return type(opts.object_name + 'InstanceForm', (form,),
- {'base_fields': base_fields, '_model': model,
- 'save': make_instance_save(instance, fields, 'changed')})
-
def form_for_fields(field_list):
"""
Returns a Form class for the given list of Django database field instances.
@@ -289,7 +228,7 @@ def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
setattr(Meta, 'fields', fields)
setattr(Meta, 'exclude', exclude)
class_name = model.__name__ + 'Form'
- return ModelFormMetaclass(class_name, (form,), {'Meta': Meta,
+ return ModelFormMetaclass(class_name, (form,), {'Meta': Meta,
'formfield_callback': formfield_callback})
@@ -410,7 +349,7 @@ class BaseInlineFormSet(BaseModelFormSet):
# is there a better way to get the object descriptor?
self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name()
super(BaseInlineFormSet, self).__init__(data, files, prefix=prefix or self.rel_name)
-
+
def _construct_forms(self):
if self.save_as_new:
self._total_form_count = self._initial_form_count
@@ -419,7 +358,7 @@ class BaseInlineFormSet(BaseModelFormSet):
def get_queryset(self):
"""
- Returns this FormSet's queryset, but restricted to children of
+ Returns this FormSet's queryset, but restricted to children of
self.instance
"""
kwargs = {self.fk.name: self.instance}
@@ -443,7 +382,7 @@ def _get_foreign_key(parent_model, model, fk_name=None):
if len(fks_to_parent) == 1:
fk = fks_to_parent[0]
if not isinstance(fk, ForeignKey) or \
- (fk.rel.to != parent_model and
+ (fk.rel.to != parent_model and
fk.rel.to not in parent_model._meta.parents.keys()):
raise Exception("fk_name '%s' is not a ForeignKey to %s" % (fk_name, parent_model))
elif len(fks_to_parent) == 0:
@@ -451,9 +390,9 @@ def _get_foreign_key(parent_model, model, fk_name=None):
else:
# Try to discover what the ForeignKey from model to parent_model is
fks_to_parent = [
- f for f in opts.fields
- if isinstance(f, ForeignKey)
- and (f.rel.to == parent_model
+ f for f in opts.fields
+ if isinstance(f, ForeignKey)
+ and (f.rel.to == parent_model
or f.rel.to in parent_model._meta.parents.keys())
]
if len(fks_to_parent) == 1:
@@ -478,7 +417,7 @@ def inlineformset_factory(parent_model, model, form=ModelForm,
"""
fk = _get_foreign_key(parent_model, model, fk_name=fk_name)
# let the formset handle object deletion by default
-
+
if exclude is not None:
exclude.append(fk.name)
else:
@@ -528,7 +467,7 @@ class ModelChoiceField(ChoiceField):
help_text=None, *args, **kwargs):
self.empty_label = empty_label
self.cache_choices = cache_choices
-
+
# Call Field instead of ChoiceField __init__() because we don't need
# ChoiceField.__init__().
Field.__init__(self, required, widget, label, initial, help_text,
@@ -545,8 +484,8 @@ class ModelChoiceField(ChoiceField):
queryset = property(_get_queryset, _set_queryset)
- # this method will be used to create object labels by the QuerySetIterator.
- # Override it to customize the label.
+ # this method will be used to create object labels by the QuerySetIterator.
+ # Override it to customize the label.
def label_from_instance(self, obj):
"""
This method is used to convert objects into strings; it's used to
@@ -554,7 +493,7 @@ class ModelChoiceField(ChoiceField):
can override this method to customize the display of the choices.
"""
return smart_unicode(obj)
-
+
def _get_choices(self):
# If self._choices is set, then somebody must have manually set
# the property self.choices. In this case, just return self._choices.
diff --git a/django/newforms/__init__.py b/django/newforms/__init__.py
deleted file mode 100644
index 5f319f8c1b..0000000000
--- a/django/newforms/__init__.py
+++ /dev/null
@@ -1,7 +0,0 @@
-import warnings
-warnings.warn(
- category = DeprecationWarning,
- message = "django.newforms is no longer new. Import django.forms instead.",
- stacklevel = 2
-)
-from django.forms import * \ No newline at end of file
diff --git a/django/utils/images.py b/django/utils/images.py
deleted file mode 100644
index c6cc37cf9a..0000000000
--- a/django/utils/images.py
+++ /dev/null
@@ -1,5 +0,0 @@
-import warnings
-
-from django.core.files.images import get_image_dimensions
-
-warnings.warn("django.utils.images has been moved to django.core.files.images.", DeprecationWarning)
diff --git a/django/views/generic/create_update.py b/django/views/generic/create_update.py
index aaa0bdc4b6..634fb1bdcd 100644
--- a/django/views/generic/create_update.py
+++ b/django/views/generic/create_update.py
@@ -7,19 +7,6 @@ from django.utils.translation import ugettext
from django.contrib.auth.views import redirect_to_login
from django.views.generic import GenericViewError
-def deprecate_follow(follow):
- """
- Issues a DeprecationWarning if follow is anything but None.
-
- The old Manipulator-based forms used a follow argument that is no longer
- needed for newforms-based forms.
- """
- if follow is not None:
- import warnings
- msg = ("Generic views have been changed to use newforms, and the"
- " 'follow' argument is no longer used. Please update your code"
- " to not use the 'follow' argument.")
- warnings.warn(msg, DeprecationWarning, stacklevel=3)
def apply_extra_context(extra_context, context):
"""
@@ -105,8 +92,7 @@ def lookup_object(model, object_id, slug, slug_field):
def create_object(request, model=None, template_name=None,
template_loader=loader, extra_context=None, post_save_redirect=None,
- login_required=False, follow=None, context_processors=None,
- form_class=None):
+ login_required=False, context_processors=None, form_class=None):
"""
Generic object-creation function.
@@ -115,7 +101,6 @@ def create_object(request, model=None, template_name=None,
form
the form for the object
"""
- deprecate_follow(follow)
if extra_context is None: extra_context = {}
if login_required and not request.user.is_authenticated():
return redirect_to_login(request.path)
@@ -143,9 +128,9 @@ def create_object(request, model=None, template_name=None,
def update_object(request, model=None, object_id=None, slug=None,
slug_field='slug', template_name=None, template_loader=loader,
- extra_context=None, post_save_redirect=None,
- login_required=False, follow=None, context_processors=None,
- template_object_name='object', form_class=None):
+ extra_context=None, post_save_redirect=None, login_required=False,
+ context_processors=None, template_object_name='object',
+ form_class=None):
"""
Generic object-update function.
@@ -156,7 +141,6 @@ def update_object(request, model=None, object_id=None, slug=None,
object
the original object being edited
"""
- deprecate_follow(follow)
if extra_context is None: extra_context = {}
if login_required and not request.user.is_authenticated():
return redirect_to_login(request.path)