1. 程式人生 > 其它 >vue表單中批量匯入功能_idcops 中批量匯入線上裝置

vue表單中批量匯入功能_idcops 中批量匯入線上裝置

技術標籤:vue表單中批量匯入功能

django-idcops 平臺之前一直都沒開發線上裝置匯入的功能,剛好一些定製的系統有這個功能的需求,所以便把批量匯入線上裝置的功能完善了。

Django 框架中資料的匯入和匯出有很大的區別,匯出的時候可以利用框架model已有的資料解析方法將資料按照一定的規則匯出。

而匯入則需要根據具體model開發相關的資料解耦方法才能繼續進行,idcops 中大量使用了必填外來鍵,必填字串,多對多這種model欄位型別。因此,在開發批量匯入線上裝置這一功能時就需要寫更多的耦合函式。

面向物件,把idcops中Online這個模型的欄位型別瞭解一波先:

(env)PSC:\Users\shoutian\Desktop\django-idcops>python.\manage.pyshell>>> from idcops.models import Online>>> for f in Online._meta.get_fields(): f, "{} 必填:{}".format(f.verbose_name, not f.blank)... (, 'ID 必填:False')(, '系統標記 必填:False')(, '建立人 必填:True')(, '修改人 必填:False')(, '建立日期 必填:True')(, '修改日期 必填:False')(, '已啟用 必填:False')(, '已刪除 必填:False')(, '所屬機房 必填:False')(, '裝置編號 必填:True')(, '所屬機櫃 必填:True')(, '所屬客戶 必填:True')(, '裝置SN號 必填:True')(, 'IP地址 必填:False')(, '裝置型號 必填:True')(, '裝置型別 必填:True')(, '狀態 必填:True')(, '裝置U位 必填:False')(, 'PDU介面 必填:False')(, '裝置標籤 必填:False')(, '備註資訊 必填:False')>>>

進一步提取必須填寫的欄位如下:

>>> for f in Online._meta.get_fields(): (f, f.verbose_name) if not f.blank else None ... (, '建立人')(, '建立日期')(, '裝置編號')(, '所屬機櫃')(, '所屬客戶')(, '裝置SN號')(, '裝置型號')(, '裝置型別')(, '狀態')>>>

因線上裝置涉及到機櫃中展示具體所在位置,所以,裝置U位也是必填的,它是一個多對多欄位。另外還有pdus和tags是可選填的多對多欄位。最終整理的欄位如下:

>>> must_fields = [f.name for f in Online._meta.get_fields() if not f.blank]     >>> extra = ['units', 'pdus', 'tags']>>> must_fields + extra['creator', 'created', 'name', 'rack', 'client', 'sn', 'model', 'style', 'status', 'units', 'pdus', 'tags']>>>

通過上面的分析,我們做一個excel表格模板,供使用者按照格式填寫,模板截圖如下:

1d6c33dac3b0b431b78ecdcbca2690c0.png

注:

1、第1行是欄位的中文說明和欄位填寫格式註釋

2、第2行對應上面得出的填寫欄位的英文標識,這樣做可以讓使用者的模板自己隨意組合順序,和在表格中填寫以下其他的內容(如Excel第M列)

下面開始分析程式碼:

1、根據表格提供的上架人字串獲取系統使用者例項,非嚴格查詢,匹配以下3個欄位中的值均可,沒查到直接返回pk最小的一個使用者。【外來鍵型別】

def get_creator(username):    fields = ['first_name', 'username', 'mobile']    query = [Q(**{k: username.strip()}) for k in fields]    query_str = reduce(operator.or_, query)    user = User.objects.filter(query_str)    if user.exists():        return user.first()    else:        return User.objects.filter().order_by('pk').first()

2、獲取當前機房裝置型別例項,沒有則新建。【外來鍵型別】

def get_or_create_style(name, onidc_id):    f = dict(        onidc_id=onidc_id, flag='Device-Style', text=name.strip()    )    qs = shared_queryset(Option.objects.filter(**f), onidc_id)    if qs.exists():        instance = qs.first()    else:        extra = dict(            description=name.strip(),            creator_id=CreatorId        )        f.update(**extra)        instance = Option.objects.create(**f)    return instance

注:shared_queryset 是一個過濾查詢的函式,主要獲得其他機房共享出來的對應資料條目。

3、獲取客戶例項,沒有則按照相關條件新建。【外來鍵型別】

def get_or_create_client(name, onidc_id):    qs = Client.objects.filter(name=name.strip())    if qs.exists():        instance = qs.first()    else:        types = Option.objects.filter(            onidc_id=onidc_id, flag='Client-Style'        )        if types.exists():            default = types.filter(master=True)            if default.exists():                style = default.first()            else:                style = types.first()        else:            return None, "客戶型別不能為空"        instance = Client.objects.create(            onidc_id=onidc_id, creator_id=CreatorId,            name=name.strip(), style=style        )    return instance, None

注:style 是一個Option例項,有動態共享和預設的屬性。這裡可能不太好理解,但不影響理解整段程式碼。作用就是能獲取該機房預設的客戶型別,那就返回它,沒有獲取到任何客戶型別就返回None和提示字串,供迴圈程式作進一步判斷處理。

4、獲取機房已分配的機櫃例項。【外來鍵型別】

def get_rack(name, onidc_id):    """    Return: (instance, error)    """    qs = Rack.objects.filter(name=name.strip(), onidc_id=onidc_id)    if qs.filter(actived=True).exists():        return qs.first(), None    elif qs.filter(actived=False).exists():        return None, "該機櫃未分配使用"    else:        return None, "找不到該機櫃"

