fixes #155, which is moving models.py to types.py. Using admins.py as an alias for the old models.py module

This commit is contained in:
Daniel Greenfeld 2013-05-30 12:33:00 +02:00
parent 096267c674
commit 9acdf9e2f8
14 changed files with 322 additions and 343 deletions

View file

@ -51,13 +51,14 @@ 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 djadmin2
from djadmin2.models import ModelAdmin2
from djadmin2.admins import ModelAdmin2
class UserAdmin2(ModelAdmin2):

8
djadmin2/admins.py Normal file
View file

@ -0,0 +1,8 @@
"""
This serves as an easy to remember alias for types.
"""
from . import types
ModelAdmin2 = types.ModelAdmin2
Admin2Inline = types.Admin2Inline

View file

@ -9,7 +9,7 @@ from django.utils.importlib import import_module
from . import apiviews
from . import models
from . import types
from . import utils
from . import views
@ -46,7 +46,7 @@ class Admin2(object):
if model in self.registry:
raise ImproperlyConfigured('%s is already registered in django-admin2' % model)
if not model_admin:
model_admin = models.ModelAdmin2
model_admin = types.ModelAdmin2
self.registry[model] = model_admin(model, admin=self, **kwargs)
# Add the model to the apps registry

View file

@ -1,330 +1 @@
"""
WARNING: This file about to undergo major refactoring by @pydanny per Issue #99.
For wont of a better name, this module is called 'models'. It's role is
synonymous with the django.contrib.admin.sites model.
"""
import logging
from django.core.urlresolvers import reverse
from django.conf.urls import patterns, url
from django.contrib.auth import models as auth_app
from django.db.models import get_models, signals
import extra_views
from djadmin2 import apiviews
from djadmin2 import constants
from djadmin2 import views
from djadmin2 import actions
from djadmin2 import utils
from djadmin2.forms import modelform_factory
logger = logging.getLogger(__name__)
class BaseAdmin2(object):
"""
Warning: This class will likely merged with ModelAdmin2
"""
search_fields = []
# Show the fields to be displayed as columns
# TODO: Confirm that this is what the Django admin uses
list_fields = []
#This shows up on the DocumentListView of the Posts
list_actions = []
# This shows up in the DocumentDetailView of the Posts.
document_actions = []
# shows up on a particular field
field_actions = {}
fields = None
exclude = None
fieldsets = None
form_class = None
filter_vertical = ()
filter_horizontal = ()
radio_fields = {}
prepopulated_fields = {}
formfield_overrides = {}
readonly_fields = ()
ordering = None
def __init__(self, name, model, admin):
super(BaseAdmin2, self).__init__()
self.name = name
self.model = model
self.admin = admin
class ModelAdmin2(BaseAdmin2):
"""
Warning: This class is targeted for reduction.
It's bloated and ugly.
"""
list_display = ('__str__',)
list_display_links = ()
list_filter = ()
list_select_related = False
list_per_page = 100
list_max_show_all = 200
list_editable = ()
search_fields = ()
save_as = False
save_on_top = False
verbose_name = None
verbose_name_plural = None
model_admin_attributes = constants.MODEL_ADMIN_ATTRS
create_form_class = None
update_form_class = None
inlines = []
# Views
index_view = views.ModelListView
create_view = views.ModelAddFormView
update_view = views.ModelEditFormView
detail_view = views.ModelDetailView
delete_view = views.ModelDeleteView
# API configuration
api_serializer_class = None
# API Views
api_list_view = apiviews.ListCreateAPIView
api_detail_view = apiviews.RetrieveUpdateDestroyAPIView
# Actions
actions = [actions.delete_selected]
def __init__(self, model, admin, name=None, **kwargs):
self.name = name
self.model = model
self.admin = admin
model_options = utils.model_options(model)
self.app_label = model_options.app_label
self.model_name = model_options.object_name.lower()
if self.name is None:
self.name = '{}_{}'.format(self.app_label, self.model_name)
if self.verbose_name is None:
self.verbose_name = model_options.verbose_name
if self.verbose_name_plural is None:
self.verbose_name_plural = model_options.verbose_name_plural
def get_default_view_kwargs(self):
return {
'app_label': self.app_label,
'model': self.model,
'model_name': self.model_name,
'model_admin': ImmutableAdmin(self),
}
def get_default_api_view_kwargs(self):
kwargs = self.get_default_view_kwargs()
kwargs.update({
'serializer_class': self.api_serializer_class,
})
return kwargs
def get_prefixed_view_name(self, view_name):
return '{}_{}'.format(self.name, view_name)
def get_index_kwargs(self):
return self.get_default_view_kwargs()
def get_create_kwargs(self):
kwargs = self.get_default_view_kwargs()
kwargs.update({
'inlines': self.inlines,
'form_class': self.create_form_class if self.create_form_class else self.form_class,
})
return kwargs
def get_update_kwargs(self):
kwargs = self.get_default_view_kwargs()
form_class = self.update_form_class if self.update_form_class else self.form_class
if form_class is None:
form_class = modelform_factory(self.model)
kwargs.update({
'inlines': self.inlines,
'form_class': form_class,
})
return kwargs
def get_detail_kwargs(self):
return self.get_default_view_kwargs()
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_api_list_kwargs(self):
kwargs = self.get_default_api_view_kwargs()
kwargs.update({
'paginate_by': self.list_per_page,
})
return kwargs
def get_api_detail_kwargs(self):
return self.get_default_api_view_kwargs()
def get_urls(self):
return patterns('',
url(
regex=r'^$',
view=self.index_view.as_view(**self.get_index_kwargs()),
name=self.get_prefixed_view_name('index')
),
url(
regex=r'^create/$',
view=self.create_view.as_view(**self.get_create_kwargs()),
name=self.get_prefixed_view_name('create')
),
url(
regex=r'^(?P<pk>[0-9]+)/$',
view=self.detail_view.as_view(**self.get_detail_kwargs()),
name=self.get_prefixed_view_name('detail')
),
url(
regex=r'^(?P<pk>[0-9]+)/update/$',
view=self.update_view.as_view(**self.get_update_kwargs()),
name=self.get_prefixed_view_name('update')
),
url(
regex=r'^(?P<pk>[0-9]+)/delete/$',
view=self.delete_view.as_view(**self.get_delete_kwargs()),
name=self.get_prefixed_view_name('delete')
),
)
def get_api_urls(self):
return patterns('',
url(
regex=r'^$',
view=self.api_list_view.as_view(**self.get_api_list_kwargs()),
name=self.get_prefixed_view_name('api-list'),
),
url(
regex=r'^(?P<pk>[0-9]+)/$',
view=self.api_detail_view.as_view(**self.get_api_detail_kwargs()),
name=self.get_prefixed_view_name('api-detail'),
),
)
@property
def urls(self):
# We set the application and instance namespace here
return self.get_urls(), None, None
@property
def api_urls(self):
return self.get_api_urls(), None, None
def get_actions(self):
actions_dict = {}
for cls in type(self).mro()[::-1]:
class_actions = getattr(cls, 'actions', [])
for action in class_actions:
actions_dict[action.__name__] = {
'name': action.__name__,
'description': actions.get_description(action),
'func': action
}
return actions_dict
class Admin2Inline(extra_views.InlineFormSet):
"""
A simple extension of django-extra-view's InlineFormSet that
adds some useful functionality.
"""
def construct_formset(self):
"""
Overrides construct_formset to attach the model class as
an attribute of the returned formset instance.
"""
formset = super(Admin2Inline, self).construct_formset()
formset.model = self.inline_model
return formset
class ImmutableAdmin(object):
"""
Only __init__ allows setting of attributes
"""
def __init__(self, model_admin):
""" The __init__ is the only method where the ImmutableModelAdmin allows
for setting of values.
"""
for attr_name in model_admin.model_admin_attributes:
setattr(self, attr_name, getattr(model_admin, attr_name))
self.__delattr__ = self._immutable
self.__setattr__ = self._immutable
def _immutable(self, name, value):
raise TypeError("Can't modify immutable model admin")
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
to add our own extra permission.
Copied from django.contrib.auth.management.create_permissions
"""
from django.contrib.contenttypes.models import ContentType
app_models = get_models(app)
# This will hold the permissions we're looking for as
# (content_type, (codename, name))
searched_perms = list()
# The codenames and ctypes that should exist.
ctypes = set()
for klass in app_models:
ctype = ContentType.objects.get_for_model(klass)
ctypes.add(ctype)
opts = utils.model_options(klass)
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 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(
content_type__in=ctypes,
).values_list(
"content_type", "codename"
))
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(perms)
if verbosity >= 2:
for perm in perms:
logger.debug("Adding permission '%s'" % perm)
signals.post_syncdb.connect(create_extra_permissions,
dispatch_uid="django-admin2.djadmin2.models.create_extra_permissions")
""" Boilerplate for now, will serve a purpose soon! """

View file

@ -2,7 +2,7 @@ from django.db import models
from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
from ..models import ModelAdmin2
from ..admins import ModelAdmin2
from ..core import Admin2

View file

@ -1 +0,0 @@
# This is just a stub file. Write moar tests!

View file

@ -1,4 +1,298 @@
from collections import namedtuple
import logging
from django.core.urlresolvers import reverse
from django.conf.urls import patterns, url
from django.contrib.auth import models as auth_app
from django.db.models import get_models, signals
import extra_views
from . import apiviews
from . import constants
from . import views
from . import actions
from . import utils
from .forms import modelform_factory
logger = logging.getLogger(__name__)
class ModelAdmin2(object):
"""
Warning: This class is targeted for reduction.
It's bloated and ugly.
"""
list_display = ('__str__',)
list_display_links = ()
list_filter = ()
list_select_related = False
list_per_page = 100
list_max_show_all = 200
list_editable = ()
search_fields = ()
save_as = False
save_on_top = False
verbose_name = None
verbose_name_plural = None
model_admin_attributes = constants.MODEL_ADMIN_ATTRS
search_fields = []
# Show the fields to be displayed as columns
# TODO: Confirm that this is what the Django admin uses
list_fields = []
#This shows up on the DocumentListView of the Posts
list_actions = []
# This shows up in the DocumentDetailView of the Posts.
document_actions = []
# shows up on a particular field
field_actions = {}
fields = None
exclude = None
fieldsets = None
form_class = None
filter_vertical = ()
filter_horizontal = ()
radio_fields = {}
prepopulated_fields = {}
formfield_overrides = {}
readonly_fields = ()
ordering = None
create_form_class = None
update_form_class = None
inlines = []
# Views
index_view = views.ModelListView
create_view = views.ModelAddFormView
update_view = views.ModelEditFormView
detail_view = views.ModelDetailView
delete_view = views.ModelDeleteView
# API configuration
api_serializer_class = None
# API Views
api_list_view = apiviews.ListCreateAPIView
api_detail_view = apiviews.RetrieveUpdateDestroyAPIView
# Actions
actions = [actions.delete_selected]
def __init__(self, model, admin, name=None, **kwargs):
self.name = name
self.model = model
self.admin = admin
model_options = utils.model_options(model)
self.app_label = model_options.app_label
self.model_name = model_options.object_name.lower()
if self.name is None:
self.name = '{}_{}'.format(self.app_label, self.model_name)
if self.verbose_name is None:
self.verbose_name = model_options.verbose_name
if self.verbose_name_plural is None:
self.verbose_name_plural = model_options.verbose_name_plural
def get_default_view_kwargs(self):
return {
'app_label': self.app_label,
'model': self.model,
'model_name': self.model_name,
'model_admin': immutable_admin_factory(self),
}
def get_default_api_view_kwargs(self):
kwargs = self.get_default_view_kwargs()
kwargs.update({
'serializer_class': self.api_serializer_class,
})
return kwargs
def get_prefixed_view_name(self, view_name):
return '{}_{}'.format(self.name, view_name)
def get_index_kwargs(self):
return self.get_default_view_kwargs()
def get_create_kwargs(self):
kwargs = self.get_default_view_kwargs()
kwargs.update({
'inlines': self.inlines,
'form_class': self.create_form_class if self.create_form_class else self.form_class,
})
return kwargs
def get_update_kwargs(self):
kwargs = self.get_default_view_kwargs()
form_class = self.update_form_class if self.update_form_class else self.form_class
if form_class is None:
form_class = modelform_factory(self.model)
kwargs.update({
'inlines': self.inlines,
'form_class': form_class,
})
return kwargs
def get_detail_kwargs(self):
return self.get_default_view_kwargs()
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_api_list_kwargs(self):
kwargs = self.get_default_api_view_kwargs()
kwargs.update({
'paginate_by': self.list_per_page,
})
return kwargs
def get_api_detail_kwargs(self):
return self.get_default_api_view_kwargs()
def get_urls(self):
return patterns('',
url(
regex=r'^$',
view=self.index_view.as_view(**self.get_index_kwargs()),
name=self.get_prefixed_view_name('index')
),
url(
regex=r'^create/$',
view=self.create_view.as_view(**self.get_create_kwargs()),
name=self.get_prefixed_view_name('create')
),
url(
regex=r'^(?P<pk>[0-9]+)/$',
view=self.detail_view.as_view(**self.get_detail_kwargs()),
name=self.get_prefixed_view_name('detail')
),
url(
regex=r'^(?P<pk>[0-9]+)/update/$',
view=self.update_view.as_view(**self.get_update_kwargs()),
name=self.get_prefixed_view_name('update')
),
url(
regex=r'^(?P<pk>[0-9]+)/delete/$',
view=self.delete_view.as_view(**self.get_delete_kwargs()),
name=self.get_prefixed_view_name('delete')
),
)
def get_api_urls(self):
return patterns('',
url(
regex=r'^$',
view=self.api_list_view.as_view(**self.get_api_list_kwargs()),
name=self.get_prefixed_view_name('api-list'),
),
url(
regex=r'^(?P<pk>[0-9]+)/$',
view=self.api_detail_view.as_view(**self.get_api_detail_kwargs()),
name=self.get_prefixed_view_name('api-detail'),
),
)
@property
def urls(self):
# We set the application and instance namespace here
return self.get_urls(), None, None
@property
def api_urls(self):
return self.get_api_urls(), None, None
def get_actions(self):
actions_dict = {}
for cls in type(self).mro()[::-1]:
class_actions = getattr(cls, 'actions', [])
for action in class_actions:
actions_dict[action.__name__] = {
'name': action.__name__,
'description': actions.get_description(action),
'func': action
}
return actions_dict
class Admin2Inline(extra_views.InlineFormSet):
"""
A simple extension of django-extra-view's InlineFormSet that
adds some useful functionality.
"""
def construct_formset(self):
"""
Overrides construct_formset to attach the model class as
an attribute of the returned formset instance.
"""
formset = super(Admin2Inline, self).construct_formset()
formset.model = self.inline_model
return formset
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
to add our own extra permission.
Copied from django.contrib.auth.management.create_permissions
# TODO - determine if this is deprecated by the new permissions.py module
"""
# Is there any reason for doing this import here?
from django.contrib.contenttypes.models import ContentType
app_models = get_models(app)
# This will hold the permissions we're looking for as
# (content_type, (codename, name))
searched_perms = list()
# The codenames and ctypes that should exist.
ctypes = set()
for klass in app_models:
ctype = ContentType.objects.get_for_model(klass)
ctypes.add(ctype)
opts = utils.model_options(klass)
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 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(
content_type__in=ctypes,
).values_list(
"content_type", "codename"
))
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(perms)
if verbosity >= 2:
for perm in perms:
logger.debug("Adding permission '%s'" % perm)
signals.post_syncdb.connect(create_extra_permissions,
dispatch_uid="django-admin2.djadmin2.models.create_extra_permissions")
def immutable_admin_factory(model_admin):

