Here are some examples of useful patterns for models and queries with Flask-SQLAlchemy.
One-to-one relationships are not especially common, and they are implemented very similarly to one-to-many relationships. The key difference is just that on the parent data table, you would set use the uselist=False
argument to indicate that a single corresponding entry on the child table is expected (so a list is not necessary).
The helper table doesn't need to be defined as a class since we will never need to access the entries on it directly. Its only purpose is to connect our other two models.
backref
instead of back_populates
. backref
only has to exist on one of the two models, and it will create the relationship on its counterpart. more info on backref vs back_populatesSometimes you may have a table that is joined to itself—you can still use a db.relationship
to make it easier to access the related entries in both directions. For instance, you might have a User model, and you want to be able to access the collection of users that a given user follows as well as the user's followers.
filter
, order_by
, limit
)True
True
and whose usernames contain shgroup_by
, join
, subquery
)If you have included the appropriate db.relationship
on the models, explicitly joining two models in your query may not be necessary. When you define a db.relationship
between two models, it means that the associated entry/entries from the other table are included as a property on any instance of that model. For example, one could get a list of all the students from a specific cohort with cohort.students
—no need to query the students table and filter by the cohort ID.
Note on seeding: Unlike the workflow for seeding that we used with Sequelize, seeding the database in our SQLAlchemy projects looks just like adding entries to our database using the ORM. In the python group project skeleton, we're using a custom command line function for seeding (those flask seed all
/flask seed undo
commands are not built into Flask anywhere—Flask just allows you to define your own functions that run on the command line). Your seed files can add data to the database just like on your routes. So the examples below could be used in seeders or in routes—it looks the same.
Recall that, in JavaScript, we typically use objects for two purposes: as a collection of key/value pairs, and also as a collection of methods and properties.
In Python, key/value pairs in dictionaries are not the same as attributes on objects.
Dictionaries consist of key/value pairs. Accessing the value associated with a particular key uses bracket notation (dictionary["my_key"] = some_value
). Keys can be any hashable type (i.e. immutable types: strings, booleans integers, and tuples).
Accessing the methods and properties on objects is different. You can access these properties and methods using dot notation (my_object.some_property = data
). When you get entries from your database using SQLAlchemy, these objects are instances of your Model classes, not dictionaries.
Python dictionaries can easily be serialized into JSON format (as can strings, numbers, booleans, None
, lists, and tuples). Objects from your Model classes cannot. If you want to send an entity from your database to the frontend of your application, you will have to convert it to a dictionary.
to_dict()
methodsYou may find it helpful to define methods on your database model classes for converting objects from that class into dictionaries. Each class would need a unique to_dict
method which captures the relevent properties. You could even create multiple dictionary conversion methods for the same class that return different information (for example, separate methods for a summary versus a detail view).
That said, you are not required to write these methods—you could instead access the values of interest and create a dictionary directly on the route where you are returning the data.
However, since you will often need to send the same data from these classes in multiple routes. In that case, you can make your code more DRY (Don't Repeat Yourself) by making reusable to_dict
methods.
Here are some example routes using the above methods.