csv
All checks were successful
Deploy to Server / deploy (push) Successful in 33s

This commit is contained in:
jeremygan2021
2026-02-28 11:33:16 +08:00
parent dba9d4f724
commit d5ad54e380
2 changed files with 95 additions and 27 deletions

View File

@@ -37,6 +37,58 @@ def get_signup_info_keys(queryset):
keys.update(flat_info.keys()) keys.update(flat_info.keys())
return sorted(list(keys)) return sorted(list(keys))
def get_row_data(obj):
"""
Build a dictionary of all exportable data for a single ActivitySignup object
"""
data = {}
# 1. 基础信息
data['ID'] = str(obj.id)
data['活动标题'] = obj.activity.title
data['报名时间'] = obj.signup_time
data['状态'] = obj.get_status_display()
# 2. 用户信息
if obj.user:
data['用户昵称'] = obj.user.nickname
data['用户ID'] = str(obj.user.id)
data['用户OpenID'] = obj.user.openid
data['用户绑定手机'] = obj.user.phone_number or ''
data['用户地区'] = f"{obj.user.country} {obj.user.province} {obj.user.city}".strip()
data['用户注册时间'] = obj.user.created_at
else:
data['用户昵称'] = 'Unknown'
data['用户ID'] = ''
data['用户OpenID'] = ''
data['用户绑定手机'] = ''
data['用户地区'] = ''
data['用户注册时间'] = ''
# 3. 订单/发货信息
if obj.order:
data['关联订单ID'] = str(obj.order.id)
data['订单状态'] = obj.order.get_status_display()
data['收货人姓名'] = obj.order.customer_name
data['收货电话'] = obj.order.phone_number
data['收货地址'] = obj.order.shipping_address
data['快递公司'] = obj.order.courier_name or ''
data['快递单号'] = obj.order.tracking_number or ''
data['订单总价'] = str(obj.order.total_price)
data['商户订单号'] = obj.order.out_trade_no or ''
else:
data['关联订单ID'] = ''
data['订单状态'] = ''
data['收货人姓名'] = ''
data['收货电话'] = ''
data['收货地址'] = ''
data['快递公司'] = ''
data['快递单号'] = ''
data['订单总价'] = ''
data['商户订单号'] = ''
return data
def export_signups_csv(modeladmin, request, queryset): def export_signups_csv(modeladmin, request, queryset):
""" """
Export selected signups to CSV, including flattened JSON fields Export selected signups to CSV, including flattened JSON fields
@@ -49,26 +101,30 @@ def export_signups_csv(modeladmin, request, queryset):
writer = csv.writer(response) writer = csv.writer(response)
# Base fields to export # Fixed headers
base_headers = ['ID', '活动标题', '用户昵称', '用户ID', '报名时间', '状态', '关联订单ID'] fixed_headers = [
'ID', '活动标题', '状态', '报名时间',
'用户ID', '用户昵称', '用户OpenID', '用户绑定手机', '用户地区', '用户注册时间',
'关联订单ID', '订单状态', '收货人姓名', '收货电话', '收货地址', '快递公司', '快递单号', '订单总价', '商户订单号'
]
# Get dynamic JSON keys # Get dynamic JSON keys
json_keys = get_signup_info_keys(queryset) json_keys = get_signup_info_keys(queryset)
# Write header # Write header
writer.writerow(base_headers + json_keys) writer.writerow(fixed_headers + json_keys)
# Write data # Write data
for obj in queryset: for obj in queryset:
row = [ row_data = get_row_data(obj)
str(obj.id),
obj.activity.title, # Build the row based on fixed_headers order
obj.user.nickname if obj.user else 'Unknown', row = []
str(obj.user.id) if obj.user else '', for header in fixed_headers:
obj.signup_time.strftime('%Y-%m-%d %H:%M:%S'), val = row_data.get(header, '')
obj.get_status_display(), if isinstance(val, (datetime.datetime, datetime.date)):
str(obj.order.id) if obj.order else '' val = val.strftime('%Y-%m-%d %H:%M:%S')
] row.append(str(val))
# Add JSON data # Add JSON data
flat_info = {} flat_info = {}
@@ -85,7 +141,7 @@ def export_signups_csv(modeladmin, request, queryset):
return response return response
export_signups_csv.short_description = "导出选中报名记录为 CSV (含详细信息)" export_signups_csv.short_description = "导出选中报名记录为 CSV (含发货/详细信息)"
def export_signups_excel(modeladmin, request, queryset): def export_signups_excel(modeladmin, request, queryset):
""" """
@@ -109,26 +165,37 @@ def export_signups_excel(modeladmin, request, queryset):
ws = wb.active ws = wb.active
ws.title = str(opts.verbose_name)[:31] # Sheet name limit is 31 chars ws.title = str(opts.verbose_name)[:31] # Sheet name limit is 31 chars
# Base fields to export # Fixed headers
base_headers = ['ID', '活动标题', '用户昵称', '用户ID', '报名时间', '状态', '关联订单ID'] fixed_headers = [
'ID', '活动标题', '状态', '报名时间',
'用户ID', '用户昵称', '用户OpenID', '用户绑定手机', '用户地区', '用户注册时间',
'关联订单ID', '订单状态', '收货人姓名', '收货电话', '收货地址', '快递公司', '快递单号', '订单总价', '商户订单号'
]
# Get dynamic JSON keys # Get dynamic JSON keys
json_keys = get_signup_info_keys(queryset) json_keys = get_signup_info_keys(queryset)
# Write header # Write header
ws.append(base_headers + json_keys) ws.append(fixed_headers + json_keys)
# Write data # Write data
for obj in queryset: for obj in queryset:
row = [ row_data = get_row_data(obj)
obj.id,
obj.activity.title, row = []
obj.user.nickname if obj.user else 'Unknown', for header in fixed_headers:
obj.user.id if obj.user else '', val = row_data.get(header, '')
obj.signup_time.replace(tzinfo=None) if obj.signup_time else '', # Remove tz for Excel # Excel handles datetime natively, but we need to remove timezone info if present to avoid Excel errors or warnings depending on version
obj.get_status_display(), # Here we convert to naive datetime for simplicity or keep as is if library handles it.
obj.order.id if obj.order else '' # openpyxl supports datetime, but usually it's safer to remove tzinfo for compatibility.
] if isinstance(val, (datetime.datetime, datetime.date)):
if hasattr(val, 'replace'):
val = val.replace(tzinfo=None)
# Ensure None becomes empty string
if val is None:
val = ""
row.append(val)
# Add JSON data # Add JSON data
flat_info = {} flat_info = {}
@@ -139,11 +206,11 @@ def export_signups_excel(modeladmin, request, queryset):
val = flat_info.get(key, '') val = flat_info.get(key, '')
if val is None: if val is None:
val = '' val = ''
row.append(str(val)) # Ensure string for simplicity, or handle types row.append(str(val)) # JSON values as strings
ws.append(row) ws.append(row)
wb.save(response) wb.save(response)
return response return response
export_signups_excel.short_description = "导出选中报名记录为 Excel (含详细信息)" export_signups_excel.short_description = "导出选中报名记录为 Excel (含发货/详细信息)"

View File

@@ -22,4 +22,5 @@ gunicorn==21.2.0
requests requests
django-filter django-filter
django-admin-sortable2 django-admin-sortable2
openpyxl