Try to join a OneToOne relationship in Django - django

I need some help doing a join using Django, which seems like it should be easy. I have looked at the documentation but it seems like it won't join for some reason.
I am trying to get in my view, the model.Photo and model.PhotoExtended with both joined and then displayed in the view. Currently I am just trying to get the model.Photo displayed but with a join which finds the request.user and filters it based on that.
They are in different apps.
models.py for model.Photo
class Photo(ImageModel):
title = models.CharField(_('title'),
max_length=60,
unique=True)
slug = models.SlugField(_('slug'),
unique=True,
help_text=_('A "slug" is a unique URL-friendly title for an object.'))
models.py for model.PhotoExtended
class PhotoExtended(models.Model):
Photo = models.OneToOneField(Photo, related_name='extended', help_text='Photo required', null=True)
user = models.ForeignKey(User, on_delete=models.CASCADE, null=True, help_text='User that uploaded the photo')
views.py
class PhotoExtendedUserView(ListView):
template_name = 'photo_user_list.html'
def get_queryset(self):
user = get_object_or_404(User, username=self.request.user)
return Photo.objects.filter(photoextended__user=user)

You set the related_name on Photo (which shouldn't be capitalized by the way) to extended so you need to filter like so:
class PhotoExtendedUserView(ListView):
template_name = 'photo_user_list.html'
def get_queryset(self):
user = get_object_or_404(User, username=self.request.user)
# 'extended' vs. 'photoextended'
return Photo.objects.filter(extended__user=user)

Related

how to filter a model in a listview

I have a page that displays a list of interviewers and some important info about each user.
One of the things that I want it to show is the number of users who have been interviewed by that specific interviewer.
I wrote a view like this:
class ManagerUsers(ListView):
model = User
template_name = 'reg/manager-users.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['scientific_interviewers'] = User.objects.filter(role='theory_interviewer').all()
context['interviewed_number'] = len(ScientificInfo.objects.filter(user__role='applicant', is_approved=True, interviewer=?????))
the interviewer field should be equal to that object's user but I don't know what to do exactly.
the output should be something like this:
object 1 : user's name, user's other info, user's interviewed_number
....
these are my models:
USER_ROLE_CHOICES = (('0', 'applicant'),
('1', 'theory_interviewer'),)
class User(AbstractUser):
id = models.AutoField(primary_key=True)
role = models.CharField(max_length=25, null=True, choices=USER_ROLE_CHOICES, default=USER_ROLE_CHOICES[0][0])
username = models.CharField(unique=True, max_length=13)
first_name = models.CharField(max_length=32, null=True, default=None)
last_name = models.CharField(max_length=64, null=True, default=None)
class ScientificInfo(models.Model):
id = models.AutoField(primary_key=True)
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='user')
interviewer = models.OneToOneField(User, on_delete=models.CASCADE, related_name='interviewer')
is_approved = boolean field
You can override the .get_queryset() method [Django-doc] to return only the interviewers. By using .annotate(…) [Django-doc] you can add an extra attribute to these Users:
from django.db.models import Count
class ManagerUsersView(ListView):
model = User
context_object_name = 'scientific_interviewers'
template_name = 'reg/manager-users.html'
def get_queryset(self):
return super().get_querset().filter(
role='1'
).annotate(
interviewed_number=Count('interviewer', filter=Q(interviewer__user__role='0', interviewer__is_approved=True))
)
The Users that arise from this queryset will have an extra attribute .interviewed_number with the number of approved ScientificInfos where that user was the interviewer.
Note: In Django, class-based views (CBV) often have a …View suffix, to avoid a clash with the model names.
Therefore you might consider renaming the view class to ManagerUsersView, instead of ManagerUsers.
Note: The related_name=… parameter [Django-doc]
is the name of the relation in reverse, so from the User model to the ScientificInfo
model in this case. Therefore it (often) makes not much sense to name it the
same as the forward relation. You thus might want to consider renaming the interviewer relation to interviews.

Filtering Django admin site by user group

i want to filter the objects displayed on the admin site by using Django user groups. Each user is assigned to one or more user groups which represent different categories of articles. Each article falls into one or more categories (the names of the user groups and categories are identical). On the admin site for the Article object, users shall only see articles from categories of which they are a group member. I found a basic approach by overriding get_queryset within ModelAdmin and retrieving a list with the user's group names. But how can i reference the ArticleCategory model within ArticleAdmin to do the comparison with the group names?
models.py
class Article(models.Model):
title = models.CharField(max_length=200, default=None)
class Category(models.Model):
name = models.CharField(max_length=50, default=None)
class ArticleCategory(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE, default=None)
category = models.ForeignKey(Category, on_delete=models.CASCADE, default=None)
def __str__ (self):
return(str(self.article) + " " + str(self.category))
def natural_key(self):
return ({'category': self.category})
admin.py
class ArticleAdmin(admin.ModelAdmin):
model = Article
def get_queryset(self, request):
if request.user.is_superuser:
queryset = Article.objects.all()
else:
try:
l = request.user.groups.values_list('name',flat = True)
l_as_list = list(l)
queryset = Article.objects.filter(??? in l_as_list)
except:
queryset = Article.objects.none()
return queryset
admin.site.register(Article, ArticleAdmin)
The following line didn't work:
queryset = Article.objects.filter(articlecategory__category.name in l_as_list)

