Flask + SQLAlchemy: как показать последнюю запись в базе данных 1-ко-многим - PullRequest
2 голосов
/ 26 октября 2019

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

Я создал базу данных с двумя таблицами «Датчики» и «Значения датчика». Должно быть отношение один ко многим, чтобы один датчик мог иметь много показаний, но показания могут принадлежать только одному датчику.

Я использую Flask и flask-sqlalchemy.

Модели базы данных:

class Sensor(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name  = db.Column(db.String(64), unique=True, nullable=False,)
    gpiopin  = db.Column(db.INT, unique=False, nullable=False,)
    stype  = db.Column(db.String(20), unique=False, nullable=False,)
    values = db.relationship('SensorValues', backref='author', lazy=True) #not a column in table, just backreference
    def __repr__(self):
        return f"Sensor('{self.name}', '{self.gpiopin}', '{self.stype}')"

class SensorValues(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    reading = db.Column(db.String(64), unique=False)
    timestamp = db.Column(db.DateTime, unique=False, nullable=False, default=datetime.utcnow)
    sensor_id = db.Column(db.Integer, db.ForeignKey('sensor.id'), nullable=False)
    def __repr__(self):
        return f"Value('{self.reading}', '{self.timestamp}')"

Вот маршрут колбы для home.html:

@app.route("/", methods=['POST', 'GET'])
def home():
  sensors = Sensor.query.all()
  posts = SensorValues.query.all()
  return render_template('home.html', posts=posts, sensors=sensors, db=db)

Как создать таблицу в home.html

  <tbody>
            {% for sensor in sensors %}
                <tr>
                <td>{{ sensor.name }}</td>
                <td>{{ sensor.gpiopin }}</td>
                <td>{{ sensor.stype }}</td>
                <td>{{ sensor.values.reading }}</td> <- Where I want the latest reading of the specific sensor in the SensorValues to appear
                </tr>
            {% endfor %}
        </tbody>

Теперь,У меня есть другая таблица, в которой перечислены все показания и названия датчиков, к которым они принадлежат. Это в основном история, и таким образом она работает, потому что я зацикливаю таблицу SensorValues ​​напрямую.

  {% for post in posts %}
                <tr>
                <td>{{ post.author.name }}</td>
                <td>{{ post.author.stype }}</td>
                <td>{{ post.reading }}</td>
                <td>{{ post.timestamp }}</td>
                </tr>
  {% endfor %}

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

{% for sensor in sensors %}
                <tr>
                <td>{{ sensor.name }}</td>
                <td>{{ sensor.gpiopin }}</td>
                <td>{{ sensor.stype }}</td>
                {{ lastvalue = SensorValues.filter_by(sensor_id=sensor.id).last()}}
                <td>{{ lastvalue.reading }}</td>
                </tr>
{% endfor %}

Где я пыталсясделать запрос фильтра к таблице SensorValues ​​с текущим sensor.id (идентификатор датчика для цикла for в настоящее время включен), и логически я думаю, что это может работать, но синтаксис неверен. Я получил ошибку Jinja:

expected token 'end of print statement', got '='

Могу ли я сделать запрос в home.html или я должен сделать это в маршрутах, прежде чем страница будет отображена?

Ответы [ 2 ]

1 голос
/ 26 октября 2019

Запрос в функции просмотра необходимо обновить, чтобы включить информацию о последнем чтении для датчиков перед передачей его в контексте шаблона.

Начните с написания запроса в SQL, а затем преобразуйте его вего вариант SQLAlchemy.

Запрос SQL для поиска последних значений чтения датчика следующий:

https://www.db -fiddle.com / f / 5fXZyPiPYawc22ffMi2tYk / 0

SELECT DISTINCT 
    s.*, 
    FIRST_VALUE(sv.reading) OVER (
  PARTITION BY sv.sensor_id ORDER BY sv.timestamp DESC
) AS last_reading
FROM sensors AS s
JOIN sensor_values AS sv ON s.id = sv.sensor_id

Теперь давайте создадим вариант SQLAlchemy

import sqlalchemy as sa

sensors = Sensor.query.join(
              SensorValues).with_entities(
                  Sensor,
                      sa.func.first_value(
                          SensorValues.reading).over(
                              partition_by=SensorValues.sensor_id,
                              order_by=SensorValues.timestamp.desc
                          ).label('latest_reading'))

Строки результата, возвращаемые из вышеприведенного запроса, являются экземплярами KeyTuple и меткой latest_readingдолжен быть доступен как прямой атрибут датчика в шаблоне Jinja2.

{% for sensor in sensors %}
    <tr>
        <td>{{ sensor.Sensor.name }}</td>
        <td>{{ sensor.Sensor.gpiopin }}</td>
        <td>{{ sensor.Sensor.stype }}</td>
        <td>{{ sensor.latest_reading }}</td>
    </tr>
{% endfor %}
0 голосов
/ 26 октября 2019

Я не уверен, стоит ли мне редактировать это в исходном сообщении, а не публиковать его в качестве ответа?

Но в любом случае у меня это получилось:

     {% for sensor in sensors %}
            <tr>
            <td>{{ sensor.name }}</td>
            <td>{{ sensor.gpiopin }}</td>
            <td>{{ sensor.stype }}</td>
            {% set lastvalue = SensorValues.query.filter_by(sensor_id=sensor.id).order_by(SensorValues.timestamp.desc()).first() %}
            <td>{{ lastvalue.reading }}</td>
            </tr>
        {% endfor %}

ИКонечно, я должен был добавить SensorValues ​​к маршруту:

@app.route("/", methods=['POST', 'GET'])
def home():
  sensors = Sensor.query.all()
  posts = SensorValues.query.all()
  #lastvalue = SensorValues.query.filter_by(sensor_id=sensor.id).last()
  return render_template('home.html', posts=posts, sensors=sensors, db=db, SensorValues=SensorValues

Если есть лучший способ непременно показать его, но пока он работает.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...