[Answer]-Django: user interface for changing objects foreign key

0👍

I realised I was being quite silly. If instead of insisting on using inline formsets for the Books, I just send back a formset of all Books (regardless of Author) and another of all Authors, then every Book has an Author drop down which is trivial to update with javascript upon dragging and dropping it into a new list (this field could made hidden for presentation purposes). Everything then just works upon save as it should.

For the problem of organising the right Books into the right Author <ol> in such a setup, a small template tag filter does the job:

@register.filter(name='forms_for_author')
def forms_for_author(book_formset, authorid):

forms = []
for form in book_formset:
    if str(form.instance.tap_id) == str(tapid):
        forms.append(form)
return forms

Used in template as

{% for authorform in authorformset %}
<ol class="author">
    {% for bookform in bookformset|forms_for_author:authorform.id.value %}
     <li>..</li>
     {% endfor %}
</ol>
{% endfor %}

1👍

It would be helpful if you also included your form and view code. However, as a general concept this doesn’t seem like it should be too hard to implement. Are you using class-based views? It sounds like one way to think about what’s going on is that Creation logic is being triggered when you’d rather have Update logic being triggered. CBVs are designed for exactly this sort of thing. In terms of your models, you need to pass a reference to the Book instance’s PK up to an Update View (either a Class-based or Functional view) along with the PK of the new Author.

Okay, without actually getting this code running locally, it’s hard to know if this will exactly solve your problem, but I think the crux is:

for pair in pairs:
            author=pair['authorform'].instance
            #For this author, delete all current books, then repopulate
            #from forms in book list that came from request.
            old_book_pks = set([book.pk for book in author.books.all()])
            new_book_pks = set([bform.instance.pk for bform in pair['bookformset'].forms])
            pks_for_trash = old_book_pks - new_book_pks
            if len(pair['bookformset'].forms): pair['bookformset'].save()
        return HttpResponseRedirect(reverse('admin:index'))

Have you tried something like this:

for pk in new_book_pks:
    book = Book.objects.get(pk=pk)
    book.author = author
    book.save()

?

Also, just a note:

if len(pair['bookformset'].forms): pair['bookformset'].save()

Personally, this looks unpythonic to me. The single line conditional probably violates PEP8. Is there a reason why you’re using len(pair...)? Are you not able to just do if pair['bookformset'].forms: ?

👤LiavK

Leave a comment