From 0938f570c11b16e6082ff8bff5602c177aaabe61 Mon Sep 17 00:00:00 2001 From: Audrey Roy Date: Sat, 18 May 2013 18:25:36 +0200 Subject: [PATCH 01/16] Display model count/name at bottom --- djadmin2/templates/admin2/bootstrap/model_list.html | 2 ++ 1 file changed, 2 insertions(+) diff --git a/djadmin2/templates/admin2/bootstrap/model_list.html b/djadmin2/templates/admin2/bootstrap/model_list.html index 8121584..44c942d 100644 --- a/djadmin2/templates/admin2/bootstrap/model_list.html +++ b/djadmin2/templates/admin2/bootstrap/model_list.html @@ -36,6 +36,8 @@ {% endfor %} + + {{ object_list|length }} {{ model }}{{ object_list|pluralize }} From 9e647ae4eec8e6b7dad25f740be39d065e8bad6b Mon Sep 17 00:00:00 2001 From: Audrey Roy Date: Sat, 18 May 2013 18:37:20 +0200 Subject: [PATCH 02/16] More of #61 --- djadmin2/templates/admin2/bootstrap/model_list.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/djadmin2/templates/admin2/bootstrap/model_list.html b/djadmin2/templates/admin2/bootstrap/model_list.html index 44c942d..2e8486b 100644 --- a/djadmin2/templates/admin2/bootstrap/model_list.html +++ b/djadmin2/templates/admin2/bootstrap/model_list.html @@ -16,6 +16,14 @@
+ Action: + + Go + TODO of {{ object_list|length }} selected + From a45f586e7f8c01d27c430a66d1b7f7fae58e046a Mon Sep 17 00:00:00 2001 From: Raphael Kimmig Date: Sat, 18 May 2013 21:45:43 +0200 Subject: [PATCH 03/16] add some docstrings to core.Admin2 --- djadmin2/core.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/djadmin2/core.py b/djadmin2/core.py index 58e2050..d0aa35a 100644 --- a/djadmin2/core.py +++ b/djadmin2/core.py @@ -9,6 +9,13 @@ from . import views class Admin2(object): + """ + The base Admin2 object. + It keeps a registry of all registered Models and collects the urls of their + related ModelAdmin2 instances. + + It also provides an index view that serves as an entry point to the admin site. + """ index_view = views.IndexView def __init__(self, name='admin2', app_name='admin2'): @@ -17,6 +24,15 @@ class Admin2(object): self.app_name = app_name def register(self, model, modeladmin=None, **kwargs): + """ + Registers the given model with the given admin class. + + If no modeladmin is passed, it will use ModelAdmin2. If keyword + arguments are given they will be passed to the admin class on + instantiation. + + If a model is already registered, this will raise ImproperlyConfigured. + """ if model in self.registry: raise ImproperlyConfigured if not modeladmin: @@ -24,13 +40,21 @@ class Admin2(object): self.registry[model] = modeladmin(model, **kwargs) def deregister(self, model): + """ + De-registers the given model. + + If the is not already registered, this will raise ImproperlyConfigured. + """ try: del self.registry[model] except KeyError: raise ImproperlyConfigured def autodiscover(self): - apps = [] + """ + Auto-discovers all admin2.py modules for apps in INSTALLED_APPS by + trying to import them. + """ for app_name in [x for x in settings.INSTALLED_APPS]: try: import_module("%s.admin2" % app_name) From f652fd65e5b6f983a2b6430dd54491ebebb0fbf3 Mon Sep 17 00:00:00 2001 From: Raphael Kimmig Date: Sat, 18 May 2013 22:10:49 +0200 Subject: [PATCH 04/16] update readme to reflect implementation --- README.rst | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/README.rst b/README.rst index 39e7556..3c9c4cb 100644 --- a/README.rst +++ b/README.rst @@ -23,20 +23,25 @@ Our goal is to make this API work: .. code-block:: python - # myapp/admin2.py + # Import your custom models + from .models import Post, Comment + from django.contrib.auth.forms import UserCreationForm, UserChangeForm + from django.contrib.auth.models import User - # Import the Admin2 base class - from admin2.models import Admin2 + import djadmin2 + from djadmin2.models import ModelAdmin2 - # Import your custom models - from blog.models import Post - # Instantiate the Admin2 class - # Then attach the admin2 object to your model - Post.admin2 = Admin2() + class UserAdmin2(ModelAdmin2): + create_form_class = UserCreationForm + update_form_class = UserChangeForm + + + # Register each model with the admin + djadmin2.default.register(Post) + djadmin2.default.register(Comment) + djadmin2.default.register(User, UserAdmin2) - -.. note:: You will notice a difference between how and django.contrib.admin and django-admin2 do configuration. The former associates the configuration class with the model object via a registration utility, and the latter does so by adding the configuration class as an attribute of the model object. Themes ======== From 684b167d117b41e9dd08b76806968d71549cdd1b Mon Sep 17 00:00:00 2001 From: Raphael Kimmig Date: Sat, 18 May 2013 22:40:35 +0200 Subject: [PATCH 05/16] Add missing word to Admin2 docstring. --- djadmin2/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djadmin2/core.py b/djadmin2/core.py index d0aa35a..4d5c3f4 100644 --- a/djadmin2/core.py +++ b/djadmin2/core.py @@ -43,7 +43,7 @@ class Admin2(object): """ De-registers the given model. - If the is not already registered, this will raise ImproperlyConfigured. + If the model is not already registered, this will raise ImproperlyConfigured. """ try: del self.registry[model] From 1818cbe553e3b0cc0ebf2522d48c6f90c6522aa0 Mon Sep 17 00:00:00 2001 From: Raphael Kimmig Date: Sat, 18 May 2013 23:32:17 +0200 Subject: [PATCH 06/16] fix typos in core docstrings --- djadmin2/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/djadmin2/core.py b/djadmin2/core.py index 4d5c3f4..4418e3e 100644 --- a/djadmin2/core.py +++ b/djadmin2/core.py @@ -41,7 +41,7 @@ class Admin2(object): def deregister(self, model): """ - De-registers the given model. + Deregisters the given model. If the model is not already registered, this will raise ImproperlyConfigured. """ @@ -52,7 +52,7 @@ class Admin2(object): def autodiscover(self): """ - Auto-discovers all admin2.py modules for apps in INSTALLED_APPS by + Autodiscovers all admin2.py modules for apps in INSTALLED_APPS by trying to import them. """ for app_name in [x for x in settings.INSTALLED_APPS]: From e963553361e9bf2a41c4bb0edd67ebd9f3fb8057 Mon Sep 17 00:00:00 2001 From: Daniel Greenfeld Date: Sun, 19 May 2013 09:47:42 +0200 Subject: [PATCH 07/16] merge in from @AndrewIngram for #58 --- djadmin2/core.py | 5 ++-- djadmin2/models.py | 25 ++++++++++++---- djadmin2/templates/admin2/bootstrap/base.html | 2 ++ .../templates/admin2/bootstrap/index.html | 2 +- .../admin2/bootstrap/model_list.html | 21 +++++++------- djadmin2/templatetags/__init__.py | 0 djadmin2/templatetags/admin2_urls.py | 8 +++++ djadmin2/views.py | 29 ++++++++++++++++++- 8 files changed, 71 insertions(+), 21 deletions(-) create mode 100644 djadmin2/templatetags/__init__.py create mode 100644 djadmin2/templatetags/admin2_urls.py diff --git a/djadmin2/core.py b/djadmin2/core.py index 58e2050..f0cea10 100644 --- a/djadmin2/core.py +++ b/djadmin2/core.py @@ -11,10 +11,9 @@ from . import views class Admin2(object): index_view = views.IndexView - def __init__(self, name='admin2', app_name='admin2'): + def __init__(self, name='admin2'): self.registry = {} self.name = name - self.app_name = app_name def register(self, model, modeladmin=None, **kwargs): if model in self.registry: @@ -60,4 +59,4 @@ class Admin2(object): @property def urls(self): - return self.get_urls(), self.app_name, self.name + return self.get_urls(), self.name, self.name diff --git a/djadmin2/models.py b/djadmin2/models.py index e844dca..7f73dca 100644 --- a/djadmin2/models.py +++ b/djadmin2/models.py @@ -4,6 +4,9 @@ For wont of a better name, this module is called 'models'. It's role is synonymous with the django.contrib.admin.sites model. """ +import functools + +from django.core.urlresolvers import reverse from django.conf.urls import patterns, include, url from django.contrib.auth import models as auth_app from django.db.models import get_models, signals @@ -110,6 +113,8 @@ class ModelAdmin2(BaseAdmin2): def __init__(self, model, **kwargs): self.model = model + self.app_label = model._meta.app_label + self.model_name = model._meta.object_name.lower() if self.verbose_name is None: self.verbose_name = self.model._meta.verbose_name @@ -118,10 +123,15 @@ class ModelAdmin2(BaseAdmin2): def get_default_view_kwargs(self): return { + 'app_label': self.app_label, 'model': self.model, + 'model_name': self.model_name, 'modeladmin': self, } + def get_prefixed_view_name(self, view_name): + return '{}_{}_{}'.format(self.app_label, self.model_name, view_name) + def get_index_kwargs(self): return self.get_default_view_kwargs() @@ -145,32 +155,35 @@ class ModelAdmin2(BaseAdmin2): def get_delete_kwargs(self): return self.get_default_view_kwargs() + def get_index_url(self): + return reverse('admin2:{}'.format(self.get_prefixed_view_name('index'))) + def get_urls(self): return patterns('', url( regex=r'^$', view=self.index_view.as_view(**self.get_index_kwargs()), - name='index' + name=self.get_prefixed_view_name('index') ), url( - regex=r'^create/$', + regex=r'^create/$', view=self.create_view.as_view(**self.get_create_kwargs()), - name='create' + name=self.get_prefixed_view_name('create') ), url( regex=r'^(?P[0-9]+)/$', view=self.detail_view.as_view(**self.get_detail_kwargs()), - name='detail' + name=self.get_prefixed_view_name('detail') ), url( regex=r'^(?P[0-9]+)/update/$', view=self.update_view.as_view(**self.get_update_kwargs()), - name='update' + name=self.get_prefixed_view_name('update') ), url( regex=r'^(?P[0-9]+)/delete/$', view=self.delete_view.as_view(**self.get_delete_kwargs()), - name='delete' + name=self.get_prefixed_view_name('delete') ), ) diff --git a/djadmin2/templates/admin2/bootstrap/base.html b/djadmin2/templates/admin2/bootstrap/base.html index 3ed6f0c..fe38092 100644 --- a/djadmin2/templates/admin2/bootstrap/base.html +++ b/djadmin2/templates/admin2/bootstrap/base.html @@ -9,6 +9,8 @@
{% block content %}{% endblock %} + + Index
diff --git a/djadmin2/templates/admin2/bootstrap/index.html b/djadmin2/templates/admin2/bootstrap/index.html index c9fc3ad..ac8d0e8 100644 --- a/djadmin2/templates/admin2/bootstrap/index.html +++ b/djadmin2/templates/admin2/bootstrap/index.html @@ -4,7 +4,7 @@

