I have django project with three below models:
models.py
from django.db import models
from django.contrib.auth.models import User
class Album(models.Model):
owner = models.ForeignKey(User)
title = models.CharField(max_length=127)
artist = models.CharField(max_length=63)
release_date = models.DateField()
logo = models.ImageField(blank=True, upload_to='album_logos', default='album_logos/no-image.jpeg')
t_added = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(null=True, blank=True, max_length=63)
class Meta:
ordering = ['-release_date']
def __str__(self):
return self.title
class Song(models.Model):
album = models.ForeignKey(Album, on_delete=models.CASCADE)
name = models.CharField(max_length=255)
# is_favorite = models.BooleanField(default=False)
favorites = models.IntegerField(default=0)
song_file = models.FileField(blank=True, null=True, upload_to='song_files', default='song_files/mektub.mp3')
class Meta:
ordering = ['name']
def __str__(self):
return self.name
class Favorite(models.Model):
user = models.ForeignKey(User)
song = models.ForeignKey(Song)
created = models.DateTimeField(auto_now_add=True)
As you can see from these models many users can favorite many songs. In template, I want to add class to songs which are favorited by authenticated user:
template
<span {% if authenticated user favorited this song %}class="favorited" {% endif %}></span>
My problem is, I don't know how to write "if authenticated user favorited this song" in template. In terminal, I can get this information by this code:
user_favorited_this = song.favorite_set.filter(user=sample_user) and True or False
I couldn't do the same thing in template, since it doesn't support passing argument to filter method. How can I overcome this problem?
A tag filter can do what you want:
If the User.favorite_set.all has something in common with Song.favorite_set.all this means the current user has favorited that song
from django import template
register = template.Library()
# Method 1 for django queryset (Better)
#register.filter
def intersection(queryset1,queryset2):
return queryset1 & queryset2
# Method 2 for queryset or python List
#register.filter
def intersection(queryset1,queryset2):
return list(set.intersection(set(queryset1),set(queryset2)))
html:
{% if request.user.favorite_set.all|intersection:song.favorite_set.all %} class="favorited" {% endif %}
It may just be simpler to do this in the view, and then pass the result to the template as a boolean flag.
For example:
def song_view(request):
...
song = Song.objects.get(pk=request.GET['song_id'])
is_favorite = song.favorite_set.filter(user=request.user).exists()
return render(request, 'my_template.html', {'is_favorite': is_favorite})
or for a generic class based view:
class SongDetail(DetailView):
...
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
song = self.get_object()
is_favorite = song.favorite_set.filter(user=self.request.user).exists()
context['is_favorite'] = is_favorite
return context
and then the code in your template can be simplified to:
<span {% if is_favorite %}class="favorited" {% endif %}></span>
Related
Django beginner here.
I am trying to make a an app for where users can make connections, post stuff, chat, etc. There are two user types - Parents and Child. For this I extended the AbstractBaseUser model and created two another models - Parent and Child with a OneToOne link to the User.
#accounts/models.py
class User(AbstractBaseUser, PermissionsMixin):
REQUIRED_FIELDS = []
EMAIL_FIELD = "email"
USERNAME_FIELD = 'email'
objects = UserManager()
email = models.EmailField(unique=True)
first_name = models.CharField(max_length=DefaultModel.MAX_LENGTH, unique=False)
last_name = models.CharField(max_length=DefaultModel.MAX_LENGTH, unique=False)
profile_photo = models.ImageField(default='uploads/profile/default_profile.jpg', upload_to=content_image_name)
cover_photo = models.ImageField(default='uploads/profile/default_cover.jpg', upload_to=content_image_name)
username = AutoSlugField(populate_from='first_name', unique=True, sep='.')
bio = models.CharField(max_length=255, blank=True, default="Nothing to see here !")
is_child = models.BooleanField(default=False)
is_parent = models.BooleanField(default=False)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)
is_superuser = models.BooleanField(default=False)
# storing timestamps for users.
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
CHOICES = (('M','Male'),('F','Female'),('O','Other'))
gender = models.CharField(max_length=10, choices=CHOICES)
def get_absolute_url(self):
return "/users/{}".format(self.username)
def __str__(self):
return "{} {}".format(self.first_name, self.last_name)
class Child(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
friends = models.ManyToManyField('self',
blank=True,
related_name='friends',
db_column='friends',)
def __str__(self):
return "{} {}".format(self.user.first_name, self.user.last_name)
class Parent(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
connections = models.ManyToManyField('self',
blank=True,
related_name='connections',
db_column='connections',)
def __str__(self):
return "{} {}".format(self.user.first_name, self.user.last_name)
As you can see a Child can only be a friend with another Child and a Parent can only connect with a Parent.
Basically I have two apps - Feeds for handling posts and accounts for handling accounts. There is a page for displaying the current users (/childs/ for Child and /parents/ for Parent) and another for friends (/friends/ for Child and /connections/ for Parent).
In the homepage (/home) of the apps there are two sidebars - one for showing users whom the request.user can send friend request to and another for displaying the friends of request.user. Since there is a single url (/home) for both user types, therefore the strategy is :
Make a base ListView for displaying both current users and friends.
Inherit it for the individual users and friends page.
Inherit it for home.html for /home.
#method_decorator(login_required, name='dispatch')
class UserList(ListView):
model = User
def get_context_data(self, *args, **kwargs):
context = super(UserList, self).get_context_data(**kwargs)
if self.request.user.is_child:
childs = Child.objects.exclude(user=self.request.user.child)
sent_requests = ChildFriendRequest.objects.filter(from_user=self.request.user.child)
recv_requests = ChildFriendRequest.objects.filter(to_user=self.request.user.child)
friends = self.request.user.child.friends.all()
recv_from = [i.from_user for i in recv_requests]
users = [i for i in childs if i not in friends and i not in recv_from]
sent_to = [ i.to_user for i in sent_requests]
context['users'] = users
context['sent'] = sent_to
context['friends'] = friends
context['recv_requests'] = recv_requests
elif self.request.user.is_parent:
parents = Parent.objects.exclude(user=self.request.user.parent)
sent_requests = ParentConnectionRequest.objects.filter(from_user=self.request.user.parent)
recv_requests = ParentConnectionRequest.objects.filter(to_user=self.request.user.parent)
connections = self.request.user.parent.connections.all()
recv_from = [i.from_user for i in recv_requests]
users = [i for i in parents if i not in connections and i not in recv_from]
sent_to = [ i.to_user for i in sent_requests]
context['users'] = users
context['sent'] = sent_to
context['connections'] = connections
context['recv_requests'] = recv_requests
return context
class ChildList(UserList):
template_name = "account/child/childs_list.html"
class FriendList(UserList):
template_name = "account/child/friend_list.html"
class ParentList(UserList):
template_name = "account/parent/parent_list.html"
class ConnectionList(UserList):
template_name = "account/parent/connection_list.html"
class Sidebar(UserList):
template_name = "feeds/home.html"
Now views of Feeds app also use home.html for it displaying feeds.
class PostListView(ListView):
model = Post
template_name = 'feeds/home.html'
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = 10
def get_context_data(self, **kwargs):
context = super(PostListView, self).get_context_data(**kwargs)
if self.request.user.is_authenticated:
liked = [i for i in Post.objects.all() if Like.objects.filter(user = self.request.user, post=i)]
context['liked_post'] = liked
return context
The problem here is that whenever /friends or /childs is accessed I can see the users but in the /home no user is shown although I can see the posts.
Here's home.html
{% extends "feeds/layout.html" %}
{% load static %}
{% block friends_sidebar %}
<div class="widget stick-widget">
<h4 class="widget-title">People Nearby</h4>
<ul class="followers">
{% if users %}
{% for user_p in users %}
<li>
<figure>
<img src="{{ user_p.user.profile_photo.url }}" width="40" height="40" alt="">
</figure>
<div class="friend-meta">
<h4>{{ user_p.user }}</h4>
{% if not user_p in sent %}
Add Friend
{% else %}
Cancel Request
{% endif %}
</div>
</li>
{% endfor %}
{% else %}
<p>No one is here !</p>
{% endif %}
</ul>
</div>
{% endblock %}
I can only see :
No one is here !
So the question is how can I go working around this ? Is it because two views are using the same template ?
I am using Django 3.2.9 and Python 3.8.
As #Razenstein mentioned I needed to have the contexts of UserList in PostListView, so I inherited UserList in PostListView and that did it. Here's what was to be done :
class PostListView(UserListView):
model = Post
template_name = 'feeds/home.html'
context_object_name = 'posts'
ordering = ['-date_posted']
paginate_by = 10
def get_context_data(self, **kwargs):
context = super(PostListView, self).get_context_data(**kwargs)
if self.request.user.is_authenticated:
liked = [i for i in Post.objects.all() if Like.objects.filter(user = self.request.user, post=i)]
context['liked_post'] = liked
return context
The key here is to inherit the UserList class and use super() in get_context_data().
I want to fetch all the foreignkey table's attribute and show it in my HTML template. Here is my code in models, views and in the template:
models.py:
class OrderDashboard(models.Model):
title = models.CharField(max_length=100,default=None)
single_slug = models.SlugField(max_length=100, default=1)
description = models.TextField(max_length=1000)
thumb = models.ImageField()
date = models.DateField()
def __str__(self):
return self.title
class OrderScenario(models.Model):
webshop = models.CharField(max_length=100)
title = models.ForeignKey(OrderDashboard, default=None, on_delete=models.SET_DEFAULT)
order_qty = models.TextField(max_length=10)
order_date = models.DateField()
current_status = models.CharField(max_length=100)
ticket = models.CharField(max_length=200)
remark = models.TextField()
class Meta:
verbose_name_plural = "Scenario"
def __str__(self):
return self.webshop
Views.py:
def single_slug(request, single_slug):
report = OrderDashboard.objects.get(single_slug=single_slug)
return render(request, 'order_dashboard/report.html', {'report': report,
'OrderScenario': OrderScenario.objects.all})
I only want to view all the scenarios added in OrderScenario with respect to Title in OrderDashboard.
You should use backward relationship here; if you are passing the slug through the url, you can use:
views.py:
def single_slug(request, slug): # why you have self as the first argument?
report = OrderDashboard.objects.get(single_slug=slug)
return render(request, 'order_dashboard/report.html', {'report': report}
report.html:
{{ report.title }}
</p>Order Scenarios:</p>
{% for scenario in report.orderscenario_set.all %}
{{ scenario }}
{% endfor %}
class IndexView(generic.ListView):
template_name = "posts/index.html"
def get_queryset(self):
return Inspectionfile.objects.all()
class DetailView(generic.DetailView):
model = Inspectionfile
template_name = "posts/detail.html"
class createposts(CreateView):
model = posts
fields = ['title','comments']
via the createposts and using forms I can populate the title and comments but they are not being linked with any Inspectionfile(the foreign key). I want the users to be linked without them having to choose. The following is my model. So I want to link every post to a particular inspectionfile.
class Inspectionfile(models.Model):
document_upload = models.FileField()
document_type = models.CharField(max_length=10)
document_title = models.CharField(max_length=250)
document_check = models.CharField(max_length=250)
def __str__(self):
return (self.document_title + self.document_type)
class posts(models.Model):
inspectionfile = models.ForeignKey(Inspectionfile, on_delete=models.CASCADE, default=1)
title = models.CharField(max_length=120)
comments = models.TextField()
flag = models.BooleanField(default=False)
def get_absolute_url(self):
return reverse('posts_form', kwargs={'pk': self.pk})
def __str__(self):
return self.title
form is a simple template:
<form class = "form_horizontal" action = "" method = "post">
{% csrf_token %}
{{form.as_p}}
<button type="submit">Submit</button>
</form>
Well I think you need to override post method in your createposts view:
def post(self, request, *args, **kwargs):
current_inspectionfile = Inspectionfile.objects.get(pk=
#enter id of current file. If id is
#parameter in url then use self.kwargs['file_id'],
#where file_id is name of parameter in url
)
new_post = posts.objects.create(inspectionfile=current_inspectionfile,
#and arguments from the form
)
return new_post
And Off-topic: class names in python are usually called in CamelCase in the singular. So class Post(models.Model) and class CreatePost(CreateView):
I wrote my / sample method and want to show it in the django template.
I am trying to {{filter.my_method}} and nothing it does not give.
Below my model with the method and view.
My model and my method:
class MyModel(models.Model):
user = models.ForeignKey(User, blank=True, null=True)
name = models.CharField(max_length=255, blank=True, null=True)
def __unicode__(self):
return self.user
def my_method(self):
return self.name
My view
class Index(ListView):
template_name = "index.html"
context_object_name = 'users'
def get(self, request):
filter = Filter(request.GET, queryset=MyModel.objects.all())
return render(request, self.template_name, {'filter': filter})
You should call this method not on the filter but on the model instance:
{% for obj in filter %}
{{ obj.my_method }}
{% endfor %}
UPDATE
I was able to accomplish this in my Project models.py, but I feel this belongs in my view. How would I go about doing this? Code:
#property
def related_project_set(self):
category_id_list = self.category.values_list("id", flat=True)
return Project.live.filter(category__id__in=category_id_list).exclude(id=self.id)[:5]
I'd like to display "projects" related by "category" in my generic class-based detail view for project. I'm also using the Django-Braces PrefetchRelated Mixin. But I don't understand why the Python interpreter displays different results than my template.
For example, given two projects in the db, I get the following result:
>>> from projects.models import Project
>>> Project.objects.all().prefetch_related('category')
[<Project: Avalon Road>, <Project: Piedmont Avenue>]
Models:
class Project(models.Model):
"""
A project completed by ICON.
"""
LIVE_STATUS = 1
DRAFT_STATUS = 2
STATUS_CHOICES = (
(LIVE_STATUS, 'Live'),
(DRAFT_STATUS, 'Draft'),
)
title = models.CharField(max_length=200)
slug = models.SlugField(help_text='Populates from title field.')
date_completed = models.DateField(null=True, blank=True)
vison_description = models.TextField(help_text='Please use Markdown syntax. No HTML is allowed.')
work_description = models.TextField(help_text='Please use Markdown syntax. No HTML is allowed.')
category = models.ManyToManyField(Category)
lead_photo = models.ImageField(upload_to='projects/photos', help_text='Will also be used as thumbnail for category listing.')
gallery = models.ForeignKey(Gallery)
status = models.IntegerField(choices=STATUS_CHOICES, default=2, help_text="Only projects with a status of 'live' will be displayed publicly.")
objects = models.Manager()
live = LiveProjectManager()
class Category(models.Model):
title = models.CharField(max_length=100)
slug = models.SlugField(help_text='Populates from title field.')
description = models.TextField(help_text='Please use Markdown syntax. No HTML is allowed.', blank=True)
class Meta:
verbose_name_plural = 'Categories'
ordering = ('title',)
def __str__(self):
return self.slug
#models.permalink
def get_absolute_url(self):
return ('categories:detail',
(),
{'slug': self.slug})
Views:
from __future__ import absolute_import
from braces.views import PrefetchRelatedMixin
from django.shortcuts import render
from django.views.generic import DetailView, ListView
from .models import Project
from categories.models import Category
from testimonials.models import Testimonial
class ProjectDetailView(PrefetchRelatedMixin, DetailView):
prefetch_related = ['category']
queryset = Project.live.all()
def get_context_data(self, **kwargs):
"""
Allow the project detail view to display a list of testimonials.
"""
context = super(ProjectDetailView, self).get_context_data(**kwargs)
context['testimonial_list'] = Testimonial.displayed.all()
return context
class ProjectListView(ListView):
queryset = Project.live.all()
def get_context_data(self, **kwargs):
"""
Allow the project list view to display a list of categories.
"""
context = super(ProjectListView, self).get_context_data(**kwargs)
context['category_list'] = Category.objects.all()
return context
Template snippet:
<ul>
{% for project in object.category.all %}
<li>{{ project.title }}</li>
{% endfor %}
</ul>