Simple JOIN in Django - django

I'm trying to accomplish something akin to twitter on my website, where one user can follow another one. I want to select all User records that are following a User with a given ID. My follow relationship model look like:
class Following(models.Model):
user = models.ForeignKey(User, related_name='is_following')
followed = models.ForeignKey(User, related_name='is_followed')
And I'm using the django.contrib.auth.models User class.
Assuming I have a User object named "myUser" representing the followed person, what's the appropriate query to get everyone following them?

mipadi's answer is missing '.all'.
In a view, the queryset
followers = my_user.is_followed.all()
returns a list of Following objects where my_user is being followed. To get the user that is following from a Following object f, use f.user
If followers is in the template context, you could do the following.
{% for f in followers %}
{{ f.user }}
{% endfor %}
You might find the Django documentation for many to many relationships useful.

That's where the related_name attribute comes into play -- it allows you to follow a relationship backwards. So, to get all the users following a particular user, you'd use my_user.is_followed.
Obviously you might want to rename your attributes to reflect these relationships, since followed and is_followed might be a bit confusing, but that's how you'd do it.

Related

Django filter objects id and search for the full name in User model

I have a problem of solving this for the whole day and can't get it right. Please someone help me...
book_obj =
Book.objects.filter(status_sold=True).order_by('-date_sold')
result will be something like this:
id = 2
title = 'bla bla bla bla...'
author = 3 ---> id from the User model (not a foreign key)
status_sold = True
date_sold = '2021-05-10'
I want to view that data in the template, but the author I want to display the name instead of number 3 as an id of the author. How to solve this ?
author_name = User.objects.get(id= ???????? )
so I can use {{ author_name.get_full_name }} later in the template
Thx in advanced....
Use this in a template. The author user object is already attached to the book object so you don’t need to fetch it from the database again.
{{ book_obj.author.get_full_name }}
Another option is to set the__str__ method on the user object to
def __str__(self):
return self.get_full_name
Then in the template you can just do
{{ book_obj.author }}
If you show a bit more of your models, it may be easier to provide a more concrete answer, but I think we've got enough to give some help.
In your template, where you have author like :
{{author}}
you can simply add on the property you want with dot notation:
{{author.full_name}}
or what ever you put for the field containing the full name.
No need to separately pass a user object in your context. This way works well in loops as well.
Link to documentation: https://docs.djangoproject.com/en/3.2/topics/templates/#variables
EDIT: apologies - missed the no foreign key bit.
you may be looking at custom template tags.
from django.template.defaulttags import register
#register.filter
def get_author_name(key):
author = Author.objects.get(pk=key)
return author.full_name
then use it in template like:
{{ author|get_author_name }}
You could probably jazz it up a bit and add an attribute as an arg. See: https://docs.djangoproject.com/en/dev/howto/custom-template-tags/#writing-custom-template-filters

Protecting user's model view from other users

I'm trying to write a generic DetailView for my model (which is related to User), but I don't know how to limit access for specific model (view) for only that User (owner). I've looked into docs but haven't found any guide how to do that the proper way. So far I've managed to overwrite test_func() method of the class using UserPassesTestMixin where i check if object.user == self.request.user but I'm not sure if that's the proper way to do that.
What I've just said may not be clear, so e.g.
Model A of id 4 is related (owned by) User A.
Now, if User B tries to request Model A DetailView (/models/4) he should be rejected with 403 Error Code, or even better, custom error page.
Have you tried a query like this?
MyTable.objects.filter(user=self.request.user)
This only returns the objects related to the current user.
There are few ways you can try
verify user permissions in your own views using the has_perm method provided in the user model.
if user.has_perm('foo.add_bar'):
return HttpResponse("You are authorized to add content!")
else:
return HttpResponse("Permission to add denied")
verify in your templates using the perms variable that is automatically added to the template context.
{% if perms.app_label.can_do_something %}
This content will be shown users with can_do_something permission.
{% endif %}
This content will be shown to all users.
You can also create your own permissions to the models
class SomeModel(models.Model):
owner = models.ForeignKey(User)
content = models.TextField()
class Meta:
permissions = (
('view_content', 'View content'),
)
have a look at this link it will give you an idea.

Django global variable based on user groups

