On ne va pas se mentir, de ce que nous avons vu de la partie admin ce n'est pas l'extase visuel. Il existe des projets -sur github notamment- qui proposent d'améliorer le design de votre projet Django . Le module que j'utilise le plus souvent est xadmin puisqu'en plus d'être sympatique visuellement (basé sur bootstrap twitter ) il propose quelques plugins très utiles, comme les exports en CSV , XML et JSON mais également des filtres très bien pensés.
Démo de xadmin
Arrêtons les éloges, vous pouvez visiter le site de démonstration pour vous faire votre propre idée: Demo xadmin .
Les identifiants sont admin / admin .
Installer xadmin
Un petit pip pour installer xadmin :
pip install django-xadmin
Le projet est sur Github: xAdmin
Implémenter xadmin
Il vous faudra indiquer la présence de xadmin et crispy_form dans le partie settings:
eboutique/settigns.py
INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'backoffice', 'crispy_forms', 'xadmin', )Puis configurez le fichier urls.py
eboutique/urls.py
from django.conf.urls import patterns, include, url import xadmin xadmin.autodiscover() urlpatterns = patterns('', # Examples: # url(r'^$', 'eboutique.views.home', name='home'), # url(r'^blog/', include('blog.urls')), #url(r'^admin/', include(admin.site.urls)), url(r'^xadmin/', include(xadmin.site.urls)), )
Nous avons défini que le module xadmin est accessible via l'uri /xadmin/
Premières captures
http://localhost:8000/xadmin
Vous pouvez vos connecter à l' interface xadmin en indiquant les identifiants que vous avez renseigné lors de votre syncb .
Responsive design
Format grand écran:
Format tablette:
Format smartphone:
Afficher ses modèles
Dans nos exemples nous utiliserons les modèles suivants:
backoffice/models.py
# coding: utf-8 from django.db import models class Product(models.Model): """ Produit : prix, code, etc. """ class Meta: verbose_name = "Produit" name = models.CharField(max_length=100) code = models.CharField(max_length=10, null=True, blank=True, unique=True) price_ht = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="Prix unitaire HT") price_ttc = models.DecimalField(max_digits=8, decimal_places=2, verbose_name="Prix unitaire TTC") def __unicode__(self): return "{0} [{1}]".format(self.name, self.code) class ProductItem(models.Model): """ Déclinaison de produit déterminée par des attributs comme la couleur, etc. """ class Meta: verbose_name = "Déclinaison Produit" product = models.ForeignKey('Product') code = models.CharField(max_length=10, null=True, blank=True, unique=True) code_ean13 = models.CharField(max_length=13) attributes = models.ManyToManyField("ProductAttributeValue", related_name="product_item", null=True, blank=True) def __unicode__(self): return "{0} [{1}]".format(self.product.name, self.code) class ProductAttribute(models.Model): """ Attributs produit """ class Meta: verbose_name = "Attribut" name = models.CharField(max_length=100) def __unicode__(self): return self.name class ProductAttributeValue(models.Model): """ Valeurs des attributs """ class Meta: verbose_name = "Valeur attribut" ordering = ['position'] value = models.CharField(max_length=100) product_attribute = models.ForeignKey('ProductAttribute', verbose_name="Unité") position = models.PositiveSmallIntegerField("Position", null=True, blank=True) def __unicode__(self): return "{0} [{1}]".format(self.value, self.product_attribute)
Comme pour la partie admin de Django , il est nécessaire d'indiquer à xadmin quel modèle nous voulons afficher dans l'interface. Créons un fichier adminx.py dans notre application:
backoffice/adminx.py
# coding: utf-8 import xadmin from models import Product xadmin.site.register(Product)
Vous pouvez maintenant visualiser votre vue Product :
http://localhost:8000/xadmin
Ajoutons un produit:
http://localhost:8000/xadmin/backoffice/product/add/
Ne remplissons pas les champs obligatoires pour voir la réaction de Django:
http://localhost:8000/xadmin/backoffice/product/add/
Validons notre produit correctement
http://localhost:8000/xadmin/backoffice/product/add/
Exporter les données
xadmin vous apporte des options en plus comme la possibilité d'exporter votre liste de données en CSV, XML ou JSON:
Inlines
Comme nous l'avons vu dans le chapitre admin , nous pouvons mettre à la suite de notre formulaire principal un formulaire pour les clés étrangères à l'aide du paramètre inlines :
backoffice/adminx.py
# coding: utf-8 import xadmin from models import * class ProductItemAdminInline(object): model = ProductItem extra = 1 class ProductAdmin(object): model = Product inlines = [ProductItemAdminInline] xadmin.site.register(Product, ProductAdmin)
Résultat de notre fiche produit:
Par défaut les items se succèdent les uns à la suite des autres, ce qui en terme d'ergonomie peut dans certains cas être génant, par exemple si les données inlines sont nombreuses. Il existe des options pour changer l'affichage:
class ProductItemAdminInline(object): model = ProductItem extra = 3 style = 'tab'
class ProductItemAdminInline(object): model = ProductItem extra = 3 style = 'table'
class ProductItemAdminInline(object): model = ProductItem extra = 3 style = 'accordion'
Créer une page personnalisée
Un backoffice n'est pas composé que de pages d'édition ou de listing de modèles, vous pouvez par exemple créer une page statistiques:
backoffice/adminx.py
from xadmin import views class StatsView(views.CommAdminView): def get(self, request, *args, **kwargs): return self.template_response('xadmin/stats.html', self.get_context()) xadmin.site.register_view(r'stats/$', StatsView, name='stats')
CSS
Amusez-vous à modifier le CSS pour personnaliser votre interface
textarea:focus,input:focus{outline:none!important} :focus{outline:none!important} :active{outline:none!important} .form-control:focus{border-color:#ededed;-webkit-box-shadow:none;box-shadow:none} .form-horizontal .control-label{margin-top:8px} .controls{padding:1px!important;margin:0} .form-horizontal .control-label{padding-top:0!important} .form-horizontal .controls{border-left:none} .form-control{display:block;width:100%;padding:2px;font-size:1em;line-height:1.428571429; color:#555;vertical-align:middle;background-color:#fff;background-image:none; border:1px solid #ededed;border-radius:0;-webkit-box-shadow:none;box-shadow:none; -webkit-transition:border-color ease-in-out 0.15s,box-shadow ease-in-out .15s; transition:border-color ease-in-out 0.15s,box-shadow ease-in-out .15s;margin-top:1px} ul.nav-sitemenu{padding:0} #left-side{padding:1px;margin:0} #left-side a{background-color:#fff;color:#333} #left-side .active a{background-color:#3276b1;color:#333;font-weight:700;color:#fff} body{background-color:#eee} #content-block{padding:0 20px!important} .form-group{background-color:none;border-bottom:0} .panel-body{margin:10px 1px} input[type="file"]{max-width:300px;border:1px solid #efefef} input[type="number"]{max-width:100px;border:1px solid #efefef;text-align:center} input[type="email"]{max-width:300px;border:1px solid #efefef} input[type="text"]{width:100%;border:1px solid #efefef} input{font-weight:700} .text-field{max-width:none;height:30px} .panel .btn{padding:4px} .panel .input-group-addon{padding:2px 5px} .input-group-addon,.input-group-btn{width:0} .textinput{max-width:500px;width:100%} .text-field{width:70%!important} .selectize-input{min-height:15px;line-height:13px} input[data-upper]{text-transform:uppercase} input[type="text"],input[type="number"],input[type="email"],input[type="file"] {padding:0;padding-left:5px;line-height:1em;height:30px} .navbar .navbar-brand{max-width:none} .select2-container{width:99%} .select2-choices{max-width:100%}
Les templates de xadmin
Vous trouverez ci-dessous tous les templates du projet xadmin . Pour écraser un template de base , il vous suffit de le recréer dans votre dossier template de votre application avec le même nom.
tree /usr/local/lib/python2.7/dist-packages/xadmin/templates/xadmin/
├── 404.html ├── 500.html ├── auth │ ├── password_reset │ │ ├── complete.html │ │ ├── confirm.html │ │ ├── done.html │ │ ├── email.html │ │ └── form.html │ └── user │ ├── add_form.html │ └── change_password.html ├── base.html ├── base_site.html ├── blocks │ ├── comm.top.setlang.html │ ├── comm.top.theme.html │ ├── comm.top.topnav.html │ ├── modal_list.left_navbar.quickfilter.html │ ├── model_form.before_fieldsets.wizard.html │ ├── model_form.submit_line.wizard.html │ ├── model_list.nav_form.search_form.html │ ├── model_list.nav_menu.bookmarks.html │ ├── model_list.nav_menu.filters.html │ ├── model_list.results_bottom.actions.html │ ├── model_list.results_top.charts.html │ ├── model_list.results_top.date_hierarchy.html │ ├── model_list.top_toolbar.exports.html │ ├── model_list.top_toolbar.layouts.html │ └── model_list.top_toolbar.refresh.html ├── edit_inline │ ├── accordion.html │ ├── base.html │ ├── blank.html │ ├── one.html │ ├── stacked.html │ ├── tab.html │ └── tabular.html ├── filters │ ├── char.html │ ├── checklist.html │ ├── date.html │ ├── fk_search.html │ ├── list.html │ ├── number.html │ ├── quickfilter.html │ └── rel.html ├── forms │ └── transfer.html ├── grids │ └── thumbnails.html ├── includes │ ├── box.html │ ├── pagination.html │ ├── sitemenu_accordion.html │ ├── sitemenu_default.html │ ├── submit_line.html │ ├── toggle_back.html │ └── toggle_menu.html ├── layout │ ├── fieldset.html │ ├── field_value.html │ ├── field_value_td.html │ ├── input_group.html │ └── td-field.html ├── views │ ├── app_index.html │ ├── batch_change_form.html │ ├── dashboard.html │ ├── form.html │ ├── invalid_setup.html │ ├── logged_out.html │ ├── login.html │ ├── model_dashboard.html │ ├── model_delete_confirm.html │ ├── model_delete_selected_confirm.html │ ├── model_detail.html │ ├── model_form.html │ ├── model_history.html │ ├── model_list.html │ ├── quick_detail.html │ ├── quick_form.html │ ├── recover_form.html │ ├── recover_list.html │ ├── revision_diff.html │ └── revision_form.html └── widgets ├── addform.html ├── base.html ├── chart.html ├── list.html └── qbutton.html
Intégrer font awesome
La version font awesome intégrée ne propose pas toutes les icones. Vous pouvez importer le projet font awesome en modifiant le fichier base.html
backoffice/template/xadmin/base.html
<link rel="stylesheet" type="text/css" href="{% static "css/font-awesome.min.css" %}" />
N'oubliez pas d'ajouter le projet font-awesome dans votre dossier static .
Ajouter une ligne dans votre menu
Les menus sont construit dans le template sitemenu_default.html
backoffice/templates/xadmin/includes/sitemenu_default.html
<li class="nav-header">Actions</li> <li class="{% if request.path == "/xadmin/stats/" %}active{% endif %}"> <a href="{% url 'xadmin:stats' %}"><i class="fa-fw fa fa-line-chart"></i> Statistique</a> </li>
À noter que request.path nécessite une action de votre part expliquée dans le chapitre context processor .
Résultat:
Associer une icone à une vue
Les icones cercle par défaut sont assez fades, vous pouvez ajouter votre propres icones font awsome en renseignant l'attribut model_icon :
backoffice/adminx.py
class ProductAdmin(object): model = Product model_icon = "fa fa-barcode" class ProductAttributeAdmin(object): model= ProductAttribute model_icon = "fa fa-list-ol"
Résultat:
Manipuler depuis les valeurs POST envoyées
Vous pouvez redéfinir la méthode post pour ajouter des contrôles ou modifier des données lors de la soumission du formulaire.
backoffice/adminx.py
class ProductAdmin(object): model = Product def post(self, request, *args, **kwargs): try: self.instance_forms() self.setup_forms() if self.valid_forms(): self.message_user("Attention ceci et cela bla bla", 'warning') return self.get_response() except: pass return super(TicketAdmin, self).post(request, *args, **kwargs)
Pour info vous pouvez récupérer les données soumis par le formulaire via l' objet request .
Voici un exemple qui récupère le nombre d'items de product
nb = int(request.POST.get("productitem_set-TOTAL_FORMS", "0"))