Search code examples
pythondjangoforeign-keys

Django Foreign Key Related Objects Not Saving Changes, Cannot Edit


I have two models, Movie and Review. Review has a foreign key field related to Movie. I have been trying to edit the Review objects associated with an instance of Movie.

models.py

class Movie(models.Model):
    title = models.CharField(max_length=160)

class Review(models.Model):
    movie = models.ForeignKey(Movie, on_delete=models.CASCADE, related_name='reviews')
    author = models.CharField(max_length=150)
    active = models.BooleanField(default=True)

views.py

    # Create and save movie object
    movie = Movie(title="Nightcrawler")
    movie.save()

    # Create and save two review objects
    review1 = Review(movie=movie, author="John", active=True)
    review2 = Review(movie=movie, author="Rob", active=False)
    review1.save()
    review2.save()

    print("Before: " + movie.title + " has " + str(len(movie.reviews.all())) + " reviews.")

    active_reviews = movie.reviews.filter(active=True)

    print("There are " + str(len(active_reviews)) + " active reviews.")
    movie.reviews.set(active_reviews)
    movie.reviews.first().author = "Abby"

    # Try and save both objects just for good measure.
    # Not sure if it is necessary to do this. Does not
    # seem to work anyway
    movie.reviews.first().save()
    movie.save()

    print("After: " + movie.title + " has " + str(len(movie.reviews.all())) + " reviews.")
    print("Author of the first review is: " + str(movie.reviews.first().author))

The output of the views.py code is as follows:

Before: Nightcrawler has 2 reviews.
There are 1 active reviews.
After: Nightcrawler has 2 reviews.
Author of the first review is: John

I want and expected the changes made to movies.reviews to be saved, but the output reveals that neither the set() method or changing the author value actually alters the Movie instance. Why are none of these edits being preserved?

Interestingly, the line movies.reviews.first().delete() does seem to actually remove the first review. I am curious why this works and the other changes do not.

Thank you for your time!


Solution

  • If you want to manipulate the object, you should store it in a variable first

    movie = Movie(title="Nightcrawler")
    movie.save()
    
    # Create and save two review objects
    review1 = Review(movie=movie, author="John", active=True)
    review2 = Review(movie=movie, author="Rob", active=False)
    review1.save()
    review2.save()
    
    print("Before: " + movie.title + " has " + str(len(movie.reviews.all())) + " reviews.")
    
    active_reviews = movie.reviews.filter(active=True).all()
    
    print("There are " + str(len(active_reviews)) + " active reviews.")
    
    movie.reviews.clear()
    movie.reviews.set(active_reviews)
    
    first_review = movie.reviews.first()
    first_review.author = "Abby"
    first_review.save()
    movie.save()
    

    It's not being saved because the object you updated is not the same with the object you saved because you ran another query by calling first() again.

    If you intend to only keep the "active" reviews, you can use remove instead to remove inactive reviews.

    movie.reviews.remove(*movie.reviews.filter(active=False))
    

    set does not have any effect here because the active_reivews you passed as parameter is already linked or already set. If you want to stick with set, do a clear first.