diff --git a/cbreader/settings/base.py b/cbreader/settings/base.py index 245e3c1..20cb683 100644 --- a/cbreader/settings/base.py +++ b/cbreader/settings/base.py @@ -38,6 +38,7 @@ INSTALLED_APPS = ( 'bootstrap4', "comic", "comic_auth", + 'db_mutex', ) MIDDLEWARE = [ diff --git a/comic/models.py b/comic/models.py index a18b51b..9b8fab9 100644 --- a/comic/models.py +++ b/comic/models.py @@ -13,7 +13,8 @@ from django.db.transaction import atomic from django.utils.http import urlsafe_base64_encode import PyPDF4 import PyPDF4.utils - +from db_mutex import DBMutexError, DBMutexTimeoutError +from db_mutex.db_mutex import db_mutex import rarfile @@ -114,15 +115,17 @@ class ComicBook(models.Model): try: page_obj = ComicPage.objects.get(Comic=self, index=page) except ComicPage.MultipleObjectsReturned: - ComicPage.objects.filter(Comic=self).delete() - self.process_comic_pages(archive, self) - page_obj = ComicPage.objects.get(Comic=self, index=page) + with db_mutex('comicpage'): + ComicPage.objects.filter(Comic=self).delete() + self.process_comic_pages(archive, self) + page_obj = ComicPage.objects.get(Comic=self, index=page) try: out = (archive.open(page_obj.page_file_name), page_obj.content_type) except rarfile.NoRarEntry: - ComicPage.objects.filter(Comic=self).delete() - self.process_comic_pages(archive, self) - out = self.get_image(page) + with db_mutex('comicpage'): + ComicPage.objects.filter(Comic=self).delete() + self.process_comic_pages(archive, self) + out = self.get_image(page) return out def is_last_page(self, page): diff --git a/comic/tests/test_models.py b/comic/tests/test_models.py index 03abbc8..0552a9f 100644 --- a/comic/tests/test_models.py +++ b/comic/tests/test_models.py @@ -304,4 +304,4 @@ class ComicBookTests(TestCase): c.login(username="test", password="test") response = c.get(f"/comic/read/{urlsafe_base64_encode(book.selector.bytes)}/0/img") - self.assertEqual(response.status_code, 200) \ No newline at end of file + self.assertEqual(response.status_code, 200) diff --git a/poetry.lock b/poetry.lock index d025bbf..fd858ec 100644 --- a/poetry.lock +++ b/poetry.lock @@ -111,6 +111,17 @@ django = ">=2.2,<4.0" [package.extras] docs = ["sphinx (>=2.4,<3.0)", "sphinx_rtd_theme (>=0.4.3,<0.5.0)", "m2r2 (>=0.2.5,<0.3.0)"] +[[package]] +name = "django-db-mutex" +version = "2.0.0" +description = "Acquire a mutex via the DB in Django" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +Django = ">=2.2" + [[package]] name = "django-recaptcha2" version = "1.4.1" @@ -363,7 +374,7 @@ dev = ["pytest (>=4.6.2)", "black (>=19.3b0)"] [metadata] lock-version = "1.1" python-versions = "^3.8" -content-hash = "2cf2bf8371f6fa6dcc2a9d8d889387667eed3a994e46797c5113f8ea7e7ffd95" +content-hash = "a4bc175a3b3f8918926aae3b74d4d5fee3111ddc61dcb3b37c353c0e99edc277" [metadata.files] asgiref = [ @@ -457,6 +468,10 @@ django-bootstrap4 = [ {file = "django-bootstrap4-2.3.1.tar.gz", hash = "sha256:2c199020ac38866cdf8d1c5561ce7468116b9685b455a29843c0225ef8568879"}, {file = "django_bootstrap4-2.3.1-py3-none-any.whl", hash = "sha256:b68f073b647b20ec7894a252a0ca4e06b7b8dafdbad995cb0cdc783d0bb4629d"}, ] +django-db-mutex = [ + {file = "django-db-mutex-2.0.0.tar.gz", hash = "sha256:b8f3466611ac0045ec4d07f47ded159d08a3780ebc95c48c86af3c909fb2e3e6"}, + {file = "django_db_mutex-2.0.0-py2.py3-none-any.whl", hash = "sha256:6d41176f4094c7a32c0ad6d157a0c268f23ca747ee5f32eadf7a18efe369715e"}, +] django-recaptcha2 = [ {file = "django-recaptcha2-1.4.1.tar.gz", hash = "sha256:c0b43851b05c6bf6ebb5ecc890c13ccedacd9bb33d64b4291c74dd6fcbc89366"}, {file = "django_recaptcha2-1.4.1-py3-none-any.whl", hash = "sha256:9ea90db0cec502741be1066c09ec1b8e02a73162a319a042e78e67c4605087af"}, diff --git a/pyproject.toml b/pyproject.toml index d1a4ecb..1bd9c2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,6 +23,7 @@ mysqlclient = "^2.0.1" psycopg2 = "^2.8.6" rarfile = "^4.0" coverage = "^5.5" +django-db-mutex = "^2.0.0" [tool.poetry.dev-dependencies]