How to extend a User's profile to save data in Django
I'm new to Django and I'm attempting to build a website where a user can view a DB of books and related authors and add any book to their 'favourites'. I've been searching lots and I can't find a satisfactory way of doing this - or indeed of saving any user data other than customising fields when you create a user.
My current idea is to extend the UserModel to add an extra field which links their favourite book to the user. Tips on UserModel found here
I can now see the UserProfile with some favourites by logging in to the Admin account, but I cannot link the View class/function with my bookfavs.html for the User to see their favourited books. The bookfavs.html states the username but not favourites.
readable_project app_name = bookshelf
#Models.py
class UserProfile(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
favorites = models.ManyToManyField(Book, related_name='favourites')
class Author(models.Model):
first_name=models.CharField(max_length=30)
last_name=models.CharField(max_length=30)
dob = models.DateField()
bio = models.CharField(max_length=300)
def __str__(self):
return self.first_name + " " + self.last_name
def get_absolute_url(self):
return reverse('authorlist')
class Book(models.Model):
id = models.AutoField(primary_key=True)
users = models.ManyToManyField(User)
title = models.CharField(max_length=50)
num_page = models.IntegerField()
date_pub = models.DateField()
tags = models.CharField(max_length=200)
blurb = models.CharField(max_length=300)
def get_absolute_url(self):
return reverse('')`
#views.py
#Add favourite button...?
def favorite(request, pk):
if request.method == 'POST':
favorite = Book.objects.get(pk=pk)
user = request.user
user.favorites.add(favorite)
messages.add_message(request, messages.INFO, 'Book Favorited.')
return redirect('home')
#List ALL books
class BookList(ListView):
model = Book
template_name='bookshelf/booklist.html'
#List favourite books
class BookFavs(TemplateView):
model = UserProfile
template_name = 'bookshelf/bookfavs.html'
def get_context_data(self,*args, **kwargs):
context = super(BookFavs, self).get_context_data(*args,**kwargs)
context['users'] = UserProfile.objects.all()
return context
#bookfavs.html
{% if user.username %}
<h4 class="header-greet">Welcome {{ user.username }} to your books!</h4>
{% endif %}
{% if user.is_authenticated %}
{% with userprofile.favorites.all as favourite_books %}
{% for book in book_list %}
{% if book in favorite_book %}
<p>{{ book.title }}</p>
{% endif %}
{% endfor %}
{% endwith %}
{% endif %}
Problems :
- you are trying to access
userprofile.favorites.all
in the template but you are passingusers
(which is a list of all user profiles) to the template context. - in your template you are looping through
book_list
, but it should befavourite_books
(the user's favorite books) to check if the book is in their favorites.
Code :
# List favorite books
class BookFavs(TemplateView):
template_name = 'bookshelf/bookfavs.html'
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
# access the current user's profile and their favorite books
if self.request.user.is_authenticated:
user_profile = self.request.user.userprofile
context['favorite_books'] = user_profile.favorites.all()
return context
You can change your bookfavs.html according to new context with some extra messages:
{% if user.is_authenticated %}
<h4>Welcome {{ user.username }}! Here are your favorited books:</h4>
<ul>
{% for book in favorite_books %}
<li>{{ book.title }}</li>
{% empty %}
<p>You have no favorite books yet.</p>
{% endfor %}
</ul>
{% else %}
<p>Please log in to view your favorite books.</p>
{% endif %}