New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Make it easy to add new views to a ModelAdmin2 subclass #142
Comments
As discussed with @pydanny during the djangocon.eu sprints, we said that it is essential to have a very easy to use way to add additional views to a My initial approach on that was to have a # in djadmin2/models.py
class ModelAdmin2(object):
...
views = [
(r'^$', ModelListView, 'index'),
(r'^add/$', ModelAddFormView, 'create'),
(r'^(?P<pk>\d+)$', ModelEditFormView, 'update'),
...
]
...
# in user code
class PostAdmin(ModelAdmin2):
views = ModelAdmin2.views[:] + [
(r'^(?P<pk>\d+)/preview/$', PreviewBlogPostView, 'preview'),
] However I don't like that too much.
So my second thought was to use more idiologies that we already have in django. I see a model as a container for db fields including some methods to work on those fields. The same is true for forms and form fields. If we see the # in djadmin2/models.py
class ModelAdmin2Metaclass(type):
# scan the ModelAdmin2 class definition of uses of `AdminView` instances,
# like models do with modelfields, and put them in the `views` attribute.
...
class ModelAdmin2(object):
__metaclass__ = ModelAdmin2Metaclass
index = AdminView(r'^$', ModelListView)
create = AdminView(r'^add/$', ModelAddFormView)
update = AdminView(r'^(?P<pk>\d+)$', ModelEditFormView)
# in user code
class PostAdmin(ModelAdmin2):
index = AdminView(r'^$', BlogPostDashboard)
preview_post = AdminView(r'^$', PreviewBlogPostView, name='preview') The semantics are:
I'm keen to here your input on this. |
I do like the metaclass approach and the API you've proposed, it's rather elegant. But I should add that you can already swap out any of the existing views, as all the view classes are stored as attributes on the ModelAdmin2. |
Just for the sake of complexity I'd like to also mention the naive approach. # replacing a view
class ModifiedModelAdmin2(ModelAdmin2):
delete_view = CustomDeleteView
# adding a view
class CustomModelAdmin2(ModelAdmin2):
extra_view = ExtraView
def get_urls(self):
patterns = super(CustomModelAdmin2, self).get_urls()
return patterns + patterns('',
url(
regex=r'^extra_url$',
view=self.extra_view.as_view(),
name=self.get_prefixed_view_name('extra_view')
),
) The problem I see with this approach is that it will require more boilerplate On the other hand it is a really simple approach, doesn't require any new What do you think are the strongest reasons for going with something more The meta class approach looks really clean but it also means that there are |
@RaphaelKimmig agreed that there's more to understand, but I think it bears enough similarities with how forms and models work to feel familiar. I think it's worth exploring further, even if it turns out to be a dead-end. @gregmuellegger we currently pass in different kwargs to as_view() on a per-view basis, can you illustrate how you see this working? |
Answering my own question, it could work like this: class PostAdmin(ModelAdmin2):
index = AdminView(r'^$', BlogPostDashboard)
preview_post = AdminView(r'^$', PreviewBlogPostView, name='preview')
def get_index_kwargs(self):
return {}
def get_preview_post_kwargs(self):
return {} ie, when calling |
@AndrewIngram I agree. Using the meta class approach (if you do not want to do anything beyond simply registering another view) would be really straight forward. I was thinking more about the additional complexity hidden beneath the surface. A question that comes to my mind is: In what order will urls be registered? That is: given a list of url patterns it is easy to see which takes precedence. How would we build the list of patterns here? By simply taking the views in the order they are defined in, with the ones defined on a subclass prepended to the ones collected from the base classes? |
objections against the naive approachFirst, the naive approach as I used a dozen times with django.contrib.admin (@RaphaelKimmig thanks for illustrating that one) has its problems mainly in overwriting the It always ends up beeing around 10-15 lines of code just for adding one view to one modeladmin. How to pass in extra arguments into the view?I would have the class AdminView(object):
def get_view_kwargs(self):
return {
'model': self.model_admin.model,
}
class AdminIndexView(AdminView):
def get_view_kwargs(self):
kwargs = super(AdminIndexView)
kwargs.update({
'list_per_page': self.list_per_page,
})
class PostAdmin(ModelAdmin2):
index = AdminIndexView(r'^$', BlogPostDashboard)
preview_post = AdminView(r'^$', PreviewBlogPostView, name='preview') The problem with that is that it's not straightforward enough to just add one more parameter to the view. This would force you into subclassing class AdminView(object):
def get_view_kwargs(self):
kwargs = {
'model': self.model_admin.model,
}
get_extra_kwargs = getattr(self.model_admin, 'get_%s_kwargs' % self.name, None)
if get_extra_kwargs:
kwargs = get_extra_kwargs(kwargs)
return kwargs
class AdminIndexView(object):
...
class PostAdmin(ModelAdmin2):
index = AdminIndexView(r'^$', BlogPostDashboard)
preview_post = AdminView(r'^$', PreviewBlogPostView, name='preview')
def get_preview_post_kwargs(self, kwargs):
kwargs.update({
'mode_info': self.some_attribute,
})
return kwargs That way you have the possibility of easily overriding kwargs for specific views, but we don't have keeping the
|
@gregmuellegger Yes, having to write ~10 lines just for adding one view is not short. On the other hand overriding get_urls is only 2 or 3 lines more than registering a view in the urls.py. Also the ordering will be even more confusing given the meta class approach because here you don't ever touch the list of urls explicitly. Anyway. I do actually like the meta class approach, my questions and remarks are merely meant as constructive input towards finding a simple and beautiful solution. Regarding the view kwargsIf I understand @pydanny correctly in #99 the views will have access to an immutable version of the model_admin. Given that: Why do we need things like |
@RaphaelKimmig urls will be registered in the order that the views are defined in I see that the order of urls is quite important. But overriding an existing view (with the same url regex), will also remove it from the urlpatterns, so its very unlikely that clashing URL-regexes will show up. |
I like following Django's ClassThing.Meta approach proposed and endorsed by @gregmuellegger and @AndrewIngram. By sticking to the Django feel of things, it means it's easier for developers to pick up and use the tool. On the other hand, I will say that I do appreciate the simplicity of the code behind the naive approach. Alright then, a few things before we start digging too deeply into code:
|
@gregmuellegger I am just looking into #27 (providing a way to define menus). Do you think that class PostAdmin(ModelAdmin2):
analytics = AdminView(r'^$', AnalyticsView, name='analytics', menu_title=_('Analytics')) |
@RaphaelKimmig yes I think that's a good approach. It makes sense to give the view a name that can be reused in the frontend. |
I will do this issue :) |
Good to hear! I wasn't able to work on this due to private stuff occupying me. |
@gregmuellegger and @pydanny I have implemented the AdminView in https://github.com/andrewsmedina/django-admin2/compare/admin_view and I have a question. To implement the
How can I do it? I thinking in one approach: define it on
But I'm not finding it good enough. Any suggestions? |
This is one of those tricky UX questions. We want to get it right, so I'm --Danny On Sun, Jun 23, 2013 at 8:45 PM, Andrews Medina notifications@github.comwrote:
'Knowledge is Power' |
@andrewsmedina completed this in #218. |
As discussed with @pydanny during the djangocon.eu sprints, we said that it is
essential to have a very easy to use way to add additional views to a
ModelAdmin
and to swap out existing ones.The text was updated successfully, but these errors were encountered: