Issue #15 resolved

more flexible AppListDashboardModule

patrickk
created an issue

I´ve been testing admin-tools for the last 2 days and so far it´s been a quite nice experience. however, there´s one thing I´m really missing. it´s the possibility to a) change the order of models within an app and b) to show models within a different apps (not necessarily the app they belong to - codewise).

example: I´m having an app "django.contrib.auth" with the models "user" and "group". and I´m having an app "user" with the model "userprofile". now, I´d like to have a headline "User Management" with the models "user", "userprofile" and "group" (in this order).

that´s currently not possible, right? I´m not sure what´s the best way to achive this (and I´m also not sure if this is something you´d like to add), but I can think of adding a callable to the include_list.

cheers, patrick

Comments (21)

  1. David Jean Louis repo owner

    Hi Patrick,

    this is definitively possible ;) just use the ModelListDashboardModule: http://packages.python.org/django-admin-tools/dashboard.html#the-modellistdashboardmodule-class

    (sorry, for some reason I forgot to include this page in the docs, but now it's there)

    So, you'd just add to your custom dashboard:

            self.children.append(ModelListDashboardModule(
                title=_('User Management'),
                include_list=(
                    'django.contrib.auth.models.User',
                    'user.models.UserProfile',
                    'django.contrib.auth.models.Group',
                ),
            ))
    

    Note: don't rely too much on the custom dashboard functionality, in version 0.2.0 it will be totally revamped, users will be able to manage dashboard/dashboard modules via the admin interface (see #14).

  2. David Jean Louis repo owner

    What you could do is creating a custom module:

    class SortedModelListDashboardModule(ModelListDashboardModule):
        def init_with_context(self, context):
            ModelListDashboardModule.init_with_context(self, context)
            # sort self.children here
            # for model in self.children:
            #    if model['title'] == 'User':
            #        # etc...
    

    This is a dirty hack but it will work...

  3. patrickk reporter

    ok. thanks for the answers. I don´t like the "hack". guess I´ll wait for the next version although I´m not sure that managing the dashboard via the admin interface is a good idea (just my 2 cents). we´ve been thinking about this functionality for a while (with django-grappelli) and what I personally like about admin-tools is that it´s very pythonic.

    besides that, adding callables to the include_list would probably improve flexibility.

  4. David Jean Louis repo owner
    • changed status to open

    I understand your argument, I've tried to keep django-admin-tools as unobstrusive as possible, but I have the feeling that it will need to access the database and have some "views" at some point.

    To explain that feeling for example, currently if you clear cookies or just use a different browser/os you loose all your bookmarks and dashboard layout. Also, there's no simple way to assign different dashboard to different users (that's a requirement for my personal projects). All this could be done requiring users to syncdb to allow admin-tools to put some tables.

    FYI I've setup a mailing list: http://groups.google.com/group/django-admin-tools/

    Feel free to join to share ideas, you are welcome.

    Greetings,

    -- David

  5. patrickk reporter

    we might be talking about different things (but maybe not).

    first off, storing user-related stuff in the database is definitely better than using cookies from my point of view. but what about defining the different dashboard modules - is that going to change? because that´s not necessarily related to the way you store bookmarks or dashboard layouts, right? as mentioned before, I do like the way the modules are defined. it´s very flexible and other 3rd-party apps (like satchmo or grappelli) could add custom modules.

  6. David Jean Louis repo owner

    Sorry for the confusion, I was just speaking more generally of the future of the project. Storing preferences and defining modules are completely unrelated, I was just speculating on the fact that django-admin-tools will have to access the database at some point.

  7. Klemens Mantzos

    to order the models in the soucecode I wrote following NOT hack (see at bottom). the SortedModelListDashboardModule has no exclude_list and include_list, just a list. the list will be rendered in the same order its written in the soucecode.

    hope you like it. would like to see this functionality in admin_tools.

    (will mail you about using grappelli as admin_tools theme on the mailinglist soon)

    # someproject/admin_tools/dashboard/models.py
    class SortedModelListDashboardModule(DashboardModule, AppListElementMixin):
        
        def __init__(self, **kwargs):
            super(SortedModelListDashboardModule, self).__init__(**kwargs)
            self.title = kwargs.get('title', '')
            self.template = kwargs.get('template',
                                       'dashboard/modules/model_list.html')
            # keep include_list and exclude_list to stay compatible
            self.include_list = kwargs.get('list', [])
            self.exclude_list = []
        
        def init_with_context(self, context):
            request = context['request']
            
            # loop throu list aka. include_list (this way we keep all in order)
            for model_name in self.include_list:
                # get Model and ModelAdmin
                try:
                    mod, inst = model_name.rsplit('.', 1)
                    mod = import_module(mod)
                except:
                    from django.core.exceptions import ImproperlyConfigured
                    raise ImproperlyConfigured(('SortedModelListDashboardModule %s seems to be ImproperlyConfigured. Can not import module: %s' % (self.title, mod)))
                
                model = getattr(mod, inst)
                model_admin = admin.site._registry.get(model)
                
                # same as ModelListDashboardModule.init_with_context() except sorting
                perms = self._check_perms(request, model, model_admin)
                if not perms:
                    continue
                model_dict = {}
                model_dict['title'] = capfirst(model._meta.verbose_name_plural)
                if perms['change']:
                    model_dict['change_url'] = self._get_admin_change_url(model)
                if perms['add']:
                    model_dict['add_url'] = self._get_admin_add_url(model)
                self.children.append(model_dict)
    
    # snippet from someproject/dashboard.py
    self.children.append(SortedModelListDashboardModule(
        title=_('User Management'),
        list=(
            'django.contrib.auth.models.User',
            'django.contrib.auth.models.Group',
            'grtest.models.SomeModel',
        ),
    ))
    
  8. David Jean Louis repo owner

    Hey, that looks good !

    I think I'm gonna refactor the AppListDashboardModule and ModelListDashboardModule API to include your changes, the project is still alpha and I prefer to break BC than to keep a bad design.

    So, that would mean, I would just remove the include/exclude_list in both models and keep the following API:

    self.children.append(ModelListDashboardModule(
        title=_('User Management'),
        models=(
            'django.contrib.auth.models.User',
            'django.contrib.auth.models.Group',
            'some_project.models.User',
        ),
    ))
    
    self.children.append(AppListDashboardModule(
        title=_('Django administration'),
        apps=(
            ('django.contrib.auth', ('User', 'Group',)), 
            ('django.contrib.sites', ('Site',)),
        )
    ))
    

    I think that's a cleaner API... what do you think ?

    Thanks !

    -- David

  9. patrickk reporter

    since I´ve been working on that with klemens ... +1 for your proposal (as long as apps/modules are properly sorted).

    btw ... there are more changes to come. one is a DashboardGroup for combining different DashboardModules (either showing the modules one below each other or using tabs).

    Klemens: if you´ve any objections, please reply.

    regards, patrick

  10. David Jean Louis repo owner

    Hi Patrick, well that's good news, grouping modules into tabs is definitevely a good idea, maybe the choices could be:

    • tabs (by default the first tab is selected)
    • accordion (by default the first accordion item is expanded)

    that's how you planned it ?

    -- David

  11. patrickk reporter

    exactly. with a third option: just one module after another (without using an accordion), probably enabled by default. but that´s only a minor detail ... I´m personally not a huge fan of either tabs and accordions (but of course, they´re useful sometimes).

    patrick

  12. David Jean Louis repo owner

    Excellent ! so maybe something like that would be good:

    self.children.append(DashboardModuleGroup(
        modules=(applistmodule, feedmodule,), # these are instances
        display='tabs', # or 'accordion' or 'stacked' (default if arg not provided)
    ))
    
  13. patrickk reporter

    sounds good to me.

    I´d use "DashboardGroup" instead of "DashboardModuleGroup". but well ... that´s not really the point. nice thing is (with either of the two names) that we reflect the already given classes (group, module) available with grappelli - so it should be easy to integrate it.

  14. David Jean Louis repo owner

    I tend to prefer DashboardModuleGroup, in english it reads "dashboard module group", a group of dashboard modules, when I read DashboardGroup it sounds "a group of dashboards".

    I'm interested in having a look at the new grappelli, do you have a demo site that uses it ? I have the feeling that our two projects combined could really make a killer django admin...

    -- David

  15. Klemens Mantzos

    hi david,

    just working on an example implementation on DashboardModuleGroup and have a problem...

    everything seems to be called and executed as it shoud, BUT for some reason the module_group.html (the default templates of DashboardModuleGroup) won't be rendered out into the response. i tried to debug it and figured that i can't even tell how the templatetags (in my case admin_tools_render_dashboard_module) renders the template. or better asked: where are these templates actually rended? the templatetags update the context and return them...on the other hand i get a error when i set "somenotexistingtemplate.html" as default temlete. somewhere i'm missing something. maybe it has something to do with the tag_func() decoration?

    a short hint on how you render templates please.

    maybe you can already give feedback or even know my fault when looking at this part of the code:

    class DashboardModuleGroup(DashboardModule, AppListElementMixin):
        
        def __init__(self, **kwargs):
            super(DashboardModuleGroup, self).__init__(**kwargs)
            self.title = kwargs.get('title', '')
            self.template = kwargs.get('template',
                                       'dashboard/modules/module_group.html')
            self.modules = kwargs.get('modules', [])
            self.display = kwargs.get('display', 'tabs')
            # keep include_list and exclude_list to stay compatible
            self.include_list = []
            self.exclude_list = []
            
        def init_with_context(self, context):
            for module in self.modules:
                print "asdf module: ", module
                module.init_with_context(context)
    
  16. David Jean Louis repo owner

    Hi fetzig,

    the templatetags use a trick of a "dummy.html" template, that just extend the module template, this makes dynamic definition of templates (module.template) possible.

    Now, I think your module is not rendered just because it is empty, see: the module.html template and the DashboardModule.is_empty() method.

    Cheers,

    -- David

  17. Mikhail Korobov

    So, that would mean, I would just remove the include/exclude_list in both models and keep the following API: ...

    The API looks good. But some kind of glob support ("myapp.*" instead of 'myapp.models.Model1', 'myapp.models.Model2') would be great. It is very convenient to be able to quickly add all models from same app. What do you think?

  18. Log in to comment