sqlalchemy таблица ассоциаций многих ко многим без вставки строк - PullRequest
1 голос
/ 03 февраля 2020

Написание моего первого веб-приложения с использованием flask / SQLAlchemy. У меня есть много-много отношений между «людьми» и «объектами». Когда я успешно добавляю человека с помощью формы регистрации, в таблицу ассоциаций не добавляется строка. Нужно ли вставлять эту строку вручную?

Вот соответствующая часть модели:

# app/models.py

from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash

from app import db, login_manager

# [START model]

# Build secondary table for many to many between facilities and persons
workers = db.Table('workers',
    db.Column('facility_id', db.Integer, db.ForeignKey('facilities.id')),
    db.Column('person_id', db.Integer, db.ForeignKey('persons.id'))
)


class Facility(db.Model):
    __tablename__='facilities'

    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(60))
    description = db.Column(db.String(128))
    persons = db.relationship('Person', secondary='workers', backref='facilities', lazy = 'dynamic')

    def __repr__(self):
        return "<Facility name='%s')" % (self.name)

class Person(UserMixin, db.Model):
    __tablename__ = 'persons'

    id = db.Column(db.Integer, primary_key=True)
    last_name = db.Column(db.String(60), index=True)
    username = db.Column(db.String(60), index=True, unique=True)
    email = db.Column(db.String(80), index=True)
    password_hash = db.Column(db.String(128))
    first_name = db.Column(db.String(60), index=True)
    role = db.Column(db.Integer, db.ForeignKey('roles.id'))
    is_person_active = db.Column(db.Boolean, index=True)
    is_admin = db.Column(db.Boolean, default=False)
    comments = db.Column(db.String(255))
    animals = db.relationship('Animal', secondary='permissions', backref='persons', lazy = 'dynamic'))

    @property
    def password(self):
        """
        Prevent password from being accessed
        """
        raise AttributeError('password is not a readable attribute.')

    @password.setter
    def password(self, password):
        """
        Set password to a hashed password
        """
        self.password_hash = generate_password_hash(password)

    def verify_password(self, password):
        """
        Check if hashed password matches actual password
        """
        return check_password_hash(self.password_hash, password)

    def __repr__(self):
        return "<Person name='%s', '%s', '%s')" % (self.first_name, self.last_name, self.username)


# Set up user_loader
@login_manager.user_loader
def load_user(user_id):
    return Person.query.get(int(user_id))

А вот вид:

# app/auth/views.py

from flask import flash, redirect, render_template, url_for
from flask_login import login_required, login_user, logout_user

from . import auth
from .forms import LoginForm, RegistrationForm
from .. import db
from ..models import Person, Facility

@auth.route('/register', methods=['GET', 'POST'])
def register():
    """
    Handle requests to the /register route
    Add a person to the database through the registration form
    """
    form = RegistrationForm()
    form.facility_id.choices = [(f.id, f.name) for f in Facility.query.order_by('name')]
    if form.validate_on_submit():
        person = Person(facility=form.facility_id.data,
                            email=form.email.data,
                            username=form.username.data,
                            first_name=form.first_name.data,
                            last_name=form.last_name.data,
                            password=form.password.data)

        # add person to the database
        db.session.add(person)
        db.session.commit()
        flash('You have successfully registered! You may now login.')

        # redirect to the login page
        return redirect(url_for('auth.login'))

    # load registration template
    return render_template('auth/register.html', form=form, title='Register')

Ответы [ 3 ]

0 голосов
/ 07 февраля 2020

Если вышеприведенное { ссылка } не работает, мне интересно, это так же просто, как орфографическая проблема? Вы сказали, что обратный реф от объекта к человеку должен называться facilities. возможно, при вызове конструктора Person в функции register вы должны изменить:

person = Person(facility=form.facility_id.data,

на

person = Person(facilities=[form.facility_id.data],
0 голосов
/ 07 февраля 2020

Спасибо за поддержку @Michael. Вы были достаточно близко, чтобы я нашел проблему; Дело в том, что я не добавлял человека в коллекцию людей для учреждения, поэтому в рабочий стол не было вставлено ни одной строки. Я добавил

        facility = Facility.query.filter_by(id=form.facility_id.data).first()
        facility.persons.append(person)
        db.session.commit()

после существующего кода

        db.session.add(person)
        db.session.commit()

в представлении регистрации, и теперь он правильно вставляет строки в рабочую таблицу.

0 голосов
/ 06 февраля 2020

Возможно, этот вопрос является дубликатом { ссылка }? Похоже, проблема в том, что в вашей функции register() нет упоминаний об объектах.

Не изменяя свои классы модели (например, чтобы модель Person знала о своих подключенных объектах в дополнение к тому, что вы сейчас делаете в Facility for Person), я думаю, что вы сможете что-то сделать в register () вроде:

#this should maybe come after db.session.add(person), but before db.session.commit()
selected_facility = Facility.query.get(form.facility_id.data)
selected_facility.persons.append(person)

или альтернативно

#this should maybe come after db.session.add(person), but before db.session.commit()
selected_facility = Facility.query.get(form.facility_id.data)
person.facilities.append(selected_facility)
...