Django點滴(2)xadmin+django-import-export導入導出的實現

Django-xadmin+django-import-export導入導出的實現

Django點滴系列是本人Django使用的一個記錄,主要用於踩坑填坑,如果能幫到你,就是我榮幸!

書接上回, 上一篇博客 Django點滴(1)xadmin+rule對象級權限的實現記錄了xadmin對象級權限相關應用,本篇將目光投向xadmin的導入導出功能。


太長不看版:
該功能已經向xadmin項目組提交了pull request,代碼已經merge,下載使用最新版xadmin即可,插件使用方法請參考xadmin/plugins/importexport.py的註釋


1. 需求vs現狀

1.1 需求

要求做一個ERP後臺輔助管理的程序,有以下幾項基本要求:
1. 基本的增刪改查功能
2. 基於對象的權限控制(如:系統用戶分爲平臺運營人員和商家用戶,商家用戶小A只能查看編輯所屬商家記錄,而管理員可以縱覽全局)
3. 數據庫記錄導入導出(xsl, json等),並且擁有對象級的權限控制(如:小A不能導出小B公司的信息,更不能導入小B公司信息進行更新和新增)

1.2 現狀

  1. 實現需求1:Django-admin讓我們能夠很方便的實現一個管理後臺程序。django-xadmin則在擁有admin基本功能的基礎上增加了更爲豐富的功能、界面也更加漂亮。類似還有django-suit等,本文使用xadmin(功能更豐富);
  2. 實現需求2:django-admin,以及xadmin都只有基於model級的權限控制機制,需要自己擴展或者使用開源解決方案,如django-guardiandjango-rules,本文結合django-rules實現了該功能;
  3. 實現需求3:xadmin雖然自帶導出功能,但是導入功能沒有實現,django自帶後臺結合django-import-export可以很容易實現,但是xadmin並不直接兼容,只有通過xadmin的插件機制實現。

2. 功能實現

本節主要展示需求3導入導出功能實現。

2.1 安裝並配置django-import-export

django-import-export主要原理是爲待導入導出的model類設置一個Resource類,通過Resource類的export()方法進行導出,import_data()方法進行導入,靈活設置Resource便能實現多種文件格式數據導入導出。
pip安裝:

$ pip install django-import-export

配置settings.py

# settings.py
INSTALLED_APPS = (
    ...
    'import_export',
)

2.2 建立model(同上篇)

新增CompanyUser模型表示商家賬戶(即對django自帶user模塊進行擴展,使每個賬號綁定自己的公司碼),新增Customer模型表示商家的客戶信息幷包含公司碼字段,商家賬號只能查看、編輯、導入、導出公司碼一致的商家客戶信息

# model.py
class CompanyUser(models.Model):
    user = models.OneToOneField(User, verbose_name='用戶名')
    is_taixiang_admin = models.BooleanField('是否運營人員', default=False)
    company_code = models.CharField('公司碼', max_length=20, blank=True, default='')

    def __unicode__(self):
        return '%s' % self.user

    class Meta:
        verbose_name = '導入賬號'
        verbose_name_plural = verbose_name
class Customer(models.Model):
    name = models.CharField('客戶姓名', max_length=50)
    phone = models.CharField('客戶電話', max_length=12)
    type_choice = ((1, '普通'), (2, '批發'), (3, 'VIP'))
    creator = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='創建人', blank=True, null=True)
    company_code = models.CharField('公司碼', max_length=20, blank=True, null=True)

    def __unicode__(self):
        return '%s-%s-%s' % (self.company_code, self.name, self.phone1)

    class Meta:
        permissions = (
            ("simulate_import_customer", "允許模擬導入客戶"),
            ("import_customer", "允許導入客戶至商家系統"),
                       )
        verbose_name = "客戶"
        verbose_name_plural = verbose_name

2.3 創建Resource

在model統計目錄新增resources.py,配置Resource類

# resources.py
from import_export import resources
from .models import Customer

