I am building an Instagram like app and trying to make a like model. Each user can like a post however, it should not be possible to like the same post twice.
class Like(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
This is my model however, I am able to create 2 identical objects. For example user 1 can have 2 like objects like to post 1.
Is there a way to do this?
Yes, you can mark the combination of the user and post field as unique with a UniqueConstraint [Django-doc]:
class Like(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
class Meta:
constraints = [
models.UniqueConstraint(fields=['user', 'post'], name='like_once')
]
Prior to django-2.2, you can make use of the unique_together option [Django-doc]:
class Like(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
post = models.ForeignKey(Post, on_delete=models.CASCADE)
created = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = [['user', 'post']]
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Related
Need help getting a better grip with 1) accessing and counting foreign key items in Django and 2) counting foreign key item that is created through Django's built-in User:
#1)
The answer here suggests using Django's annotate function: eg. questions = Question.objects.annotate(number_of_answers=Count('answer')) (question being a foreign key item to answer). But it is not clear to me where this line belongs to (in the case of the example, question or answer), or which part of an app-models.py or views.py. I tested in both models.py and views.py
Here are my models (only the essential lines here):
class Post(models.Model):
post = models.TextField(max_length=1000)
author = models.ForeignKey(User, related_name="author", on_delete=models.CASCADE)
class Comment(models.Model):
comment = models.TextField(max_length=1000)
commenter = models.ForeignKey(User, on_delete=models.CASCADE)
post_connected = models.ForeignKey(Post, related_name='posts', on_delete=models.CASCADE, default=None, null=True)
#2) How does one count a foreign key item created using Django's built-in model, User?
Should I have created a model called "Author" first?
I want to tell you something about related_name:
related_name help you access objects of another model for example
class Post(models.Model):
post = models.TextField(max_length=1000)
author = models.ForeignKey(User, related_name="posts", on_delete=models.CASCADE)
class Comment(models.Model):
comment = models.TextField(max_length=1000)
commenter = models.ForeignKey(User, on_delete=models.CASCADE)
post_connected = models.ForeignKey(Post, related_name='comments', on_delete=models.CASCADE, default=None, null=True)
with modelobject.related_name.all() get objects of another models in this example userobject.posts.all() you can get all post related to user object.
and with post.comments.all() can get all comments related to the post.
So I have a user model and each user should be able to rate other users but shouldn't be able to rate themselves.
Model for rating is
#Rating field in User model
ratings = models.ManyToManyField('self', through='Rating',
symmetrical=False,
related_name='rated_by')
#Rating model
class Rating(models.Model):
rating = models.IntegerField(validators=[validate_rating])
from_user = models.ForeignKey(User, related_name="from_people", on_delete=models.CASCADE)
to_user = models.ForeignKey(User, related_name="to_people", on_delete=models.CASCADE)
Is there a way I can make this functionality? I was thinking django model constraints will work but I do not know how to go about it. How do I filter a request to grab the specific user to prevent them from rating themselves?
I tried:
class Rating(models.Model):
rating = models.IntegerField(validators=[validate_rating])
from_user = models.ForeignKey(User, related_name="from_people",
on_delete=models.CASCADE)
to_user = models.ForeignKey(User, related_name="to_people",
on_delete=models.CASCADE)
class Meta:
constraints = [
CheckConstraint(
check = Q(from_user != User,
to_user != User),
name = 'check_user',
)
]
but I get a Metacheck = Q(from_user != User, NameError: name 'from_user' is not defined as an error
You can add a database level check constraint to your model that will prevent the Rating model from having a record with the from_user and to_user the same.
from django.db.models import Q, F, CheckConstraint
class Rating(models.Model):
rating = models.IntegerField(validators=[validate_rating])
from_user = models.ForeignKey(User, related_name="from_people", on_delete=models.CASCADE)
to_user = models.ForeignKey(User, related_name="to_people", on_delete=models.CASCADE)
class Meta:
constraints = [
CheckConstraint(check=~Q(from_user=F('to_user')), name='no_self_rating')
]
Django docs constraint reference.
The above adds the constraint at the database level, but does not do any form validation.
You also ask
How do I filter a request to grab the specific user to prevent them from rating themselves?
It's not clear what you mean here. The request object passed to the view function has a user attribute that is the user using the application. I'm not sure what your view looks like but:
def my_view(request):
request.user # This is the user making the action
... # Other view code
return HttpResponse(...)
You can pass request.user to a form and validate the user is not rating themselves.
A negative CheckConstraint should do what you want.
from django.db import models
class Rating(models.Model):
rating = models.IntegerField(validators=[validate_rating])
from_user = models.ForeignKey(User, related_name="from_people", on_delete=models.CASCADE)
to_user = models.ForeignKey(User, related_name="to_people", on_delete=models.CASCADE)
class Meta:
constraints = [
models.CheckConstraint(
check=~models.Q(from_user=models.F('to_user')),
name='users_cannot_rate_themselves'
),
]
I have two django models as follows:
The first one is a user profile, which has a FK to User model:
class Profile(models.Model):
PRF_user = models.OneToOneField(User, related_name='related_PRF_user', on_delete=models.CASCADE)
PRF_Priority_Support = models.BooleanField(default=False)
and the second is ticket model which has a FK to User model:
class ticket(models.Model):
ticket_status_options = [
('open', 'open'),
('wait_customer_reply', 'wait_customer_reply'),
('replied_by_staff', 'replied_by_staff'),
('replied_by_customer', 'replied_by_customer'),
('solved', 'solved'),
]
TKT_USER = models.ForeignKey(User, related_name='TKT_USER', on_delete=models.CASCADE)
TKT_DEB = models.ForeignKey('Site_departments', related_name='related_ticket_department', on_delete=models.CASCADE)
TKT_SUB = models.CharField(max_length=50, db_index=True, verbose_name="ticket subject")
TKT_BOD = models.TextField(verbose_name="ticket body")
TKT_image_attachment = models.ImageField(upload_to='TKT_img_attachment', blank=True, null=True , default=None)
TKT_CREATED_DATE = models.DateTimeField(auto_now_add=True)
TKT_UPDATED_DATE = models.DateTimeField(auto_now=True)
I want to sort the tickets based on user profile Priority_Support:
If the user profile PRF_Priority_Support is True, I want to sort it first inside my views QuerySet, otherwise (if PRF_Priority_Support is False) I want to sort it normally.
How can I do this?
You should name your model starting with a capital letter.
And for ordering the tickets, you can use something like this:
' queryset_list = ticket.objects.order_by('-TKT_USER__related_PRF_user__PRF_Priority_Support')
In filtering, when you want to span relationships, you use double underscore __ .
More on this here:
https://docs.djangoproject.com/en/3.1/topics/db/queries/#lookups-that-span-relationships
Another way is adding ordering to your model's Meta class.
For Example:
MyModel(models.Model):
class Meta:
ordering = ('-my_boolean_field ',)
Hi you should filter as follow:
Model.objects.filter(field=True) or False depending on what you need
Regards
I want to link two models using foreignKey, The problem is when i try to do that, one model does not get foreignKey value for the next model in the database table.
The aim is for user to fill information on the first page (have its own model and template) then click next (fill more info in the next page having its own model and template) then click next for the same logic. then when other users view this post it must show all content from different models in one page. here is my code.
1st model
class Map(models.Model):
user = models.ForeignKey(User, default=None, blank=True, null=True, on_delete=models.CASCADE)
name = models.CharField(max_length=100)
position = GeopositionField()
HAVING ITS OWN TEMPLATE
2nd Model
class Post(models.Model):
parent = models.ForeignKey("self", default=None, blank=True, null=True, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, default=None, on_delete=models.CASCADE)
title = models.CharField(max_length=50)
content = models.TextField()
map = models.ForeignKey(Map, related_name='mapkey', default=None, blank=True, null=True, on_delete=models.CASCADE)
HAVING ITS OWN TEMPLATE BUT also has serializer method(API) below:
class PostModelSerializer(serializers.ModelSerializer):
user = UserDisplaySerializer(read_only=True)
parent = ParentPostModelSerializer()
map = serializers.SerializerMethodField()
class Meta:
start_date = forms.DateField(widget = forms.SelectDateWidget())
end_date = forms.DateField(widget = forms.SelectDateWidget())
model = Post
fields = [
'id',
'user',
'title',
'content'
'image',
'map',
]
Please focus only on the map field as its isolated in the above codes
everything works perfectly, but the foreignKey. also i didnt see the need to include all the code here but snippets.
i have been struggling with this for days now. do i need to write an api for 1st model also? for views i used class based views.
the database table for model 2, on the map field it shows null all the time.
I have i have provided enough information.Thanks
Let us say I have a model for social network posts, users and likes:
class Post(models.Model):
submitter = models.ForeignKey(User, null=False, default=None)
content = models.CharField()
date = models.DateField()
with_likes = PostLikeCountManager()
objects = models.Manager()
class Like(models.Model):
user = models.ForeignKey(User)
post = models.ForeignKey(Post)
time = models.DateTimeField(auto_now_add=True)
It would be helpful to think of Post model as representing a Facebook post. Now, I would like to limit one like per post per user. How do I achieve that? One way would be to create a composite primary key on (user, post) attributes of Like class. I don't know how to achieve that in Django. The other would be to use unique=True on two attributes simultaneously. Is that possible?
Thanks.
Yes, use unique_together:
class Like(models.Model):
user = models.ForeignKey(User)
post = models.ForeignKey(Post)
time = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = ('user', 'post')
unique_together will be deprecated in the future version, instead you could apply UniqueConstraint. This and this link gives example code.
class Like(models.Model):
user = models.ForeignKey(User)
post = models.ForeignKey(Post)
time = models.DateTimeField(auto_now_add=True)
class Meta:
constraints = [
models.UniqueConstraint(fields=['user', 'post'], name='unique_user_post'),
]