如何将Scrapy爬取数据存入Django PostgreSQL的两张关联表并配置Sqlalchemy关系
解决方案
1 调整SQLAlchemy侧models.py关联配置
补全导入,添加外键和关联关系,修改后代码如下:
# models.py - Sqlalchemy from sqlalchemy import Column, Integer, String, create_engine, Boolean, DateTime, ForeignKey, Table, event from sqlalchemy.orm import relationship, declarative_base # 新增导入relationship from . import settings DeclarativeBase = declarative_base() class Shop_Product(DeclarativeBase): __tablename__ = "shop_product" id = Column(Integer, primary_key=True) slug = Column("slug", String, unique=True) title = Column("title", String) # 新增反向关联,和Django侧related_name保持一致,cascade设置为删除商品时同步删除关联图片 product_image = relationship("Shop_ProductImage", back_populates="product", cascade="all, delete-orphan") class Shop_ProductImage(DeclarativeBase): __tablename__ = "shop_productimage" id = Column(Integer, primary_key=True) # 修改product_id为外键,关联商品表id product_id = Column("product_id", Integer, ForeignKey("shop_product.id"), nullable=False) image = Column("image", String) # 新增正向关联,指向商品实例 product = relationship("Shop_Product", back_populates="product_image")
修改说明:
- 给
product_id字段添加ForeignKey约束,和Django侧的外键逻辑对齐,保证数据一致性 - 添加双向
relationship关联,后续可以直接通过商品实例操作关联的图片数据,不需要手动查询关联id cascade配置和Django侧on_delete=models.CASCADE逻辑一致,删除商品时自动删除关联的图片数据
2 修改Scrapy侧Pipeline实现双表同步写入
调整process_item方法,在商品数据写入完成后,同步写入图片数据,修改后代码如下:
# piplines.py from sqlalchemy.orm import sessionmaker from .models import Shop_Product, Shop_ProductImage, create_items_table, db_connect # 新增导入Shop_ProductImage class ShoppSpinderPipeline(object): def __init__(self): engine = db_connect() create_items_table(engine) Session = sessionmaker(bind=engine) self.session = Session() def process_item(self, item, spider): item_exists = self.session.query(Shop_Product).filter_by(slug=item['slug']).first() if item_exists: item_exists.title = item['title'] print('SP {} updated.'.format(item['title'])) product_instance = item_exists else: new_item = Shop_Product(title=item['title'], slug=item['slug']) # 不要直接传**item,避免把image字段传给商品模型 self.session.add(new_item) self.session.flush() # flush后可以拿到新增商品的自增id,不需要先commit product_instance = new_item print('Item {} created.'.format(item['title'])) # 处理图片数据写入 if item.get('image'): # 单图逻辑:查询该商品是否已有图片,有则更新,无则新增;多图可调整为循环写入 image_exists = self.session.query(Shop_ProductImage).filter_by(product_id=product_instance.id).first() if image_exists: image_exists.image = item['image'] else: new_image = Shop_ProductImage(product_id=product_instance.id, image=item['image']) self.session.add(new_image) return item def close_spider(self, spider): try: self.session.commit() except: self.session.rollback() raise finally: self.session.close()
修改说明:
- 新增商品时不要直接用
**item实例化商品模型,避免把image字段传入商品表导致报错 - 新增商品后调用
session.flush()即可拿到自增id,不需要提前提交事务,保证数据一致性 - 图片写入逻辑支持单图更新/新增,若业务为多图,可将item的image字段改为列表,循环创建
Shop_ProductImage实例即可
注意:如果需要Scrapy自动下载图片,需先开启Scrapy自带的
ImagesPipeline,将下载后的本地存储路径赋值给item的image字段,路径需要和Django侧upload_to="product_images/"的配置匹配,保证Django可以正常读取图片。
内容的提问来源于stack exchange,提问作者datnguyen
相关产品推荐
相关产品推荐

