I am currently using django 1.8 and I'd like to create a more intelligent way to display information about users. Say I have something like this:
class User(models.Model):
name = models.CharField()
class Invitation(models.Model):
inviter = models.ForeignKey(User)
invitee = models.ForeignKey(User)
I want to create a field that is the unique number of user's an inviter has invited. I could see how this could be done with something like set("SELECT invitee FROM INVITATIONS WHERE inviter = 'my-user';"), but if I want this displayed in the admin panel, is there a simple way to present this?
Also, I would want this done for every user, so it feels like there is a simple way to make a field generated for every user in the table.
First, let's setup proper related_name- it'll help reduce a lot of confusion in the code.
class Invitation(models.Model):
inviter = models.ForeignKey(User, related_name="invitation_sent")
invitee = models.ForeignKey(User, related_name="invitation_recv")
With the related_name setup, we can do queries such as
user = User.objects.get(pk=1)
# retrieve all invitation objects sent by this user
user.invitation_sent.all()
# retrieve all invitation objects received by this user
user.invitation_recv.all()
Now we can actually count the number of unique invitations a user has sent out quite easily:
# count number of distinct invitee for user
user.invitation_sent.all().values('invitee').distinct().count()
Next, we can actually count the number of unique users a user has invited in a single database query for all users:
user_list = User.objects.all().annotate(
uniq_inv=Count('invitation_sent__invitee', distinct=True)
)
Each user object returned will have an additional property called uniq_inv which contains the count of unique users the user has invited
for user in user_list:
print(user.name + ' invited ' + user.uniq_inv + ' unique users')
To apply this to the admin interface, you'll need to override get_queryset method:
class MyAdmin(admin.ModelAdmin):
...
list_display = [..., 'uniq_inv']
def uniq_inv(self, obj):
return obj.uniq_inv
uniq_inv.short_description = 'Unique Invitees'
def get_queryset(self, request):
qs = super(MyAdmin, self).get_queryset(request)
qs = qs.annotate(uniq_inv=Count('invitation_sent__invitee', distinct=True))
return qs
You can use annotate, which allows to add calculated fields to a queryset.
Models:
class User(models.Model):
name = models.CharField()
class Invitation(models.Model):
inviter = models.ForeignKey(User,related_name="inviter_user")
invitee = models.ForeignKey(User,related_name="invited_user")
Queryset:
from django.db.models import Count
q = Invitation.objects.annotate(count_invitee=Count('invitee')).all()
Now "count_invitee" field has the number for each invitation object.
If you want to filter invitee from the user side.
For a single user:
User.objects.get(pk=1).invited_user.all.count()
For all users queryset:
User.objects.annotate((count_invitee=Count('invited_user')).all()
Related
Let's say I have 2 Models:
class Auction(models.Model):
seller = models.ForeignKey(User, on_delete=models.CASCADE, related_name="seller")
title = models.CharField(max_length=64)
class Watchlist(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_watchlist')
auction = models.ForeignKey(Auction, on_delete=models.CASCADE, related_name='auction_watchlist')
The view receives a request, creates a context variable with the auction objects the are:
associated with the user who made the request and
that have been added to the Watchlist Model,
sends it to the template.
I have set up my view to work like this:
#login_required
def watchlist(request):
watchlist_objects = Watchlist.objects.filter(user=request.user)
auction_objects = Auction.objects.filter(auction_watchlist__in=watchlist_objects).all()
context = {'watchlist_auctions': auction_objects}
print(context)
return render(request, "auctions/watchlist.html", context)
-I make the first query to get the list of items in the watchlist associate with the user.
-Then I use that to get another query from the Auction Model and I pass it to the template.
In the template I can access the attributes of Auction to display them. (title, author, and others that I did not include for simplicity)
The question is:
Is this the "right way? Is there a better way to access the attributes in Auction from the first Watchlist query?
It seems to me that I'm doing something overcomplicated.
This is not that bad, considering that it will probably be executed as one query, because of the lazy queryset evaluations. You can skip the .all() if you already have .filter().
However, there is a more convenient way to do this, using lookups that span relationships.:
auction_objects = Auction.objects.filter(auction_watchlist__user_id=request.user.id)
I'm creating a model in Django with the following syntax. The model has a foreign key of registered users. However, I want to serialize this model that will return a Json file only associated with the logged in user. Can you give your recommendations? Or is there an alternative way to extract the information from the model using different approach?
class Education(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
school = models.CharField(max_length=50)
year = models.CharField(max_length=10)
Sample photo
For instance, it will only show the first field associated with the account
To filter objects related to a user, simply get hold of the user and apply filter based on the user.
user_education_queryset = Education.objects.filter(user=user)
if you are doing this in the view where user objects is available in request context, you could simply do user=request.user Note: the user has to be logged in, else this will return anonymouse user object which will fail to query the database.
Summary:
Get the user object and filter by the user field in education model using the user object obtained.
mike = User.objects.get(id=1) # or mike = User.objects.get(username='mike')
then
mike_education_query = Education.objects.filter(user=mike)
Because of the suggestions, I was able to query the results per user with this code:
class EducationViewSet(viewsets.ModelViewSet):
serializer_class = EducationSerializer
def get_queryset(self):
user = self.request.user
account = User.objects.get(id=user.id)
return Education.objects.filter(user=account)
This is my model:
class Company(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,related_name="Company_Owner",on_delete=models.CASCADE,null=True,blank=True)
name = models.CharField(max_length=50,blank=False)
auditor = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='main_auditor',blank=True)
I want to perform a query which will display the list of company to which a particular auditor is associated with...
How to do query with settings.AUTH_USER_MODEL in django?
Any idea anyone?
Please help
You can do it like this using get_user_model():
from django.contrib.auth import get_user_model
User = get_user_model()
first_user = User.objects.first()
first_user.Company_Owner.all() # will get all companies associated with the User
Your model relationships are badly named, change the related_name parameters, since they need to represent that you're fetching Company objects:
class Company(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,related_name="owned_companies",on_delete=models.CASCADE,null=True,blank=True)
name = models.CharField(max_length=50,blank=False)
auditor = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='audited_companies',blank=True)
Then you'll be able, once you have a user (e.g. user = self.request.user) to do:
user.owned_companies.all() # companies for which the user is the owner
user.audited_companies.all() # companies for which the user is a main auditor
user.owned_companies.filter(auditor=user) # companies of which the user is both owner and auditor
My project is a social networking site that can send requests to friends and make friends.
I have extended django's existing user model using oneToone field .
So far i've been able to do the above said thing but when ever a user accepts the request , Both the user who sent request and also the user accepted it must increment a value in their extended user model which stores the value of their friends count .
I'm facing difficulties trying to solve this .
I've also used signals but it doesn't work .
Here is my code:
models.py:
class Friends(models.Model):
"""Model for saving relationship of user and friends"""
request_id = models.ForeignKey(User,on_delete=models.CASCADE,related_name='current_user')
friend_id = models.ForeignKey(User,on_delete=models.CASCADE,related_name='user_friend')
created_on = models.DateTimeField(auto_now_add=True,auto_now=False)
class Meta:
verbose_name_plural = "Friends"
def __str__(self):
return str(self.friend_id)
class Profile(models.Model):
user = models.OneToOneField(User,related_name='profile',on_delete=models.CASCADE)
location = models.CharField(max_length=30,blank=True)
friends_count = models.PositiveIntegerField(default=0)
profile_pic = models.ImageField(upload_to='profile_pics/',blank=True,null=True)
def natural_key(self):
return (self.user.username,)
signals.py:
#receiver(post_save,sender=Friends)
def update_friends_count(sender,instance,created,**kwargs):
if created:
user_profile = Profile(user = instance.request_id)
user_profile.friends_count=F('friends_count')+1
user_profile.save()
Any help would be greatly appreciated!!!!! Thank you in advance!!!
You were initializing a new instance of Profile every time a friend is made. Instead you can use get_or_create to generate a profile or retrieve the one associated with that id. Next, you want to update both users so fetch the other update the counts and save.
#receiver(post_save,sender=Friends)
def update_friends_count(sender,instance,created,**kwargs):
if created:
user_profile, profile_created = Profile.objects.get_or_create(user = instance.request_id)
user_profile.friends_count=F('friends_count')+1
user_profile.save()
friend_profile, profile_created = Profile.objects.get_or_create(user = instance.friend_id)
friend_profile.friends_count=F('friends_count')+1
friend_profile.save()
With Q filter and update
Profile.objects.filter(
Q(user=instance.request_id) |
Q(user=instance.friend_id)
).update(
friends_count=F('friends_count')+1
)
This last query uses the django.db.models Q object to make an SQL OR statement to retrieve the two profile instances; that of the requested and the requester: the update call acts on the query and updates all instances in the list
I wonder what to do here.
So I have an EmailAddress class which has a ForeignKey relationship to my User class
class EmailAddress(models.Model):
user = models.ForeignKey(allauth_app_settings.USER_MODEL, verbose_name=_('user'))
email = models.EmailField(unique=app_settings.UNIQUE_EMAIL,
verbose_name=_('e-mail address'))
I also have a UserProfile class which has a OneToOneField relationship with the mentioned User like so:
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name='profile')
attribute= models.CharField("attr", max_length=150, blank=False)
Now in admin.py I want to filter the Users so that the current user who is logged in to admintool can only see emails from users who share some attribute. The catch is that the attribute is saved in UserProfile and I need to get there through the email.
Basically I don't know how to access the 'attribute' which is in the UserProfile. I understand that I want to filter users by taking the email finding the user that this email belongs to and then compare his 'attribute' to the user who send the request to decide whether to show it to him or not.
def get_queryset(self, request):
user = [User whos email is in the list]
qs = [the queryset]
[and then]
return qs.filter([filter so the user attribute = the request.user attribute])
Im using allauth btw. in case you wonder what
allauth_app_settings.USER_Model
stands for.
Have a nice day!
You need to use the double-underscore syntax to follow the relationships.
qs.filter(user__userprofile__attribute=request.user.attribute)