Home > Software engineering >  Django: how to delete item in one model AND change many to many relationship in another model that w
Django: how to delete item in one model AND change many to many relationship in another model that w

Time:05-21

This website allows users to select Rolling Stones concerts they've been to. It will add the Concert and the Song to the model from the API if they select it. And Concert.song has a many to many relationship with the user model.

If a user removes a concert from their concert list, the songs still appear. I don't necessarily want the song to be deleted because they could have heard the song from another concert. I also don't think it's the best idea to change the user of the song from the usernmae to --- (because null=True). I think my only choice is to change the usersonglist.html template tags somehow?

Models.py

class Concert(models.Model):
venue = models.CharField(max_length=200, null=True)
concertid = models.CharField(max_length = 200, null=False, default='didnotsaveproperly') 
date = models.DateField(null=True) 
city = models.CharField(max_length=100, null=True)
country = models.CharField(max_length=200, null=True)
user = models.ForeignKey(USER_MODEL, related_name="concerts", on_delete=models.CASCADE, null=True) 
song = models.ManyToManyField("Song", blank=True, null=True)

    def __str__(self): 
         return str(self.concertid)   " "   str(self.venue)   " "   str(self.date) 

class Song(models.Model): 
    name = models.CharField(max_length = 100, null=True) 
    user = models.ForeignKey(USER_MODEL, related_name="songs", on_delete=models.CASCADE, null=True)

    def __str__(self):
        return self.name

URLS.py

urlpatterns = [
    path("", views.display_concerts, name="choose_concerts"),
    path('add/<str:concertdict>/', views.log_concert_and_song, name='concert_add'),
    path('userconcerts/', views.ConcertListView.as_view(), name='user_concert_list'),
    path('<int:pk>/delete/', views.ConcertDeleteView.as_view(), name='delete_concert'),
    path('usersonglist/', views.SongListView.as_view(), name='user_song_list'),
]

Views.py/SongListView for usersonglist.html

class SongListView(LoginRequiredMixin, ListView):
model = Song 
template_name = "concerts/usersonglist.html" 

def get_queryset(self):
    queryset = Song.objects.filter(user=self.request.user)
    return queryset

Views.py/function view to log concert and corresponding songs for user

def log_concert_and_song(request, concertdict):
    if request.method == "POST":
        url = 'https://api.setlist.fm/rest/1.0/'
        setlist_path = f'setlist/{concertdict}'
        header = {
        "x-api-key": "******",
        "Accept": "application/json"
        } 
        params = {
        "p": 1
        }
        
        setlists = requests.get(f"{url}{setlist_path}", params=params, headers=header).json() 
        
        concertdict = {} 
        user_concert_list = []
        concertdict['venue'] = setlists['venue']['name']
        concertdict['eventDate'] = format_date(setlists['eventDate'])
        concertdict['city'] = setlists['venue']['city']['name']
        concertdict['country'] = setlists['venue']['city']['country']['name']
        concertdict['id'] = setlists['id'] 
        concert_id_to_generate_songs = concertdict['id']
        Concert_save = Concert(
            concertid=concertdict['id'], 
            date=concertdict['eventDate'], 
            venue=concertdict['venue'], 
            city=concertdict['city'], 
            country=concertdict['country'], 
            user = request.user
            )
        
        Concert_save.save() 
        user_concert_list.append(concertdict)  

        songdict = {} # {concertid: [song1, song2, etc.]} #not needed for saving song name but in case I need to reference 
        list_of_songs_from_api = setlists['sets']['set'][0]['song'] #[{"name": "Shame, Shame, Shame","cover": {},},{"name": "Down the Road Apiece", "cover": { },}]
        final_list = []
        
        for song in list_of_songs_from_api: #song = {"name": "Shame, Shame, Shame","cover": {},}
            song_name = song['name'] 
            try: 
                Song_save = Song.objects.get(name=song_name, user=request.user) 
            except: 
                Song_save = Song(
                    name=song_name,
                    user=request.user
                )
                Song_save.save() 
            
            Concert_save.song.add(Song_save) 

            final_list.append(song['name'])
            songdict[concert_id_to_generate_songs] = final_list 
        
        return redirect("user_concert_list")
    else:
        return render(request, 'concerts/concertslist.html')

Views/DeleteView to delete concert (but I need this to also remove the song from the usersonglist.html)

class ConcertDeleteView(DeleteView):
    model = Concert
    success_url = reverse_lazy('user_concert_list')

   def get(self, *a, **kw):
        return self.delete(*a, **kw)

HTML form to delete concert

             <form method="post" action="{% url 'delete_concert' concert.pk %}">
                {% csrf_token %}
                <input type="submit" value="Delete" />             
            </form> 

CodePudding user response:

Bryant, When we link the user to another model via ForeignKey we have to tell django what to do with the model if the user is deleted. so we say

 user = models.ForeignKey(USER_MODEL, related_name="songs", 
                          on_delete=models.CASCADE, null=True)

here we are telling django to delete all the songs related to user if the user is deleted because we have set on_delete=models.CASCADE here.

but if we say on_delete=models.DO_NOTHING then the django will not delete the songs or concerts related to the django model if the user is deleted.

  • Related