Django getting especific object using QuerySet in reverse relationship many-to-one - python

Lets say I have the following models:
Chat(models.Model):
community = models.ForeignKey(Community, related_name="chats", null=True, blank=True)
....
Community(models.Model):
slug = models.CharField(max_length=250, unique=True, default='')
....
Profile(models.Model):
public_name = models.CharField(max_length=20, unique=True)
community_subscriptions = models.ManyToManyField('Community', through='CommunitySubscription')
chat_subscriptions = models.ManyToManyField('Chat', through='ChatSubscription')
....
ChatSubscription(models.Model):
profile = models.ForeignKey(Profile, unique=False, related_name='chat_subscription')
chat = models.ForeignKey(Chat, null=True, blank=True, related_name='chat_subscribers')
....
CommunitySubscription(models.Model):
....
In a view I want to do the following:
profileA = Profile.objects.get(public_name=public_nameA)
profileB = Profile.objects.get(public_name=public_nameB)
community = Community.objects.get(slug=community_slug)
try:
# I want to get the unique chat that involves both profiles for a specific community or get DoesNotExist (or None) if there is not such chat.
chat = Chat.objects.????????????
except Chat.DoesNotExist:
....
If the chat exist then both profiles will have a ChatSubscription object relating the profile with the chat.
Is it possible to do this kind of filtering using Django QuerySets in one single line? If not what would be the most efficient way to do it in more than one line?
Thanks a lot in advance.

