Я учусь использовать vu.js самостоятельно. Так что моих знаний пока почти нет. Я пытался разобраться в сообщениях с одной и той же проблемой, но не смог найти решения.
Во-первых, вот моя проблема:
Я пытаюсь добавить фрагмент с его тегами в базу данных.
Есть 3 таблицы: tags, snippets и snippet_tags.
У меня есть функция, которая создает фрагмент, и он отлично работает, когда я не использую теги.
У меня есть функция, которая связывает теги с фрагментами, и она отлично работает сама по себе.
Теперь, когда я пытаюсь объединить это, это не работает, поэтому я не могу создать фрагмент с тегами.
Вот моя схема БД:
CREATE TABLE public.snippet_tags (
snippet_id integer NOT NULL,
tag_id integer NOT NULL
);
CREATE TABLE public.snippets (
id integer NOT NULL,
title character varying(255) NOT NULL,
description text NOT NULL,
content text NOT NULL,
category_id integer NOT NULL,
subcategory_id integer,
public boolean,
user_id integer NOT NULL,
created_at timestamp without time zone,
language character varying(50) NOT NULL
);
CREATE TABLE public.tags (
id integer NOT NULL,
name character varying(50) NOT NULL
);
Вот содержимое snippet_controller.py:
import logging
from sqlalchemy.exc import SQLAlchemyError
from flask import request, jsonify
from app import db
from app.models.snippet import Snippet
from app.models.tag import Tag
logger = logging.getLogger('my_logger')
def create_snippet():
data = request.get_json()
title = data.get('title')
description = data.get('description')
content = data.get('content')
category_id = data.get('category_id')
subcategory_id = data.get('subcategory_id')
public = data.get('public', False)
user_id = data.get('user_id')
language = data.get('language')
tags = data.get('tags', []) # Use tags from request body
if not all([title, description, content, category_id, user_id, language]):
logger.error("Required fields are missing")
return jsonify({"error": "All required fields must be provided"}), 400
try:
logger.debug("Creating new snippet")
new_snippet = Snippet(
title=title,
description=description,
content=content,
category_id=category_id,
subcategory_id=subcategory_id,
public=public,
user_id=user_id,
language=language
)
db.session.add(new_snippet)
db.session.commit()
logger.debug(f"New snippet created with ID: {new_snippet.id}")
# Call associate_tags function to handle tag association
response = associate_tags(new_snippet.id, tags)
if response[1] != 200:
return response
return jsonify(new_snippet.to_dict()), 201
except SQLAlchemyError as e:
db.session.rollback()
logger.error(f'SQLAlchemyError: {str(e)}', exc_info=True)
return jsonify({"error": str(e.orig)}), 500
except Exception as e:
db.session.rollback() # Ensure rollback on any exception
logger.error(f'Unhandled Exception: {str(e)}', exc_info=True)
return jsonify({"error": str(e)}), 500
def get_snippets():
try:
snippets = Snippet.query.all()
return jsonify([snippet.to_dict() for snippet in snippets])
except SQLAlchemyError as e:
return jsonify({"error": str(e.orig)}), 500
def get_snippet(snippet_id):
try:
snippet = Snippet.query.get_or_404(snippet_id)
return jsonify(snippet.to_dict())
except SQLAlchemyError as e:
return jsonify({"error": str(e.orig)}), 500
def update_snippet(snippet_id):
data = request.get_json()
try:
snippet = Snippet.query.get_or_404(snippet_id)
snippet.title = data.get('title', snippet.title)
snippet.description = data.get('description', snippet.description)
snippet.content = data.get('content', snippet.content)
snippet.category_id = data.get('category_id', snippet.category_id)
snippet.subcategory_id = data.get('subcategory_id', snippet.subcategory_id)
snippet.public = data.get('public', snippet.public)
snippet.language = data.get('language', snippet.language)
db.session.commit()
return jsonify(snippet.to_dict()), 200
except SQLAlchemyError as e:
db.session.rollback()
return jsonify({"error": str(e.orig)}), 500
def delete_snippet(snippet_id):
try:
snippet = Snippet.query.get_or_404(snippet_id)
db.session.delete(snippet)
db.session.commit()
return '', 204
except SQLAlchemyError as e:
db.session.rollback()
return jsonify({"error": str(e.orig)}), 500
def associate_tags(snippet_id, tags):
try:
snippet = Snippet.query.get_or_404(snippet_id)
snippet.tags = [] # Clear existing tags
for tag_name in tags:
logger.debug(f"Processing tag: {tag_name}")
tag = Tag.query.filter_by(name=tag_name).first()
if not tag:
logger.debug(f"Creating new tag: {tag_name}")
tag = Tag(name=tag_name)
db.session.add(tag)
db.session.commit() # Commit new tag immediately
logger.debug(f"Appending tag: {tag_name} -> Tag instance: {tag}")
snippet.tags.append(tag)
db.session.commit()
logger.debug("Tags associated and committed to DB")
return jsonify({"message": "Tags associated successfully"}), 200
except SQLAlchemyError as e:
db.session.rollback()
logger.error(f'SQLAlchemyError: {str(e)}', exc_info=True)
return jsonify({"error": str(e.orig)}), 500
except Exception as e:
db.session.rollback() # Ensure rollback on any exception
logger.error(f'Unhandled Exception: {str(e)}', exc_info=True)
return jsonify({"error": str(e)}), 500
def get_snippets_by_tag(tag_id):
try:
tag = Tag.query.get_or_404(tag_id)
snippets = [snippet.to_dict() for snippet in tag.snippets]
return jsonify(snippets), 200
except SQLAlchemyError as e:
return jsonify({"error": str(e.orig)}), 500
Тогда в snippet.py вы увидите это:
from app import db
from sqlalchemy.orm import relationship
class Snippet(db.Model):
__tablename__ = 'snippets'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(255), nullable=False)
description = db.Column(db.Text, nullable=False)
content = db.Column(db.Text, nullable=False)
category_id = db.Column(db.Integer, db.ForeignKey('categories.id'), nullable=False)
subcategory_id = db.Column(db.Integer, db.ForeignKey('subcategories.id'))
public = db.Column(db.Boolean, default=False)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
created_at = db.Column(db.DateTime, default=db.func.current_timestamp())
language = db.Column(db.String(50), nullable=False)
# Relationships
category = relationship('Category', backref=db.backref('snippets', lazy=True))
subcategory = relationship('Subcategory', backref=db.backref('snippets', lazy=True))
user = relationship('User', backref=db.backref('snippets', lazy=True))
tags = relationship('Tag', secondary='snippet_tags', back_populates='snippets')
def to_dict(self):
return {
'id': self.id,
'title': self.title,
'description': self.description,
'content': self.content,
'category_id': self.category_id,
'subcategory_id': self.subcategory_id,
'public': self.public,
'user_id': self.user_id,
'created_at': self.created_at.isoformat() if self.created_at else None,
'language': self.language,
'tags': [tag.name for tag in self.tags] # Include tag names in the dictionary
}
class Favorite(db.Model):
__tablename__ = 'favorites'
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), primary_key=True)
snippet_id = db.Column(db.Integer, db.ForeignKey('snippets.id'), primary_key=True)
# Relationships
user = relationship('User', backref=db.backref('favorites', cascade="all, delete-orphan"))
snippet = relationship('Snippet', backref=db.backref('favorites', cascade="all, delete-orphan"))
Можете ли вы объяснить мою ошибку и внести исправление?
Единственная ошибка, которую я вижу в докере: : Объект 'str' не имеет атрибута '_sa_instance_state'ОБНОВЛЕНИЕ: логи Docker:
2024-07-13 12:26:49 INFO:werkzeug:WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
2024-07-13 12:26:49 * Running on all addresses (0.0.0.0)
2024-07-13 12:26:49 * Running on http://127.0.0.1:5000
2024-07-13 12:26:49 * Running on http://192.168.128.4:5000
2024-07-13 12:26:49 INFO:werkzeug:Press CTRL+C to quit
2024-07-13 12:26:49 INFO:werkzeug: * Restarting with stat
2024-07-13 12:26:50 WARNING:werkzeug: * Debugger is active!
2024-07-13 12:26:50 INFO:werkzeug: * Debugger PIN: 837-231-156
2024-07-13 12:26:49 * Serving Flask app 'server.py'
2024-07-13 12:26:49 * Debug mode: on
2024-07-13 12:28:17 /usr/local/lib/python3.9/site-packages/flask_sqlalchemy/model.py:22: SAWarning: relationship 'SnippetTag.snippet' will copy column snippets.id to column snippet_tags.snippet_id, which conflicts with relationship(s): 'Snippet.tags' (copies snippets.id to snippet_tags.snippet_id), 'Tag.snippets' (copies snippets.id to snippet_tags.snippet_id). If this is not the intention, consider if these relationships should be linked with back_populates, or if viewonly=True should be applied to one or more if they are read-only. For the less common case that foreign key constraints are partially overlapping, the orm.foreign() annotation can be used to isolate the columns that should be written towards. To silence this warning, add the parameter 'overlaps="snippets,tags"' to the 'SnippetTag.snippet' relationship. (Background on this warning at: https://sqlalche.me/e/20/qzyx) (This warning originated from the `configure_mappers()` process, which was invoked automatically in response to a user-initiated operation.)
2024-07-13 12:28:17 return cls.query_class(
2024-07-13 12:28:17 /usr/local/lib/python3.9/site-packages/flask_sqlalchemy/model.py:22: SAWarning: relationship 'SnippetTag.tag' will copy column tags.id to column snippet_tags.tag_id, which conflicts with relationship(s): 'Snippet.tags' (copies tags.id to snippet_tags.tag_id), 'Tag.snippets' (copies tags.id to snippet_tags.tag_id). If this is not the intention, consider if these relationships should be linked with back_populates, or if viewonly=True should be applied to one or more if they are read-only. For the less common case that foreign key constraints are partially overlapping, the orm.foreign() annotation can be used to isolate the columns that should be written towards. To silence this warning, add the parameter 'overlaps="snippets,tags"' to the 'SnippetTag.tag' relationship. (Background on this warning at: https://sqlalche.me/e/20/qzyx) (This warning originated from the `configure_mappers()` process, which was invoked automatically in response to a user-initiated operation.)
2024-07-13 12:28:17 return cls.query_class(
2024-07-13 12:28:17 INFO:werkzeug:192.168.128.1 - - [13/Jul/2024 10:28:17] "POST /api/tags HTTP/1.1" 201 -
2024-07-13 12:28:37 ERROR:app:Unhandled Exception: 'str' object has no attribute '_sa_instance_state'
2024-07-13 12:28:37 INFO:werkzeug:192.168.128.1 - - [13/Jul/2024 10:28:37] "POST /api/snippets HTTP/1.1" 500 -
Подробнее здесь: https://stackoverflow.com/questions/787 ... ance-state