Django ORM for mutual follow query - django

user_id user_follow
3 2
3 2
3 2
2 3
login user id=3, now i want to get people who follow me and i follow them, mutual follow in django framework for login user. above senario show login user(id=3) follow the user(id=2)
and user(id=2) also follow the login user(id=3) now I want to show to login user, how many user follow you and you follow him. using django orm
class Cause(models.Model):
description = models.TextField()
start_date = models.DateTimeField(null=True, blank=True)
creation_date = models.DateTimeField(default=datetime.now)
creator = models.ForeignKey(User, related_name='cause_creator_set')
attendees = models.ManyToManyField(User)
class Attendance(models.Model):
user = models.ForeignKey(User)
cause = models.ForeignKey(Cause)
user_follow = models.IntegerField(max_length=255)
registration_date = models.DateTimeField(default=datetime.now)
follow = models.BooleanField(default=False)
def __unicode__(self):
return "%s is attending %s" % (self.user.username, self.event)
class Meta(object):
verbose_name_plural = "Attendance"

There are multiple issues here, I'm going to try and address them all.
The relationship your models describe is what called a Extra field on ManyToMany relationship. Simply put, Cause has a ManyToMany to User, with Attendance being an intermediatry table. The benefits of using the through instruction for this are mostly for ease-of-access, and you can read the documentation I linked for examples. So:
attendees = models.ManyToManyField(User, through='Attendance')
Now let's talk about Attendance. I have a problem with two fields:
user = models.ForeignKey(User)
user_follow = models.IntegerField(max_length=255)
follow = models.BooleanField(default=False)
Yes, I showed 3 because only 2 of them are problematic. So you have a ForeignKey to User, right? But what is a ForeignKey? It's simply an IntegerField that points on the row (pk) on a different table. There are other stuff that happens here, but the basic thing is it's just an integerfield. And then you wanted to link that model to user again, right? So instead of adding another ForeignKey, you used an IntegerField.
Do you understand why that's a bad thing? I'll explain - you can't use the ORM on your own logic. Now the basic thing I'm assuming you are trying to accomplish here is actually this:
# let's pretend that User isn't built-in
class User(models.Model):
follow = models.ManyToManyField('self')
Then there's the follow Boolean which I really don't get - if a user is linked to another one, doesn't that automatically mean he's following him? seems more like a method than anything.
In anyway, I can think of two very valid options:
Use another ForeignKey. Really, if that what makes sense, there's nothing wrong with multiple ForeignKeys. In fact, it will create a sort of ManyToMany from user to himself going through Attendance as an intermediary table. I think this makes sense
Create your own User class. Not a terribly complicate feat. I also recommend this, since it seems you have plenty of customization. The built in User is great because it comes pretty much out of the box. But at the same time, the built-in user is pretty much built around the admin interface and kinda limits you a lot. A custom one would be easier. Then you can do the recursive relationship that seems to be what you're looking for, then implament is_following as a boolean method (seems more appropriate).
The last thing I'm gonna do is show you the method you're looking for. I'm showing you how to do it though as I mentioned, I strongly recommend you first take my suggestion and build your own User model. The method would look different of course, but it wouldn't be hard to convert.
That being said, the key here is simply creating two methods - one for checking if a user is related. A second one for checking if you're being followed by him. So if I understand you correctly, you're trying to achieve:
#using your way
def follows(a, b):
if Attendence.objects.filter(user__pk=a, user_follow=b).count() > 0:
return True
return False
def mutual(a, b):
if follows(a, b) and follows(b, a):
return True
return False
a, b = 2, 3
mutual(2, 3) # will output True
#using a custom User Class:
class MyUser(AbstractBaseUser):
# required fields here...
follow = models.ManyToManyField('self')
def is_following(self, user):
if user in self.follow.all():
return True
return False
def mutual(self, user):
if self.is_following(user) and user.is_following(self):
return True
return False

Related

Use of select_related in simple query in django

