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ề https://datvnn.com/t/tao-models-thu-vien-hinh-anh-cho-san-pham-trong-django/313

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.