如何同时使用 SQL 和 Python 用 SQL 数据填充模板文件?

问题描述 投票:0回答:1

我有一个文本文件,它是这样的模板:

Question: {QuestionStem}

{answers}

Correct answer: {correctAnswer}
Date: {examDate}
Class: {examClass}

我需要填充它并在考试中的每个问题中保存一份副本。

考试数据来自 T-SQL 查询 (MSSQL),如下所示:

QuestionID  QuestionStem             QuestionOrder  AnswerId        AnswerText      AnswerOrder     isCorrect  Class        Date
100         What color is the sky?   0              1               Red             0               0          Astronomy    10/1/2024
100         What color is the sky?   0              2               Blue            1               1          Astronomy    10/1/2024
100         What color is the sky?   0              3               Orange          2               0          Astronomy    10/1/2024
100         What color is the sky?   0              4               Green           3               0          Astronomy    10/1/2024
200         The Sun is bright(T/F)   1              11              True            0               1          Astronomy    5/1/2024
200         The Sun is bright(T/F)   1              12              False           1               0          Astronomy    5/1/2024

我需要每次考试的数据来填充模板文件,如下所示:

Question: What color is the sky?

a. Red 
b. Blue 
c. Orange 
d. Green

Correct answer: b 
Date: 10/1/2024 
Class: Astronomy


Question: The Sun is bright(T/F)

a. True 
b. False

Correct answer: a 
Date: 5/1/2024 
Class: Astronomy

Python 是否有办法获取 SQL 数据,用数据填充模板,然后将其保存为文本文件?

python python-3.x tsql sql-server-2012
1个回答
1
投票

如果您想直接从Python获取T-SQL结果,您可以查看SQLAlchemy,但这取决于您的设置的具体情况,所以我只是假设您可以以某种方式将此查询输出转换为字符串。

你当然可以只使用 fstrings,但是如果这个项目涉及更多一点,或者你希望能够将模板与代码分开,你应该使用 Python 的 Template 类。 一个更复杂、但也更强大的替代方案是成熟的模板引擎,例如 Jinja

我编写了一些示例代码,它将查询输出作为字符串并将其替换为模板。 这本来可以用更少的参与来完成,但是通过使用数据类,我们可以保持一切都漂亮整洁。

from collections import defaultdict
from dataclasses import dataclass
from operator import attrgetter
from string import Template

# define the template string
# this could come from a file etc.
_TEMPLATE = Template("""
Question: ${question_stem}

${options}

Correct answer: ${correct_answer}
Date: ${exam_date}
Class: ${exam_class}
""")


# define helper classes to do the work
@dataclass
class Answer:
    order: str
    text: str
    correct: bool

    def __str__(self):
        return self.text


@dataclass
class Question:
    order: str
    text: str
    options: list[Answer]

    def __str__(self):
        # ensure correct order of options
        self.options.sort(key=attrgetter('order'))
        # supports multiple correct answers
        correct_answer = ", ".join([
            str(answer)
            for answer
            in self.options
            if answer.correct
        ])

        # substitute only the known values
        return _TEMPLATE.safe_substitute({
            'question_stem': self.text,
            'options': "\n".join([f'-[ ] {option}' for option in self.options]),
            'correct_answer': correct_answer,
        })


@dataclass
class Exam:
    klass: str
    date: str
    questions: list[Question]

    def __str__(self):
        # ensure correct order of options
        self.questions.sort(key=attrgetter('order'))
        return Template("\n".join([
            str(question)
            for question
            in self.questions
        ])).substitute({
            'exam_class': self.klass,
            'exam_date': self.date
        }).strip()

    @classmethod
    def from_data(cls, data: list[list[str]]) -> list['Exam']:
        # nested dict with klass -> question -> answer
        exams = defaultdict(dict)

        # loop over rows
        for (question_id, question_stem, question_order,
             answer_id, answer_text, answer_order, is_correct,
             klass, date) in data:

            # one answer per row
            answer = Answer(answer_order, answer_text, True if is_correct == "1" else False)

            question = exams[(klass, date)].get(question_id)
            if question is not None:
                question.options.append(answer)
            else:
                question = Question(question_order, question_stem, [answer])
                exams[(klass, date)][question_id] = question

        return [cls(klass, date,
                    [question
                     for question
                     in questions.values()])
                for (klass, date), questions
                in exams.items()]


if __name__ == '__main__':
    # your toy data, representing rows from a database query
    query_result = """
    QuestionID  QuestionStem             QuestionOrder  AnswerId        AnswerText      AnswerOrder     isCorrect  Class        Date
    100         What color is the sky?   0              1               Red             0               0          Astronomy    10/1/2024
    100         What color is the sky?   0              2               Blue            1               1          Astronomy    10/1/2024
    100         What color is the sky?   0              3               Orange          2               0          Astronomy    10/1/2024
    100         What color is the sky?   0              4               Green           3               0          Astronomy    10/1/2024
    200         The Sun is bright(T/F)   1              11              True            0               1          Astronomy    5/1/2024
    200         The Sun is bright(T/F)   1              12              False           1               0          Astronomy    5/1/2024
    """

    # clean this up a bit
    rows = [[field.strip() for field in line.split('  ') if field] for line in query_result.splitlines()[2:-1]]

    for exam in Exam.from_data(rows):
        print(exam)

显然,您必须使用真实数据运行它并调整清理,此外,也许可以使用模板字符串,并将其放入额外的文件中,等等。如果您沿着 SQLAlchemy 的道路走下去,您可能想要挂钩这些类也符合 ORM。

© www.soinside.com 2019 - 2024. All rights reserved.