5、下面程式碼是處理裝置U位,Pdu位,tags的程式碼【多對多型別】

def clean_units(data, rack_id):    units = sorted([int(i) for i in data.split('|') if len(i) != 0])    units_list = [        str(x).zfill(2) for x in range(units[0], units[-1]+1)    ]    instances = Unit.objects.filter(rack_id=rack_id, name__in=units_list)    if instances.exists():        used = instances.filter(actived=False)        if used.count() > 0:            return None, "有U位被佔用中"        return instances, None    else:        return None, "找不到U位資訊"def clean_pdus(data, rack_id):    pdus = re.split('[, |]', data)    pdus_list = [x.strip() for x in pdus if x]    instances = Pdu.objects.filter(rack_id=rack_id, name__in=pdus_list)    if instances.exists():        used = instances.filter(actived=False)        if used.count() > 0:            return instances.filter(actived=True), "部分PDU位被佔用中"        return instances, None    else:        return None, "找不到PDU位資訊"def clean_tags(tags, onidc_id, creator_id):    tags = re.split('[, |]', tags)    tags_list = [x.strip() for x in tags if x]    default = dict(onidc_id=onidc_id, flag='Device-Tags')    instances = []    for tag in tags_list:        default.update(**dict(text=tag))        verify = Option.objects.filter(**default)        if verify.exists():            instance = verify.first()        else:            default.update(**dict(creator_id=creator_id))            instance = Option.objects.create(**default)        instances.append(instance)    return instances

注:跟外來鍵型別的處理方式差不多,先查出例項,在返回。這邊都是返回模型的例項列表。

Python中使用xlrd讀取Excel資料內容

是不是感覺跟django表單的清理資料有點大同小異,是的,匯入的時候我們主要的工作就是做資料的清理和校驗。下面是讀取excel表格資料的具體處理程式碼:

def import_online(path, onidc_id):    fileds = [        'name', 'creator', 'rack', 'client', 'created', 'onidc',        'sn', 'model', 'ipaddr', 'style', 'units', 'pdus', 'tags'    ]    workbook = xlrd.open_workbook(path)    sheets = workbook.sheet_names()    worksheet = workbook.sheet_by_name(sheets[0])#設定匯入錯誤日誌記錄到各種列表中    handler_error = []    handler_warning = []    handler_success = []    for index, row in enumerate(worksheet.get_rows(), 1):        # header = index        if index == 1:            # 跳過表頭            continue        if index == 2:            # 獲取欄位名稱            headers = [h.value for h in row]            continue        if index > 503:            # 每次只處理500條資料            msg = "一次最多匯入500條資料"            handler_error.append(msg)            break        data = dict(zip(headers, [k.value for k in row]))        raw = {k: data.get(k) for k in fileds}        created = datetime.strptime(data.get('created'), '%Y-%m-%d')        raw.update(**dict(created=created))        verify = Online.objects.filter(name=raw.get('name'))        if verify.exists():            msg = "第{}行:{}裝置已存在".format(index, raw.get('name'))            handler_error.append(msg)            continue        else:            style = get_or_create_style(raw.get('style'), onidc_id)            creator = get_creator(raw.get('creator'))            # 獲取機櫃資訊            rack, err = get_rack(raw.get('rack'), onidc_id)            if not rack:                msg = "第{}行:{}".format(index, err)                handler_error.append(msg)                continue            # 獲取客戶資訊            client, err = get_or_create_client(raw.get('client'), onidc_id)            if not client:                msg = "第{}行:{}".format(index, err)                handler_error.append(msg)                continue            # 例項化線上裝置            instance = Online(                created=created, style=style, creator=creator,                rack=rack, client=client, name=raw.get('name'),                sn=raw.get('sn'), ipaddr=raw.get('ipaddr'),                model=raw.get('model'), onidc_id=onidc_id            )            instance.save()            # 儲存U位            units, err = clean_units(raw.get('units'), rack.pk)            if units:                for u in units:                    instance.units.add(u)                units.update(actived=False)                instance.save()            else:                msg = "第{}行:{}".format(index, err)                handler_error.append(msg)                # U位不對,刪除本例項                instance.delete()                continue            handler_success.append(instance.name)            # 儲存PDU            pdus, err = clean_pdus(raw.get('pdus'), rack.pk)            if pdus:                for p in pdus:                    instance.pdus.add(p)                pdus.update(actived=False)                instance.save()            else:                msg = "第{}行:{}".format(index, err)                handler_warning.append(msg)            # 儲存TAGS            tags = clean_tags(raw.get('tags'), onidc_id, creator.pk)            if tags:                for t in tags:                    instance.tags.add(t)                instance.save()    total = (index-2)    return handler_error, handler_warning, handler_success, total

注:

1、表頭處理的 index=1 或 index=2 ,從 index=3 開始手機我們要的資料

2、巧妙的使用 fields 限制從使用者表格中獲取我們真正需要的欄位內容

3、匯入出錯的時候要銷燬 instance.save() 儲存的例項

4、匯入的日誌要記錄清除,精確到使用者excel第幾行第幾列

總體來說,批量匯入是另一種提交資料內容的方式,我們主要的工作就是做資料的清理和校驗,需針對到具體模型。並沒有像匯出資料那樣有很多的通用方法可實現。

文中程式碼不能直接套用,有興趣的朋友可以檢視本次功能開發的Gitee提交:

https://gitee.com/wenvki/django-idcops/commit/b9822ee543f541fef5325ccf075a7aefb726fa07

IDC運維管理平臺會盡力更新有關django-idcops開發或使用相關文章。如果文章中的內容對你有幫助,可以通過右下角點選在看支援一下,謝謝。