You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于CSV文件更新Django数据库:增删改功能实现求助

CSV同步汽车数据库时的重复数据问题及修复方案

问题背景

要实现CSV与数据库的同步,需求如下:

  • VIN作为唯一标识,CSV中对应条目字段变更时,更新除VIN外的所有字段
  • 删除数据库中存在但CSV里已不存在的条目
  • 新增CSV中出现但数据库未有的条目

但目前修改CSV内容后上传,会产生重复数据,相关代码如下:

def upload_file__view(request):
    form = form(request.POST or None, request.FILES or None)
    company = Comp_info.objects.last()
    if form.is_valid():
        form.save()
        obj = c.objects.get(activated=False)
        with open(obj.file_name.path, 'r+') as f:
            reader = c.reader(f)

            for i, row in enumerate(reader):
                if i==0:
                    pass
                else:
                    print(row)
                    print(type(row))
                    vin = row[0].upper()
                    condition = row[1].replace("U", "Used").replace("N", "New")
                    stock_no = row[2]
                    year = int(row[5])
                    make = row[3]
                    model = row[4]
                    trim = row[6]
                    mileage = row[8]
                    mpg_city = row[18]
                    mpg_hwy = row[19]
                    engine = row[9]
                    transmission = row[12]
                    fuel_type = row[11]
                    vehicle_type = row[7]
                    drive_type = row[20].replace("4X2", "2WD").replace("4X4", "4WD")
                    exterior_color = row[15]
                    interior_color = row[16]
                    price = row[13].replace("0", "")
                    description = row[22]
                    features_2 = row[21]
                    images_data = row[23]
                    raw_images_list = images_data.split(',')
                    images_list = [""] * 25
                    # 注意:这里image_count未定义,需要补充或改为range(len(raw_images_list))
                    for x in range(image_count):
                        if x == 25:
                            break
                        images_list[x] = raw_images_list[x]
                    
                    for x in images_list:
                        print(x)
                    if images_list[0] == "":
                        images_list[0] = "https://www.beverlyhillscarclub.com/template/images/ina_f.jpg"
                    car_photo =  images_list[0]
                    car_photo_1 = images_list[1]
                    car_photo_2 = images_list[2]
                    car_photo_3 = images_list[3]
                    car_photo_4 = images_list[4]
                    car_photo_5 = images_list[5]
                    car_photo_6 = images_list[6]
                    car_photo_7 = images_list[7]
                    car_photo_8 = images_list[8]
                    car_photo_9 = images_list[9]
                    car_photo_10 = images_list[10]
                    car_photo_11 = images_list[11]
                    car_photo_12 = images_list[12]
                    car_photo_13 = images_list[13]
                    car_photo_14 = images_list[14]
                    car_photo_15 = images_list[15]
                    car_photo_16 = images_list[16]
                    car_photo_17 = images_list[17]
                    car_photo_18 = images_list[18]
                    car_photo_19 = images_list[19]
                    car_photo_20 = images_list[20]
                    car_photo_21 = images_list[21]
                    car_photo_22 = images_list[22]
                    car_photo_23 = images_list[23]
                    car_photo_24 = images_list[24]
                    Cars.objects.update_or_create(
                         vin = vin,
                         condition = condition,
                         stock_no = stock_no,
                         year = year,
                         make = make,
                         model = model,
                         trim = trim,
                         mileage = mileage,
                         mpg_city = mpg_city,
                         engine = engine,
                         transmission = transmission,
                         fuel_type = fuel_type,
                         vehicle_type = vehicle_type,
                         drive_type = drive_type,
                         exterior_color = exterior_color,
                         interior_color = interior_color,
                         price = price,
                         description = description,
                         company_name = company.company_name,
                         address = company.company_address,
                         city = company.city,
                         state = company.state,
                         zip = company.zip_code,
                         phone_number = company.phone_number,
                         email = company.fax_number,
                         features_2 = features_2,
                         car_photo = downloadFile(car_photo),
                         car_photo_1 = downloadFile(car_photo_1),
                         car_photo_2 = downloadFile(car_photo_2),
                         car_photo_3 = downloadFile(car_photo_3),
                         car_photo_4 = downloadFile(car_photo_4),
                         car_photo_5 = downloadFile(car_photo_5),
                         car_photo_6 = downloadFile(car_photo_6),
                         car_photo_7 = downloadFile(car_photo_7),
                         car_photo_8 = downloadFile(car_photo_8),
                         car_photo_9 = downloadFile(car_photo_9),
                         car_photo_10 = downloadFile(car_photo_10),
                         car_photo_11 = downloadFile(car_photo_11),
                         car_photo_12 = downloadFile(car_photo_12),
                         car_photo_13 = downloadFile(car_photo_13),
                         car_photo_14 = downloadFile(car_photo_14),
                         car_photo_15 = downloadFile(car_photo_15),
                         car_photo_16 = downloadFile(car_photo_16),
                         car_photo_17 = downloadFile(car_photo_17),
                         car_photo_18 = downloadFile(car_photo_18),
                         car_photo_19 = downloadFile(car_photo_19),
                         car_photo_20 = downloadFile(car_photo_20),
                         car_photo_21 = downloadFile(car_photo_21),
                         car_photo_22 = downloadFile(car_photo_22),
                         car_photo_23 = downloadFile(car_photo_23),
                         car_photo_24 = downloadFile(car_photo_24)                      
                     )
        
        obj.activated = True
        
        obj.save()

    data = {
        'form' : form,
        'now' : now,
    }
    return render(request, 'uploads.html', data)

