Ví dụ về Relationship và Foreignkey khi lưu dữ liệu từ Scrapy Crawl

Có mô hình như sau (django)

# models.py - django
class Product(models.Model):  # thêm các trường, chay migration database
    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200, unique=True)

    def __str__(self):
        return self.title

class Image(models.Model):
    products = models.ForeignKey(Product, blank=True, null=True, on_delete=models.CASCADE, related_name="product_image")  # sẽ tạo 1 cột products_id
    image_name = models.ImageField(upload_to="product_images_gallery/", default="no_image.jpg")
    alt_text = models.CharField(max_length=255,null=True,blank=True)
   
    def __str__(self):
        return self.products.title

Trong mô hình trên, lớp Image là Gallery hình ảnh của lớp Product. Nó có mối quan hệ Một-Nhiều: 1 sản phẩm có thể có nhiều hình ảnh. Khóa ngoại được đặt bên trong lớp Image.

Xem thêm về Tạo models Thư viện hình ảnh cho sản phẩm trong Django

Theo mô hình trên, trong database sẽ có 2 bảng là productimage. Và để lưu dữ liệu vào 2 bảng trên trong 1 lần chạy Crawl và giữ mối liên quan với nhau thì sử dụng SQLAlchemy Relationship.

Trong mô hình SQLALchemy thiết lập như sau

#models.py - sqlalchemy
class Shop_Product(DeclarativeBase):

    __tablename__ = "shop_product"

    id = Column(Integer, primary_key=True)
    slug = Column("slug", String, unique=True)
    title = Column("title", String)


class Shop_Image(DeclarativeBase):
    __tablename__ = "shop_image"

    id = Column(Integer, primary_key=True)
    image_name = Column("image_name", String)
    products_id = Column(Integer, ForeignKey('shop_product.id'))

    # relationship
    products = relationship('Shop_Product', backref='image1') # image1 được sử dụng trong pipelines

Trong nhiều hướng dẫn về Relationship, họ không chỉ bạn phải làm gì trong pipelines.py

Tiếp theo tới tệp pipelines.py

# pipelines.py - scrapy
class SaveSpinderPipeline(object):
    def __init__(self):
        """ 
        Tạo kết nối với database
        """
        engine = db_connect()
        create_items_table(engine)
        self.Session = sessionmaker(bind=engine)

    def process_item(self, item, spider):
        session = self.Session()
        pro = Shop_Product()
        img = Shop_Image()

        pro.slug = item['slug']
        pro.title = item['title']
        img.image_name = item['image']

        # kiểm tra xem hình ảnh đã tồn tại chưa
        exist_img = session.query(Shop_Image).filter_by(image_name=img.image_name).first()
        if exist_img is not None:
            pro.image1 = exist_img
        else:
            pro.image1.append(img)

        try:
            session.add(pro)
            session.commit()
        except:
            session.rollback()
            raise
        finally:
            session.close()

        return item

Trường hợp trên đây sử dụng khi bạn muốn lấy nhiều hình ảnh cho sản phẩm. Nếu bạn chỉ có cần hình ảnh cho 1 sản phẩm, hãy thêm trường image vào class Product. Khi đó mọi thứ sẽ dễ dàng hơn.