15

I have a library with shelves and books. I point each book to one shelf in a one-to-many relationship. If a book is pointing to a Null it means that it's in the library, but not in the shelf yet.

#models.py

class Shelf(models.Model):
    pass

class Book(models.Model):
    shelf = models.ForeignKey(Shelf, blank=True, null=True)

Then:

#admin.py

class BookInLine(admin.TabularInLine):
    model = Book
    extra = 0

class Shelf(admin.ModelAdmin):
    inlines = [ BookInLine, ]

When I edit the Shelf I can see and modify all the books that are in that shelf.

Problem:

  • I have a lot of books already in the library (pointing to Null).
  • If I click 'Add another Book' from the inline it will create a totally new book. But I want to avoid that. I would like to select from the books that are already in the library but doesn't belong to any shelf yet.
EricPb
  • 560
  • 1
  • 8
  • 21

1 Answers1

9

Hi the following code worked for me:

from widgets import ImproveRawIdFieldsForm

class BookInline(admin.TabularInline):
    model = Book
    raw_id_fields=('shelf',)
    extra =1
class Shelf(ImproveRawIdFieldsForm):
    inlines = [BookInline,]

It creates an admin view where you will se the normal Shelf stuff and the additional inline which is a raw id field and you have the posssibility to add new relations and you can chose from existing objects with the "magnifier" icon, which results in a pop-up of a list of all existing books. Besides chose one Book in the pop-up you can also create new Books there. So from my understanding this solves all your requirements

a better solution for this problem is explained here: one-to-many inline select with django admin

edited for your use-case:

#models.py
class Book(models.Model):
    shelf = models.ForeignKey(Shelf, blank=True, null=True, related_name="in_shelf")

#admin.py
class ShelfForm(forms.ModelForm):
    class Meta:
        model = Shelf

    books = forms.ModelMultipleChoiceField(queryset=Book.objects.all())

    def __init__(self, *args, **kwargs):
        super(ShelfForm, self).__init__(*args, **kwargs)
        if self.instance:
            if self.instance.in_shelf:
                self.fields['books'].initial = self.instance.in_shelf.all()
            else:
                self.fields['books'].initial = []

    def save(self, *args, **kwargs):    
        instance = super(ShelfForm, self).save(commit=False)
        self.fields['books'].initial.update(shelf=None)
        self.cleaned_data['books'].update(shelf=instance)
        return instance
Community
  • 1
  • 1
gaw
  • 1,960
  • 2
  • 14
  • 18
  • I get the error: `'ReverseSingleRelatedObjectDescriptor' object has no attribute 'through'` – EricPb Jul 30 '14 at 07:20
  • Also if I set up as `model = Book` then I get the error: `'BookInLine.raw_id_fields' refers to field 'book' that is missing from model 'myapp.Book'` – EricPb Jul 30 '14 at 07:38
  • you can have a look at http://stackoverflow.com/questions/6034047/one-to-many-inline-select-with-django-admin – gaw Jul 30 '14 at 09:05
  • the new inlines keeps beeing new records (not existing ones). There isn't any pop-up or "magnifier" icon. The `raw_id_fields` only works if I set it on `BookAdmin`. If I set it inside `BookInLine` it doesn't have any effect as it's being loaded from the `Shelf`. – EricPb Jul 30 '14 at 09:32
  • have a look at the updated solution that worked for me its a bit more complex but you can specify all relevant parameters – gaw Jul 30 '14 at 09:35
  • 1
    Thanks @gaw it works the second option. Will need a lot of things to fix though... I will mark it as answered. But isn't there any option to change the "Add another Book" for "Attach another Book" through a pop-up window or something like that? Would be much easier and convenient for the user. Thanks! – EricPb Jul 30 '14 at 09:46
  • 3
    Where does ImproveRawIdFieldsForm come from? I am also looking for the exact same solution as Eric. 'attach another book' instead of 'add another book' – David Schumann Jul 03 '15 at 15:38
  • @DavidNathan It should be coming from [this snippet](https://djangosnippets.org/snippets/2217/) which is [already built-in](https://stackoverflow.com/a/35991918) since [django-1.10](https://docs.djangoproject.com/en/dev/releases/1.10/#minor-features) `Selected objects for fields in ModelAdmin.raw_id_fields now have a link to object’s change form.` – raratiru May 28 '19 at 09:25