问题原因及修复方案

1. 修复update_or_create的核心用法

当前代码把所有字段都作为update_or_create的查找条件,这意味着只要CSV中任意字段变化,系统会认为是新条目并创建,导致重复。正确的用法是:仅将唯一标识(VIN)作为查找条件,其余字段放入defaults字典中,这样系统会先根据VIN查找记录,存在则更新defaults里的字段,不存在则创建新记录。

2. 实现删除逻辑

需要收集本次CSV中所有的VIN,然后删除数据库中不在该集合里的Cars记录,满足“删除数据库存在但CSV没有的条目”的需求。

3. 代码细节优化

  • image_count变量未定义,需替换为len(raw_images_list)避免索引越界
  • price = row[13].replace("0", "")逻辑有问题,比如价格1000会被处理成1,需根据实际需求调整(比如去掉非数字字符或保留有效格式)
  • 图片下载逻辑可添加缓存判断,避免重复下载相同URL的图片

修改后的完整代码示例

def upload_file__view(request):
    form = form(request.POST or None, request.FILES or None)
    company = Comp_info.objects.last()
    # 用于存储本次CSV中的所有VIN,后续删除用
    csv_vins = set()
    
    if form.is_valid():
        form.save()
        obj = c.objects.get(activated=False)
        with open(obj.file_name.path, 'r+') as f:
            reader = c.reader(f)

            for i, row in enumerate(reader):
                if i == 0:
                    continue  # 跳过表头
                vin = row[0].upper()
                csv_vins.add(vin)
                
                # 处理字段
                condition = row[1].replace("U", "Used").replace("N", "New")
                stock_no = row[2]
                year = int(row[5])
                make = row[3]
                model = row[4]
                trim = row[6]
                mileage = row[8]
                mpg_city = row[18]
                mpg_hwy = row[19]
                engine = row[9]
                transmission = row[12]
                fuel_type = row[11]
                vehicle_type = row[7]
                drive_type = row[20].replace("4X2", "2WD").replace("4X4", "4WD")
                exterior_color = row[15]
                interior_color = row[16]
                # 修正price处理逻辑,示例:仅保留数字和小数点
                price = ''.join(filter(lambda c: c.isdigit() or c == '.', row[13]))
                description = row[22]
                features_2 = row[21]
                images_data = row[23]
                raw_images_list = images_data.split(',')
                images_list = [""] * 25
                
                # 处理图片列表,避免索引越界
                for x in range(min(len(raw_images_list), 25)):
                    images_list[x] = raw_images_list[x]
                
                if images_list[0] == "":
                    images_list[0] = "https://www.beverlyhillscarclub.com/template/images/ina_f.jpg"
                
                # 整理图片字段
                photo_fields = {
                    f'car_photo{x if x > 0 else ""}': downloadFile(images_list[x]) 
                    for x in range(25)
                }
                
                # 正确使用update_or_create
                Cars.objects.update_or_create(
                    vin=vin,
                    defaults={
                        'condition': condition,
                        'stock_no': stock_no,
                        'year': year,
                        'make': make,
                        'model': model,
                        'trim': trim,
                        'mileage': mileage,
                        'mpg_city': mpg_city,
                        'engine': engine,
                        'transmission': transmission,
                        'fuel_type': fuel_type,
                        'vehicle_type': vehicle_type,
                        'drive_type': drive_type,
                        'exterior_color': exterior_color,
                        'interior_color': interior_color,
                        'price': price,
                        'description': description,
                        'company_name': company.company_name,
                        'address': company.company_address,
                        'city': company.city,
                        'state': company.state,
                        'zip': company.zip_code,
                        'phone_number': company.phone_number,
                        'email': company.fax_number,
                        'features_2': features_2,
                        **photo_fields  # 展开图片字段
                    }
                )
        
        # 删除数据库中不在本次CSV里的记录
        if csv_vins:
            Cars.objects.exclude(vin__in=csv_vins).delete()
        
        obj.activated = True
        obj.save()

    data = {
        'form': form,
        'now': now,
    }
    return render(request, 'uploads.html', data)

内容的提问来源于stack exchange,提问作者Pablo Espinal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 22:40:34