QMDB – کوئریها (مدیریت دادهها)
این تمرین در اصل برای ارسال چند فایل (در یک zip) نوشته شده است. در این نسخه همهچیز را در همین یک فایل بنویس: اول مدلها، بعد Managerها. خطهای
from models import …هم همانطور که در صورت سؤال آمده کار میکنند. داور یک پایگاهدادهی SQLite در حافظه میسازد، جدولها را از رویBaseتو درست میکند و متغیرsessionرا در اختیار آزمونها میگذارد.
در این تمرین شما باید متدهای مربوط به عملیات پایه و کوئریهای پرکاربرد روی پایگاهداده برای سیستم امتیازدهی فیلم کوئرا را پیادهسازی کنید. شما باید این متدها را در کلاسهای Manager که برای تعامل با مدلهایی که در قسمت قبل تعریف کردهاید ایجاد شدهاند، پیادهسازی کنید. ⚠️ توجه کنید که باید حتماً کد خود از قسمت قبلی را به این تمرین انتقال دهید.
📄 فایل models.py
این فایل شامل تعریف مدلهای Base، Movie، User، Genre، Review و MovieGenre است که در تمرین قبلی پیادهسازی کردهاید.
🎯 کلاس GenreManager
این کلاس در فایل genre_manager.py تعریف شدهاست.
class GenreManager:
def __init__(self, session: Session):
self.session = session
def create(self, name: str) -> Genre:
pass
def get(self, genre_id: int) -> Genre | None:
pass
def get_all(self):
pass
def get_genre_by_name(self, name: str) -> Genre | None:
pass
📄 genre_manager.py
🔹 create(self, name: str) -> Genre
یک ژانر جدید در پایگاهداده ایجاد میکند. این متد باید یک رکورد جدید در جدول
genresایجاد کند.ورودیها:
nameاز نوعstrکه نام ژانر است.خروجی: شیء
Genreایجادشده.
🔹 get(self, genre_id: int) -> Genre | None
اطلاعات یک ژانر را با استفاده از شناسه (id) آن از پایگاهداده دریافت میکند. این متد باید شیء ژانر از جنس
Genreرا برگرداند.ورودیها:
genre_idاز نوعintکه شناسهی ژانر است.خروجی: شیء
Genreبا شناسهی دادهشده یاNoneاگر ژانری با این شناسه وجود نداشته باشد.💡 راهنمایی: برای دریافت یک شیء در صورت وجود یا None در صورت عدم وجود، میتوانید از متد scalar_one_or_none() استفاده کنید.
🔹 get_all(self)
لیستی از تمام ژانرهای موجود در پایگاهداده را برمیگرداند.
خروجی: لیستی از اشیاء
Genre.
🔹 get_genre_by_name(self, name: str) -> Genre | None
اطلاعات یک ژانر را با استفاده از نام آن دریافت میکند.
ورودیها:
nameاز نوعstrکه نام ژانر است.خروجی: شیء
Genreبا نام دادهشده یاNoneاگر ژانری با این نام وجود نداشته باشد.
🎯 کلاس MovieManager
این کلاس در فایل movie_manager.py تعریف شدهاست.
class MovieManager:
def __init__(self, session: Session) -> None:
self.session = session
def create(self, title: str, release_year: int) -> Movie:
pass
def get(self, movie_id: int) -> Movie | None:
pass
def get_all(self):
pass
def add_genre(self, movie_id: int, genre: Genre) -> Movie:
pass
def get_reviews(self, movie_id: int):
pass
def get_average_rating(self, movie_id: int):
pass
📄 movie_manager.py
🔹 create(self, title: str, release_year: int) -> Movie
یک فیلم جدید در پایگاهداده ایجاد میکند. این متد باید یک رکورد جدید در جدول
moviesایجاد کند.ورودیها:
titleاز نوعstrکه عنوان فیلم است وrelease_yearاز نوعintکه سال انتشار فیلم است.خروجی: شیء
Movieایجادشده.
🔹 get(self, movie_id: int) -> Movie | None
اطلاعات یک فیلم را با استفاده از شناسه (id) آن از پایگاهداده دریافت میکند. این متد باید شیء فیلم از جنس
Movieرا برگرداند.ورودیها:
movie_idاز نوعintکه شناسهی فیلم است.خروجی: شیء
Movieبا شناسهی داده شده یاNoneاگر فیلمی با این شناسه وجود نداشته باشد.
🔹 get_all(self)
لیستی از تمام فیلمهای موجود در پایگاهداده را برمیگرداند.
خروجی: لیستی از اشیاء
Movie.
🔹 add_genre(self, movie_id: int, genre: Genre) -> Movie
یک ژانر را به فیلم اضافه میکند. این متد باید رابطهی بین فیلم و ژانر را در جدول میانی
movie_genresبرقرار کند.ورودیها:
movie_idاز نوعintکه شناسهی فیلم است وgenreاز نوعGenreکه شیء ژانر موردنظر است.خروجی: شیء
Movieکه ژانر به آن اضافه شدهاست.
🔹 get_reviews(self, movie_id: int)
لیستی از نقدها و امتیازهای مربوط به یک فیلم را دریافت میکند. این متد باید تمام نقدهای ثبتشده برای فیلم مشخص شده را برگرداند.
ورودیها:
movie_idاز نوعintکه شناسهی فیلم است.خروجی: لیستی از اشیاء
Reviewمربوط به فیلم.
🔹 get_average_rating(self, movie_id: int)
میانگین امتیاز یک فیلم را محاسبه میکند. این متد باید میانگین امتیازات دادهشده برای فیلم را محاسبه و برگرداند.
ورودیها:
movie_idاز نوعintکه شناسهی فیلم است.خروجی: میانگین امتیاز فیلم بهصورت
floatیاNoneاگر فیلم هیچ نقدی نداشته باشد.
🎯 کلاس UserManager
این کلاس در فایل user_manager.py تعریف شدهاست.
class UserManager:
def __init__(self, session: Session):
self.session = session
def create(self, name: str, email: str) -> User:
pass
def get(self, user_id: int) -> User | None:
pass
def get_all(self):
pass
def get_user_by_email(self, email: str) -> User | None:
pass
def get_most_active_users(self, limit=5):
pass
📄 user_manager.py
🔹 create(self, name: str, email: str) -> User
یک کاربر جدید در پایگاهداده ایجاد میکند. این متد باید رکورد جدیدی در جدول
usersاضافه کند.ورودیها:
nameاز نوعstrکه نام کاربر است وstrکه ایمیل کاربر است.خروجی: شیء
Userایجادشده.
🔹 get(self, user_id: int) -> User | None
اطلاعات یک کاربر را با استفاده از شناسه (id) آن از پایگاهداده دریافت میکند. این متد باید شیء کاربر از جنس
Userرا برگرداند.ورودیها:
user_idاز نوعintکه شناسهی کاربر است.خروجی: شیء
Userبا شناسهی دادهشده یاNoneاگر کاربری با این شناسه وجود نداشته باشد.
🔹 get_all(self)
لیستی از تمام کاربران موجود در پایگاه داده را برمیگرداند.
خروجی: لیستی از اشیاء
User.
🔹 get_user_by_email(self, email: str) -> User | None
اطلاعات یک کاربر را با استفاده از ایمیل آن دریافت میکند.
ورودیها:
strکه ایمیل کاربر است.خروجی: شیء
Userبا ایمیل دادهشده یاNoneاگر کاربری با این ایمیل وجود نداشته باشد.
🔹 get_most_active_users(self, limit=5)
لیستی از فعالترین کاربران (کاربرانی که بیشترین تعداد نقد را دارند) را بر اساس تعداد نقدهای ثبتشده برمیگرداند.
ورودیها:
limitاز نوعint(اختیاری) که حداکثر تعداد کاربران موردنظر است (پیشفرض ۵).خروجی: لیستی از تاپلها، که هر تاپل شامل شناسهی کاربر (
user_id) از نوعintو تعداد نقدهای آن کاربر (review_count) از نوعintاست. لیست بر اساس تعداد نقدها بهصورت نزولی مرتب شدهاست.
🎯 کلاس ReviewManager
این کلاس در فایل review_manager.py تعریف شدهاست.
class ReviewManager:
def __init__(self, session: Session):
self.session = session
def create(self, movie_id: int, user_id: int, rating: int, comment: str = None) -> Review:
pass
def get(self, review_id: int) -> Review | None:
pass
def get_all(self):
pass
def get_reviews_by_user(self, user_id: int):
pass
def get_latest_reviews_for_movie_by_time(self, movie_id: int, limit: int = 5):
pass
def get_highest_rated_reviews(self, movie_id: int, limit: int = 5):
pass
def get_average_rating_by_user(self):
pass
📄 review_manager.py
🔹 create(self, movie_id: int, user_id: int, rating: int, comment: str = None) -> Review
یک امتیاز و نقد برای یک فیلم توسط یک کاربر اضافه میکند. این متد باید یک رکورد جدید در جدول
reviewsایجاد کند.ورودیها:
movie_idاز نوعintکه شناسهی فیلم است،user_idاز نوعintکه شناسهی کاربر است،ratingاز نوعintکه امتیاز (از ۱ تا ۵) است وcommentاز نوعstr(اختیاری) که متن نقد است.خروجی: شیء
Reviewایجادشده.
🔹 get(self, review_id: int) -> Review | None
اطلاعات یک نقد را با استفاده از شناسه (id) آن از پایگاهداده دریافت میکند. این متد باید شیء نقد از جنس
Reviewرا برگرداند.ورودیها:
review_idاز نوعintکه شناسهی نقد است.خروجی: شیء
Reviewبا شناسهی دادهشده یاNoneاگر نقدی با این شناسه وجود نداشته باشد.
🔹 get_all(self)
لیستی از تمام نقدهای موجود در پایگاهداده را برمیگرداند.
خروجی: لیستی از اشیاء
Review.
🔹 get_reviews_by_user(self, user_id: int)
لیستی از نقدهای ثبتشده توسط یک کاربر خاص را دریافت میکند.
ورودیها:
user_idاز نوعintکه شناسهی کاربر است.خروجی: لیستی از اشیاء
Reviewثبتشده توسط کاربر.
🔹 get_latest_reviews_for_movie_by_time(self, movie_id: int, limit: int = 5)
لیستی از جدیدترین نقدهای یک فیلم را بر اساس زمان ایجاد دریافت میکند.
ورودیها:
movie_idاز نوعintکه شناسهی فیلم است وlimitاز نوعint(اختیاری) که حداکثر تعداد نقدهای موردنظر است (پیشفرض ۵).خروجی: لیستی از اشیاء
Reviewمربوط به فیلم، مرتبشده بر اساس زمان ایجاد بهصورت نزولی (جدیدترین ابتدا).
🔹 get_highest_rated_reviews(self, movie_id: int, limit: int = 5)
لیستی از نقدهای با بالاترین امتیاز برای یک فیلم را دریافت میکند.
ورودیها:
movie_idاز نوعintکه شناسهی فیلم است وlimitاز نوعint(اختیاری) که حداکثر تعداد نقدهای موردنظر است (پیشفرض ۵).خروجی: لیستی از اشیاء
Reviewمربوط به فیلم، مرتبشده بر اساس امتیاز بهصورت نزولی (بالاترین امتیاز ابتدا).
🔹 get_average_rating_by_user(self)
میانگین امتیازاتی که هر کاربر به فیلمها داده است را محاسبه میکند.
خروجی: لیستی از تاپلها، که هر تاپل شامل شناسهی کاربر (
user_id) از نوعintو میانگین امتیازات دادهشده توسط آن کاربر (average_rating) از نوعfloatاست.
✅ پاسخ نهایی: پیادهسازی کامل Managerها
📄 genre_manager.py
from sqlalchemy import select
from sqlalchemy.orm import Session
from models import Genre
class GenreManager:
def __init__(self, session: Session):
self.session = session
def create(self, name: str) -> Genre:
genre = Genre(name=name)
self.session.add(genre)
self.session.commit()
return genre
def get(self, genre_id: int) -> Genre | None:
return self.session.get(Genre, genre_id)
def get_all(self):
stmt = select(Genre)
return self.session.execute(stmt).scalars().all()
def get_genre_by_name(self, name: str) -> Genre | None:
stmt = select(Genre).where(Genre.name == name)
return self.session.execute(stmt).scalars().first()
📄 movie_manager.py
from sqlalchemy import select, func
from sqlalchemy.orm import Session
from models import Movie, Genre, Review
class MovieManager:
def __init__(self, session: Session) -> None:
self.session = session
def create(self, title: str, release_year: int) -> Movie:
movie = Movie(title=title, release_year=release_year)
self.session.add(movie)
self.session.commit()
return movie
def get(self, movie_id: int) -> Movie | None:
return self.session.get(Movie, movie_id)
def get_all(self):
stmt = select(Movie)
return self.session.execute(stmt).scalars().all()
def add_genre(self, movie_id: int, genre: Genre) -> Movie:
movie = self.get(movie_id)
if movie is None:
raise ValueError(f"فیلم با شناسه {movie_id} وجود ندارد")
movie.genres.append(genre)
self.session.commit()
return movie
def get_reviews(self, movie_id: int):
movie = self.get(movie_id)
if movie is None:
raise ValueError(f"فیلم با شناسه {movie_id} وجود ندارد")
return movie.reviews
def get_average_rating(self, movie_id: int):
stmt = select(func.avg(Review.rating)).where(Review.movie_id == movie_id)
return self.session.execute(stmt).scalar_one_or_none()
📄 user_manager.py
from sqlalchemy import select, func
from sqlalchemy.orm import Session
from models import User, Review
class UserManager:
def __init__(self, session: Session):
self.session = session
def create(self, name: str, email: str) -> User:
user = User(name=name, email=email)
self.session.add(user)
self.session.commit()
return user
def get(self, user_id: int) -> User | None:
return self.session.get(User, user_id)
def get_all(self):
stmt = select(User)
return self.session.execute(stmt).scalars().all()
def get_user_by_email(self, email: str) -> User | None:
stmt = select(User).where(User.email == email)
return self.session.execute(stmt).scalars().first()
def get_most_active_users(self, limit=5):
stmt = select(
Review.user_id,
func.count(Review.id).label('review_count')
).group_by(Review.user_id).order_by(
func.count(Review.id).desc()
).limit(limit)
return self.session.execute(stmt).all()
📄 review_manager.py
from sqlalchemy import select, func
from sqlalchemy.orm import Session
from models import Review
class ReviewManager:
def __init__(self, session: Session):
self.session = session
def create(self, movie_id: int, user_id: int, rating: int, comment: str = None) -> Review:
review = Review(
movie_id=movie_id,
user_id=user_id,
rating=rating,
comment=comment
)
self.session.add(review)
self.session.commit()
return review
def get(self, review_id: int) -> Review | None:
return self.session.get(Review, review_id)
def get_all(self):
stmt = select(Review)
return self.session.execute(stmt).scalars().all()
def get_reviews_by_user(self, user_id: int):
stmt = select(Review).where(Review.user_id == user_id)
return self.session.execute(stmt).scalars().all()
def get_latest_reviews_for_movie_by_time(self, movie_id: int, limit: int = 5):
stmt = select(Review).where(
Review.movie_id == movie_id
).order_by(Review.created_at.desc()).limit(limit)
return self.session.execute(stmt).scalars().all()
def get_highest_rated_reviews(self, movie_id: int, limit: int = 5):
stmt = select(Review).where(
Review.movie_id == movie_id
).order_by(Review.rating.desc()).limit(limit)
return self.session.execute(stmt).scalars().all()
def get_average_rating_by_user(self):
stmt = select(
Review.user_id,
func.avg(Review.rating).label('average_rating')
).group_by(Review.user_id)
return self.session.execute(stmt).all()
- از
session.get()برای دریافت یک شیء با شناسه استفاده کنید- از
session.execute(select(Model)).scalars().all()برای دریافت تمام رکوردها استفاده کنید- از
session.execute(select(Model).where(...)).scalars().first()برای دریافت اولین نتیجه استفاده کنید- از
session.execute(select(Model)).scalar_one_or_none()برای دریافت یک مقدار اسکالر یاNoneاستفاده کنید- برای روابط چندبهچند از
append()روی لیست رابطه استفاده کنید- برای کوئریهای گروهبندی از
group_by()و توابع تجمیعی مانندfunc.count()وfunc.avg()استفاده کنید- برای مرتبسازی از
order_by()باdesc()یاasc()استفاده کنید
🚀 پس از پیادهسازی Managerها، میتوانید از آنها برای انجام عملیات CRUD روی پایگاهداده استفاده کنید.
نمونهها
genres = GenreManager(session)
drama = genres.create("Drama")
comedy = genres.create("Comedy")
[drama.id, drama.name]
sorted(genre.name for genre in genres.get_all())
genres.get(drama.id).name
genres.get_genre_by_name("Comedy").name
[genres.get(999) is None, genres.get_genre_by_name("Horror") is None][1, 'Drama'] ['Comedy', 'Drama'] 'Drama' 'Comedy' [True, True]
movies = MovieManager(session)
genres = GenreManager(session)
inception = movies.create("Inception", 2010)
up = movies.create("Up", 2009)
[inception.title, inception.release_year]
sorted(movie.title for movie in movies.get_all())
movies.get(999) is None
movies.add_genre(inception.id, genres.create("Sci-Fi")).title
[genre.name for genre in movies.get(inception.id).genres]['Inception', 2010] ['Inception', 'Up'] True 'Inception' ['Sci-Fi']
movies = MovieManager(session)
users = UserManager(session)
reviews = ReviewManager(session)
inception = movies.create("Inception", 2010)
tenet = movies.create("Tenet", 2020)
ali = users.create("Ali", "ali@example.com")
sara = users.create("Sara", "sara@example.com")
first = reviews.create(inception.id, ali.id, 5, "great")
second = reviews.create(inception.id, sara.id, 4)
[first.rating, first.comment, second.comment]
movies.get_average_rating(inception.id)
movies.get_average_rating(tenet.id) is None
sorted(review.rating for review in movies.get_reviews(inception.id))[5, 'great', None] 4.5 True [4, 5]
users = UserManager(session)
ali = users.create("Ali", "ali@example.com")
[ali.name, ali.email, ali.is_verified]
users.get(ali.id).email
users.get_user_by_email("ali@example.com").name
[users.get(999) is None, users.get_user_by_email("nobody@example.com") is None]
len(users.get_all())['Ali', 'ali@example.com', False] 'ali@example.com' 'Ali' [True, True] 1
import time
movies = MovieManager(session)
users = UserManager(session)
reviews = ReviewManager(session)
a = movies.create("A", 2000)
b = movies.create("B", 2001)
u1 = users.create("U1", "u1@example.com")
u2 = users.create("U2", "u2@example.com")
u3 = users.create("U3", "u3@example.com")
for movie, user, rating in [(a, u1, 2), (a, u2, 5), (b, u1, 4), (a, u1, 3), (b, u3, 1), (b, u1, 5), (b, u2, 3)]:
reviews.create(movie.id, user.id, rating)
time.sleep(0.01)
[tuple(row) for row in users.get_most_active_users(2)]
sorted(review.rating for review in reviews.get_reviews_by_user(u1.id))
[review.rating for review in reviews.get_latest_reviews_for_movie_by_time(a.id, 2)]
[review.rating for review in reviews.get_highest_rated_reviews(b.id, 2)]
sorted((row[0], round(row[1], 2)) for row in reviews.get_average_rating_by_user())
[len(reviews.get_all()), reviews.get(999) is None][(1, 4), (2, 2)] [2, 3, 4, 5] [3, 5] [5, 4] [(1, 3.5), (2, 4.0), (3, 1.0)] [7, True]
برای نوشتن پاسخ و دریافت امتیاز، ابتدا وارد حساب شوید.
ورود