How to reverse access ManyToManyField of instance of a model - python

class User(models.Model):
username = models.CharField(max_length=30)
email = models.CharField(max_length=60, primary_key=True)
registration_date = models.DateField(default=datetime.date.today)
class Task(models.Model):
author = models.ForeignKey(
User, on_delete=models.PROTECT,
related_name="%(app_label)s_%(class)s_author"
)
likes = models.ManyToManyField(
User,
related_name="%(app_label)s_%(class)s_like"
)
dislikes = models.ManyToManyField(
User,
related_name="%(app_label)s_%(class)s_dislike"
)
subscribed_users = models.ManyToManyField(User)
I want to make a method for User, which returns all tasks liked or disliked by that user. I looked through django orm's documentation and didn't find how I can, given an instance of User, find all Tasks he liked.

You just need to use the related_name like:
some_user.app_label_class_like.all()

Related

How to get a list of objects using several model relationships in Django?

I would like to get a list of UserProfile of a user's followers
I have the following models :
class User(AbstractBaseUser, PermissionsMixin):
username = models.CharField(max_length=16, unique=True, db_index=True)
# ...
class UserProfile(models.Model):
user = models.OneToOneField(User, related_name="profile", on_delete=models.CASCADE)
# ...
class UserFollow(models.Model):
follower = models.ForeignKey(User, related_name="follow_follower", on_delete=models.CASCADE)
following = models.ForeignKey(User, related_name="follow_following", on_delete=models.CASCADE)
date_created = models.DateTimeField(auto_now_add=True, editable=False)
# ...
How can I accomplish this while optimizing database queries ?
I use rest framework.
If I understand correctly, given a username of a user (say user1), you want to retrieve all the userprofiles from the users following user1, right?
Whereas I do not think this is possible with a one-liner, I think the following should to the trick:
followers = UserFollow.objects.filter(following__username=username).values('follower')
follower_profiles = UserProfile.objects.filter(user__in=followers)
I saw your message, you want : follower_profiles = User.objects.get(username=username).follow_following.all().follower.profile
You should want this.
query_set = User.objects.get(username='username').follow_follower.select_related('follower__profile').all().follower.profile
for query in query_set.all():
print(query.follower.user) # Here is the model for UserProfile.

create django user based on existing model object

I'm working on Property Management django app where my base model is Tenant with all basic info like name, surname, email etc. Willing tenants will be able to create user account so they can log in and book Gym/Cinema, but not all Tenants will need to have user account. My problem is:
How can I create new user accounts based on existing Tenant objects? Obviously user will have Tenant ForeignKey but how can I extract Tenant.name, Tenant.surname etc to more than 1 field in my user model?
ForeignKey only gives me reference to object but can I somehow access certain fields of that object during creation of new user so I make sure that Tenant.email is the same as user.email?
Edit
tenancy = (('Tenant', 'Tenant'),('Owner', 'Owner'), ('Other', 'Other'))
class Tenant(models.Model):
name = models.CharField(max_length=15, null=True, blank=False)
surname = models.CharField(max_length=30, null=True, blank=False)
email = models.EmailField(max_length=50, unique=True)
phone_number = models.CharField(max_length=20, null=True, blank=True, unique=True)
flat = models.ForeignKey(Flat, on_delete=models.PROTECT)
status = models.CharField(max_length=10, choices=tenancy, null=True, blank=False)
stay_length = models.CharField(max_length=20, null=True, blank=False)
pet_licence = models.CharField(max_length=20, null=True, blank=False)
additional_notes= models.TextField(max_length=300, blank=True)
date_added = models.DateField(auto_now_add=True, null=True)
moved_out = models.BooleanField(default=False)
date_moved_out= models.DateField(auto_now_add=False, null=True)
class Meta:
unique_together = ('name', 'surname',)
def __str__(self):
return f'{self.name} {self.surname}'
Now I'd like to create user account model where name, surname, email, phone_number and flat will be ForeignKeys of Tenant model. Is it even possible to have 4 ForeignKeys from 1 object populating new model?
I've tried playing around with ForeignKey.limit_choices_to, ForeignKey.related_name, ForeignKey.to_field (this was close but field related to has to be unique which doesn't work for my case) but everything gives errors. I just want to find out if it's even possible that more than 1 ForeignKey of 1 object can be directed to multiple different fields of new model object.
I would approach it in a way that the foreign key is in Tenant, instead of User, and define it as a nullable one-to-one. This way you keep your User model free of foreign keys:
class Tenant(models.Model):
user = models.OneToOneField(
User,
related_name='tenant',
on_delete=models.CASCADE,
null=True,
blank=True,
default=None,
)
Then to create the related user, you can add a method in your Tenant model like so:
class Tenant(models.Model):
...
def create_user(self):
if not self.user:
user = User.objects.create(
first_name=self.name,
last_name=self.surname,
...
)
self.user = user
self.save()
Have a look at the example from the docs here.
You can use to_field to create fkeys to non-pk fields of another model, however, those fields need to have a unique constraint (i.e. unique=True) - which seems unlikely for name/surname.
It sounds like you want a transparent access from the User model to the Tenant models fields, and that is not possible.
You can create a fkey from User to Tenant:
class User(models.Model):
tenant = models.OneToOneField(Tenant, null=True, blank=True, related_name='user')
...
and then access the fields with
user = User.objects.get(...)
user.tenant.surname
to keep the fields in sync you can override the save() method:
class Tenant(...)
...
def save(self, *args, **kwargs):
if self.user:
self.user.last_name = self.surname
...
self.user.save()
super().save(*args, **kwargs)
aside: null=True indicates that the database should allow null in the field, blank=True says that the field can be empty in the admin interface. You should probably have null=True, blank=True in most cases.

