Реализация отношений один-к-одному в Flask SQLAlchemy - PullRequest
0 голосов
/ 09 ноября 2019

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

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, db.Sequence('user_id_seq'), primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(64), index=True, unique=True)
    pswd_hash = db.Column(db.String(128))
    expenses = db.relationship('Expense', backref='user')
    currency = db.relationship('Currency', backref='user')
    curr_default = ?

    # ...

class Currency(db.Model):
    id = db.Column(db.Integer, db.Sequence('expense_id_seq'), primary_key=True)
    abbr = db.Column(db.String(10))
    name = db.Column(db.String(64))
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    default = ?

    # ...

То, чего я хочу добиться, - это присвоить каждому user.id один currency.id (один к одному)

Я хотел бы спросить совета, как лучше всего это сделать.

После рассмотрения проблемы у меня есть некоторыетакие идеи, как:

  1. Таблица ассоциации с uselist = Ложные отношения,
  2. Создать новый класс CurrencyDefault (id, user_id, currency_id),
  3. Или, может быть, есть другие, лучший способ добиться этого?

Мне очень любопытна ваша точка зрения на эту проблему.

Реализация решения:

Вот так мои занятиявыглядит так:

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, db.Sequence('user_id_seq'), primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    email = db.Column(db.String(64), index=True, unique=True)
    pswd_hash = db.Column(db.String(128))
    currency_default_choice = db.Column(db.Integer, b.ForeignKey('currency.id'))
    expenses = db.relationship('Expense', backref='user')
    currency = db.relationship('Currency', backref='user', foreign_keys="Currency.user_id")
    # currency_default = db.relationship(
    #       'Currency',
    #       foreign_keys='User.currency_default_choice',
    #       backref='currency_default',
    #       uselist=False,
    # )

# ...

class Currency(db.Model):
    id = db.Column(db.Integer, db.Sequence('expense_id_seq'), primary_key=True)
    abbr = db.Column(db.String(10), b.ForeignKey('currency_official_abbr.abbr'))
    name = db.Column(db.String(64))
    user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
    currency_default = db.relationship(
            'User',
            foreign_keys='User.currency_default_choice',
            backref='currency_default',
            uselist=False,
    )
  1. Первая проблема, которую я нахожу, состоит в том, что я могу установить Currency.id объект, созданный other_user, как currency_default_choice. Как ограничить currency_default_choice только <Currency>, созданным этим user?

  2. В чем разница между установочными отношениями, имеющими foreign key в User классе (currency_default_choice = db.Column(db.Integer, b.ForeignKey('currency.id'))) с:

class Currency(db.Model):
# ...
currency_default = db.relationship(
            'User',
            foreign_keys='User.currency_default_choice',
            backref='currency_default',
            uselist=False,
    )

и установкой этого отношения на стороне User с:

class User(db.Model):
# ...
    currency_default = db.relationship(
          'Currency',
          foreign_keys='User.currency_default_choice',
          backref='currency_default',
          uselist=False,
    )

Ad.2. Мне кажется, что между этими двумя способами нет никакой разницы, потому что параметр backref подразумевает bidirectional behavior, поэтому не имеет значения, поместил ли я db.relationship() в класс User или Currency. Это правильно?

Используя оболочку Python, я добавил значение к User.currency_default
>>> app = create_app()
>>> app.app_context().push()
>>> admin = User.query.filter_by(username='admin').first()
<User(id= 1, username = admin, email = admin@admin.com)
>>> currency = Currency.query.filter_by(user=admin)
>>> currency
<flask_sqlalchemy.BaseQuery object at 0x03EA05D0>
>>> currency[0].id
1
>>> admin.currency_default = currency[0]
>>> db.session.commit()
>>> currency[0].currency_default
<User(id= 1, username = admin, email = admin@admin.com)
>>> admin.currency_default_choice
1

, а затем с помощью Админ-панели после запуска flask run Я хотел удалить введенное значение, но получил ошибку, которую не вижуТ понять. Почему существует круговая зависимость между (Currency.currency_default), (User.currency_default) и (User.currency)? Я не понимаю, что происходит. Как это исправить?

sqlalchemy.exc.CircularDependencyError: Circular dependency detected. 
(ProcessState(OneToManyDP(Currency.currency_default), 
<Currency at 0x46542b0>, delete=False), 
ProcessState(ManyToOneDP(User.currency_default), 
<User at 0x4669b10>, delete=False), 
SaveUpdateState(<Currency at 0x46542b0>), 
ProcessState(OneToManyDP(User.currency), <User at 0x4669b10>, delete=False), 
SaveUpdateState(<User at 0x4669b10>))
...