View file

@ -24,13 +24,14 @@ 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 djadmin2
from djadmin2.models import ModelAdmin2
from djadmin2.admins import ModelAdmin2
class UserAdmin2(ModelAdmin2):
@ -44,7 +45,7 @@ Our goal is to make this API work:
djadmin2.default.register(User, UserAdmin2)
.. _GitHub: https://github.com/pydanny/django-admin2
.. _GitHub: https://github.com/twoscoops/django-admin2
Content
-------

View file

@ -7,7 +7,7 @@ from rest_framework.relations import PrimaryKeyRelatedField
import djadmin2
from djadmin2.forms import floppify_form
from djadmin2.models import ModelAdmin2, Admin2Inline
from djadmin2.admins import ModelAdmin2, Admin2Inline
from djadmin2.apiviews import Admin2APISerializer

View file

@ -8,7 +8,7 @@ from django.utils import simplejson as json
from djadmin2 import apiviews
from djadmin2 import default
from djadmin2.models import ModelAdmin2
from djadmin2.admins import ModelAdmin2
from ..models import Post

View file

@ -1,8 +1,10 @@
import floppyforms
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.test.client import RequestFactory
import floppyforms
import djadmin2
from blog.admin2 import UserAdmin2

View file

@ -1,5 +1,6 @@
from django.contrib.auth.models import Group, User
from django.core.urlresolvers import reverse
from .test_apiviews import APITestCase

View file

@ -1,6 +1,8 @@
import floppyforms
from django import forms
from django.test import TestCase
import floppyforms
from djadmin2.forms import floppify_widget, floppify_form, modelform_factory
from ..models import Post

View file

@ -4,10 +4,10 @@ from django.template import Template, Context
from django.test import TestCase
from django.test.client import RequestFactory
import djadmin2
from djadmin2.models import ModelAdmin2
from djadmin2.admins import ModelAdmin2
from djadmin2.permissions import TemplatePermissionChecker
from blog.models import Post