Как структурировать проект Django DRF для обработки нескольких тестов и ответов
Я работаю над проектом на Django, используя фреймворк Django REST Framework (DRF), где мне нужно определить список тестов. Каждый тест состоит из нескольких вопросов, и на эти вопросы есть соответствующие ответы.
Моя первоначальная идея - создать приложение под названием tests и использовать два CSV-файла:
Один CSV-файл, содержащий все вопросы для разных тестов. Другой CSV-файл, содержащий все ответы. Однако я обеспокоен тем, что такой подход может привести к путанице в коде и усложнить его обслуживание.
Вопросы: Какова наилучшая архитектура для реализации этой функции в Django DRF? Является ли хранение тестовых вопросов и ответов в CSV-файлах хорошим подходом или вместо этого мне следует использовать модели и базу данных? Если на GitHub есть какие-либо аналогичные проекты с открытым исходным кодом, не могли бы вы предоставить ссылки? Я был бы признателен за любые рекомендации или рекомендации по эффективной структуре этого проекта.
Является ли хранение тестовых вопросов и ответов в CSV-файлах хорошим подходом?
Нет. Файлы - ужасный способ хранения данных. Это то, что люди делали в самые ранние времена на магнитных лентах.
Основная проблема заключается в том, что файл - это поток данных. Если вы хотите использовать данные для двух или более целей, вам обычно нужна база данных. База данных предназначена для хранения, извлечения и агрегирования данных эффективным способом. Определив таблицы, вы можете запрашивать их различными способами. Данные обычно не предназначены для какой-то одной цели. Если вы хотите сделать данные доступными в том смысле, что вы можете фильтровать, упорядочивать, извлекать, подсчитывать, обновлять и т.д., то именно для этого и нужна база данных.
Базы данных также обычно имеют множество инструментов для параллельного выполнения запросов и, таким образом, для обслуживания нескольких процессов одновременно. Обычно файл записывается одним процессом/пользователем, а затем считывается другим, и этот синхронный поток нарушает параллелизм.
приложение называется tests и использует два CSV-файла.
Вы все еще можете импортировать csv-файлы. Затем у вас есть небольшой процесс, который выполняет некоторую "гимнастику" с данными csv и превращает их в записи на стороне базы данных.
Использование моделей и базы данных - лучший подход для удобства обслуживания: вместо ручного управления CSV-файлами вы можете создать фикстуру в формате JSON или CSV (очень похожую на ту, что у вас уже есть) и загрузить ее с помощью пользовательской команды управления Django.
Вот пример load_questions.py
:
from django.core.management.base import BaseCommand
import csv
import json
from your_app.models import Question
class Command(BaseCommand):
help = "Load questions from a CSV file"
def handle(self, *args, **kwargs):
try:
with open("questions.csv", "r") as file:
reader = csv.DictReader(file)
for row in reader:
Question.objects.get_or_create(
text=row["text"],
defaults={"test_id": row["test_id"]}
)
self.stdout.write(self.style.SUCCESS("Questions loaded successfully"))
except Exception as e:
self.stderr.write(self.style.ERROR(f"Error: {e}"))
Это позволяет избежать дублирования вопросов, используя get_or_create
из Django.
Вы можете расширить его, чтобы аналогичным образом обрабатывать ответы.
Если у вас много данных и вам необходимо оптимизировать процесс, вы также можете рассмотреть вариант bulk_create
.
PS: Я предлагаю вам найти более подходящее название для вашего приложения, поскольку "тест" обычно используется для обозначения файлов и папок, связанных с тестированием кода.
Что ж, если вы хотите разработать масштабируемое, удобное для использования и сопровождения приложение, особенно в Django и Django Rest Framework, вы, вероятно, захотите использовать модель и базу данных.
Например, в вашем проекте у вас есть тестов, несколько вопросов и соответствующих ответов. Между этими моделями существуют взаимосвязи, и использование CSV-файлов усложнит задачу. Вероятно, вам также захочется выполнять сложные запросы, в которых задействованы эти взаимосвязи. Лучше всего использовать базу данных и модель.
Архитектура
Что касается архитектуры, я бы посоветовал вам сначала наметить атрибуты, соответствующие каждой из ваших сущностей. Под сущностями я подразумеваю тесты, вопросы и ответы. Затем проверьте их взаимосвязи и используйте их для определения соответствующих моделей для вашей базы данных. Ниже я приведу несколько примеров
В приведенном ниже коде вы можете видеть, что тест и вопрос связаны с помощью внешнего ключа, это потому, что, как вы упомянули, в тесте может быть много вопросов. То же самое применимо к случаям, когда вопрос и ответ связаны с помощью внешнего ключа, поскольку на вопрос может быть много ответов.
Также обратите внимание, что дочерняя модель всегда имеет внешний ключ.
Как только вы это поймете, можете приступать.
Вы также можете добавлять или удалять дополнительные поля в соответствии с требованиями вашего проекта
from django.db import models
class Test(models.Model):
name = models.CharField(max_length=255)
description = models.TextField(blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.name
class Question(models.Model):
test = models.ForeignKey(Test, related_name='questions', on_delete=models.CASCADE)
text = models.TextField()
order = models.IntegerField(default=0) # To order questions within a test
def __str__(self):
return self.text
class Answer(models.Model):
question = models.ForeignKey(Question, related_name='answers', on_delete=models.CASCADE)
text = models.TextField()
is_correct = models.BooleanField(default=False) # To mark the correct answer
def __str__(self):
return self.text
В качестве альтернативы, если у вас уже есть данные в csv-файлах и вы хотите импортировать их в определенные модели, вы можете сделать это с помощью команды управления Django.
Вам просто нужно сопоставить ваши строки и столбцы с соответствующими полями и значениями полей вашей модели.
Основываясь на приведенной выше модели, я приведу вам пример того, как вы можете достичь этого ниже
# management/commands/import_tests.py
import csv
from django.core.management.base import BaseCommand
from tests.models import Test, Question, Answer
class Command(BaseCommand):
help = 'Import tests, questions, and answers from CSV files'
def handle(self, *args, **kwargs):
with open('tests.csv', 'r') as test_file:
test_reader = csv.DictReader(test_file)
for row in test_reader:
test = Test.objects.get_or_create(name=row['name'], description=row['description'])
with open('questions.csv', 'r') as question_file:
question_reader = csv.DictReader(question_file)
for q_row in question_reader:
question = Question.objects.get_or_create(test=test, text=q_row['text'], order=q_row['order'])
with open('answers.csv', 'r') as answer_file:
answer_reader = csv.DictReader(answer_file)
for a_row in answer_reader:
Answer.objects.get_or_create(question=question, text=a_row['text'], is_correct=a_row['is_correct'])
Конечно, это метод грубой силы, и вы, вероятно, захотите оптимизировать его, если ваши CSV-файлы очень большие. Затем вы можете использовать bulk_create и добавить транзакцию, чтобы обеспечить атомарность ваших данных.