diff --git a/djadmin2/forms.py b/djadmin2/forms.py index 75ec62d..554560b 100644 --- a/djadmin2/forms.py +++ b/djadmin2/forms.py @@ -6,6 +6,7 @@ from copy import deepcopy from django.contrib.auth import authenticate from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.forms import UserCreationForm, UserChangeForm +import django import django.forms import django.forms.models import django.forms.extras.widgets @@ -27,7 +28,13 @@ def _copy_attributes(original, new_widget, attributes): for attr in attributes: original_value = getattr(original, attr) original_value = deepcopy(original_value) - setattr(new_widget, attr, original_value) + + # Don't set the attribute if it is a property. In that case we can be + # sure that the widget class is taking care of the calculation for that + # property. + old_value_on_new_widget = getattr(new_widget.__class__, attr, None) + if not isinstance(old_value_on_new_widget, property): + setattr(new_widget, attr, original_value) def _create_widget(widget_class, copy_attributes=(), init_arguments=()): @@ -182,6 +189,33 @@ _django_field_to_floppyform_widget = { } +def allow_floppify_widget_for_field(field): + ''' + We only allow to replace a widget with the floppyform counterpart if the + original, by django determined widget is still in place. We don't want to + override custom widgets that a user specified. + ''' + # There is a special case for IntegerFields (and all subclasses) that + # replaces the default TextInput with a NumberInput, if localization is + # turned off. That applies for Django 1.6 upwards. + # See the relevant source code in django: + # https://github.com/django/django/blob/1.6/django/forms/fields.py#L225 + if django.VERSION >= (1, 6): + if isinstance(field, django.forms.IntegerField) and not field.localize: + if field.widget.__class__ is django.forms.NumberInput: + return True + + # We can check if the widget was replaced by comparing the class of the + # specified widget with the default widget that is specified on the field + # class. + if field.widget.__class__ is field.__class__.widget: + return True + + # At that point we are assuming that the user replaced the original widget + # with a custom one. So we don't allow to overwrite it. + return False + + def floppify_widget(widget, field=None): ''' Get an instance of django.forms.widgets.Widget and return a new widget @@ -203,10 +237,7 @@ def floppify_widget(widget, field=None): create_widget = _django_field_to_floppyform_widget.get( field.__class__) if create_widget is not None: - # check if the default widget was replaced by a different one, in - # that case we cannot create the field specific floppyforms - # widget. - if field.widget.__class__ is field.__class__.widget: + if allow_floppify_widget_for_field(field): return create_widget(widget) create_widget = _django_to_floppyforms_widget.get(widget.__class__) if create_widget is not None: diff --git a/djadmin2/tests/test_types.py b/djadmin2/tests/test_types.py index 6986868..288fd97 100644 --- a/djadmin2/tests/test_types.py +++ b/djadmin2/tests/test_types.py @@ -2,8 +2,8 @@ from django.db import models from django.test import TestCase from django.views.generic import View +from .. import views from ..types import ModelAdmin2, immutable_admin_factory -from ..views import AdminView from ..core import Admin2 @@ -48,7 +48,7 @@ class ModelAdminTest(TestCase): def setUp(self): class MyModelAdmin(ModelAdmin2): - my_view = AdminView(r'^$', View) + my_view = views.AdminView(r'^$', views.ModelListView) self.model_admin = MyModelAdmin diff --git a/djadmin2/tests/test_views.py b/djadmin2/tests/test_views.py index 9773a9c..8609e71 100644 --- a/djadmin2/tests/test_views.py +++ b/djadmin2/tests/test_views.py @@ -1,19 +1,19 @@ from django.test import TestCase from django.views.generic import View -from ..views import AdminView +from .. import views class AdminViewTest(TestCase): def setUp(self): - self.admin_view = AdminView(r'^$', View, name='admin-view') + self.admin_view = views.AdminView(r'^$', views.ModelListView, name='admin-view') def test_url(self): self.assertEquals(self.admin_view.url, r'^$') def test_view(self): - self.assertEquals(self.admin_view.view, View) + self.assertEquals(self.admin_view.view, views.ModelListView) def test_name(self): self.assertEquals(self.admin_view.name, 'admin-view') diff --git a/djadmin2/types.py b/djadmin2/types.py index 3674d82..fc15e98 100644 --- a/djadmin2/types.py +++ b/djadmin2/types.py @@ -4,6 +4,7 @@ from __future__ import division, absolute_import, unicode_literals from collections import namedtuple import logging import os +import sys from django.core.urlresolvers import reverse from django.conf.urls import patterns, url @@ -210,16 +211,25 @@ class ModelAdmin2(with_metaclass(ModelAdminBase2)): def get_urls(self): pattern_list = [] - for view in self.views: - view.model_admin = self - get_kwargs = getattr(self, "get_%s_kwargs" % view.name, None) + for admin_view in self.views: + admin_view.model_admin = self + get_kwargs = getattr(self, "get_%s_kwargs" % admin_view.name, None) if not get_kwargs: - get_kwargs = view.get_view_kwargs + get_kwargs = admin_view.get_view_kwargs + try: + view_instance = admin_view.view.as_view(**get_kwargs()) + except Exception as e: + trace = sys.exc_info()[2] + new_exception = TypeError( + 'Cannot instantiate admin view "{}.{}". ' + 'The error that got raised was: {}'.format( + self.__class__.__name__, admin_view.name, e)) + raise new_exception, None, trace pattern_list.append( url( - regex=view.url, - view=view.view.as_view(**get_kwargs()), - name=self.get_prefixed_view_name(view.name) + regex=admin_view.url, + view=view_instance, + name=self.get_prefixed_view_name(admin_view.name) ) ) return patterns('', *pattern_list) diff --git a/docs/conf.py b/docs/conf.py index 6dcfc9e..633df25 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -29,10 +29,6 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "example.example.settings") sys.path.insert(0, os.path.abspath('../../')) sys.path.insert(0, os.path.abspath('../')) -from example.example import settings -from django.core.management import setup_environ -setup_environ(settings) - # For intersphinx ext_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "_ext")) sys.path.append(ext_path) diff --git a/example/blog/tests/__init__.py b/example/blog/tests/__init__.py index dee6a40..8a7bd1e 100644 --- a/example/blog/tests/__init__.py +++ b/example/blog/tests/__init__.py @@ -1,3 +1,7 @@ +# make sure that everything is setup for tests. Django 1.6 doesn't necessarily +# load the urls.py before the tests are run. +import example.urls + from test_apiviews import * from test_builtin_api_resources import * from test_permissions import *