- {% if list.curation != "open" and request.user != list.user and not list.group|is_member:request.user %}
- {% trans "You successfully suggested a book for this list!" %}
- {% else %}
- {% trans "You successfully added a book to this list!" %}
- {% endif %}
+ {% if add_failed %}
+
+
+
+ {% trans "That book is already on this list." %}
+
+
+ {% elif add_succeeded %}
+
+
+
+ {% if list.curation != "open" and request.user != list.user and not list.group|is_member:request.user %}
+ {% trans "You successfully suggested a book for this list!" %}
+ {% else %}
+ {% trans "You successfully added a book to this list!" %}
+ {% endif %}
+
{% endif %}
diff --git a/bookwyrm/views/books/books.py b/bookwyrm/views/books/books.py
index e04230ba..ad7ee943 100644
--- a/bookwyrm/views/books/books.py
+++ b/bookwyrm/views/books/books.py
@@ -83,6 +83,7 @@ class Book(View):
}
if request.user.is_authenticated:
+ data["list_options"] = request.user.list_set.exclude(id__in=data["lists"])
data["file_link_form"] = forms.FileLinkForm()
readthroughs = models.ReadThrough.objects.filter(
user=request.user,
diff --git a/bookwyrm/views/list/list.py b/bookwyrm/views/list/list.py
index fbbbee9f..dc1843fa 100644
--- a/bookwyrm/views/list/list.py
+++ b/bookwyrm/views/list/list.py
@@ -1,11 +1,10 @@
""" book list views"""
from typing import Optional
-from urllib.parse import urlencode
from django.contrib.auth.decorators import login_required
from django.core.exceptions import PermissionDenied
from django.core.paginator import Paginator
-from django.db import IntegrityError, transaction
+from django.db import transaction
from django.db.models import Avg, DecimalField, Q, Max
from django.db.models.functions import Coalesce
from django.http import HttpResponseBadRequest, HttpResponse
@@ -26,7 +25,7 @@ from bookwyrm.views.helpers import is_api_request
class List(View):
"""book list page"""
- def get(self, request, list_id):
+ def get(self, request, list_id, add_failed=False, add_succeeded=False):
"""display a book list"""
book_list = get_object_or_404(models.List, id=list_id)
book_list.raise_visible_to_user(request.user)
@@ -37,33 +36,10 @@ class List(View):
query = request.GET.get("q")
suggestions = None
- # sort_by shall be "order" unless a valid alternative is given
- sort_by = request.GET.get("sort_by", "order")
- if sort_by not in ("order", "title", "rating"):
- sort_by = "order"
-
- # direction shall be "ascending" unless a valid alternative is given
- direction = request.GET.get("direction", "ascending")
- if direction not in ("ascending", "descending"):
- direction = "ascending"
-
- directional_sort_by = {
- "order": "order",
- "title": "book__title",
- "rating": "average_rating",
- }[sort_by]
- if direction == "descending":
- directional_sort_by = "-" + directional_sort_by
-
- items = book_list.listitem_set.prefetch_related("user", "book", "book__authors")
- if sort_by == "rating":
- items = items.annotate(
- average_rating=Avg(
- Coalesce("book__review__rating", 0.0),
- output_field=DecimalField(),
- )
- )
- items = items.filter(approved=True).order_by(directional_sort_by)
+ items = book_list.listitem_set.filter(approved=True).prefetch_related(
+ "user", "book", "book__authors"
+ )
+ items = sort_list(request, items)
paginated = Paginator(items, PAGE_LENGTH)
@@ -106,10 +82,10 @@ class List(View):
"suggested_books": suggestions,
"list_form": forms.ListForm(instance=book_list),
"query": query or "",
- "sort_form": forms.SortListForm(
- {"direction": direction, "sort_by": sort_by}
- ),
+ "sort_form": forms.SortListForm(request.GET),
"embed_url": embed_url,
+ "add_failed": add_failed,
+ "add_succeeded": add_succeeded,
}
return TemplateResponse(request, "lists/list.html", data)
@@ -131,6 +107,36 @@ class List(View):
return redirect(book_list.local_path)
+def sort_list(request, items):
+ """helper to handle the surprisngly involved sorting"""
+ # sort_by shall be "order" unless a valid alternative is given
+ sort_by = request.GET.get("sort_by", "order")
+ if sort_by not in ("order", "title", "rating"):
+ sort_by = "order"
+
+ # direction shall be "ascending" unless a valid alternative is given
+ direction = request.GET.get("direction", "ascending")
+ if direction not in ("ascending", "descending"):
+ direction = "ascending"
+
+ directional_sort_by = {
+ "order": "order",
+ "title": "book__title",
+ "rating": "average_rating",
+ }[sort_by]
+ if direction == "descending":
+ directional_sort_by = "-" + directional_sort_by
+
+ if sort_by == "rating":
+ items = items.annotate(
+ average_rating=Avg(
+ Coalesce("book__review__rating", 0.0),
+ output_field=DecimalField(),
+ )
+ )
+ return items.order_by(directional_sort_by)
+
+
@require_POST
@login_required
def save_list(request, list_id):
@@ -179,8 +185,8 @@ def add_book(request):
form = forms.ListItemForm(request.POST)
if not form.is_valid():
- # this shouldn't happen, there aren't validated fields
- raise Exception(form.errors)
+ return List().get(request, book_list.id, add_failed=True)
+
item = form.save(commit=False)
if book_list.curation == "curated":
@@ -196,17 +202,9 @@ def add_book(request):
) or 0
increment_order_in_reverse(book_list.id, order_max + 1)
item.order = order_max + 1
+ item.save()
- try:
- item.save()
- except IntegrityError:
- # if the book is already on the list, don't flip out
- pass
-
- path = reverse("list", args=[book_list.id])
- params = request.GET.copy()
- params["updated"] = True
- return redirect(f"{path}?{urlencode(params)}")
+ return List().get(request, book_list.id, add_succeeded=True)
@require_POST