Пример вопроса 'Essential SQLAlchemy', вызывающий повторяющуюся запись IntegrityError - PullRequest
0 голосов
/ 14 сентября 2018

Я изучаю SQLAlchemy с помощью книги Essential SQLAlchemy .

Один из примеров является ключевым для того, что я хочу изучать - добавление связанных объектов.И я просто не могу заставить его работать.И я надеюсь, что это сообщество поможет определить, что я что-то упустил, или есть ли в примере недостаток.Очевидно, что первое является наиболее вероятным.Но, надеюсь, это может быть полезно для других.Для меня книга была понятна до этой критической точки.

Приведенные примеры взяты из главы 7, а проблемный код "заказов" - это пример 7-25.Примеры полного кода: https://github.com/jasonamyers/essential-sqlalchemy-2e

Я начинаю со следующей настройки:

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

engine = create_engine('mysql+pymysql://root:password@localhost/Alberta', pool_recycle=3600)

Session = sessionmaker(bind=engine)

session = Session()

from datetime import datetime
from sqlalchemy import (Table, Column, Integer, Numeric, String, DateTime, ForeignKey, Boolean, desc, func)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship, backref

Base = declarative_base()

class Cookie(Base):
    __tablename__ = 'cookies'
    cookie_id = Column(Integer, primary_key=True)
    cookie_name = Column(String(50), index=True)
    cookie_recipe_url = Column(String(255))
    cookie_sku = Column(String(55))
    quantity = Column(Integer())
    unit_cost = Column(Numeric(12, 2))

    def __repr__(self):
        return "Cookie(cookie_name='{self.cookie_name}', " \
                   "cookie_recipe_url='{self.cookie_recipe_url}', " \
                   "cookie_sku='{self.cookie_sku}', " \
                   "quantity={self.quantity}, " \
                   "unit_cost={self.unit_cost})".format(self=self)


class User(Base):
    __tablename__ = 'users'
    user_id = Column(Integer(), primary_key=True)
    username = Column(String(15), nullable=False, unique=True)
    email_address = Column(String(255), nullable=False)
    phone = Column(String(20), nullable=False)
    password = Column(String(25), nullable=False)
    created_on = Column(DateTime(), default=datetime.now)
    updated_on = Column(DateTime(), default=datetime.now, onupdate=datetime.now)

def __repr__(self):
    return "User(username='{self.username}', " \
                 "email_address='{self.email_address}', " \
                 "phone='{self.phone}', " \
                 "password='{self.password}')".format(self=self)

class Order(Base):
    __tablename__ = 'orders'
    order_id = Column(Integer(), primary_key=True)
    user_id = Column(Integer(), ForeignKey('users.user_id'))
    shipped = Column(Boolean(), default=False)

     user =  relationship("User", backref=backref('orders', order_by=order_id))

    def __repr__(self):
        return "Order(user_id={self.user_id}, " \
                  "shipped={self.shipped})".format(self=self)


class LineItem(Base):
    __tablename__ = 'line_items'
    line_item_id = Column(Integer(), primary_key=True)
    order_id = Column(Integer(), ForeignKey('orders.order_id'))
    cookie_id = Column(Integer(), ForeignKey('cookies.cookie_id'))
    quantity = Column(Integer())
    extended_cost = Column(Numeric(12, 2))

    order = relationship("Order", backref=backref('line_items', order_by=line_item_id))
    cookie = relationship("Cookie", uselist=False)

    def __repr__(self):
        return "LineItems(order_id={self.order_id}, " \
                      "cookie_id={self.cookie_id}, " \
                      "quantity={self.quantity}, " \
                      "extended_cost={self.extended_cost})".format(
                self=self)

Base.metadata.create_all(engine)

Я успешно запускаю следующий пример, чтобы заполнить базу данных пользователями именем пользователя cookiemon:

cookiemon = User(username='cookiemon',
    email_address='wellhithere@gmail.com',
    phone='416-555-1212',
    password='password'
    )

session.add(cookiemon)
session.commit()

