Flask-SQLalchemy met à jour les informations d'une ligne


122

Comment puis-je mettre à jour les informations d'une ligne?

Par exemple, je voudrais modifier la colonne de nom de la ligne qui a l'ID 5.

Réponses:


206

Récupérez un objet à l'aide du didacticiel présenté dans la documentation Flask-SQLAlchemy . Une fois que vous avez l'entité que vous souhaitez modifier, changez l'entité elle-même. Ensuite, db.session.commit().

Par exemple:

admin = User.query.filter_by(username='admin').first()
admin.email = 'my_new_email@example.com'
db.session.commit()

user = User.query.get(5)
user.name = 'New Name'
db.session.commit()

Flask-SQLAlchemy est basé sur SQLAlchemy, alors assurez-vous de consulter également la documentation SQLAlchemy .


2
Merci Mark. Une autre chose. Je l'ai vu faire comme ceci 'db.add (user)' puis 'dv.session.commit ()'. Pourquoi les deux fonctionnent-ils? et quelle est la différence?
pocorschi

11
Cela a à voir avec les différences entre les objets transitoires, détachés et attachés dans SQLAlchemy (voir sqlalchemy.org/docs/orm/session.html#what-does-the-session-do ). Lisez également le commentaire de Michael Bayer sur la liste de diffusion ( groups.google.com/group/sqlalchemy/browse_thread/thread/… ) pour plus d'informations.
Mark Hildreth

1
Si vous êtes toujours confus sur les différences après avoir lu ce document, pensez à poser une autre question.
Mark Hildreth

si le type de données de la colonne est json, utilisez la méthode ci-dessous. bashelton.com/2014/03/…
Aram

@MarkHildreth Je n'ai pas pu mettre à jour une valeur datetime dans le champ, que dois-je faire? La colonne est que uesd_at = db.Column(db.DateTime)je viens de courir obj.used_at = datetime.datetime.now() db.session.commit()mais pas la valeur définie sur le champ.
Rukeith

96

Il existe une méthode updatesur l'objet BaseQuery dans SQLAlchemy, qui est renvoyée par filter_by.

admin = User.query.filter_by(username='admin').update(dict(email='my_new_email@example.com')))
db.session.commit()

L'avantage d'utiliser par updaterapport à la modification de l'entité survient lorsqu'il y a de nombreux objets à mettre à jour.

Si vous voulez donner la add_userpermission à tous les admins,

rows_changed = User.query.filter_by(role='admin').update(dict(permission='add_user'))
db.session.commit()

Notez que filter_byprend des arguments de mot-clé (utilisez un seul =) par opposition à filterqui prend une expression.


1
dans la première requête, le résultat est nommé comme admin, ce qui peut être trompeur car le résultat sera le nombre de lignes mises à jour. N'est-ce pas?
Vikas Prasad

et y a-t-il un moyen, où je peux obtenir les Useréléments affectés par la requête, pas le nombre d'utilisateurs concernés?
Vikas Prasad

nombre de lignes correspondantes
Vikas Prasad

18

Cela ne fonctionne pas si vous modifiez un attribut décapé du modèle. Les attributs marinés doivent être remplacés pour déclencher des mises à jour:

from flask import Flask
from flask.ext.sqlalchemy import SQLAlchemy
from pprint import pprint

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqllite:////tmp/users.db'
db = SQLAlchemy(app)


class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), unique=True)
    data = db.Column(db.PickleType())

    def __init__(self, name, data):
        self.name = name
        self.data = data

    def __repr__(self):
        return '<User %r>' % self.username

db.create_all()

# Create a user.
bob = User('Bob', {})
db.session.add(bob)
db.session.commit()

# Retrieve the row by its name.
bob = User.query.filter_by(name='Bob').first()
pprint(bob.data)  # {}

# Modifying data is ignored.
bob.data['foo'] = 123
db.session.commit()
bob = User.query.filter_by(name='Bob').first()
pprint(bob.data)  # {}

# Replacing data is respected.
bob.data = {'bar': 321}
db.session.commit()
bob = User.query.filter_by(name='Bob').first()
pprint(bob.data)  # {'bar': 321}

# Modifying data is ignored.
bob.data['moo'] = 789
db.session.commit()
bob = User.query.filter_by(name='Bob').first()
pprint(bob.data)  # {'bar': 321}

1
Quelle est la meilleure approche dans de tels cas?
kampta

Vous devrez le copier dataet le réaffecter.
sas

@sas Que voulez-vous dire?
Mote Zart

Vous devrez copier et affecter la propriété data pour déclencher le mécanisme de mise à jour. Jetez un œil à la réponse ci-dessous:user.data = data
sas

9

Le simple fait d'attribuer la valeur et de les valider fonctionnera pour tous les types de données à l'exception des attributs JSON et Pickled. Étant donné que le type pickled est expliqué ci-dessus, je noterai un moyen légèrement différent mais facile de mettre à jour les JSON.

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), unique=True)
    data = db.Column(db.JSON)

def __init__(self, name, data):
    self.name = name
    self.data = data

Disons que le modèle est comme ci-dessus.

user = User("Jon Dove", {"country":"Sri Lanka"})
db.session.add(user)
db.session.flush()
db.session.commit()

Cela ajoutera l'utilisateur dans la base de données MySQL avec les données {"country": "Sri Lanka"}

La modification des données sera ignorée. Mon code qui n'a pas fonctionné est le suivant.

user = User.query().filter(User.name=='Jon Dove')
data = user.data
data["province"] = "south"
user.data = data
db.session.merge(user)
db.session.flush()
db.session.commit()

Au lieu de passer par le travail pénible de copier le JSON dans un nouveau dict (ne pas l'assigner à une nouvelle variable comme ci-dessus), ce qui aurait dû fonctionner, j'ai trouvé un moyen simple de le faire. Il existe un moyen de signaler au système que les JSON ont changé.

Voici le code de travail.

from sqlalchemy.orm.attributes import flag_modified
user = User.query().filter(User.name=='Jon Dove')
data = user.data
data["province"] = "south"
user.data = data
flag_modified(user, "data")
db.session.merge(user)
db.session.flush()
db.session.commit()

Cela a fonctionné comme un charme. Il y a une autre méthode proposée avec cette méthode ici J'espère avoir aidé quelqu'un.


2
db.session.merge(user)l'ajout de ce code a fonctionné pour moi, FYI.
Jeff Bluemel
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.