class CustomerResource(resources.ModelResource):

    class Meta:
        model = Customer
        fields = ('id', 'name', 'phone1', 'c_type', 'company_code')

2.4 原生django-admin導入導出實現

使用原生的django-admin,ModelAdminClass可以通過繼承ImportExportModelAdmin類實現導入導出,繼承ImportExportActionModelAdmin類實現導出action:

# admin.py
from django.contrib import admin
from .models import Customer
from import_export.admin import ImportExportModelAdmin, ImportExportActionModelAdmin
from .resources import CustomerResource


class CustomerAdmin(ImportExportActionModelAdmin, ImportExportModelAdmin):
    resource_class = CustomerResource

admin.site.register(Customer, CustomerAdmin)

這裏寫圖片描述

2.5 django-xadmin導入導出實現(代碼修改挺多的,此處展示基本思路,希望能push到xadmin項目中)

基於xadmin強大的plugin功能(參考Xadmin插件製作),這次我們開發一個importexport插件。具體實現轉下章。

3. xadmin-importexport-plugin開發

開發思路:

  1. 類似admin使用importexport,需要在adminx.py ModelAdmin類中設置import_export_args屬性用於關聯Resource類,在list頁面中會顯示導入和導出按鈕(ImportMenuPlugin,ExportMenuPlugin生效)。
# adminx.py
class CustomerAdmin(object):

    import_export_args = {'import_resource_class': CustomerResource, 'export_resource_class': CustomerResource}
    ...

這裏寫圖片描述
2. 點擊導出按鈕,會彈出若干選項,點擊確定發送get請求後,觸發ExportPlugin插件調用resource的export()方法進行導出。
這裏寫圖片描述
3. 點擊導入按鈕,則會跳轉至導入頁面(ImportView),上傳文件後點擊導入會進行基本的檢測
這裏寫圖片描述
4. 點擊確認導入(ImportProcessView)調用django-import-export導入至數據庫。
這裏寫圖片描述

3.1.1 插件部分代碼
# xadmin/plugins/importexport.py

# 繼承BaseAdminPlugin類
class ImportMenuPlugin(BaseAdminPlugin):
    # 使用插件時需要在ModelAdmin類中設置import_export_args屬性,插件初始化時使用ModelAdmin的import_export_args進行賦值
    import_export_args = {}

    # 返回True則加載插件,在list列表中顯示導入按鈕
    def init_request(self, *args, **kwargs):
        return bool(self.import_export_args.get('import_resource_class'))

    def block_top_toolbar(self, context, nodes):
        has_change_perm = self.has_model_perm(self.model, 'change')
        has_add_perm = self.has_model_perm(self.model, 'add')
        if has_change_perm and has_add_perm:
            model_info = (self.opts.app_label, self.opts.model_name)
            import_url = reverse('xadmin:%s_%s_import' % model_info, current_app=self.admin_site.name)
            context = get_context_dict(context or {})  # no error!
            context.update({
                'import_url': import_url,
            })
            nodes.append(loader.render_to_string('xadmin/blocks/model_list.top_toolbar.importexport.import.html',
                                                 context=context))


class ExportMenuPlugin(BaseAdminPlugin):
    import_export_args = {}

    # Media
    def get_media(self, media):
        return media + self.vendor('xadmin.plugin.importexport.css', 'xadmin.plugin.importexport.js')

    def init_request(self, *args, **kwargs):
        return bool(self.import_export_args.get('export_resource_class'))

    def block_top_toolbar(self, context, nodes):
        formats = self.get_export_formats()
        form = ExportForm(formats)

        context = get_context_dict(context or {})  # no error!
        context.update({
            'form': form,
            'opts': self.opts,
            'form_params': self.admin_view.get_form_params({'_action_': 'export'}),
        })
        nodes.append(loader.render_to_string('xadmin/blocks/model_list.top_toolbar.importexport.export.html',
                                             context=context))


site.register_plugin(ImportMenuPlugin, ListAdminView)
site.register_plugin(ExportMenuPlugin, ListAdminView)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章