寫在開頭: 建議使用源碼安裝xadmin, 這樣可以根據自己實際需求做更靈活的處理
在xadmin
中圖片選擇按鈕默認是隻能單選
,如圖:
並不能一次性選擇多張圖片並存儲, 這裏只需要修改一點xadmin源代碼
, 就可以實現一次性選擇多張圖片並存儲.
在xadmin源碼目錄xadmin/plugins/images.py
裏找到第38行AdminImageWidget
這個類:
# xadmin/plugins/images.py 第38行
class AdminImageWidget(forms.FileInput):
"""
A ImageField Widget that shows its current value if it has one.
"""
def __init__(self, attrs={}):
super(AdminImageWidget, self).__init__(attrs)
def render(self, name, value, attrs=None):
output = []
if value and hasattr(value, "url"):
label = self.attrs.get('label', name)
output.append('<a href="%s" target="_blank" title="%s" data-gallery="gallery"><img src="%s" class="field_img"/></a><br/>%s ' %
(value.url, label, value.url, _('Change:')))
output.append(super(AdminImageWidget, self).render(name, value, attrs))
return mark_safe(u''.join(output))
AdminImageWidget
的註釋就說明了它就是xadmin處理ImageField的插件,如果有圖片, 就顯示當前圖片,下面的方法render
返回的就包含前面支付截圖選項(xadmin是在這裏根據加載的數據生成前端頁面所需要的a標籤並返回-----xadmin管理頁面有很多都是這中處理方式), 而選擇圖片也只需要在return mark_safe
前面加上幾行代碼就可以把單選改爲多選:
class AdminImageWidget(forms.FileInput):
"""
...
"""
def render(self, name, value, attrs=None):
output = []
if value and hasattr(value, "url"):
label = self.attrs.get('label', name)
output.append('<a href="%s" target="_blank" title="%s" data-gallery="gallery"><img src="%s" class="field_img"/></a><br/>%s ' %
(value.url, label, value.url, _('Change:')))
output.append(super(AdminImageWidget, self).render(name, value, attrs))
"""
因爲前端頁面中選擇按鈕變多選,需要在對應標籤裏添加上multiple="multiple"
而這裏的output就是xadmin後端生成的html所需要的標籤的文本流
所以我們只需要在output的對應位置用代碼添加上這一條件
"""
if self.attrs['label'] and self.attrs['label'] == '訪問截圖':
if len(output) > 1:
output[-1] = output[-1][:-2] + ' multiple="multiple">'
else:
output[0] = output[0][:-2] + ' multiple="multiple">'
return mark_safe(u''.join(output))
這裏我自己的邏輯是隻給訪問截圖添加多張圖片,支付截圖沒有這個需求,所以對標籤做了篩選, 支付截圖保持單選, 其中 self.attrs['label'] == '訪問截圖'
中的訪問截圖
, 就是我們頁面中對應選項的文字描述:
保存刷新, 此時測試頁面點擊選擇文件, 在選擇的時候按住ctrl
就能夠同時選擇多張圖片了:
現在能夠選擇多張圖片, 但是xadmin在存儲的時候還是隻會存儲一張, 需要在自己的admin.py
文件裏面的插件下添加方法save_models
來處理自己的存儲邏輯, 我這裏處理的很簡單, visit_img是多張圖, 另建了一張表來存路徑+圖片名, 另外由於沒有巨量圖片的存儲需求, 也沒有用到雲存儲, 直接在服務器上把圖片寫入一個文件夾:
# admin.py
import xadmin
from .models import TestBasci, VisitImg
class TestBasicView(object):
list_display = ['name', 'url', 'record_num', 'record_company', 'ip', 'addr', 'client_type', 'pay_type', 'order', 'image_data', 'vi_img_data', ]
"""
...
"""
def save_models(self):
obj = self.new_obj
files = self.request.FILES.getlist('visit_image')
if files:
for f in files:
fname = ''
if VisitImg.objects.filter(image='visit_imgs/%s' % f.name).first():
fname = str(datetime.timestamp(datetime.now())).split('.')[0] + '_' + f.name
else:
# 如果圖片名不存在,則使用當前圖片名
fname = f.name
with open('%s/visit_imgs/%s' % (settings.MEDIA_ROOT, fname), 'wb+') as pic:
for chunk in f.chunks():
pic.write(chunk)
# 圖片名信息寫入數據庫
# 這裏數據庫多加了一個字段 name, 是爲了下面詳情頁多圖展示裏把相同歸屬的訪問圖拿到
img_obj = VisitImg(name=self.new_obj.name,
image='visit_imgs/%s' % fname,)
img_obj.save()
obj.save()
# 註冊插件
xadmin.site.register(TestBasic, TestBasicView)
再保存刷新一次, 選擇多張圖片後就可以在後臺數據庫中看到成功保存所選圖片
之前忽略了添加多圖後詳情頁面展示的問題,這裏補充一下
還是要回到xadmin源碼目錄xadmin/plugins/images.py
裏的AdminImageWidget
;
其實這個跟前面修改選擇按鈕單選
爲多選
差不多的, 只是需要自己去分析一下這裏output
的輸出格式(如果對html5
有一點了解的話,就比較容易知道應該怎麼去修改output
的輸出結果了);
詳情頁面多圖展示的代碼, 只需要根據自己的邏輯, 添加到render
裏多選框之前就可以了:
# xadmin/plugins/images.py
from BasicInfo.models import VisitImg
class AdminImageWidget(forms.FileInput):
"""
A ImageField Widget that shows its current value if it has one.
"""
def __init__(self, attrs={}):
super(AdminImageWidget, self).__init__(attrs)
def render(self, name, value, attrs=None, renderer=None):
output = []
if value and hasattr(value, "url"):
label = self.attrs.get('label', name)
# fixme code: 訪問截圖多圖展示
if label == '訪問截圖':
# 具體要怎麼展示, 根據自己的邏輯, 按照 output 的格式適當修改就可以了
# 導航欄-訪問截圖 詳情頁面 只顯示單張
if value.name.split('/')[0] == 'visit_imgs':
output.append(
'<a href="%s" target="_blank" title="%s" data-gallery="gallery"><img src="%s" class="field_img"/></a><br/>%s ' %
(value.url, label, value.url, _('Change:')))
else:
# 導航欄-基礎信息 詳情頁面-訪問截圖 多圖展示
# 根據上面提到的存儲訪問圖時增加的 name 字段,找出相同歸屬的訪問圖
img_names = VisitImg.objects.filter(name=value.instance.name).values('image')
end = ''
if img_names:
for img in img_names:
# 這個判斷可要可不要, 我自己給最後一張圖片後邊加上了 '添加' 兩個字
if img['image'][11:] == value.name[10:]:
# end = '<a href="/media/%s" target="_blank" title="%s" data-gallery="gallery"><img src="/media/%s" class="field_img"/></a><br/>%s ' %\
# (jsq["image"], label, jsq["image"], _('Change:'))
end = '<a href="%s" target="_blank" title="%s" data-gallery="gallery"><img src="%s" class="field_img"/></a>' \
'<br/>%s ' %\
(value.url, label, value.url, '添加')
else:
# output.append('<a href="%s" target="_blank" title="%s" data-gallery="gallery"><img src="%s" class="field_img"/></a><br/>%s ' %
# (value.url, label, value.url, _('Change:')))
output.append('<a href="/media/%s" target="_blank" title="訪問截圖" data-gallery="gallery"> '
'<img src="/media/%s" class="field_img">'
'</a>' % (img["image"], img["image"]))
output.append(end)
else:
# 其他圖片單張顯示(目前只有 基礎信息-支付截圖)
output.append('<a href="%s" target="_blank" title="%s" data-gallery="gallery"><img src="%s" class="field_img"/></a><br/>%s ' %
(value.url, label, value.url, _('Change:')))
output.append(super(AdminImageWidget, self).render(name, value, attrs, renderer))
# fixme code: 訪問截圖圖片選擇框改爲多選
if self.attrs['label'] and self.attrs['label'] == '訪問截圖':
if len(output) > 1:
output[-1] = output[-1][:-2] + ' multiple="multiple">'
else:
output[0] = output[0][:-2] + ' multiple="multiple">'
return mark_safe(u''.join(output))
我的導航欄裏的基礎信息
和訪問截圖
,在上面的註釋裏有說到,只給基礎信息
的詳情頁面裏做做圖展示:
我自己純屬無聊,畫蛇添足弄的添加
兩個字:
而訪問截圖
自己本身的詳情頁面, 就仍然只顯示一張圖片:
還有一點之前忘記了,這裏一併添加上
既然做了多圖存儲,我這種直接把圖片寫入服務器的操作,要記得自定義 delete_model
在做刪除操作的時候,把相關的圖片信息都要刪掉,否則它們就永遠存在服務器上了
當然,如果不想在做刪除操作的時候直接刪除對應圖片,而是有其他處理方式,也是可以的
# admin.py
import xadmin
from .models import TestBasci, VisitImg
class TestBasicView(object):
"""
...
"""
def delete_model(self):
# 刪除 TestBasicView 自己本身的圖片
delete_image(self.obj.image.name)
delete_image(self.obj.image.thumbnail.name)
delete_image(self.obj.visit_image.name)
delete_image(self.obj.visit_image.thumbnail.name)
# 與 TestBasicView 相關聯的 VisitImg 也跟着刪除
visit_images = VisitImg.objects.filter(name=self.obj.name)
if visit_images:
for img in visit_images:
# 刪除服務器上對應的圖片文件
delete_image(img.image.name)
# 刪除數據庫記錄
VisitImg.objects.filter(name=self.obj.name).delete()
self.obj.delete()
另外,這裏可能有個小問題就是:我目前有圖片處理的地方並不多, 所以也比較簡單, 如果處理圖片的模塊不止兩三個, 各自展示如果還有其他條件, 可能就需要做更多的邏輯判斷