Repository: bookwyrm-social/bookwyrm

Issue:
Can not change link from free to lendable
**Describe the bug**
I tried to correct a link on a book from "free" to lendable in the tinkliest. This throws a 500 error. I get this form the logs

```equest, *callback_args, **callback_kwargs)
  File "/home/wyrm/bookwyrm/venv/lib64/python3.9/site-packages/django/views/generic/base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "/home/wyrm/bookwyrm/venv/lib64/python3.9/site-packages/django/utils/decorators.py", line 43, in _wrapper
    return bound_method(*args, **kwargs)
  File "/home/wyrm/bookwyrm/venv/lib64/python3.9/site-packages/django/contrib/auth/decorators.py", line 21, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "/home/wyrm/bookwyrm/venv/lib64/python3.9/site-packages/django/utils/decorators.py", line 43, in _wrapper
    return bound_method(*args, **kwargs)
  File "/home/wyrm/bookwyrm/venv/lib64/python3.9/site-packages/django/contrib/auth/decorators.py", line 21, in _wrapped_view
    return view_func(request, *args, **kwargs)
  File "/home/wyrm/bookwyrm/venv/lib64/python3.9/site-packages/django/views/generic/base.py", line 98, in dispatch
    return handler(request, *args, **kwargs)
  File "/home/wyrm/bookwyrm/bookwyrm/views/books/links.py", line 37, in post
    form.save(request)
  File "/home/wyrm/bookwyrm/bookwyrm/forms/custom_form.py", line 36, in save
    return super().save(*args, **kwargs)
  File "/home/wyrm/bookwyrm/venv/lib64/python3.9/site-packages/django/forms/models.py", line 460, in save
    raise ValueError(
ValueError: The FileLink could not be changed because the data didn't validate.
```
**To Reproduce**
Go to Link list of a book
Change link action fromm anything to anything

**Expected behavior**
Link is saved. 

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Instance**
wyrm.jascha.wtf, version 5.0.1
local instance on main branch




Discussion:

Resolving patch:
diff --git a/bookwyrm/forms/links.py b/bookwyrm/forms/links.py
--- a/bookwyrm/forms/links.py
+++ b/bookwyrm/forms/links.py
@@ -36,13 +36,16 @@ def clean(self):
                         "This domain is blocked. Please contact your administrator if you think this is an error."
                     ),
                 )
-            elif models.FileLink.objects.filter(
+        if (
+            not self.instance
+            and models.FileLink.objects.filter(
                 url=url, book=book, filetype=filetype
-            ).exists():
-                # pylint: disable=line-too-long
-                self.add_error(
-                    "url",
-                    _(
-                        "This link with file type has already been added for this book. If it is not visible, the domain is still pending."
-                    ),
-                )
+            ).exists()
+        ):
+            # pylint: disable=line-too-long
+            self.add_error(
+                "url",
+                _(
+                    "This link with file type has already been added for this book. If it is not visible, the domain is still pending."
+                ),
+            )
diff --git a/bookwyrm/views/books/links.py b/bookwyrm/views/books/links.py
--- a/bookwyrm/views/books/links.py
+++ b/bookwyrm/views/books/links.py
@@ -21,11 +21,7 @@ class BookFileLinks(View):
     def get(self, request, book_id):
         """view links"""
         book = get_object_or_404(models.Edition, id=book_id)
-        links = book.file_links.order_by("domain__status", "created_date")
-        annotated_links = []
-        for link in links.all():
-            link.form = forms.FileLinkForm(instance=link)
-            annotated_links.append(link)
+        annotated_links = get_annotated_links(book)
 
         data = {"book": book, "links": annotated_links}
         return TemplateResponse(request, "book/file_links/edit_links.html", data)
@@ -34,8 +30,30 @@ def post(self, request, book_id, link_id):
         """Edit a link"""
         link = get_object_or_404(models.FileLink, id=link_id, book=book_id)
         form = forms.FileLinkForm(request.POST, instance=link)
-        form.save(request)
-        return self.get(request, book_id)
+        if form.is_valid():
+            form.save(request)
+            return redirect("file-link", book_id)
+
+        # this form shouldn't ever really get here, since it's just a dropdown
+        # get the data again rather than redirecting
+        book = get_object_or_404(models.Edition, id=book_id)
+        annotated_links = get_annotated_links(book, form=form)
+
+        data = {"book": book, "links": annotated_links}
+        return TemplateResponse(request, "book/file_links/edit_links.html", data)
+
+
+def get_annotated_links(book, form=None):
+    """The links for this book, plus the forms to edit those links"""
+    links = book.file_links.order_by("domain__status", "created_date")
+    annotated_links = []
+    for link in links.all():
+        if form and link.id == form.instance.id:
+            link.form = form
+        else:
+            link.form = forms.FileLinkForm(instance=link)
+        annotated_links.append(link)
+    return annotated_links
 
 
 @require_POST


Test patch:
diff --git a/bookwyrm/tests/views/books/test_links.py b/bookwyrm/tests/views/books/test_links.py
--- a/bookwyrm/tests/views/books/test_links.py
+++ b/bookwyrm/tests/views/books/test_links.py
@@ -15,6 +15,7 @@
 class LinkViews(TestCase):
     """books books books"""
 
+    # pylint: disable=invalid-name
     def setUp(self):
         """we need basic test data and mocks"""
         self.factory = RequestFactory()