django - deleting User raises integrity error - django

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 )

Related

Write django query to select users and order users by latest conversation they had?

My models:
class User(models.Model):
id = models.UUIDField(primary_key=True)
first_name = models.Charfield()
class Conversation(models.Model):
id = models.UUIDField(primary_key=True)
user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
class Message(models.Model):
id = models.UUIDField(primary_key=True)
conversation = models.ForeignKey(Conversation, on_delete=models.PROTECT, null=False)
text = models.TextField()
created_at = models.DateTimeField(auto_now_add=True, blank=True)
I tried to to order by adding annotate and adding order by. Can some body help me to select all users and order them by the latest message they had. I need user with latest message at to and then accordingly.
Try this
queryset = User.objects.all().order_by(
'-conversation__message__created_at'
).distinct()

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.

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

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()

How to associate a comment with a parent comment in Django models

I have built an application with comments that are commented on a parent comment. I have the following comment model. How can I associate the comment with a parent comment ?
class Comment(models.Model):
uuid = models.UUIDField(max_length=255, default = uuid.uuid4)
description = models.CharField(max_length=5000, default="")
likes = models.PositiveIntegerField(default=0)
dislikes = models.PositiveIntegerField(default=0)
uploaded_at = models.DateTimeField(null=True, blank=True)
commentinguser = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.PROTECT)
video = models.ForeignKey(Video, on_delete=models.CASCADE)
You can have a recursive ForeignKey (a foreign key to the same model) by passing "self"
parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True)

Django annotate with 'field' of 'related_name' of 'related_name'

I have three models : Domain Topic Post
Topic has a foreign key to Domain and Post has a foreign key to Topic.
Post has a field updated_on.
I want to annotate last_updated field in Domain queryset which would contain the latest post object from Post .
Edit 1: Added models definition:
class Board(models.Model):
name = models.CharField(max_length=30, unique=True)
description = models.CharField(max_length=50)
creation_time = models.DateTimeField(auto_now_add=True)
class Topic(models.Model):
subject = models.CharField(max_length=300, unique=True)
board = models.ForeignKey(Board, on_delete=models.SET_NULL, related_name='topics', null=True)
created_by = models.ForeignKey(User, on_delete=models.SET_NULL, related_name='topics', null=True)
created_on = models.DateTimeField(auto_now_add=True)
views = models.PositiveIntegerField(default=0)
class Post(models.Model):
message = models.CharField(max_length=5000)
created_by = models.ForeignKey(User, on_delete=models.SET_NULL, related_name='posts', null=True)
created_on = models.DateTimeField(auto_now_add=True)
topic = models.ForeignKey(Topic, on_delete=models.SET_NULL, related_name='posts', null=True)
updated_on = models.DateTimeField(default=timezone.now)