Django: Query to check whether the request.user is group's admin - django

class Group(models.Model):
group_name = models.CharField(max_length=256)
group_desc = models.CharField(max_length=2000, blank=True)
created_by = models.ForeignKey(User, on_delete=models.PROTECT, blank=True, null=True, related_name='created_groups')
group_pic = models.ImageField(blank=True)
users = models.ManyToManyField(User, through='GroupMember')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class GroupMember(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
is_group_admin = models.BooleanField(default=False)
I have these two models and I want to query whether the request.user is part of a particular group and is group_admin or not:
I have successfully queried the first part but not able to do the latter part. For the first part, I have written the following query:
GroupMember.objects.filter(id=pk, group__users__in=[request.user])
Any kind of help will be appreciated.

Try:
GroupMember.objects.filter(user=request.user, group=<group_to_check>, is_group_admin=True).exists()

Please try
GroupMember.objects.filter(user=request.user, group__group_name=<group_name_to_be_checked>, is_group_admin=True).exists()

Related

How to make a query across multiple models in Django

I'm using Django and I want to know how to get objects through 3 models
These are my models
class Participant(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
is_leader = models.BooleanField(default=False)
team = models.ForeignKey(Team, on_delete=models.CASCADE, null=True, related_name="participants")
application_date = models.DateField(auto_now_add=True, null=True)
resolution_date = models.DateField(null=True, blank=True)
accepted = models.BooleanField(default=False)
class Team(models.Model):
name = models.TextField(default="")
is_public = models.BooleanField(default=False)
institution = models.ForeignKey(Institution, on_delete=models.CASCADE, null=True, related_name='teams')
campaign = models.ForeignKey(Campaign, on_delete=models.CASCADE, null=True, related_name='teams')
class Campaign(models.Model):
name = models.TextField(default="")
description = models.TextField(default="")
initial_date = models.DateTimeField(auto_now_add=False, null=True, blank=True)
end_date = models.DateTimeField(auto_now_add=False, null=True, blank=True)
qr_step_enabled = models.BooleanField(default=True)
image_resolution = models.IntegerField(default=800)
sponsor = models.ForeignKey(Sponsor, on_delete=models.CASCADE, null=True, related_name='campaigns')
I have the user through a request, and I want to get all campaigns of that user.
I tried doing it with for loops but I want to do it with queries
this is what I had:
user = request.user
participants = user.participant_set.all()
for participant in participants:
participant.team.campaign.name
is there a way to make a query through these models and for all participants?
A user can have many participants, and each participant has a Team, each team has a campaign
The best way is to merge the two modela Team and Campaign in one model.
Something as simple as this should work:
Campaign.objects.filter(team__participant__user=request.user)
The Django ORM is smart enough to follow foreign key relationships in both directions.
Thanks to Daniel W. Steinbrook to guide me to the answer, I had to do this to get the query:
Campaign.objects.filter(teams__participants__user__exact=request.user)

how to write a signal to change a field in a model after another filed from another model is changed

I have a user model that has to go through several tasks such as completing their information, taking some tests, and interviews. So I added a progress level field that shows the user status at the moment. this is my model:
class User(AbstractUser):
id = models.AutoField(primary_key=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
isPreRegistered = models.BooleanField(default=False)
role = models.CharField(max_length=25, null=True, choices=USER_ROLE_CHOICES, default=USER_ROLE_CHOICES[0][0])
role_id = models.CharField(max_length=10, default='applicant')
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)
gender = models.CharField(max_length=10, null=True)
personalInfo = models.OneToOneField(PersonalInfo, on_delete=models.CASCADE, null=True)
contactInfo = models.OneToOneField(ContactInfo, on_delete=models.Case, null=True)
eliteInfo = models.OneToOneField(EliteInfo, on_delete=models.CASCADE, null=True)
progress_level = models.CharField(max_length=25, null=True, choices=USER_PROGRESS_LEVELS, default=USER_PROGRESS_LEVELS[0][0])
and there are multiple models which are connected to the user model using a foreign key relation.
this is one of the models I added here for instance:
class PsychologicInfo(models.Model):
id = models.AutoField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
final_assessment = models.TextField(null=True, blank=True)
is_approved = models.CharField(max_length=64, blank=True)
is_interviewed = models.BooleanField(default=False)
I want to write a signal or a save method that does something like this:
if the is_interviewed field was True, change that progress_level to USER_ROLE_CHOICES[1][0]
I have no idea how to do this so thanks for the tips
You can override the .save() method of PsychologicInfo
class PsychologicInfo:
...
def save(self):
super().save()
if self.is_interviewed:
self.user.role = USER_ROLE_CHOICES[1][0] # or enter value here
self.user.save()
or you can use Django signals.

Filter data in a model with list of ids filtered from the related model

I've 3 models which are related weirdly though it is a one-to-many relationship (between 2 models). Now I've no option to change the model structure but to work with it however it is.
Models
class Note(models.Model):
user_owner = models.ForeignKey('User',blank=True, null=True,related_name='note_owner')
message = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
... ... ...
class SalesLead(models.Model):
user_owner = models.ForeignKey(User, models.DO_NOTHING, blank=True, null=True)
status = models.ForeignKey(LeadStatus, models.DO_NOTHING, blank=True, null=True)
name = models.CharField(max_length=255)
first_name = models.CharField(max_length=255, blank=True, null=True)
middle_name = models.CharField(max_length=255, blank=True, null=True)
last_name = models.CharField(max_length=255, blank=True, null=True)
... ... ...
class LeadNoteRelation(models.Model):
note = models.ForeignKey(Note, models.DO_NOTHING, primary_key=True, related_name = "lead_note")
lead = models.ForeignKey('SalesLead', models.DO_NOTHING)
class Meta:
unique_together = (('note', 'lead'),)
I'm trying to write a function-based view which takes the SalesLead id and User id as inputs. The response should include all Notes related to this lead id and user_owner as the input User id
I tried:
LeadNoteRelation.objects.filter(lead__id = INPUT ID).select_related()
But this did not give me the desired result. Kindly help me with this one. Thanks in advance.
You can filter with:
Note.objects.filter(
leadnoterelation__lead_id=lead_id,
user_owner_id=user_owner_id
).distinct()
Where you replace lead_id and user_owner_id with the primary keys of the lead and user respectively. The .distinct() [Django-doc] can be used to avoid fetching the same Note multiple times per lead_id that matches.

django - deleting User raises integrity error

I am doing a "Reddit Clone" using django and these are my models:
models.py
class Profile(models.Model):
owner = models.OneToOneField(User, on_delete=models.CASCADE)
dob = models.DateField()
karma = models.IntegerField(default=0)
class Subreddit(models.Model):
owner = models.ForeignKey(Profile, on_delete=models.DO_NOTHING, null=True, blank=True, related_name='subreddits')
name = models.CharField(max_length=100)
title = models.CharField(max_length=100)
description = models.CharField(max_length=500)
subscribers = models.ManyToManyField(Profile, blank=True, related_name='subscriptions')
moderators = models.ManyToManyField(Profile, blank=True, related_name='moderates')
class Post(models.Model):
owner = models.ForeignKey(Profile, on_delete=models.DO_NOTHING)
title = models.CharField(max_length=300)
content = models.CharField(max_length=1000)
votes = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
subreddit = models.ForeignKey(Subreddit, on_delete=models.CASCADE)
class Comment(models.Model):
owner = models.ForeignKey(Profile, on_delete=models.DO_NOTHING)
content = models.CharField(max_length=500)
votes = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
parent_post = models.ForeignKey(Post, on_delete=models.CASCADE)
parent_comment = models.ForeignKey("Comment", null=True, blank=True, on_delete=models.CASCADE)
Now, I tried to delete a user who just made 1 comment. When I tried to delete the user from django admin, I got the following error.
IntegrityError at /admin/auth/user/2/delete/ (1451, 'Cannot delete or
update a parent row: a foreign key constraint fails
(redditdb.redditapp_comment, CONSTRAINT
redditapp_comment_owner_id_fdc65fee_fk_redditapp_profile_id FOREIGN
KEY (owner_id) REFERENCES redditapp_profile (id))')
I did user on_delete=models.CASCADE so I don't know why I am getting this error. Do I need to restructure the foreign keys in my models?
Actually reddit follows a structure where when a parent comment is deleted, its child comments won't get deleted. The deleted parent comment will be dashed or striked out and its child comments are still shown. Knowing this, how do I move forward with my code, should I delete profiles at all?
You Comment is not directly related to User. It is related through Profile. SO when you delete User, its Profile is deleted. which affects the comment, as it becomes dangling.
You can either have
owner = models.ForeignKey(Profile, on_delete=models.CASCADE)
or
owner = models.ForeignKey(Profile, on_delete=models.DO_NOTHING ,null=True)
The error mention that the owner_id of the app's redditapp Comment model references
the id of the app's redditapp Profile model, and the Comment model owner ForeignKey on_delete attribute
is set to models.DO_NOTHIN
DO_NOTHING:
Take no action. If your database backend enforces
referential integrity, this will cause an IntegrityError unless you
manually add an SQL ON DELETE constraint to the database field.
Your best choice is on_delete=models.CASCADE because comments are associated with a specific Profile, if the Profile is deleted its Comments become anonymous with no identifying Profile.
Edit:
Here if the parent comment is deleted the child will not, and the reference to the parent comment will be set to null
class Comment(models.Model):
owner = models.ForeignKey(Profile, on_delete=models.CASCADE)
content = models.CharField(max_length=500)
votes = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
parent_post = models.ForeignKey(Post, on_delete=models.CASCADE)
parent_comment = models.ForeignKey("Comment", null=True, blank=True, on_delete=models.SET_NULL)
The same goes for Post model:
class Post(models.Model):
owner = models.ForeignKey(Profile, on_delete=models.CASCADE)
title = models.CharField(max_length=300)
content = models.CharField(max_length=1000)
votes = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
subreddit = models.ForeignKey(Subreddit, on_delete=models.CASCADE)
Or you can prevent the deletion of the profile all along using models.PROTECT
You have two options:
parent_post = models.ForeignKey(Post, on_delete=models.CASCADE)
or
parent_post = models.ForeignKey(Post, on_delete=models.DO_NOTHING )

Query Django ORM with foreign key fields

I am having issues with using Django ORM queries in order to retrieve specific information. I have three models all linked by foreign keys, these are Hosts, Groups and Organisations. So each host belong to a group and these groups belong to an organisation. I need to get the query set of all hosts that belong to a specific organisation named 'Y' for example. Below are my model.py, can anybody help me to formulate a query set that will achieve this? or point me in the correct direction to figure this out?
Hosts
class Host(models.Model):
host_name = models.CharField(max_length=120, blank=False, unique=True)
url = models.CharField(max_length=120, blank=False, unique=True)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
slug = models.SlugField(blank=True, null=True)
def __str__(self):
return self.host_name
Groups
class Group(models.Model):
org_name = models.ForeignKey(Organization, on_delete=models.CASCADE)
group_name = models.CharField(max_length=120, blank=False, unique=True)
def __str__(self):
return self.group_name
Organizations
class Organization(models.Model):
org_name = models.CharField(max_length=120, blank=False, unique=True)
org_code = models.CharField(max_length=120, blank=False, unique=True, default=GenerateOrganozationCode)
def __str__(self):
return self.org_name
host_queryset = Host.objects.filter(group__org_name__org_name='Y')
For more detailed information and examples, please do refer to the official django doc