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:Django-admin讓我們能夠很方便的實現一個管理後臺程序。django-xadmin則在擁有admin基本功能的基礎上增加了更爲豐富的功能、界面也更加漂亮。類似還有django-suit等,本文使用xadmin(功能更豐富);
- 實現需求2:django-admin,以及xadmin都只有基於model級的權限控制機制,需要自己擴展或者使用開源解決方案,如django-guardian,django-rules,本文結合django-rules實現了該功能;
- 實現需求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開發
開發思路:
- 類似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)