Pretty complex, so try using the Q object to do a 'AND' query (assuming I got what you're trying to do):
from django.db.models import Q
Chat.objects.filter(Q(chatsubscription__profile__in=[profileA,profileB]) & Q(community=communty))

Related

Django usage of ForeignKey

I have this micro ticket "system"
\\models.py
class Ticket(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
default=None,
null=True,
on_delete=models.CASCADE,
)
title = models.CharField(max_length=200)
description = models.TextField()
creator_adress = models.GenericIPAddressField(null=True)
start_date = models.DateTimeField(default=timezone.now)
ticket_waiting = models.BooleanField(default=True)
ticket_solved = models.BooleanField(default=False)
reopened_counter = models.IntegerField(default=0)
...
Until now, only one message (when the user opens the ticket) can be sent by the user, which will be forwarded to the admin. But the admin and the user can't write any other messages back and forth under the ticket yet.
If you use a new model for this, which is connected to every single ticket like so?
class TicketMessages(models.Model):
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
admin_message = models.TextField()
user_message = models.TextField()
What is the best way to implement this?
Thank you very much :>
you can achieve this efficiently with the following method,
create a model for ticket message :-
class TicketMessages(models.Model):
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
message = models.TextField() #this will be the message
message_by = # you can use a foreign key or a choices option to select between the admin or user
reply_to = models.ForeignKey("self") # if this is a reply to existing message
This is kind of an good and efficient solutions as per my understanding of your problem .... if you need something else or something more, let me know I'd love to help you.
Thanks !

How to filter by BooleanField in Django?

I have been having a hard time accomplishing this task and it seems that I cannot get help anywhere I turn. I am trying to send Memos to specific user groups in Django. Each user in each group should receive the Memo and be able to change the BooleanField to True to signify that they have read it.
I then need to be able to access the amount of users which received and have marked the BoolenField as True so I can create a table in a template which says [3/31 Read] and such.
I would like to not use GenericForeignkeys if possible and I would like to keep it as one Model if possible but I know that may not work. Currently I was trying this:
class Memo(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date_time = models.DateTimeField(default=timezone.now)
sender = models.ForeignKey(User, on_delete=models.CASCADE)
receiver = models.ManyToManyField(Group)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('memos-detail', kwargs={'pk': self.pk})
I was going to access each user (receiver) within the group which is selected on the MemoCreateForm then apply that user to this model:
class ReceivedMemo(models.Model):
memo = models.ForeignKey(
Memo, related_name='user_memos', on_delete=models.CASCADE)
receiver = models.ForeignKey(
User, related_name='memos', on_delete=models.CASCADE)
read = models.BooleanField(default=False)
Then I was going to try to filter the ReceivedMemos by memo to see if each receiver has read the memo or not. But this is starting to get complicated and I am not sure if it will work. Am I going about this the right way? Or should I be able to have one Model such as:
class Memo(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date_time = models.DateTimeField(default=timezone.now)
sender = models.ForeignKey(User, on_delete=models.CASCADE)
receiver = models.ForeignKey(User, on_delete=models.CASCADE)
read = models.BooleanField(default=True)
Seems that each object would have the BooleanField applied to the object and not the user though.
The ReceivedMemo model seems more appropriate rather than the read bit, but the issue with this approach is that whenever you create a new Memo you need to also create lots of (for every User in the Group) ReceivedMemo objects with read=False? This seems pointless. Maybe you can just store the Users which actually read this thing, and for everyone that's left, consider he has not read it. I.e.
class Memo(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date_time = models.DateTimeField(default=timezone.now)
sender = models.ForeignKey(User, on_delete=models.CASCADE)
receiver = models.ManyToManyField(Group)
read_by = models.ManyToManyField(User)

Using Model.objects.all() as a blueprint for a secondary table entry

I am having a bit of trouble with the logic of how this should work so I am hoping it is possible.
I figured out 1 possible solution that is written as an answer below, I will accept it in a few days, but if someone comes up with a better solution, I will negate any answer I post.
Overall I am working on an Apartment Move-Out/Move-In Inspection Application in Django, and in both portions I have universal Locations that must be inspected for each report. I have allowed the InspectionLocations objects to be updated/submitted by clients, which is presenting an issue in how submitted reports should be stored in my Database.
What I want is to use the InspectionLocations table as a blueprint to build an Inspection Report for Move-Ins where the form-fields are generated based on the InspectionLocations objects' location, status, and information attributes/fields.
My issue is right at this point, how do I reference those values as a blueprint to build a report submission when the number of fields in the InspectionLocations can change?
from django.db import models
from apps.units.models import Unit
class Inspections(models.Model):
class Meta:
abstract = True
id = models.AutoField(primary_key=True)
inspection_date = models.DateField()
submitted_by = models.ForeignKey(
'users.CustomUser',
default=None,
null=True,
on_delete=models.SET_NULL,
db_column='submitted_by')
last_update = models.DateTimeField(auto_now=True)
date_added = models.DateTimeField(auto_now_add=True, editable=False)
class MoveInInspections(Inspections):
unit = models.ForeignKey(Unit, on_delete=models.CASCADE, db_column='unit_id')
# should have reference to all InspectionLocation items as reference for submission, how?
class MoveOutInspections(Inspections):
unit = models.ForeignKey(Unit, on_delete=models.CASCADE, db_column='unit_id')
date_notice_given = models.DateField(blank=True, null=True, default=None)
date_vacated = models.DateField(blank=True, null=True, default=None)
# should have reference to all InspectionLocation items as reference for submission, how?
class InspectionLocations(models.Model):
'''
Defualt Inspection Locations are created when a
client is created using code like this:
InspectionLocation.objects.get_or_create(location='Living Room')
InspectionLocation.objects.get_or_create(location='Dining Room')
InspectionLocation.objects.get_or_create(location='Kitchen')
InspectionLocation.objects.get_or_create(location='Bedroom')
InspectionLocation.objects.get_or_create(location='Bathroom')
InspectionLocation.objects.get_or_create(location='Other')
'''
id = models.AutoField(primary_key=True)
location = models.CharField(max_length=50)
status = models.BooleanField(default=None)
information = models.TextField(default=None, blank=True)
I have tried ManyToMany fields and FKs but I cannot seem to get the logic working as anytime an object references an InspectionLocations object it is universally changing data for every report, which is leading be to the idea that I somehow need to use it as a blueprint.
I didn't post this in my question because it was getting long, but my best option so far seems to be to use a Django JSONField (as I am using Postgres), like so:
from django.contrib.postgres.fields import JSONField
class MoveInInspections(Inspections):
unit = models.ForeignKey(Unit, on_delete=models.CASCADE, db_column='unit_id')
data = JSONField()
class MoveOutInspections(Inspections):
unit = models.ForeignKey(Unit, on_delete=models.CASCADE, db_column='unit_id')
date_notice_given = models.DateField(blank=True, null=True, default=None)
date_vacated = models.DateField(blank=True, null=True, default=None)
data = JSONField()
To where I store the values of the InspectionLocations object's in a Dictionary

Django Queryset for Getting Feeds from Following Users

I am trying to get feeds from following users such as Facebook, Instagram, and Twitter. I wrote a view to show feeds from following people.
class Following(View):
def get(self, request):
user = request.user
posts = models.Post.objects.filter(creator__in=user.following.all()).order_by('created_date')[:5]
I was worrying if it might cause a performance issue due to user.following.all() part. I do not know why
filter(creator=user.following)
does not work.
following is a self-referencing ManyToManyField.
followers = models.ManyToManyField("self", blank=True)
following = models.ManyToManyField("self", blank=True)
Is there any better solution?
I have another issues with counting a number of ManyToManyFields.
class Popular(View):
def get(self, request):
user = request.user
posts = models.Post.objects.annotate(number_of_likes=Count('likes'), number_of_comments=Count('comments')).filter(Q(channels__users=user) & (Q(number_of_likes__gte=5) | Q(number_of_comments__gte=5))).order_by('created_date')[:5]
It is so long because likes__count and comments__count did not work. The only case I found on the internet that works was this, using annotate(). Is there a better solution for this? I want to filter out posts that have channels a user is subscribing and among them, I want to only show number of likes or number of comments are above certain range. Why does this not work?
models.Post.objects.filter(Q(channels__users=user) & (Q(likes__count__gte=5) | Q(comments__count__gte=5))).order_by('created_date')[:5]
class Comment(news_models.TimeStampedModel):
post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
class Like(news_models.TimeStampedModel):
comment = models.ForeignKey(Comment, null=True, on_delete=models.CASCADE, related_name='likes')
class Channel(models.Model):
name = models.CharField(max_length=100, blank=False, unique=True)
users = models.ManyToManyField(settings.AUTH_USER_MODEL, related_name='channels')
posts = models.ManyToManyField(Post, related_name='channels')

Get site-specific user profile fields from user-created object

I am using Django sites framework (Django 2.1) to split an app into multiple sites. All of my models except the User model are site-specific. Here is my Post model:
post.py
class Post(models.Model):
parent = models.ForeignKey(
'self',
on_delete=models.CASCADE,
related_name='children',
related_query_name='child',
blank=True,
null=True,
)
title = models.CharField(
max_length=255,
blank=True,
)
body_raw = models.TextField()
body_html = models.TextField(blank=True)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
site = models.ForeignKey(Site, on_delete=models.CASCADE)
on_site = CurrentSiteManager()
I have no problem separating posts out by site. When I want to get the posts, I call:
posts = Post.on_site.filter(...)
I have a separate model called UserProfile. It is a many-to-one profile where there is a unique profile created for each user-site combination (similar to profile implementation at SE). The profile has a reputation attribute that I want to access when I get any post. This reputation attribute should be different for each site (like how on SE you have different rep on each site you are a member of).
user_profile.py
class UserProfile(models.Model):
user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
reputation = models.PositiveIntegerField(default=1)
site = models.ForeignKey(Site, on_delete=models.CASCADE)
on_site = CurrentSiteManager()
How do I access the user's username (on the User model) as well as the user's reputation (on the UserProfile model) when I get Posts from a query?
I'd like to do something like:
Post.on_site.filter(...)
.select_related('user__userprofile')
.filter_related(user__userprofile.site == get_current_site())
How do I filter a Many-To-One related model?
Better to make UserProfile -> User relationship to be OnetoOne,
because Django doesn't know which of many profiles to show
(but you also need to define related_name)
models.OneToOneField(get_user_model(), related_name='userprofile_rev')
Then you will be able to do this
qs = Post.on_site.filer().select_related('user', 'user__userprofile_rev')
for post in qs:
print(post.user.username, post.user.userprofile_rev.reputation)
If you don't want to change your DB structure you can do like this
(but you need to specify which profile to return)
qs = Post.on_site.filer().select_related('user').prefetch_related('user__userprofile_set')
for post in qs:
print(post.user.username, post.user.userprofile_set[0].reputation)

Categories

Resources