def closure(cls): class_name = cls.__name__ + 'Localized' tablename = plural_underscored(class_name) if db.metadata.tables.get(tablename) is not None: return cls cls_columns = cls.__table__.get_children() columns = dict([(c.name, c.copy()) for c in cls_columns if isinstance(c.type, (db.Unicode, db.UnicodeText))]) localized_names = columns.keys() columns.update({ 'parent_id': db.Column(db.Integer, db.ForeignKey(cls.__tablename__ + '.id', ondelete="CASCADE", onupdate="CASCADE"), nullable=True), 'parent': db.relationship(cls, backref='localized_ref'), 'locale': db.Column(db.Unicode(255), default=lang, index=True) }) cls_localized = type(class_name, (db.Model, CRUDMixin), columns) for field in localized_names: create_property(cls, cls_localized, columns, field) return cls
class Address(db.Model, CRUDMixin): """ Represents address data for users By default model inherits id and created_at fields from the CRUDMixin """ __mapper_args__ = { 'order_by': ['country_id', 'city'] } city = db.Column(db.Unicode(255), nullable=False) street = db.Column(db.Unicode(255), nullable=False) apartment = db.Column(db.Unicode(20)) zip_code = db.Column(db.String(20)) customer_id = db.Column(db.Integer, db.ForeignKey('customers.id', ondelete='CASCADE', use_alter=True, name='fk_customer_id')) country_id = db.Column(db.Integer, db.ForeignKey('countries.id')) country = db.relationship('Country') def __repr__(self): return "<Address:('%s','%s')>" % (self.city, self.street) def save(self, commit=True): instance = super(Address, self).save(commit) if (instance.customer_id is not None and instance.id == instance.customer.billing_address_id): billing_data_changed.send(self, user_id=instance.customer.user_id) return instance def _type_get(self): if self.id == self.customer.billing_address_id: return 'billing' elif self.id == self.customer.delivery_address_id: return 'delivery' else: return None type = property(_type_get, lambda x, y: None) def as_dict(self, include=None, exclude=None): include = include or [] include.extend(['type']) return super(Address, self).as_dict(include, exclude)
class SocialConnection(db.Model, CRUDMixin): user_id = db.Column(db.Integer(), db.ForeignKey('users.id')) user = db.relationship('User', backref=db.backref('connections', lazy='dynamic'), cascade='all') provider_id = db.Column(db.String(255)) provider_user_id = db.Column(db.String(255)) access_token = db.Column(db.String(255)) secret = db.Column(db.String(255)) display_name = db.Column(db.Unicode(255)) profile_url = db.Column(db.String(512)) image_url = db.Column(db.String(512)) rank = db.Column(db.Integer)
class BankAccount(db.Model, CRUDMixin): bank_name = db.Column(db.Unicode(512)) iban = db.Column(db.String(256)) swift = db.Column(db.String(256)) updated_at = db.Column(db.DateTime, onupdate=datetime.now) user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False) user = db.relationship('User', backref=db.backref('accounts', lazy='dynamic')) def check_owner(self, user): return user.id == self.user_id def save(self, commit=True): instance = super(BankAccount, self).save(commit) billing_data_changed.send(self, user_id=instance.user_id) return instance
class FlatPage(db.Model, SlugMixin): """ A flatpage representation model """ content = db.Column(db.UnicodeText) template_name = db.Column(db.Unicode(512)) registration_required = db.Column(db.Boolean, default=False) @hybrid_property def slug(self): return self._slug @slug.setter def slug(self, name): self._slug = slugify(name, prefix=False) def save(self, commit=True): if not self.slug: return super(FlatPage, self).save(commit) return super(SlugMixin, self).save(commit)
class GalleryMixin(CRUDMixin): """Base mixin for Gallery objects """ name = db.Column(db.Unicode(512), nullable=False, default=u'') description = db.Column(db.UnicodeText, nullable=False, default=u'') is_public = db.Column(db.Boolean, default=True) @declared_attr def author_id(cls): return db.Column(db.Integer, db.ForeignKey("users.id"), nullable=False) @declared_attr def author(cls): return db.relationship('User', backref=db.backref(cls.__tablename__, lazy='dynamic')) def __repr__(self): return "<%s:%s>" % (self.__class__.__name__, self.name)
class Customer(db.Model, CRUDMixin): MIN_BIRTHDATE_YEAR = 1900 sex = db.Column(db.Unicode(1), index=True) birthdate = db.Column(db.DateTime, index=True) first_name = db.Column(db.Unicode(255), default=u'') last_name = db.Column(db.Unicode(255), default=u'') email = db.Column(db.String(80), index=True) phone = db.Column(db.String(80), default='') fax = db.Column(db.String(80), default='') gender = db.Column(db.String(1), default='') company = db.Column(db.Unicode(255), default=u'') updated_at = db.Column(db.DateTime, default=datetime.utcnow) notes = db.Column(db.UnicodeText) user_id = db.Column(db.Integer, db.ForeignKey('users.id')) user = db.relationship("User", backref=db.backref("customer", uselist=False)) addresses = db.relationship('Address', backref=db.backref('customer'), primaryjoin="Address.customer_id==Customer.id", cascade='all, delete', lazy='dynamic') billing_address_id = db.Column(db.Integer, db.ForeignKey('addresses.id', use_alter=True, name='fk_billing_address')) _billing_address = db.relationship("Address", cascade='all, delete', primaryjoin="Customer.billing_address_id==Address.id") delivery_address_id = db.Column(db.Integer, db.ForeignKey('addresses.id', use_alter=True, name='fk_delivery_address')) _delivery_address = db.relationship("Address", cascade='all, delete', primaryjoin="Customer.delivery_address_id==Address.id") @validates('birthdate') def validate_birthdate(self, key, value): min_date = datetime(self.MIN_BIRTHDATE_YEAR, 1, 1) if value < min_date: raise CustomerIsTooOldError() return value def __unicode__(self): return u"{0.first_name} {0.last_name}".format(self) @property def __addresses_ids(self): return map(attrgetter('id'), self.addresses) def set_address(self, addr_type, value): """ :param addr_type: Either `billing` or `delivery` to describe type the address will be used for :param value: Instance of the Address model """ if value.id not in self.__addresses_ids: self.addresses.append(value) setattr(self, "{}_address_id".format(addr_type), value.id) db.session.commit() if addr_type == 'billing': billing_data_changed.send(self, user_id=self.user_id) return self @hybrid_property def billing_address(self): """ Hybrid property allowing only one billing-address per-customer """ return self._billing_address @billing_address.setter def billing_address(self, value): """ setter for billing_address property """ self.set_address('billing', value) @hybrid_property def delivery_address(self): """ Hybrid property allowing only one delivery_address per-customer """ return self._delivery_address @delivery_address.setter def delivery_address(self, value): """ setter for delivery_address property """ self.set_address('delivery', value) @property def organizer_ready(self): if self.user: return self.billing_address and self.user.accounts.count() else: return False
def name(cls): return db.Column(db.Unicode(512), nullable=False)
class OrderMixin(CRUDMixin): shop_id = db.Column(db.String(128), default=SHOP_ID) billing_city = db.Column(db.Unicode(255)) billing_street = db.Column(db.Unicode(255)) billing_apartment = db.Column(db.Unicode(20)) billing_zip_code = db.Column(db.String(20)) delivery_city = db.Column(db.Unicode(255)) delivery_street = db.Column(db.Unicode(255)) delivery_apartment = db.Column(db.Unicode(20)) delivery_zip_code = db.Column(db.String(20)) # summary cost of all cart items linked with this order goods_price = db.Column(db.Numeric(precision=18, scale=2)) vat = db.Column(db.Numeric(precision=18, scale=2)) total_price = db.Column(db.Numeric(precision=18, scale=2)) payment_method = db.Column(db.String, nullable=False, index=True) state = db.Column(db.Integer, index=True) # stored cost for the order delivery #delivery_method = db.Column(db.String, nullable=False, index=True) delivery_price = db.Column(db.Numeric(precision=18, scale=2)) @declared_attr def billing_country_id(cls): return db.Column(db.Integer, db.ForeignKey('countries.id', use_alter=True, name='fk_billing_country')) @declared_attr def delivery_country_id(cls): return db.Column(db.Integer, db.ForeignKey('countries.id', use_alter=True, name='fk_delivery_country')) @declared_attr def customer_id(cls): return db.Column(db.Integer, db.ForeignKey('customers.id'), nullable=False, index=True) @declared_attr def customer(cls): return db.relationship('Customer', backref=db.backref('orders', **lazy_cascade)) @declared_attr def goods(cls): return db.relationship('Cart', backref='order', **lazy_cascade) @classmethod def create_from_api(cls, customer_id, **kwargs): """ This method should be overrided in Order model implemetation """ raise NotImplementedError() def mark_paid(self): order_paid.send(current_app._get_current_object(), order=self) return self.update(state=OrderStates.paid) def resolve_payment(self, method=None): payment_method = self.payment_method or method method = current_app.config['PAYMENT_METHODS'][payment_method] class_string = method['module'] PaymentMethod = import_string(class_string) return PaymentMethod(self) def set_payment_details(self, **kwargs): raise NotImplementedError("Payment Details: %s", kwargs) @classmethod def get_by_payment_details(cls, **kwargs): raise NotImplementedError("Payment Details: %s", kwargs) @classmethod def _prepare_address(cls, addr_type, address_instance): exclude_fields = ['customer_id', 'created_at', 'id', 'type'] address_dict = address_instance.as_dict(exclude=exclude_fields) return dict(('{}_{}'.format(addr_type, key), value) for key, value in address_dict.iteritems()) @classmethod def _resolve_delivery(cls, delivery, address): return delivery.calculate_price(address) @classmethod def cancel_payment(cls, payment_method, **kwargs): """ This method is called when payment is canceled by customer. Override it :param payment_method: string which identifies payment method :param kwargs: additional params passed to identify the payment """ raise NotImplementedError() def cancel_by_merchant(self): """ Cancels order as if merchant decided to do so, e.g. when customer didn't pay in time """ self._delete_carts() self.update(state=OrderStates.merchant_canceled) def _delete_carts(self): cart_cls = get_cart_class() carts = cart_cls.query.filter_by(order_id=self.id) # we don't use bulk delete, because there's a special Cart.delete() for cart in carts: cart.delete() @classmethod def expired(cls, max_age): """ Returns all order items in 'created' state with age >= max_age :param max_age: timedelta object """ min_created_at = datetime.utcnow() - max_age return cls.query.filter(cls.created_at <= min_created_at, cls.state == OrderStates.created)