Django: Unsupported lookup 'following' for CharField or join on the field not permitted

I try to build follow system, when I tried to let the users see the posts of the users which that follow them I got error Unsupported lookup 'following' for CharField or join on the field not permitted
The profiles model
class Account(AbstractBaseUser):
email = models.EmailField(verbose_name="email", max_length=60, unique=True)
username = models.CharField(max_length=30, unique=True)
date_joined = models.DateTimeField(
verbose_name="date joined", auto_now_add=True
)
following = models.ManyToManyField(
settings.AUTH_USER_MODEL, blank=True, related_name="follow"
)
The posts model
class Video(models.Model):
author = models.ForeignKey(Account, on_delete=models.CASCADE)
video = models.FileField(upload_to='post-videos')
title = models.CharField(max_length=100)
description = models.TextField(null=True, blank=True)
my view
class home_screen_view(LoginRequiredMixin, View):
def get(self, request, *args, **kwargs):
logged_in_user = request.user
post = Video.objects.filter(
author__username__following__in=[logged_in_user.id]
).order_by('-created_date')
context = {
'post_list': post,
}
return render(request, "personal/home.html", context)
Your post = ... query is wrong because you are retrieving the author's username (which exists), but then treating it as if this field were a foreign key or m2m with a property following, which is flat out wrong. username is a CharField.
Instead let's try to clarify the concepts that will be useful to making our solution:
Each Video, has an author.
Each author, is a user.
Each user has an m2m relationship following which is the users he follows.
Each user has a REVERSE m2m relationship follow which is the users that follow him.
Therefore in your view, your query should be:
post = Video.objects.filter(
author__follow=logged_in_user
).order_by('-created_date')
With django M2Ms, so long as the logged_in_user is in that list, ...follow=logged_in_user will return true and filter the item in.

Remove option from generic class view in Django

I just started learning Django this week and I'm trying to figure out how I can remove an option from a select menu being rendered in a class based view. The dropdown is for a Foreign Key field that links to my users table.
The functionality here is that I do not want the current user logged into show up on that list (basically I don't want someone to be able to select themself). How can I go about doing this?
View:
class TransferCreateView(CreateView):
model = Transfer
template_name = 'points/transfer_form.html'
fields = ['receiver', 'message', 'amount']
Model:
class Transfer(models.Model):
receiver = models.ForeignKey(User, null=False,
on_delete=models.CASCADE, related_name='receiver')
sender = models.ForeignKey(User, null=False, on_delete=models.CASCADE, related_name='sender')
amount = models.IntegerField(
validators=[
MinValueValidator(1),
MaxValueValidator(1000)],
null=False)
message = models.CharField(max_length=100)
date_sent = models.DateTimeField(default=timezone.now)
Basically, I don't want the person who is the sender (which i was going to set in the code in a form_valid() function) to be an option for 'receiver' in the template when it renders.
Override the get_form method of the CreateView and change the queryset of that field, something like this:
class TransferCreateView(CreateView):
model = Transfer
template_name = 'points/transfer_form.html'
fields = ['receiver', 'message', 'amount']
def get_form(self, form_class):
form = super().get_form(form_class)
form.fields['receiver'].queryset = User.objects.exclude(id=self.request.user.id)
return form

How do I reference Django Model from another model

Im looking to create a view in the admin panel for a test program which logs Books, publishers and authors (as on djangoproject.com)
I have the following two models defined.
class Author(models.Model):
first_name = models.CharField(max_length=30)
last_name = models.CharField(max_length=30)
email = models.EmailField()
def __unicode__(self):
return u'%s %s' % (self.first_name, self.last_name)
class Book(models.Model):
title = models.CharField(max_length=100)
authors = models.ManyToManyField(Author)
publisher = models.ForeignKey(Publisher)
publication_date = models.DateField()
def __unicode__(self):
return self.title
What I want to do, is change the Book model to reference the first_name of any authors and show this using admin.AdminModels.
#Here is the admin model I've created.
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date') # Author would in here
list_filter = ('publication_date',)
date_hierarchy = 'publication_date'
ordering = ('-publication_date',)
fields = ('title', 'authors', 'publisher', 'publication_date')
filter_horizontal = ('authors',)
raw_id_fields = ('publisher',)
As I understand it, you cannot have two ForeignKeys in the same model. Can anyone give me an example of how to do this?
I've tried loads of different things and its been driving me mad all day. Im pretty new to Python/Django.
Just to be clear - I'd simply like the Author(s) First/Last name to appear alongside the book title and publisher name.
Thanks
You can have more than one Foreign Key on a model.
If you would put a Foreign-Key field's name in list_display you will always just see the __unicode__ representation of the associated model.
But you can add a function like this to your BookAdmin:
def first_names(self, obj):
return ','.join(a.first_name for a in obj.authors.all())
get_sites.short_description = 'First Names'
Then add 'first_names' to list_display.