Wpis z mikrobloga

No hej.

Mam pewien problem z combem Flask + SQLAlchemy +Postgres.

Mam sobie modele zdefiniowane w SQLAlchemy:

post_tags = Table('post_tags', Base.metadata,
Column('post_id', Integer, ForeignKey('post.id')),
Column('tag_nametag', Text, ForeignKey('tag.nametag'))
)

class Post(Base):
__tablename__ = 'post'
id = Column(Integer, primary_key = True, autoincrement='auto')
author_id = Column(Integer, ForeignKey('user.id'),nullable=False)
created = Column(DateTime, nullable = False, default = datetime.now().date())
score = Column(Integer, nullable = False, default = 0)
title = Column(Text, nullable = False, unique = True)
body = Column(Text, nullable = False)

tags = relationship('Tag', secondary = post_tags,back_populates= 'posts')

def __repr__(self):
return '' % self.title

class Tag(Base):
__tablename__ = 'tag'
id = Column(Integer, autoincrement='auto')
nametag = Column(Text, primary_key = True)
posts = relationship('Post', secondary = post_tags, back_populates = 'tags')

def __repr__(self):
return '' % self.nametag

Relacja Many-To-Many, gdzie mamy wiele postów, te posty moga mieć wiele tagów i te tagi mogą miec przypisane wiele postów, na które wskazują - taka sama sytuacja, jak na Mirko.

Funkcja dodająca z formularza post wraz z tagami:

@bp.route("/create", methods=("GET", "POST"))
@login_required
def create():
"""Create a new post for the current user."""
if request.method == "POST":
title = request.form["title"]
body = request.form["body"]
tags = request.form["tags"].lower()
new_tags = tags.split(' ')
error = None

if not title:
error = "Title is required."

if error is not None:
flash(error)
else:
add_post = Post(title= title,body = body, author_id = g.user.id)

if tags is not None:
for i in new_tags:
try:
add_post.tags.append(Tag(nametag=i))
db.session.add(add_post)

except SQLAlchemyError as e:
print(e)
pass
db.session.add(add_post)
db.session.commit()
print('Tags: ', tags)
return redirect(url_for("blog.index"))
return render_template("blog/create.html")

Wszystko jest git, kiedy dodaję jeden post z wieloma tagami - wtedy w tabeli asocjacji **post_tags** kolejne recordy wyglądają następująco:
[(1, 'a'), (1, 'q')], gdzie 1 jest ID postu, a 'a' i 'q' są testowymi tagami.

Problem jest przy próbie dodania drugiego postu, który ma inne ID, ale te same tagi np.
[(2, 'a'), (2, 'q')]

Wtedy apka wyrzuca:

sqlalchemy.exc.IntegrityError: (psycopg2.errors.UniqueViolation) duplicate key value violates unique constraint "tag_pkey"

DETAIL: Key (nametag)=(a) already exists.

[SQL: INSERT INTO tag (id, nametag) VALUES (%(id)s, %(nametag)s)]
[parameters: ({'id': None, 'nametag': 'a'}, {'id': None, 'nametag': 'q'})]

Jest to jak najbardziej zrozumiałe, gdyż dodawanie recordów do tabeli asocjacji dorzuca je też do tabeli Tag, ale dlaczego - zamiast szukać recordów, do których może się odnosić - na siłę tam wciska te nazwy tagów generując błąd?

To jest raczej jakiś prosty błąd gdzieś przeze mnie zrobiony, ale na Stacku i na necie nie mogłem nigdzie znaleźć podobnego modelu wielu postów z wieloma, powtarzającymi się po postach, tagami.

inb4: dokumentacja SQLAlchemy (dla ORMu) pokazuje tylko pojedynczy przykład dla jednego takiego wczytania. Ewentualnie może ktoś z was mógłby to przetestować i powiedzieć czy działa, bo tu przyczyna jest zupełnie gdzie indziej?

Dzięki wielkie za ewentualną pomoc.

#python #sqlalchemy #flask #programowanie
  • 2