import random import string from uuid import uuid4 from django.db import models from django.utils import timezone def generate_isbn() -> str: """ Generate a random ISBN number. Returns: A 13-digit string. """ digits = string.digits return "".join(random.choice(digits) for _ in range(13)) class BookQuerySet(models.QuerySet): """ QuerySet class for books. """ def available(self) -> models.QuerySet: """ Get books available for a loan. Excludes books with at least a loan whose `borrowed` field is `True`. Given the constraints, only one loan for a book can have the `borrowed` status to `True`. """ return self.exclude(loans__borrowed=True) def late_returns(self) -> models.QuerySet: """ Get books that should have been returned by now. """ now = timezone.now() return self.filter(loans__expected_return__lt=now, borrowed=True) class Book(models.Model): """ Description of a book. """ uuid = models.UUIDField(default=uuid4, db_index=True, verbose_name="UUID") isbn = models.CharField(max_length=64, default=generate_isbn, blank=False, verbose_name="ISBN") name = models.CharField(max_length=128, blank=False, verbose_name="name") description = models.TextField(blank=True, verbose_name="description") registration_date = models.DateTimeField(auto_now_add=True, verbose_name="creation date") year = models.PositiveIntegerField(default=1950, null=True, db_index=True, verbose_name="published") authors = models.ManyToManyField("library.Author", related_name="books", verbose_name="authors") genre = models.ForeignKey( "library.Genre", on_delete=models.SET_NULL, null=True, related_name="books", verbose_name="genre" ) objects = BookQuerySet.as_manager() class Meta: verbose_name = "book" verbose_name_plural = "books" def __str__(self): return f"{self.name} ({self.isbn})"