Теперь в следующем примере в книге мне сказали, что следующий код будет работать.

o1 = Order()
o1.user = cookiemon
session.add(o1)

cc = session.query(Cookie).filter(Cookie.cookie_name == "chocolate chip").one()
line1 = LineItem(cookie=cc, quantity=2, extended_cost=1.00)

pb = session.query(Cookie).filter(Cookie.cookie_name == 
                              "peanut butter").one()
line2 = LineItem(quantity=12, extended_cost=3.00)
line2.cookie = pb 
line2.order = o1

o1.line_items.append(line1)
o1.line_items.append(line2)
session.commit()

Однако, так как пользователь cookiemon уже существует в базе данных, я получаю следующую ошибку:

sqlalchemy.exc.IntegrityError: (возникает в результате запроса, вызванного Queryautoflush; рассмотрите возможность использования блока session.no_autoflush, если эта очистка происходит преждевременно) (pymysql.err.IntegrityError) (1062, u «Дублирующая запись cookiemon для ключа username»): SQL: u'INSERT INTO users (username, email_адрес, телефон, пароль, create_on, updated_on) ЗНАЧЕНИЯ (% (имя пользователя) s,% (email_адрес) s,% (телефон) s,% (пароль) s,% (made_on) s,% (updated_on) s) '] [параметры: {'имя пользователя': 'cookiemon', 'телефон': '416-555-1212', 'create_on': datetime.datetime (2018, 9, 14, 13, 7, 29, 353673), 'updated_on': datetime.datetime (2018, 9, 14, 13, 7, 29, 353701),' password ':' passwordEW ',' email_address ':' wellhithere@gmail.com '}]

Приведенный выше код заказа работает как талисман, если я сначала удаляю пользователя cookiemon (нет ничего удивительного).Но это вряд ли сработает в реальном мире.

И я вижу, что есть решение с no_autoflush.Но нужно ли это?Или я просто упускаю что-то простое, но важное здесь?

Вот обратная связь:

cd "/Users/chris/Desktop/Banff Experiments" ; env "PYTHONIOENCODING=UTF-8" "PYTHONUNBUFFERED=1" "PYTHONPATH=/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd" "/Users/chris/Desktop/Banff Experiments/banff/bin/python" -m ptvsd --host localhost --port 59094 "/Users/chris/Desktop/Banff Experiments/workspace4.py"
Traceback (most recent call last):
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/runpy.py", line 162, in _run_module_as_main
"__main__", fname, loader, pkg_name)
File "/System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/runpy.py", line 72, in _run_code
exec code in run_globals
 File "/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd/ptvsd/__main__.py", line 211, in <module>
singlesession=args.single_session)
 File "/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd/ptvsd/__main__.py", line 205, in main
debug_main(addr, name, kind, *extra, **kwargs)
 File "/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd/ptvsd/_local.py", line 30, in debug_main
run_file(address, name, *extra, **kwargs)
 File "/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd/ptvsd/_local.py", line 64, in run_file
run(argv, addr, **kwargs)
 File "/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd/ptvsd/_local.py", line 125, in _run
_pydevd.main()
 File "/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd/ptvsd/_vendored/pydevd/pydevd.py", line 1743, in main
debugger.connect(host, port)
 File "/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd/ptvsd/_vendored/pydevd/pydevd.py", line 1099, in run
return self._exec(is_module, entry_point_fn, module_name, file, globals, locals)
 File "/Users/chris/.vscode/extensions/ms-python.python-2018.8.0/pythonFiles/experimental/ptvsd/ptvsd/_vendored/pydevd/pydevd.py", line 1106, in _exec
pydev_imports.execfile(file, globals, locals)  # execute the script
 File "/Users/chris/Desktop/Banff Experiments/workspace4.py", line 94, in <module>
