如何保存关联多张图片的Django模型?解决后台仅显示单图问题
问题根因
- Image实例初始化位置错误:你在公寓遍历循环的外层仅创建了一个
Image对象,遍历同个公寓的多张图片时反复修改同一个实例,最终数据库只会保留最后一张图片的记录 - 未执行Image模型的入库操作:仅调用了
ImageField的save()方法处理文件存储,没有调用Image实例自身的save()方法把数据写入数据库 - save_data调用逻辑错误:在爬取每个公寓的循环内部就调用
save_data,会重复处理列表中已经存在的公寓数据,产生冗余记录 - 下载的临时图片文件没有清理,会残留多余文件在项目根目录
代码修正方案
1. 调整scraper中save_data的调用位置
把save_data(apartments)从for link循环内移到循环结束后,爬完所有公寓再统一执行保存:
@receiver(post_save, sender=Url) def saved_url(instance, created, **kwargs): apartments = [] if created: url = instance.url soup = make_request(url) for link in soup.find_all('div', attrs={'data-name': 'LinkArea'}): url = link.find('a').get('href') driver.get(url) time.sleep(1) try: rooms = driver.find_element(By.XPATH, '//div[1][@data-name="OfferTitle"]/h1').text except: rooms = None try: price = driver.find_element(By.XPATH, '//div[1]/div/span/span[1][@itemprop="price"]').text except: price = None try: address = driver.find_element(By.XPATH, '//section/div/div[1]/div[2]/span[@itemprop="name"]').get_attribute( 'content') except: address = None try: desc = driver.find_element(By.XPATH, '//div/span/p[@itemprop="description"]').text except: desc = None try: floor = driver.find_element(By.XPATH, '//div/div[4]/div[1][@data-testid="object-summary-description-value"]').text except: floor = None try: photos = [img.get_attribute('src') for img in driver.find_elements(By.CLASS_NAME, 'fotorama__img')] except: photos = None apartments.append( { 'rooms': rooms, 'price': price, 'address': address, 'desc': desc, 'floor': floor, 'photos': photos, } ) # 移到循环外,爬完所有公寓再保存 save_data(apartments)
2. 重写save_data存储逻辑
import os def save_data(apartments_list): for ap in apartments_list: try: # 先创建公寓记录 apartment = Apartment.objects.create( rooms=ap['rooms'], price=ap['price'], address=ap['address'], desc=ap['desc'], floor=ap['floor'], ) # 每张图片都新建一个Image实例 for image in ap['photos']: pic_name = image.split('/')[-1] # 下载图片到临时路径 pic_path = urllib.request.urlretrieve(image, pic_name)[0] # 新建Image对象 im = Image() im.apartment = apartment # 保存图片字段 im.img.save(pic_name, File(open(pic_path, 'rb'))) # 把Image实例写入数据库 im.save() # 清理临时下载的图片文件 os.remove(pic_path) except Exception as e: print(e) break
额外校验项
确保你在admin.py中已经配置了Image的内联展示,才能在Apartment的详情页看到所有关联图片:
from django.contrib import admin from .models import Apartment, Image, Url class ImageInline(admin.TabularInline): model = Image extra = 0 @admin.register(Apartment) class ApartmentAdmin(admin.ModelAdmin): inlines = [ImageInline] @admin.register(Url) class UrlAdmin(admin.ModelAdmin): pass
内容的提问来源于stack exchange,提问作者Evgeny
相关产品推荐
相关产品推荐

