Search code examples
djangomany-to-many

How to save multiple vlaues, which are retreived from frontend, in database in django Many to Many relationship?


While adding a book, I have to get data from frontend. I created a AddBookForm form. Book and Author have Many-to-Many relationship. From the frontend Form I get the names of the authors for the corresponding book as a list(from dynamic input field). Now the data is saved but is stored incorrectly. I know this is not the optimized way to save many to many relationship data. Please help me do this in an optimized way. models.py:

class Book(models.Model):
    name = models.CharField(max_length=200, null=False, blank=False)
    description = models.TextField(null=True, blank=True)
    no_of_copies = models.IntegerField(default=1, null=True, blank=True)
    status = models.CharField(max_length=15, choices=[('Out of Stock', 'Out of Stock'), ('Available', 'Available')], default='Available')
    updated_at = models.DateTimeField(auto_now=True)
    created_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name
    

class Author(models.Model):
    name = models.CharField(max_length=100, null=True, blank=True)
    books = models.ManyToManyField(Book)

    def __str__(self):
        return self.name
    

class Genre(models.Model):
    name = models.CharField(max_length=50, null=True, blank=True)
    books = models.ManyToManyField(Book)
    
    def __str__(self):
        return self.name

forms.py

class AddBookForm(forms.ModelForm):
    authors = forms.CharField(max_length=255, required=False)
    genres = forms.CharField(max_length=255, required=False)

    class Meta:
        model = Book
        fields = '__all__'

views.py

def add_book(request):
    if request.method == 'POST':
        form = AddBookForm(request.POST)
        if form.is_valid():
            book = form.save(commit=False)
            authors = request.POST.getlist('authors')
            book.save()
            for name in authors:
                author, created = Author.objects.get_or_create(name=name)
                author.books.add(book)

            return redirect("/")

    else:
        form = AddBookForm()

    context = {'form': form}
    return render(request, 'book/add_book.html', context)

I want to add the authors corresponding to that books to the database. I found out that .set() can be used but I am not sure how to use this in this context. Could you please mention if it is possible to do this with .set() And also should I create another ManyToManyField in the Book model? Kindly help me with this problem. Thank you


Solution

  • hello my friend first of all you just need to override the save in your form:

    so it will be like this:

    def save(self, commit=True):
        book = super().save(commit=False) # get the instance
        if commit:
            book.save()# normally save the book
        authors = self.cleaned_data.get('authors') # get the authors (ids also valid)
        if authors:
            book.authors_set.set(authors) # set the authors to the book
        return book