Source code for fardel.apps.ecommerce.product.models

import math
import time

from sqlalchemy.dialects.postgresql import JSONB

from fardel.core.seo import SeoModel
from fardel.ext import db

__all__ = (
    "ProductCategory", "Collection", "ProductType",
    "Product", "ProductVariant", "ProductAttribute",
    "AttributeChoiceValue",
)


[docs]class ProductCategory(db.Model, SeoModel): __tablename__ = "product_categories" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(32), nullable=False) description = db.Column(db.Text) hidden = db.Column(db.Boolean, default=False) parent_id = db.Column(db.Integer, db.ForeignKey('product_categories.id')) parent = db.relationship('ProductCategory', remote_side=[id]) children = db.relationship('ProductCategory') def dict(self, products=False, page=None, per_page=None): obj = dict( id=self.id, name=self.name, subcategories=[cat.dict() for cat in self.children] ) if products: query = Product.query.filter_by(category_id=self.id, is_published=True) pages = math.ceil(query.count() / per_page) products = query.paginate(page=page, per_page=per_page, error_out=False).items obj.update(pages=pages, products=[p.dict() for p in products]) obj.update(self.seo_dict()) return obj
class ProductType(db.Model): __tablename__ = "product_product_types" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128), nullable=False) has_variants = db.Column(db.Boolean, default=True) product_attributes = db.relationship('ProductAttribute', secondary="product_product_types_attributes") variant_attributes = db.relationship('ProductVariant', secondary="product_product_types_variants") is_shipping_required = db.Column(db.Boolean, default=False) def dict(self): pass class ProductTypeAttributes(db.Model): __tablename__ = "product_product_types_attributes" id = db.Column(db.Integer, primary_key=True) product_type_id = db.Column(db.Integer, db.ForeignKey('product_product_types.id')) product_attributes_id = db.Column(db.Integer, db.ForeignKey('product_products_attributes.id')) class ProductTypeVariants(db.Model): __tablename__ = "product_product_types_variants" id = db.Column(db.Integer, primary_key=True) product_type_id = db.Column(db.Integer, db.ForeignKey('product_product_types.id')) product_variants_id = db.Column(db.Integer, db.ForeignKey('product_product_variants.id')) class Product(db.Model, SeoModel): __tablename__ = "product_products" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(32), nullable=False) description = db.Column(db.Text) price = db.Column(db.Integer, nullable=False) weight = db.Column(db.Integer, default=0) updated_at = db.Column(db.Integer, default=time.time, onupdate=time.time) images = db.Column(JSONB(), default={}) attributes = db.Column(JSONB(), default={}) is_published = db.Column(db.Boolean, default=True) is_featured = db.Column(db.Boolean, default=False) category_id = db.Column(db.Integer, db.ForeignKey('product_categories.id')) product_type_id = db.Column(db.Integer, db.ForeignKey('product_product_types.id')) product_type = db.relationship('ProductType') product_variants = db.relationship('ProductVariant') def dict(self, detailed=False): obj = dict(name=self.name, price=self.price) if detailed: obj.update(variants=[v.dict() for v in self.product_variants]) obj.update(description=self.description, images=self.images) obj.update(self.seo_dict()) else: obj.update(image=self.images[0]) return obj class ProductVariant(db.Model): __tablename__ = "product_product_variants" id = db.Column(db.Integer, primary_key=True) sku = db.Column(db.String(32), unique=True, nullable=True) name = db.Column(db.String(255), default="") price_override = db.Column(db.Integer, nullable=True) product_id = db.Column(db.Integer, db.ForeignKey('product_products.id')) attributes = db.Column(JSONB(), default={}) quantity = db.Column(db.Integer, default=1) quantity_allocated = db.Column(db.Integer, default=0) product = db.relationship("Product") @property def quantity_available(self): return max(self.quantity - self.quantity_allocated, 0) def check_quantity(self, quantity): if quantity > self.quantity_available: return False return True def get_price_per_item(self, discounts=None): price = self.price_override or self.product.price price = calculate_discounted_price(self.product, price, discounts) return price @property def is_shipping_required(self): return self.product.product_type.is_shipping_required @property def first_image(self): return self.product.images[0] def dict(self): obj = dict(sku=self.sku, name=self.name, price_override=self.price_override,) class ProductAttribute(db.Model): __tablename__ = "product_products_attributes" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(128)) choices = db.relationship('AttributeChoiceValue', lazy="joined") def dict(self): pass class AttributeChoiceValue(db.Model): __tablename__ = "product_products_attributechoicevalue" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100)) attribute_id = db.Column(db.Integer, db.ForeignKey("product_products_attributes.id")) attribute = db.relationship('ProductAttribute') def dict(self): pass class Collection(db.Model, SeoModel): __tablename__ = "product_collections" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String(100), unique=True) is_published = db.Column(db.Boolean, default=False) products = db.relationship('Product', secondary='product_products_collections', lazy='select') def dict(self): return { }.update(self.seo_dict()) def __str__(self): return self.name class ProductCollection(db.Model): __tablename__ = "product_products_collections" id = db.Column(db.Integer, primary_key=True) product_id = db.Column(db.Integer, db.ForeignKey('product_products.id')) collection_id = db.Column(db.Integer, db.ForeignKey('product_collections.id'))