Index

{% for modeladmin in registry.values %} - + {% endfor %}
{{ modeladmin.verbose_name_plural }}
{{ modeladmin.verbose_name_plural }}
{% endblock content %} diff --git a/djadmin2/templates/admin2/bootstrap/model_list.html b/djadmin2/templates/admin2/bootstrap/model_list.html index 8121584..7e3eb36 100644 --- a/djadmin2/templates/admin2/bootstrap/model_list.html +++ b/djadmin2/templates/admin2/bootstrap/model_list.html @@ -1,4 +1,5 @@ {% extends "admin2/bootstrap/base.html" %} +{% load admin2_urls %} {% block content %}
@@ -6,9 +7,9 @@

Select {{ model }} to change

- {% if has_add_permission %} - Add {{ model|title }} - {% endif %} + {# if has_add_permission #} + add + {# endif #}
@@ -25,13 +26,13 @@ {% for obj in object_list %} - {{ obj }} detail - {% if has_edit_permission %} - edit - {% endif %} - {% if has_delete_permission %} - delete - {% endif %} + {{ obj }} detail + {# if has_edit_permission #} + edit + {# endif #} + {# if has_delete_permission #} + delete + {# endif #} {% endfor %} diff --git a/djadmin2/templatetags/__init__.py b/djadmin2/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/djadmin2/templatetags/admin2_urls.py b/djadmin2/templatetags/admin2_urls.py new file mode 100644 index 0000000..e5e5395 --- /dev/null +++ b/djadmin2/templatetags/admin2_urls.py @@ -0,0 +1,8 @@ +from django import template + +register = template.Library() + + +@register.filter +def admin2_urlname(value, arg): + return 'admin2:%s_%s_%s' % (value.app_label, value.model_name, arg) diff --git a/djadmin2/views.py b/djadmin2/views.py index d0250ad..475f405 100644 --- a/djadmin2/views.py +++ b/djadmin2/views.py @@ -1,5 +1,6 @@ import os +from django.core.urlresolvers import reverse from django.conf import settings from django.contrib.auth.views import redirect_to_login from django.core.exceptions import PermissionDenied @@ -14,9 +15,25 @@ ADMIN2_THEME_DIRECTORY = getattr(settings, "ADMIN2_THEME_DIRECTORY", "admin2/boo class Admin2Mixin(object): + modeladmin = None + model_name = None + app_label = None + def get_template_names(self): return [os.path.join(ADMIN2_THEME_DIRECTORY, self.default_template_name)] + def get_model(self): + return self.model + + def get_queryset(self): + return self.get_model()._default_manager.all() + + def get_form_class(self): + if self.form_class is not None: + return self.form_class + return modelform_factory(self.get_model()) + + class AdminModel2Mixin(Admin2Mixin, AccessMixin): modeladmin = None @@ -70,7 +87,9 @@ class IndexView(Admin2Mixin, generic.TemplateView): }) return data -class ModelListView(AdminModel2Mixin, generic.ListView): + +class ModelListView(Admin2Mixin, generic.ListView): + default_template_name = "model_list.html" permission_type = 'view' @@ -79,6 +98,10 @@ class ModelListView(AdminModel2Mixin, generic.ListView): context['model'] = self.get_model()._meta.verbose_name context['model_pluralized'] = self.get_model()._meta.verbose_name_plural return context + + def get_success_url(self): + view_name = 'admin2:{}_{}_detail'.format(self.app_label, self.model_name) + return reverse(view_name, kwargs={'pk': self.object.pk}) class ModelDetailView(AdminModel2Mixin, generic.DetailView): @@ -99,6 +122,10 @@ class ModelAddFormView(AdminModel2Mixin, generic.CreateView): default_template_name = "model_add_form.html" permission_type = 'add' + def get_success_url(self): + view_name = 'admin2:{}_{}_detail'.format(self.app_label, self.model_name) + return reverse(view_name, kwargs={'pk': self.object.pk}) + class ModelDeleteView(AdminModel2Mixin, generic.DeleteView): success_url = "../../" From 29fc882c4b0fa5e771f823968dbc63b36f2561fb Mon Sep 17 00:00:00 2001 From: Daniel Greenfeld Date: Sun, 19 May 2013 09:53:37 +0200 Subject: [PATCH 08/16] merge from @rivo --- djadmin2/core.py | 2 +- djadmin2/templates/admin2/bootstrap/base.html | 42 ++++++++++++------- djadmin2/views.py | 2 +- 3 files changed, 29 insertions(+), 17 deletions(-) diff --git a/djadmin2/core.py b/djadmin2/core.py index f0cea10..c3720b0 100644 --- a/djadmin2/core.py +++ b/djadmin2/core.py @@ -45,7 +45,7 @@ class Admin2(object): def get_urls(self): urlpatterns = patterns('', - url(r'^$', self.index_view.as_view(**self.get_index_kwargs()), name='index'), + url(r'^$', self.index_view.as_view(**self.get_index_kwargs()), name='dashboard'), ) for model, modeladmin in self.registry.iteritems(): app_label = model._meta.app_label diff --git a/djadmin2/templates/admin2/bootstrap/base.html b/djadmin2/templates/admin2/bootstrap/base.html index fe38092..231b9a8 100644 --- a/djadmin2/templates/admin2/bootstrap/base.html +++ b/djadmin2/templates/admin2/bootstrap/base.html @@ -1,21 +1,33 @@ - - django-admin2 - - - - - -
- {% block content %}{% endblock %} + + - Index -
+ django-admin2 + + + + + + - +
+ {% block content %}{% endblock %} +
+ + + + {% block extrajs %}{% endblock %} + + \ No newline at end of file diff --git a/djadmin2/views.py b/djadmin2/views.py index 475f405..00e261a 100644 --- a/djadmin2/views.py +++ b/djadmin2/views.py @@ -129,5 +129,5 @@ class ModelAddFormView(AdminModel2Mixin, generic.CreateView): class ModelDeleteView(AdminModel2Mixin, generic.DeleteView): success_url = "../../" - default_template_name = "model_delete.html" + default_template_name = "model_delete_form.html" permission_type = 'delete' From fcdaa9c9fcc2ec4046c3cafa44b6cc4003438438 Mon Sep 17 00:00:00 2001 From: Raphael Kimmig Date: Sun, 19 May 2013 09:59:28 +0200 Subject: [PATCH 09/16] add more tests for the example app --- example/blog/tests.py | 86 ++++++++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 26 deletions(-) diff --git a/example/blog/tests.py b/example/blog/tests.py index 683f350..e2d7a14 100644 --- a/example/blog/tests.py +++ b/example/blog/tests.py @@ -1,36 +1,70 @@ -from django.utils import unittest -from django.test.client import RequestFactory +from django.contrib.auth import get_user_model +from django.core.urlresolvers import reverse +from django.test import TestCase, Client -from djadmin2 import views +from .models import Post -class ViewTest(unittest.TestCase): + +class BaseIntegrationTest(TestCase): + """ + Base TestCase for integration tests. + """ def setUp(self): - self.factory = RequestFactory() + self.client = Client() + self.user = get_user_model()(username='user', is_staff=True, + is_superuser=True) + self.user.set_password("password") + self.user.save() + self.client.login(username='user', password='password') -class IndexViewTest(ViewTest): - def test_response_ok(self): - request = self.factory.get('/admin/blog/post/') - response = views.IndexView.as_view()(request) +class AdminIndexTest(BaseIntegrationTest): + def test_view_ok(self): + response = self.client.get(reverse("admin2:dashboard")) + self.assertContains(response, reverse("admin2:blog_post_index")) + + +class PostListTest(BaseIntegrationTest): + def test_view_ok(self): + post = Post.objects.create(title="a_post_title", body="body") + response = self.client.get(reverse("admin2:blog_post_index")) + self.assertContains(response, post.title) + + +class PostDetailViewTest(BaseIntegrationTest): + def test_view_ok(self): + post = Post.objects.create(title="a_post_title", body="body") + response = self.client.get(reverse("admin2:blog_post_detail", + args=(post.pk, ))) + self.assertContains(response, post.title) + + +class PostCreateViewTest(BaseIntegrationTest): + def test_view_ok(self): + response = self.client.get(reverse("admin2:blog_post_create")) self.assertEqual(response.status_code, 200) - -class ModelListViewTest(ViewTest): - pass + def test_create_post(self): + response = self.client.post(reverse("admin2:blog_post_create"), + {"title": "a_post_title", + "body": "a_post_body"}, + follow=True) + self.assertTrue(Post.objects.filter(title="a_post_title").exists()) + post = Post.objects.get(title="a_post_title") + self.assertRedirects(response, reverse("admin2:blog_post_detail", + args=(post.pk, ))) -class ModelDetailViewTest(ViewTest): - pass - - -class ModelEditFormViewTest(ViewTest): - pass - - -class ModelAddFormViewTest(ViewTest): - pass - - -class ModelDeleteViewTest(ViewTest): - pass +class PostDeleteViewTest(BaseIntegrationTest): + def test_view_ok(self): + post = Post.objects.create(title="a_post_title", body="body") + response = self.client.get(reverse("admin2:blog_post_delete", + args=(post.pk, ))) + self.assertContains(response, post.title) + def test_delete_post(self): + post = Post.objects.create(title="a_post_title", body="body") + response = self.client.post(reverse("admin2:blog_post_delete", + args=(post.pk, ))) + self.assertRedirects(response, reverse("admin2:blog_post_index")) + self.assertFalse(Post.objects.filter(pk=post.pk).exists()) From 774b49ed9f7c29e214e31547fb87a7d9391b1742 Mon Sep 17 00:00:00 2001 From: Daniel Greenfeld Date: Sun, 19 May 2013 10:01:50 +0200 Subject: [PATCH 10/16] merge --- .../{model_delete_form.html => model_confirm_delete.html} | 0 djadmin2/views.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename djadmin2/templates/admin2/bootstrap/{model_delete_form.html => model_confirm_delete.html} (100%) diff --git a/djadmin2/templates/admin2/bootstrap/model_delete_form.html b/djadmin2/templates/admin2/bootstrap/model_confirm_delete.html similarity index 100% rename from djadmin2/templates/admin2/bootstrap/model_delete_form.html rename to djadmin2/templates/admin2/bootstrap/model_confirm_delete.html diff --git a/djadmin2/views.py b/djadmin2/views.py index 00e261a..a8db5d5 100644 --- a/djadmin2/views.py +++ b/djadmin2/views.py @@ -129,5 +129,5 @@ class ModelAddFormView(AdminModel2Mixin, generic.CreateView): class ModelDeleteView(AdminModel2Mixin, generic.DeleteView): success_url = "../../" - default_template_name = "model_delete_form.html" + default_template_name = "model_confirm_delete.html" permission_type = 'delete' From 28b6a0f72a56906e0a5825001a7b295313742e22 Mon Sep 17 00:00:00 2001 From: Daniel Greenfeld Date: Sun, 19 May 2013 10:09:12 +0200 Subject: [PATCH 11/16] pep8 of code --- djadmin2/__init__.py | 2 +- djadmin2/core.py | 2 +- djadmin2/models.py | 10 +++------- djadmin2/tests/test_core.py | 4 +++- djadmin2/views.py | 9 +++------ example/blog/admin2.py | 2 +- example/blog/models.py | 2 +- 7 files changed, 13 insertions(+), 18 deletions(-) diff --git a/djadmin2/__init__.py b/djadmin2/__init__.py index 5b32917..81c83de 100644 --- a/djadmin2/__init__.py +++ b/djadmin2/__init__.py @@ -7,7 +7,7 @@ VERSION = __version__ # synonym # Default datetime input and output formats ISO_8601 = 'iso-8601' -from . import core +from . import core default = core.Admin2() diff --git a/djadmin2/core.py b/djadmin2/core.py index a8477b5..e9a7e49 100644 --- a/djadmin2/core.py +++ b/djadmin2/core.py @@ -73,7 +73,7 @@ class Admin2(object): ) for model, modeladmin in self.registry.iteritems(): app_label = model._meta.app_label - model_name = model._meta.object_name.lower() + model_name = model._meta.object_name.lower() urlpatterns += patterns('', url('^{}/{}/'.format(app_label, model_name), diff --git a/djadmin2/models.py b/djadmin2/models.py index 7f73dca..9d25c6a 100644 --- a/djadmin2/models.py +++ b/djadmin2/models.py @@ -4,10 +4,10 @@ For wont of a better name, this module is called 'models'. It's role is synonymous with the django.contrib.admin.sites model. """ -import functools + from django.core.urlresolvers import reverse -from django.conf.urls import patterns, include, url +from django.conf.urls import patterns, url from django.contrib.auth import models as auth_app from django.db.models import get_models, signals @@ -19,7 +19,6 @@ except ImportError: from django import forms - class BaseAdmin2(object): search_fields = [] @@ -49,13 +48,11 @@ class BaseAdmin2(object): readonly_fields = () ordering = None - def __init__(self, model): super(BaseAdmin2, self).__init__() self.model = model - def _user_has_permission(self, user, permission_type, obj=None): """ Generic method for checking whether the user has permission of specified type for the model. Type can be one of view, add, change, delete. @@ -166,7 +163,7 @@ class ModelAdmin2(BaseAdmin2): name=self.get_prefixed_view_name('index') ), url( - regex=r'^create/$', + regex=r'^create/$', view=self.create_view.as_view(**self.get_create_kwargs()), name=self.get_prefixed_view_name('create') ), @@ -193,7 +190,6 @@ class ModelAdmin2(BaseAdmin2): return self.get_urls(), None, None - def create_permissions(app, created_models, verbosity, **kwargs): """ Creates 'view' permissions for all models. diff --git a/djadmin2/tests/test_core.py b/djadmin2/tests/test_core.py index be17459..bd9397b 100644 --- a/djadmin2/tests/test_core.py +++ b/djadmin2/tests/test_core.py @@ -6,9 +6,11 @@ from django.core.exceptions import ImproperlyConfigured from ..models import ModelAdmin2 from ..core import Admin2 + class Thing(models.Model): pass + class Admin2Test(unittest.TestCase): def setUp(self): self.admin2 = Admin2() @@ -28,7 +30,7 @@ class Admin2Test(unittest.TestCase): def test_deregister_error(self): self.assertRaises(ImproperlyConfigured, self.admin2.deregister, Thing) - + def test_get_urls(self): self.admin2.register(Thing) self.assertEquals(2, len(self.admin2.get_urls())) diff --git a/djadmin2/views.py b/djadmin2/views.py index a8db5d5..6d95db6 100644 --- a/djadmin2/views.py +++ b/djadmin2/views.py @@ -6,10 +6,8 @@ from django.contrib.auth.views import redirect_to_login from django.core.exceptions import PermissionDenied from django.forms.models import modelform_factory from django.views import generic -from django.db import models - -from braces.views import LoginRequiredMixin, StaffuserRequiredMixin, AccessMixin +from braces.views import AccessMixin ADMIN2_THEME_DIRECTORY = getattr(settings, "ADMIN2_THEME_DIRECTORY", "admin2/bootstrap") @@ -18,7 +16,7 @@ class Admin2Mixin(object): modeladmin = None model_name = None app_label = None - + def get_template_names(self): return [os.path.join(ADMIN2_THEME_DIRECTORY, self.default_template_name)] @@ -34,7 +32,6 @@ class Admin2Mixin(object): return modelform_factory(self.get_model()) - class AdminModel2Mixin(Admin2Mixin, AccessMixin): modeladmin = None # Permission type to check for when a request is sent to this view. @@ -98,7 +95,7 @@ class ModelListView(Admin2Mixin, generic.ListView): context['model'] = self.get_model()._meta.verbose_name context['model_pluralized'] = self.get_model()._meta.verbose_name_plural return context - + def get_success_url(self): view_name = 'admin2:{}_{}_detail'.format(self.app_label, self.model_name) return reverse(view_name, kwargs={'pk': self.object.pk}) diff --git a/example/blog/admin2.py b/example/blog/admin2.py index 2c5c38f..e2cbad3 100644 --- a/example/blog/admin2.py +++ b/example/blog/admin2.py @@ -17,4 +17,4 @@ class UserAdmin2(ModelAdmin2): # Register each model with the admin djadmin2.default.register(Post) djadmin2.default.register(Comment) -djadmin2.default.register(User, UserAdmin2) \ No newline at end of file +djadmin2.default.register(User, UserAdmin2) diff --git a/example/blog/models.py b/example/blog/models.py index cc845d8..cf41af3 100644 --- a/example/blog/models.py +++ b/example/blog/models.py @@ -14,4 +14,4 @@ class Comment(models.Model): body = models.TextField() def __unicode__(self): - return self.body \ No newline at end of file + return self.body From aecb2ba8794cd4d044e22e3c310face69ef483a9 Mon Sep 17 00:00:00 2001 From: Daniel Greenfeld Date: Sun, 19 May 2013 10:15:50 +0200 Subject: [PATCH 12/16] Docs and mixins cleanup --- djadmin2/mixins.py | 0 docs/index.rst | 13 ++++++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) delete mode 100644 djadmin2/mixins.py diff --git a/djadmin2/mixins.py b/djadmin2/mixins.py deleted file mode 100644 index e69de29..0000000 diff --git a/docs/index.rst b/docs/index.rst index f9b5cd7..2908670 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,10 +1,21 @@ Welcome to django-admin2's documentation! ========================================= -**django-admin2** aims to replace django's builtin admin that lives in +**django-admin2** aims to replace django's built-in admin that lives in ``django.contrib.admin``. Come and help us, have a look at the :doc:`contributing` page and see our `GitHub`_ page. +This project is intentionally backwards-incompatible with ``django.contrib.admin``. + +Features +========== + +* Easy-to-extend API that follows similar patterns to ``django.contrib.admin``. +* Built-in RESTFUL API powered by ``django-rest-framework`` +* Default theme built on Twitter Bootstrap +* Easy to implement theme system. + + Basic API ============== From 368899b8532f492d030470534e702a241cf83ba1 Mon Sep 17 00:00:00 2001 From: Daniel Greenfeld Date: Sun, 19 May 2013 10:17:45 +0200 Subject: [PATCH 13/16] adding requirements to documentation --- README.rst | 8 ++++++++ docs/index.rst | 10 ++++++++++ 2 files changed, 18 insertions(+) diff --git a/README.rst b/README.rst index 3c9c4cb..742666c 100644 --- a/README.rst +++ b/README.rst @@ -15,6 +15,14 @@ Contributing Yes please! Please read our formal contributing document at: https://github.com/pydanny/django-admin2/blob/master/docs/contributing.rst +Requirements +============= + +* Django 1.5+ +* Python 2.7+ (Python 3.3+ support is pending) +* django-braces +* django-rest-framework +* Sphinx (for documentation) Basic Pattern ============== diff --git a/docs/index.rst b/docs/index.rst index 2908670..503c556 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -15,6 +15,15 @@ Features * Default theme built on Twitter Bootstrap * Easy to implement theme system. +Requirements +============= + +* Django 1.5+ +* Python 2.7+ (Python 3.3+ support is pending) +* django-braces +* django-rest-framework +* Sphinx (for documentation) + Basic API ============== @@ -55,6 +64,7 @@ Content design meta + Indices and tables ================== From 403733e1d417ae2663ccb37fc2b6e9f422477b2f Mon Sep 17 00:00:00 2001 From: Daniel Greenfeld Date: Sun, 19 May 2013 10:20:09 +0200 Subject: [PATCH 14/16] merge in @inglesp --- djadmin2/models.py | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/djadmin2/models.py b/djadmin2/models.py index 9d25c6a..60787d5 100644 --- a/djadmin2/models.py +++ b/djadmin2/models.py @@ -189,8 +189,7 @@ class ModelAdmin2(BaseAdmin2): # We set the application and instance namespace here return self.get_urls(), None, None - -def create_permissions(app, created_models, verbosity, **kwargs): +def create_extra_permissions(app, created_models, verbosity, **kwargs): """ Creates 'view' permissions for all models. django.contrib.auth only creates add, change and delete permissions. Since we also support read-only views, we need @@ -199,9 +198,6 @@ def create_permissions(app, created_models, verbosity, **kwargs): """ from django.contrib.contenttypes.models import ContentType - def _get_permission_codename(action, opts): - return u'%s_%s' % (action, opts.object_name.lower()) - app_models = get_models(app) # This will hold the permissions we're looking for as @@ -214,10 +210,10 @@ def create_permissions(app, created_models, verbosity, **kwargs): ctypes.add(ctype) opts = klass._meta - perm = (_get_permission_codename('view', opts), u'Can view %s' % opts.verbose_name_raw) + perm = ('view_%s' % opts.object_name.lower(), u'Can view %s' % opts.verbose_name_raw) searched_perms.append((ctype, perm)) - # Find all the Permissions that have a context_type for a model we're + # Find all the Permissions that have a content_type for a model we're # looking for. We don't need to check for codenames since we already have # a list of the ones we're going to create. all_perms = set(auth_app.Permission.objects.filter( @@ -226,16 +222,16 @@ def create_permissions(app, created_models, verbosity, **kwargs): "content_type", "codename" )) - objs = [ + perms = [ auth_app.Permission(codename=codename, name=name, content_type=ctype) for ctype, (codename, name) in searched_perms if (ctype.pk, codename) not in all_perms ] - auth_app.Permission.objects.bulk_create(objs) + auth_app.Permission.objects.bulk_create(perms) if verbosity >= 2: - for obj in objs: - print "Adding permission '%s'" % obj + for perm in perms: + print "Adding permission '%s'" % perm -signals.post_syncdb.connect(create_permissions, - dispatch_uid = "django-admin2.djadmin2.models.create_permissions") +signals.post_syncdb.connect(create_extra_permissions, + dispatch_uid = "django-admin2.djadmin2.models.create_extra_permissions") From f3da1352f9a596da12495d59adc593245b8a20c8 Mon Sep 17 00:00:00 2001 From: Daniel Greenfeld Date: Sun, 19 May 2013 10:23:38 +0200 Subject: [PATCH 15/16] fix #80 --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 27d50bf..de5b777 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: python python: - - "2.6" - "2.7" before_install: - export PIP_USE_MIRRORS=true From 950b68bd75fd9e836be0473806fbcc0a43fa5e0a Mon Sep 17 00:00:00 2001 From: Daniel Greenfeld Date: Sun, 19 May 2013 10:28:37 +0200 Subject: [PATCH 16/16] version bump --- djadmin2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/djadmin2/__init__.py b/djadmin2/__init__.py index 81c83de..c6030e9 100644 --- a/djadmin2/__init__.py +++ b/djadmin2/__init__.py @@ -1,4 +1,4 @@ -__version__ = '0.1.1' +__version__ = '0.2.0' __author__ = 'Daniel Greenfeld'