Is there any way to select a value dynamically for a model's related field in runtime?

I want to achieve a functionality, where I need to select a django model (e.g from a drop down list), and after selecting one, all the objects of that model shows up.
class Thread(models.Model):
sender = models.(???) # This need to be a field that can store a different model on a run time.
receiver = models.(???) # same here.
Is there any way that I can dynamically first select the model and then pick an object of that list. I have seen this functionality in odoo. But is there anything in Django?
Use Inheritance for in your Model and map your foreign key to User, and then pass either a teacher of student object.
You can use the many-to-many filed with multiple available choices of "Student" and "Teacher" from another Model.
class UserRole(models.Model):
STUDENT = 'STUDENT'
TEACHER = 'TEACHER'
ROLE_CHOICES = (
(STUDENT, 'student'),
(TEACHER, 'teacher'),
)
role_name = models.CharField(max_length=255, choices=ROLE_CHOICES)
def __str__(self):
return "{}".format(self.role_name)
class User(AbstractUser):
username = models.CharField(max_length=50, unique=True)
email = models.EmailField(_('email address'))
role = models.ManyToManyField(UserRole)
Class Thread(models.Model):
sender = models.OneToOneField(User, on_delete=models.CASCADE)
receiver = models.OneToOneField(User, on_delete=models.CASCADE)
This way you can only put available roles in sender and receiver fields of Thread.
The solution was possible with ajax too, but there also is another way in django which I was searching for.
class Test(models.Model):
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
Have a good one.

Django filtering queries with multi-table inheritance

I'm using Django multi-table inheritance to implement a notifications system.
It looks like this:
class Notification(models.Model):
# this allows us to check the type without having to query another table
type = models.CharField(max_length=2, choices=type_choices)
user = models.ForeignKey(User, related_name='+', null=True)
date = models.DateTimeField(default=datetime.now)
read = models.BooleanField(default=False)
class Meta:
ordering = ["-date"]
# Users can comment on items.
class CommentNotification(Notification):
comment = models.ForeignKey(Comment, related_name='+')
class ShareNotification(Notification):
share = models.ForeignKey(Share, related_name='+')
# If user unsubscribes from an item, they will not receive notifications of comments on that item
class UnsubscribeItem(models.Model):
user = models.ForeignKey(User, related_name='+')
item = models.ForeignKey(Item, related_name='+')
class Comment(models.Model):
item = models.ForeignKey(Item, related_name='comments')
user = models.ForeignKey(User, related_name='+')
comment = models.TextField()
If I want to get all notifications for a user, I can simply query the Notification table. But I also want to exclude any CommentNotification entries if the user has unsubscribed from that item (only if there is an UnsubscribeItem with user=request.user and item=comment.item).
The problem of course is the field I want to filter is not on the base class. Is it possible to modify the queryset itself to exclude those entries? Or do I need to exclude them while serializing the collection? (I'm using django-rest-framework to serialize for my API, if that helps.)

Using Django ORM to get a related model in a method

Giving the following models...
class ProjectComment(RewardBase):
user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True)
class User (AbstractBaseUser):
email = models.EmailField()
class Profile(models.Model):
bio = models.CharField(max_length=80)
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, unique=True)
From project I want to get the Users Profile bio, this is how I'm doing it....
def get_profile_bio(self):
return Profile.objects.get(user=self.user)
I now print a list of all projects I can get a profile bio, but is this the right way to do it? I'm worried that for every project it makes a new SQL call to the DB, is this correct?
class ProjectComment(RewardBase):
user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True, related_name="projects")
class User (AbstractBaseUser):
email = models.EmailField()
class Profile(models.Model):
bio = models.CharField(max_length=80)
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, unique=True, related_name="profile")
Then you can fetch the users and the profiles:
projects = ProjectComment.select_related('user', 'user__profile').exclude(user__isnull=True).all()
for project in projects:
users = [user for user in project.user.all()]
And then:
for user in users:
profiles = [profile for profile in user.profile.all()]
Why do you have a unique constrain in your ForeignKey? if you need Uniquness create a OneToOneField

Categories