In my Django project I have a database that is populated from outside Django, but needs the Django functionality to display data in the database in a user friendly manner. The legacy database structure is as follows:
class SomeModel(models.Model):
#some fields
group_access = models.ForeignKey(AccessGroup,on_delete=models.CASCADE()
class AccessGroup(models.Model):
group_name = models.CharField(max_length=200)
The users have a custom User profile with manytomany relationship with group names assigned to the specific user:
class CustomUser(models.Model):
#some values
projects = models.ManyToManyField(AccessGroup)
Currently I am able to display data from all groups a user has access to, but what I am looking for is a way to create a drop down menu so that users can switch between groups without the need to log out or reenter group on every view.
You could try something like this:
AccessGroup.objects.filter(CustomUser__pk=1)
Or
CustomUser.objects.filter(AccessGroup__group_name='GropName')
https://docs.djangoproject.com/en/2.0/topics/db/examples/many_to_many/
you can extend the django user model, somthing like
from django.contrib.auth.models import User
class CustomUser(models.Model):
projects = models.ManyToManyField(AccessGroup)
class UserProfile(models.Model):
user = models.OneToOneField(User, unique=True)
custom_user = models.ForeignKey(CustomUser, unique=False)
class SomeModel(models.Model):
#some fields
group_access = models.ForeignKey(AccessGroup,on_delete=models.CASCADE()
class AccessGroup(models.Model):
group_name = models.CharField(max_length=200)
then something like this to get the data in your view
def index(request):
AccessGroup.objects.filter(user__id=persion(request.user).id)
I'll assume you know how to get the list of groups, and are just looking as to how to get this list into templates. If not, let me know and I'll explain that as well.
If you're trying to get a global variable into templates, there are really 3 main options:
Make a custom template tag that takes the current user as input, and generates this list as output.
Use Middleware to generate the list, and append it to the current context for each request
Use a method on your user class, or a mixin of it (really easy if you use a custom user class), and just call that method as user.method in your templates. Remember to exclude parentheses from the method call (only in templates), and keep in mind that this method shouldn't accept any parameters other than self.
Thank you everybody for getting me on the right track. What I ended up doing is writing a context processor for checking the user permissions:
#context_processors.py
def check_groups(request):
group_check = AccessGroup.objects.values('id','group_name').filter(projects=request.user.id)
return {
'group_check': group_check,
}
Afterwards I created a Bootstrap-select dropdown in my base.html
<select class="selecpicker">
<optgroup>
<option data-hidden="true">Choose group</option>
{% for grpup in group_check %}
<option val="group.id">{{ group.group_name }}</option>
{% endfor %}
</optgroup>
And the it is just a matter of users using it as means to switch access groups in views and passing the value via ajax to any other template views I come across.
Not the 100% what I was looking for, but it works and my users are happy.

Django: How do I pass different urls to a generic view?

I'm building an app that is similar to an address book. For one of my views, I have a list of Individuals. Each Individual has a Role which links them to their job title and employer. There are several different types of employers so this is a generic relation.
Using the ListView generic view, I have created a list of individuals that displays their name, their job title and their employer, i.e.
"Jimmy, Officer, Chase Bank"
"Mary, CEO, General Hospital"
I need the names of the companies to be links that go to the companies detail page.
The Problem
Since I have one template for this list, I am having a hard time understanding how to change the url based on what type of employer the individual has i.e. Bank or Hospital.
What Might Work
Should I write a function for the view that takes the employer content type and creates a variable? Or should I put the function under the Role model? I am a noob to python/Django and am not sure how to even write this.
I attempted to use if/else statements in the template, i.e. if bank, use this url, but it did not work. Probably because it is a generic relation and my query was not right.
In response to the comment below:
I'm not sure exactly what you meant by inheritance structures but here are a simplified version of my models;
class Individual(models.Model)
name = models.CharField(max_length=30)
class Bank(models.Model):
name = models.CharField(max_length=30)
staff = generic.GenericRelation('Role',
content_type_field='employer_content_type',
object_id_field='employer_id'
)
class Hospital(models.Model):
name = models.CharField(max_length=30)
staff = generic.GenericRelation('Role',
content_type_field='employer_content_type',
object_id_field='employer_id'
)
... and so on for each different employer
class Role(models.Model):
title = models.CharField(max_length=70)
job_holder = models.ForeignKey(Individual)
employer_content_type = models.ForeignKey(ContentType,
limit_choices_to={"model__in": ('venue', 'festival', 'artsorganization','bookingagent', 'managementcompany', 'mediaoutlet', 'otherorganization', 'presentercompany', 'publishingcompany', 'presenter', 'recordcompany', 'musician', 'ensemble')}, related_name="employer")
employer_id = models.PositiveIntegerField()
employer = generic.GenericForeignKey('employer_content_type', 'employer_id')
At one point I had all employers inheriting from a Company model. I changed it because each employer has different attributes which was making things very complicated.
Thanks!
I believe each kind of employer should be responsible for providing a URL to itself (see get_absolute_url). That is, each employee model supplies get_absolute_url to determine a URL for itself.
Then, you can simply use (in your template):
{{ individual.name }}
<a href="{{ individual.role.employer.get_absolute_url }}">
{{ individual.role.employer.name }}
</a>
It's simplified here since the "Role to Individual" relation is One-to-Many, but use whatever mechanism you use to determine the employer's name in this instance. If your list is of roles, you obviously just use role.employer and instead role.job_holder to get the individual etc.

Django Two Way relationship

I am building a blog site and have models in respect of Category and Posts. Posts have a Many to Many relationship for Category.
class Post(models.Model):
categories = models.ManyToManyField(Category)
Everything is working fine aside from the fact that in the Category list in the template I only want to load categories that actually have posts.
If a category is empty I don't want to display it, I have tried to define a relationship in Category to Post to allow me to use something like {{ if category.posts }}. At the moment using another Many to Many field in Category is presently giving me an extra field in admin which I don't really want or feel that's needed.
How is best to navigate this relationship, or create one that's suitable?
Cheers
Kev
Django automatically creates a field on the related model of any ForeignKey or ManyToMany relationship. You can control the name of the field on the related model via the related_name option like that:
class Post(models.Model):
categories = models.ManyToManyField(Category,related_name='posts')
This way, your approach works without any additional fields. Btw, if you leave out the related_name argument, Django will create by default one with [field_name]_set.
You can use reverse relations on ManyToMany fields. In the reverse filter, you must use the related model name (if you did not use related_name attribute). So in your question you can use model name as the reverse name like:
{% if category.post %}
You can also use this in your filtering functions in views:
Category.objects.filter(post__isnull=False)
Reverse relation name must be lowercase.
Check the documentation here