I have a Friendship model with two related user objects associated with it. I would like to write a method that takes a user and returns a list of that user's friends. I was doing it via:
friends = Friendship.objects.filter(Q(user1=user) | Q(user2=user))
friends_list = [f.user1 if user == f.user2 else f.user2 for f in friends]
but this incurs a query for every user that is returned in the query set (e.g. hundreds of queries). I could write it via:
friends = Friendship.objects.select_related().filter(Q(user1=user) | Q(user2=user))
friends_list = [f.user1 if user == f.user2 else f.user2 for f in friends]
but this does an INNER JOIN on the user table. I could also write it via custom SQL,
friends = User.objects.raw("""
SELECT * FROM usertable WHERE id IN (SELECT
user1_id FROM friendstable WHERE user2_id=%d) OR id IN
(SELECT user2_id FROM lists_friendship WHERE user1_id=%d);
""" % (user.pk, user.pk))
but I was thinking there must a way to do it inside the ORM without all those extra lookups. I was trying to do something with id__in, but I couldn't find a way to get the user ids out of the friends query set.
Assuming you set up your Friendship model similar to the example below and use friend_set as the related name of the from_friend field in your model, getting a list of a users friends can be as simple as a basic list comprehension:
friends = [friendship.to_friend for friendship in user.friend_set.all()]
This would even allow you to access a user's friends in your template without having to pass that as a variable:
{% for friendship in user.friend_set.all %}
{{ friendship.to_friend.username }}
{% endfor %}
Your Friendship model would look like this:
class Friendship(models.Model):
from_friend = models.ForeignKey(
User, related_name='friend_set'
)
to_friend = models.ForeignKey(
User, related_name='to_friend_set'
)
def __unicode__(self):
return u'%s, %s' % (
self.from_friend.username,
self.to_friend.username
)
class Meta:
unique_together = (('to_friend', 'from_friend'), )
Here's a great article on building friend networking in Django:
http://www.packtpub.com/article/building-friend-networks-with-django-1.0
Note that you shouldn't have to check both from_friend and to_friend to get a users friend list. If you have a following/follower friendship model you only need all friendship instances with a from_friend = user, and if you have a double opt-in friendship model you could follow what Facebook does and add an instance for the invited friend as the from_friend once they accept the friend invite.
Related
I trying to select some data using Peewee ORM,but I'm confused how to use foreign key correctly.
I wanna select post_title,user_name,act_title by Act.id(default primary key in act).
So I use this
Post.select(Post.post_tile,User.user_name,Act.act_title).join(Act).join(User).where(Act.id==actId)
But I got this:
[{"post_title": null,"user": {}, "act": {}}]
Here is my model:
class User(BaseModel):
user_name = CharField(max_length=30,unique=True)
user_email = CharField(max_length=60,unique=True)
class Act(BaseModel):
user = ForeignKeyField(User, related_name='users_act_id') #foreignkey
act_title = CharField(max_length=30)
class Post(BaseModel):
act = ForeignKeyField(Act,related_name='acts_id') #foreignkey
user = ForeignKeyField(User,related_name='users_post_id') #foreignkey
post_title = CharField(max_length=30)
If you want to join both the User and the Act table on the Post table, you need to put a switch in the query, so you would have
Post.select(Post.post_tile,User.user_name,Act.act_title).join(Act).switch(Post).join(User).where(Act.id==actId)
Although it's possible that's not the result you're looking for because you have user as a foreign key in both the Act and Post models
I think the only thing you're missing is not looking up the values on the joined instances:
posts = (Post
.select(Post.post_tile,User.user_name,Act.act_title)
.join(Act)
.switch(Post)
.join(User)
.where(Act.id==actId))
for post in posts:
print post.post_title, post.user.user_name, post.act.act_title
If you want the attributes all assigned to the post object, just tack on a call to .naive(), e.g.:
posts = (Post.select()...where(Act.id==actId).naive())
I have the following models: User, UserProfile, and SalesCompany
Relationship: Every User has a UserProfile and every UserProfile has a SalesCompany.
I need to get all Users at SalesCompanys with more than one UserProfile.
Is there a more efficient way to do it than my following solution? Some combo of annotate and traversing ForeignKeys seem to be the solution, but I'm stumped.
# get all users
all_users = User.objects.all().order_by('username')
users = []
# for each user
for user in all_users:
try:
# get profile
profile = UserProfile.objects.get(user=user)
# get count of profiles (i.e. users) at current user's company
count_users = len(UserProfile.objects.filter(company=profile.company))
# if more than one user at company (so not just current user)
if count_users > 1:
# add to users list
users.append(user)
except Exception, e:
pass
This should execute just one SQL query:
companies_with_more_than_1_user = (
Company.objects
.annotate(num_users=Count('userprofile'))
.filter(num_users__gt=1)
)
users = User.objects.filter(userprofile__company__in=companies_with_more_than_1_user)
Stuff like this is a reason to like the Django ORM, even though I'm generally ambivalent or even slightly disliking towards Django and its way of doing things.
I've been using django-relationships to allow users to follow each other. If Bob follows Joe. Bob will be able to see all of Joe's photos. However if Bob blocks John, John will not be Bob's photos.
My problem is that I don't know how to restrict content from a blocked user. I've seen the examples but I still can't seem to find a solution.
Assume Photographer is a FK to User
Here is my FollowingPhoto resource (this resources returns all photos that belong to the people that the user is following):
FollowingPhoto(ModelResource):
photographer = fields.ForeignKey(PhotographerResource, 'photographer', full=True)
class Meta:
queryset = Photo.objects.all().order_by('-postTime')
resource_name = 'following'
fields = ['id', 'title', 'url', 'likes','postTime','photographer', 'location_id', 'location_name']
authentication = BasicAuthentication()
authorization = DjangoAuthorization()
serializer = Serializer(formats=['json'])
include_resource_uri = False
filtering = {
'postTime': ALL,
'photographer' : ALL_WITH_RELATIONS,
}
def get_object_list(self, request):
return super(FollowingPhoto, self).get_object_list(request).filter(photographer__user__in = request.user.relationships.following())
Now as you may have noticed with my get_object_list, it returns all the content from the users I am following. How can prevent users who are blocked from showing up on this list?
The Django-relationships app generates two tables in postgresql, the table below is the relationships_relationships table:
id from_user to_user_id status_id created
[PK] serial integer integer timestamp
6 1 5 1 2012-10-05 20:10:29.848667+00"
7 1 3 1 2012-10-05 20:11:23.319961+00"
And the other table is the relationships_relationshipstatus table:
id name verb from_slug login_required private
[PK] serial character character character varying boolean boolean
1 Following follow friends FALSE FALSE
2 Blocking block ! TRUE TRUE
Below I have added a link to Django-relationships models.py so that you may get further clarification:
models.py
It looks to me like you just need a .exclude clause - something like:
def get_object_list(self, request):
return super(FollowingPhoto, self).get_object_list(request).filter(photographer__user__in = request.user.relationships.following()).exclude(photographer__user__in=request.user.relationships.blocking())
That will prevent Bob from seeing John's photos if Bob is blocking John. Given that Bob only sees John's photos if Bob is following John, it will do nothing unless Bob is both following and blocking John, which seems odd to me - do you maybe want Bob to not see John's photos if John is blocking Bob instead? If so:
def get_object_list(self, request):
return super(FollowingPhoto, self).get_object_list(request).filter(photographer__user__in = request.user.relationships.following()).exclude(photographer__user__in=request.user.relationships.blockers())
Create a custom manager. Depending on whether you are selecting all photos that a user can see or all users whose photos you are allowed to see makes a difference on whether you want a RelationshipManager or PhotosManager. I'm assuming you want a feed of photos - you can adapt this to limit users pretty easily w/ a bit of sql.
PhotosManager(models.Manager):
def get_unblocked(self, user):
sql = """SELECT myapp_photo.*
FROM myapp_photo
INNER JOIN auth_user
ON myapp_photo.user_id = auth_user.id
WHERE auth_user.id != %d /*DO NOT SHOW YOUR OWN PHOTOS*/
AND myapp_photo.user_id NOT IN(
SELECT myapp_blocked.blocked_user_id
FROM myapp_blocked
WHERE myapp_blocked.user_id = %d
)""" % (user.pk, user.pk)
return self.model.objects.raw(sql)
Apologies for the horrible sql, I don't know your table structure I'm just trying to illustrate a point
Then in your Photo model add objects = PhotosManager()
And you can now get a list of photos that are not blocked in your view:
Photos.objects.get_unblocked(request.user)
EDIT:
I just realized my code only takes into account blocked users and not followers. Again w/o knowing your table structure I'd just be guessing. You can add another AND myapp_photo.user_id IN( clause to allow only photos from users that are following. Also, you could write better sql for sure, this was a quick and dirty example. my solution: write a manager, the actual provided sql is just for illustrative purposes.
I have several Customers who book Appointments. Each Appointment has exactly one customer, though a customer can be booked for multiple appointments occurring at different times.
class Customer(model.Model):
def __unicode__(self):
return u'%s' % (self.name,)
name = models.CharField(max_length=30)
# and about ten other fields I'd like to see from the admin view.
class Appointment(models.Model):
datetime = models.DateTimeField()
customer = models.ForeignKey("Customer")
class Meta:
ordering = ('datetime',)
Now when an admin goes to browse through the schedule by looking at the Appointments (ordered by time) in the admin, sometimes they want to see information about the customer who has a certain appointment. Right now, they'd have to remember the customer's name, navigate from the Appointment to the Customer admin page, find the remembered Customer, and only then could browse their information.
Ideally something like an admin inline would be great. However, I can only seem to make a CustomerInline on the Appointment admin page if Customer had a ForeignKey("Appointment"). (Django specifically gives me an error saying Customer has no ForeignKey to Appointment). Does anyone know of a similar functionality, but when Appointment has a ForeignKey('Customer')?
Note: I simplified the models; the actual Customer field currently has about ~10 fields besides the name (some free text), so it would be impractical to put all the information in the __unicode__.
There is no easy way to do this with django. The inlines are designed to follow relationships backwards.
Potentially the best substitute would be to provide a link to the user object. In the list view this is pretty trivial:
Add a method to your appointment model like:
def customer_admin_link(self):
return 'Customer' % reverse('admin:app_label_customer_change %s') % self.id
customer_admin_link.allow_tags = True
customer_admin_link.short_description = 'Customer'
Then in your ModelAdmin add:
list_display = (..., 'customer_admin_link', ...)
Another solution to get exactly what you're looking for at the cost of being a bit more complex would be to define a custom admin template. If you do that you can basically do anything. Here is a guide I've used before to explain:
http://www.unessa.net/en/hoyci/2006/12/custom-admin-templates/
Basically copy the change form from the django source and add code to display the customer information.
Completing #John's answer from above - define what you would like to see on the your changelist:
return '%s' % (
reverse('admin:applabel_customer_change', (self.customer.id,)),
self.customer.name # add more stuff here
)
And to add this to the change form, see: Add custom html between two model fields in Django admin's change_form
In the ModelAdmin class for your Appointments, you should declare the following method:
class MySuperModelAdmin(admin.ModelAdmin):
def get_form(self, request, obj=None, **kwargs):
if obj:
# create your own model admin instance here, because you will have the Customer's
# id so you know which instance to fetch
# something like the following
inline_instance = MyModelAdminInline(self.model, self.admin_site)
self.inline_instances = [inline_instance]
return super(MySuperModelAdmin, self).get_form(request, obj, **kwargs)
For more information, browser the source for that function to give you an idea of what you will have access to.
https://code.djangoproject.com/browser/django/trunk/django/contrib/admin/options.py#L423
There is a library you can use it.
https://github.com/daniyalzade/django_reverse_admin
But if you want to use link to object in showing table you can like this code:
def customer_link(self, obj):
if obj.customer:
reverse_link = 'admin:%s_%s_change' % (
obj.customer._meta.app_label, obj.customer._meta.model_name)
link = reverse(reverse_link, args=[obj.customer.id])
return format_html('More detail' % link)
return format_html('<span >-</span>')
customer_link.allow_tags = True
customer_link.short_description = 'Customer Info'
And in list_display:
list_display = (...,customer_link,...)
I am trying to understand the 1-to-many relationships in datastore; but I fail to understand how query and update the record of a user when the model includes ReferenceProperty. Say I have this model:
class User(db.Model):
userEmail = db.StringProperty()
userScore = db.IntegerProperty(default=0)
class Comment(db.Model):
user = db.ReferenceProperty(User, collection_name="comments")
comment = db.StringProperty()
class Venue(db.Model):
user = db.ReferenceProperty(User, collection_name="venues")
venue = db.StringProperty()
If I understand correctly, the same user, uniquely identified by userEmail can have many comments and may be associated with many venues (restaurants etc.)
Now, let's say the user az#example.com is already in the database and he submits a new entry.
Based on this answer I do something like:
q = User.all()
q.filter("userEmail =", az#example.com)
results = q.fetch(1)
newEntry = results[0]
But I am not clear what this does! What I want to do is to update comment and venue fields which are under class Comment and class Venue.
Can you help me understand how this works? Thanks.
The snippet you posted is doing this (see comments):
q = User.all() # prepare User table for querying
q.filter("userEmail =", "az#example.com") # apply filter, email lookup
- this is a simple where clause
results = q.fetch(1) # execute the query, apply limit 1
the_user = results[0] # the results is a list of objects, grab the first one
After this code the_user will be an object that corresponds to the user record with email "az#example.com". Seing you've set up your reference properties, you can access its comments and venues with the_user.comments and the_user.venues. Some venue of these can be modified, say like this:
some_venue = the_user.venues[0] # the first from the list
some_venue.venue = 'At DC. square'
db.put(some_venue) # the entry will be updated
I suggest that you make a general sweep of the gae documentation that has very good examples, you will find it very helpful:
http://code.google.com/appengine/docs/python/overview.html
** UPDATE **: For adding new venue to user, simply create new venue and assign the queried user object as the venue's user attribute:
new_venue = Venue(venue='Jeferson memorial', user=the_user) # careful with the quoting
db.put(new_venue)
To get all Comments for a given user, filter the user property using the key of the user:
comments = Comment.all().filter("user =", user.key()).fetch(50)
So you could first lookup the user by the email, and then search comments or venues using its key.