session.commit()
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 943, in commit
self.transaction.commit()
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 467, in commit
self._prepare_impl()
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 447, in _prepare_impl
self.session.flush()
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 2254, in flush
self._flush(objects)
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 2380, in _flush
transaction.rollback(_capture_exception=True)
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/util/langhelpers.py", line 66, in __exit__
compat.reraise(exc_type, exc_value, exc_tb)
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 2344, in _flush
flush_context.execute()
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/unitofwork.py", line 391, in execute
rec.execute(self)
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/unitofwork.py", line 556, in execute
uow
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/persistence.py", line 181, in save_obj
mapper, table, insert)
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/orm/persistence.py", line 866, in _emit_insert_statements
execute(statement, params)
File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 948, in execute
return meth(self, multiparams, params)
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/sql/elements.py", line 269, in _execute_on_connection
return connection._execute_clauseelement(self, multiparams, params)
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1060, in _execute_clauseelement
compiled_sql, distilled_params
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1200, in _execute_context
context)
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1413, in _handle_dbapi_exception
exc_info
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/util/compat.py", line 265, in raise_from_cause
reraise(type(exception), exception, tb=exc_tb, cause=cause)
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/engine/base.py", line 1193, in _execute_context
context)
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/sqlalchemy/engine/default.py", line 509, in do_execute
cursor.execute(statement, parameters)
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/pymysql/cursors.py", line 170, in execute
result = self._query(query)
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/pymysql/cursors.py", line 328, in _query
conn.query(q)
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/pymysql/connections.py", line 516, in query
self._affected_rows = self._read_query_result(unbuffered=unbuffered)
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/pymysql/connections.py", line 727, in _read_query_result
result.read()
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/pymysql/connections.py", line 1066, in read
first_packet = self.connection._read_packet()
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/pymysql/connections.py", line 683, in _read_packet
packet.check_error()
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/pymysql/protocol.py", line 220, in check_error
err.raise_mysql_exception(self._data)
 File "/Users/chris/Desktop/Banff Experiments/banff/lib/python2.7/site-packages/pymysql/err.py", line 109, in raise_mysql_exception
raise errorclass(errno, errval)
sqlalchemy.exc.IntegrityError: (pymysql.err.IntegrityError) (1062, u"Duplicate entry 'cookiemon' for key 'username'") [SQL: u'INSERT INTO users (username, email_address, phone, password, created_on, updated_on) VALUES (%(username)s, %(email_address)s, %(phone)s, %(password)s, %(created_on)s, %(updated_on)s)'] [parameters: {'username': 'cookiemon', 'phone': '416-555-1212', 'created_on': datetime.datetime(2018, 9, 14, 13, 44, 46, 916687), 'updated_on': datetime.datetime(2018, 9, 14, 13, 44, 46, 916712), 'password': 'passwordEW', 'email_address': 'wellhithere@gmail.com'}] (Background on this error at: http://sqlalche.me/e/gkpj)

1 Ответ

0 голосов
/ 14 сентября 2018

Теперь, читая комментарии, я думаю, что понимаю вашу проблему.Этот кусок кода:

cookiemon = User(username='cookiemon',
    email_address='wellhithere@gmail.com',
    phone='416-555-1212',
    password='password'
    )

session.add(cookiemon)
session.commit()

Создает нового пользователя и сохраняет его в базе данных.Вы не можете запустить его дважды, потому что он уже хранится в базе данных.Вы можете выполнить это только один раз, или вы получите ошибку.

Теперь позже, если вы хотите использовать того же пользователя, вы не сможете создать его снова.Если у вас все еще есть та же самая переменная cookiemon в памяти, вы можете просто использовать ее, но если у вас ее нет, вам нужно выбрать существующего пользователя из базы данных вместо создания нового:

cookiemon = session.query(User).filter(User.username=='cookiemon').one()

Вы можете объединить оба в одном исполнении:

# try to get the user from the databae
cookiemon = session.query(User).filter(User.username=='cookiemon').first()
if cookiemon is None: # not found, create new one:
    cookiemon = User(
        username='cookiemon',
        email_address='wellhithere@gmail.com',
        phone='416-555-1212',
        password='password'
    )
    session.add(cookiemon)
    session.commit()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...