Полиморфные отношения FlaskSQLAlchemy «многие ко многим»

#python #sqlite #sqlalchemy #flask-sqlalchemy

#python #sqlite #sqlalchemy #flask-sqlalchemy

Вопрос:

У меня есть модель продукта, а также варианты модели продукта. Я хочу создать отношения «многие ко многим» между моделями продуктов и моделью категории (включая варианты продукта, которые наследуются от базовой модели продукта).

 product_category = db.Table('product_category', db.Model.metadata,
    db.Column('product_id', db.Integer, db.ForeignKey('product.id'), primary_key=True),
    db.Column('category_id', db.Integer, db.ForeignKey('category.id'), primary_key=True)
)

class Category(db.Model):
    __table_name__ = 'category'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(MaxLengths.category_name))
    # products = db.relationship('Product', secondary='product_category')

    def __repr__(self):
        return '<Category %s>' % self.name

class Product(db.Model):
    '''
    Generic Product Model - this is the parent of SubProducts for specific purposes
    '''
    id = db.Column(db.Integer, primary_key=True)
    product_type = db.Column(
        db.String(MaxLengths.product_type), nullable=False)
    name = db.Column(db.String(MaxLengths.product_name),
                     unique=True, nullable=False)
    price = db.Column(db.Float())
    description = db.Column(
        db.String(MaxLengths.product_description), nullable=False)
    categories = db.relationship('Category', secondary=product_category)
    reviews = db.relationship('Review', backref='product')
    orders_count = db.Column(db.Integer, default=0)
    stock = db.Column(db.Integer(), nullable=False)
    order_id = db.Column(db.Integer, db.ForeignKey('orders.id'))
    is_raffle_product = db.Column(db.Boolean, default=False)
    raffle_start_date = db.Column(ArrowType)
    raffle_end_date = db.Column(ArrowType)

    __mapper_args__ = {
        'polymorphic_identity': 'product',
        'polymorphic_on': product_type
    }

class ProductVariant(Product):
    id = db.Column(db.Integer, db.ForeignKey('product.id'), primary_key=True)
    prices = db.Column(db.JSON(), nullable=False)  # This is a list object
    weights = db.Column(db.JSON(), nullable=False)

    __mapper_args__ = {'polymorphic_identity': 'product_variant'}
 

Однако при попытке создать вариант модели продукта со следующим кодом я получаю следующую ошибку

 db.drop_all()
db.create_all()
product = ProductVariant(name="test product")
category = Category(name='test category')
product.categories.append(category)
 
 [jack@DESKTOP-6FAK8Q8 DIRECTORY]$ ./flask.sh testdb
[ ] Started redis-server
/usr/lib/python3.9/site-packages/flask_sqlalchemy/__init__.py:833: FSADeprecationWarning: SQLALCHEMY_TRACK_MODIFICATIONS adds significant overhead and will be disabled by default in the future.  Set it to True or False to suppress this warning.
  warnings.warn(FSADeprecationWarning(
/usr/lib/python3.9/site-packages/sqlalchemy/sql/ddl.py:903: SAWarning: Can't sort tables for DROP; an unresolvable foreign key dependency exists between tables: discount_code, orders, user; and backend does not support ALTER.  To restore at least a partial sort, apply use_alter=True to ForeignKey and ForeignKeyConstraint objects involved in the cycle to mark these as known cycles that will be ignored.
  util.warn(
Traceback (most recent call last):
  File "/usr/sbin/flask", line 33, in <module>
    sys.exit(load_entry_point('Flask==1.1.2', 'console_scripts', 'flask')())
  File "/usr/lib/python3.9/site-packages/flask/cli.py", line 967, in main
    cli.main(args=sys.argv[1:], prog_name="python -m flask" if as_module else None)
  File "/usr/lib/python3.9/site-packages/flask/cli.py", line 586, in main
    return super(FlaskGroup, self).main(*args, **kwargs)
  File "/usr/lib/python3.9/site-packages/click/core.py", line 782, in main
    rv = self.invoke(ctx)
  File "/usr/lib/python3.9/site-packages/click/core.py", line 1259, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/lib/python3.9/site-packages/click/core.py", line 1066, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/lib/python3.9/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/usr/lib/python3.9/site-packages/click/decorators.py", line 21, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/usr/lib/python3.9/site-packages/flask/cli.py", line 426, in decorator
    return __ctx.invoke(f, *args, **kwargs)
  File "/usr/lib/python3.9/site-packages/click/core.py", line 610, in invoke
    return callback(*args, **kwargs)
  File "/mnt/d/code/python/DIRECTORY/commands.py", line 29, in testdb
    product_variant = ProductVariant(name='test product', product_type="product_variant")
  File "<string>", line 4, in __init__
  File "/usr/lib/python3.9/site-packages/sqlalchemy/orm/state.py", line 433, in _initialize_instance
    manager.dispatch.init_failure(self, args, kwargs)
  File "/usr/lib/python3.9/site-packages/sqlalchemy/util/langhelpers.py", line 68, in __exit__
    compat.raise_(
  File "/usr/lib/python3.9/site-packages/sqlalchemy/util/compat.py", line 182, in raise_
    raise exception
  File "/usr/lib/python3.9/site-packages/sqlalchemy/orm/state.py", line 430, in _initialize_instance
    return manager.original_init(*mixed[1:], **kwargs)
  File "<string>", line 6, in __init__
  File "/usr/lib/python3.9/site-packages/sqlalchemy/ext/declarative/base.py", line 842, in _declarative_constructor
    setattr(self, k, kwargs[k])
  File "/usr/lib/python3.9/site-packages/sqlalchemy/orm/attributes.py", line 272, in __set__
    self.impl.set(
  File "/usr/lib/python3.9/site-packages/sqlalchemy/orm/attributes.py", line 1334, in set
    collections.bulk_replace(
  File "/usr/lib/python3.9/site-packages/sqlalchemy/orm/collections.py", line 801, in bulk_replace
    appender(member, _sa_initiator=initiator)
  File "/usr/lib/python3.9/site-packages/sqlalchemy/orm/collections.py", line 1116, in append
    item = __set(self, item, _sa_initiator)
  File "/usr/lib/python3.9/site-packages/sqlalchemy/orm/collections.py", line 1081, in __set
    item = executor.fire_append_event(item, _sa_initiator)
  File "/usr/lib/python3.9/site-packages/sqlalchemy/orm/collections.py", line 717, in fire_append_event
    return self.attr.fire_append_event(
  File "/usr/lib/python3.9/site-packages/sqlalchemy/orm/attributes.py", line 1176, in fire_append_event
    value = fn(state, value, initiator or self._append_token)
  File "/usr/lib/python3.9/site-packages/sqlalchemy/orm/attributes.py", line 1485, in emit_backref_from_collection_append_event
    child_state, child_dict = instance_state(child), instance_dict(child)
AttributeError: 'str' object has no attribute '_sa_instance_state'
[-] redis-server has shutdown

 

Комментарии:

1. Ваше определение таблицы категорий неверно. Удалите product_id = и category_id = . Имена столбцов определяются первым аргументом Column .

2. Большое вам спасибо, я тоже только что это заметил, хотя, к сожалению, сейчас я получаю другую ошибку :/

3. Ошибка, которую вы получаете, не связана ни с одним из вышеперечисленных. Между пользователем, кодом скидки и заказами существует несвязанная зависимость от внешнего ключа.