I have a model in Django in which a field has a fk relationship with the teacher model. I have came across select_related in django and want to use it in my view. However, I am not sure whether to use it in my query or not.
My models:
class Teacher(models.Model):
name = models.OneToOneField(max_length=255, default="", blank=True)
address = models.CharField(max_length=255, default="", blank=True)
college_name = models.CharField(max_length=255, default="", blank=True)
class OnlineClass(models.Model):
teacher = models.ForeignKey(Teacher,on_delete=models.CASCADE)
My view:
def get(self, request,*args, **kwargs):
teacher = self.request.user.teacher
classes = Class.objects.filter(teacher=teacher) #confusion is here..............
serializer_class = self.get_serializer_class()
serializer = serializer_class(classes,many=True)
return Response(serializer.data,status=status.HTTP_200_OK)
I have commented on the line or the section of the problem. So I wanted to list all the classes of that teacher. Here I have used filter. But can we use select_related here?? What I understood is if I want to show another fields of teacher model as well, for eg name or college_name, then I have to use it. Otherwise the way I have done it is correct. Also, select_related is only used for get api not for post api, is that correct??
First, the easiest way to get all classes per teacher is by using the related_name attribute (https://docs.djangoproject.com/en/3.2/ref/models/fields/#django.db.models.ForeignKey.related_name).
class OnlineClass(models.Model):
teacher = models.ForeignKey(
Teacher,
on_delete=models.CASCADE,
related_name='classes'
)
# All classes of a teacher
teacher.classes.all()
When select_related is used, new sql joins are added to the Django internals SQL query. It is useful to reduce the workload in the database engine, getting the data quickly, and yes, is only for reading.
for obj in OnlineClass.objects.all():
# This hits the database every cycle to get the teacher data,
# with a new query like: select * from teacher_table where id = ...
print(obj.teacher)
for obj in OnlineClass.objects.select_related('teacher').all():
# This don'ts hits the database.
# Previously, the Django ORM joined the
# OnlineClass and Teacher data with a single SQL query.
print(obj.teacher)
I think that, in your example, with only one teacher, using "select_related" or not don't make big difference.
select_related is used to select additional data from related objects when the query is executed. It results in a more complex query. But it boosts performance if you have to access related data, since no additional database queries will be required.
See documentation here.
In your code it would be possible to use select_related, but it would be inefficient, because you're not accessing related objects of the queried classes. So using select_related would result in a more complex query without any advantage.
If you wanted to use select_related, the syntax would be classes = Class.objects.select_related('teacher').filter(teacher=teacher)

Django project architecture advice

I have a django project and I have a Post model witch look like that:
class BasicPost(models.Model):
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
published = models.BooleanField(default=False)
created_date = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=False)
body = models.TextField(max_length=999)
media = models.ImageField(blank=True)
def get_absolute_url(self):
return reverse('basic_post', args=[str(self.pk)])
def __str__(self):
return self.title
Also, I use the basic User model that comes with the basic django app.
I want to save witch posts each user has read so I can send him posts he haven't read.
My question is what is the best way to do so, If I use Many to Many field, should I put it on the User model and save all the posts he read or should I do it in the other direction, put the Many to Many field in the Post model and save for each post witch user read it?
it's going to be more that 1 million + posts in the Post model and about 50,000 users and I want to do the best filters to return unread posts to the user
If I should use the first option, how do I expand the User model?
thanks!
On your first question (which way to go): I believe that ManyToMany by default creates indices in the DB for both foreign keys. Therefore, wherever you put the relation, in User or in BasicPost, you'll have the direct and reverse relationships working through an index. Django will create for you a pivot table with three columns like: (id, user_id, basic_post_id). Every access to this table will index through user_id or basic_post_id and check that there's a unique couple (user_id, basic_post_id), if any. So it's more within your application that you'll decide whether you filter from a 1 million set or from a 50k posts.
On your second question (how to overload User), it's generally recommended to subclass User from the very beginning. If that's too late and your project is too far advanced for that, you can do this in your models.py:
class BasicPost(models.Model):
# your code
readers = models.ManyToManyField(to='User', related_name="posts_already_read")
# "manually" add method to User class
def _unread_posts(user):
return BasicPost.objects.exclude(readers__in=user)
User.unread_posts = _unread_posts
Haven't run this code though! Hope this helps.
Could you have a separate ReadPost model instead of a potentially large m2m, which you could save when a user reads a post? That way you can just query the ReadPost models to get the data, instead of storing it all in the blog post.
Maybe something like this:
from django.utils import timezone
class UserReadPost(models.Model):
user = models.ForeignKey("auth.User", on_delete=models.CASCADE, related_name="read_posts")
seen_at = models.DateTimeField(default=timezone.now)
post = models.ForeignKey(BasicPost, on_delete=models.CASCADE, related_name="read_by_users")
You could add a unique_together constraint to make sure that only one UserReadPost object is created for each user and post (to make sure you don't count any twice), and use get_or_create() when creating new records.
Then finding the posts a user has read is:
posts = UserReadPost.objects.filter(user=current_user).values_list("post", flat=True)
This could also be extended relatively easily. For example, if your BasicPost objects can be edited, you could add an updated_at field to the post. Then you could compare the seen_at of the UserReadPost field to the updated_at field of the BasicPost to check if they've seen the updated version.
Downside is you'd be creating a lot of rows in the DB for this table.
If you place your posts in chronological order (by created_at, for example), your option could be to extend user model with latest_read_post_id field.
This case:
class BasicPost(models.Model):
# your code
def is_read_by(self, user):
return self.id < user.latest_read_post_id

Filter M2M in template?

In my model, I have the following M2M field
class FamilyMember(AbstractUser):
...
email_list = models.ManyToManyField('EmailList', verbose_name="Email Lists", blank=True, null=True)
...
The EmailList table looks like this:
class EmailList(models.Model):
name = models.CharField(max_length=50, default='My List')
description = models.TextField(blank=True)
is_active = models.BooleanField(verbose_name="Active")
is_managed_by_user = models.BooleanField(verbose_name="User Managed")
In the app, the user should only see records that is_active=True and is_managed_by_user=True.
In the Admin side, the admin should be able to add a user to any/all of these groups, regardless of the is_active and is_managed_by_user flag.
What happens is that the Admin assigns a user to all of the email list records. Then, the user logs in and can only see a subset of the list (is_active=True and is_managed_by_user=True). This is expected behavior. However, what comes next is not.
The user deselects an email list item and then saves the record. Since M2M_Save first clears all of the m2m records before it calls save() I lose all of the records that the Admin assigned to this user.
How can I keep those? I've tried creating multiple lists and then merging them before the save, I've tried passing the entire list to the template and then hiding the ones where is_managed_by_user=False, and I just can't get anything to work.
What makes this even more tricky for me is that this is all wrapped up in a formset.
How would you go about coding this? What is the right way to do it? Do I filter out the records that the user shouldn't see in my view? If so, how do I merge those missing records before I save any changes that the user makes?
You might want to try setting up a model manager in your models.py to take care of the filtering. You can then call the filter in your views.py like so:
models.py:
class EmailListQuerySet(models.query.QuerySet):
def active(self):
return self.filter(is_active=True)
def managed_by_user(self):
return self.filter(is_managed_by_user=True)
class EmailListManager(models.Manager):
def get_queryset(self):
return EmailListQuerySet(self.model, using=self._db)
def get_active(self):
return self.get_queryset().active()
def get_all(self):
return self.get_queryset().active().managed_by_user()
class EmailList(models.Model):
name = models.CharField(max_length=50, default='My List')
description = models.TextField(blank=True)
is_active = models.BooleanField(verbose_name="Active")
is_managed_by_user = models.BooleanField(verbose_name="User Managed")
objects = EmailListManager()
views.py:
def view(request):
email = EmailList.objects.get_all()
return render(request, 'template.html', {'email': email})
Obviously there is outstanding data incorporated in my example, and you are more than welcome to change the variables/filters according to your needs. However, I hope the above can give you an idea of the possibilities you can try.
In your views you could do email = EmailList.objects.all().is_active().is_managed_by_user(), but the loading time will be longer if you have a lot of objects in your database. The model manager is preferred to save memory. Additionally, it is not reliant on what the user does, so both the admin and user interface have to talk to the model directly (keeping them in sync).
Note: The example above is typed directly into this answer and has not been validated in a text editor. I apologize if there are some syntax or typo errors.

Django - Alternative to multi-table inheritance in Appengine

I'm developing a Django application on Google Appengine, and I came across a db design problem where multi-table inheritance seems to be the best solution. Unfortunately, however, multi-table inheritance is not supported on Appengine because it requires JOINs. I'm looking for an alternative solution that meets the following requirements:
** See updates at bottom **
There are 3 different user types, or profiles:
Business (i.e. owner)
Employee
Client
These profiles share certain attributes, but also have unique attributes for their respective types. For example, all profiles have a contact email and phone number, but only Businesses need to provide a logo or specify their business type.
In addition, I need to be able to retrieve Profile objects from the db (regardless of type), and get the extended or child profile for each (Business, Employee, or Client). Also, a Business, Employee, or Client object should also be able to access the parent profile easily. In other words, the relationship needs to work in both direction (like profile.employee or employee.profile).
So far, I have come up with two possible solutions:
OneToOneField in child model:
class Profile(models.Model):
# Profile may exist before user claims it
user = models.OneToOneField(User, blank=True, null=True)
email ...
phone ...
... other common fields ...
class Business(models.Model):
profile = models.OneToOneField(Profile, verbose_name="user profile", related_name="biz_profile")
class Employee(models.Model):
profile = models.OneToOneField(Profile, verbose_name="user profile", related_name="employee_profile")
class Client(models.Model):
profile = models.OneToOneField(Profile, verbose_name="user profile", related_name="client_profile")
This will allow me to do the following: profile.biz_profile and biz.profile
Unique Generic Foreign Key in parent model:
class Profile(models.Model):
content_type=models.ForeignKey(ContentType)
object_id=models.PositiveIntegerField()
content_object=generic.GenericForeignKey('content_type','object_id')
email ...
phone ...
... other common fields ...
class Meta:
unique_together = ('content_type', 'object_id')
class Business(models.Model):
profiles = generic.GenericRelation(Profile)
class Employee(models.Model):
profiles = generic.GenericRelation(Profile)
class Client(models.Model):
profiles = generic.GenericRelation(Profile)
This will allow me to do the following: profile.content_object and biz.profiles.all()[0]
The first approach (OneToOneField) seems the most straight forward, but I would need to come up with a better way to know which child to call, perhaps by setting the content_type in the Profile model creating a method like:
def get_instance(self):
# Need to look at contenttype framework, but you get the idea
if self.content_type == 'business':
return self.biz_profile
elif self.content_type == 'employee':
return self.employee_profile
elif self.content_type == 'client':
return self.client_profile
return None
I'm not set on either of these solutions, so I welcome any alternative solutions or improvements to what I have here.
Thanks in advance!
UPDATE
My original requirements have changed since I first posted. It turns out I only need parent>child access NOT child>parent. In light of this, I'm going to use the unique Generic Foreign Key approach instead. However, I am still looking for an answer to the original question, so don't be shy if you have a solution.

Copy all fields of a django model instance

ok i think this is very basic, but since I am new to Django I don't know how to handle this.
I need to copy an instance of a django-model. As explained here, there is a problem with copying ManyToMany relations. But the attachment "django-model-copying.diff" has that function I guess. So I don't know - does my Django already have that function? I don't know how to call it.
Help would be appreciated.
The docs include directions on how to do this - including how to handle many to many relationships.
You can just do the following:
m = MyModel.objects.get(pk=1)
m.id = None
m.save()
That way new instance will be created with new id of course in case of any unique properties it will trigger errors during validation.
NOTE:
As for the function you've mentioned - it is not yet added to the trunk, the status is design decision needed, but if you know what you're doing you can manually apply the diff to your django instance - it's called patching btw. Here are some details about how to do it: http://ariejan.net/2007/07/03/how-to-create-and-apply-a-patch-with-subversion/.
I'll try to answer your actual problem, because what you are asking for in the question is a problem with your proposed solution, which as many have pointed out is not really ideal.
In the comments you mention this:
i'll try to explain.. So we have 2 models: User and Book. A User has a
book called "Titanic" with some content. Now, another user wants a
relation to that book too. But, the second user wants exactly the same
book, but it should be called "Ship is going under".. I would copy the
book, and rename it. - I know, i could also put the content of the
book in another model - but my model is a little bit more complex.
Looks like you have three things to track:
Users
Their Books
Some custom notes that users have about their "owned" book.
For each Book, store its common information, for example:
class Author(models.Model):
name = models.CharField(max_length=50)
email = models.EmailField()
def __unicode__(self):
return unicode(self.name)
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.ManyToMany(Author)
isbn = models.CharField(max_length=13)
# and so on
def __unicode__(self):
return unicode(self.title)
Now, you need to store Book -> User relation, such as one user can have many books, two users may own the same book, with their own meta information attached.
class BookClub(models.Model):
username = models.ForeignKey(User)
book = models.ForeignKey(Book)
comments = models.TextField()
If you structure your models like that, you'll be able to do queries like:
"How many books does each member have?"
"What is the most popular book?"
"What is the least popular book?"
"What is the most popular author?"
It's worth noting that as of django 1.8 you may have UUIDFields. If you copy a model instance and then save it the unique constraint will fail on this column, in which case you have to do something like:
import uuid
m = MyModel.objects.get(pk=1)
m.pk = None
m.uuid = uuid.uuid4() //generate new uuid
m.save()