Bidirectional one-to-may django - django

I would like to create a bidirectional one-to-many and many-to-one relationship in django like:
class User(models.Model):
device = dont_know_what_to_write()
class Device(models.Model):
user = models.ForeignKey(
User,
on_delete = models.CASCADE
)
What should I do?

In Django if you define a relation from A to B, then Django will automatically add a conceptual relation from B to A which you can query. Django thus already has added the relation in reverse. Indeed, if you implement the models with:
class User(models.Model):
# no device
pass
class Device(models.Model):
user = models.ForeignKey(
User,
on_delete = models.CASCADE
)
then you can access the set of device related to the user with:
myuser.device_set.all()
this is a QuerySet that will contain all Devices that have myuser as user.
You can specify another name with the related_name=… parameter [Django-doc]:
class Device(models.Model):
user = models.ForeignKey(
User,
on_delete = models.CASCADE,
related_name='devices'
)
then you can obtain the devices with:
myuser.devices.all()
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

Related

Django makemigrations error on many to many relationship

What i want:
Store information about running of group of people.
What i did:
from django.db import models
from django.contrib.auth.models import User
from datetime import timedelta
class Route(models.Model):
name = models.CharField(max_length=50)
class Run(models.Model):
date = models.DateField()
type = models.ForeignKey(Route, on_delete=models.PROTECT)
runners = models.ManyToManyField(User, through='RunnerResult', through_fields=["user", "run"])
class RunnerResult(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT)
run = models.ForeignKey('Run', on_delete=models.PROTECT)
result = models.DurationField(default=timedelta())
Problem:
When i do makemigrations i have the following error:
SystemCheckError: System check identified some issues:
ERRORS:
run.Run.runners: (fields.E339) 'RunnerResult.run' is not a foreign key to 'User'.
HINT: Did you mean one of the following foreign keys to 'User': user?
run.Run.runners: (fields.E339) 'RunnerResult.user' is not a foreign key to 'Run'.
HINT: Did you mean one of the following foreign keys to 'Run': run?
Tried to swap through_fields and models between each other and some other actions. I'm starting to think of my misunderstanding of M2M relationship.
You specified the through_fields in the wrong order. You first should specify the relation that refers to the source, and then then one to the target, so:
class Run(models.Model):
date = models.DateField()
type = models.ForeignKey(Route, on_delete=models.PROTECT)
runners = models.ManyToManyField(
User,
through='RunnerResult',
through_fields=('run', 'user')
)
Since there is however only one ForeignKey to Run, and one to User, you do not need to specify the through_fields=… parameter [Django-doc]. So you can implement this as:
class Run(models.Model):
date = models.DateField()
type = models.ForeignKey(Route, on_delete=models.PROTECT)
runners = models.ManyToManyField(
User,
through='RunnerResult'
# no through_fields
)
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

Filtering one model based on another model's field

I have the following models:
class User(AbstractUser):
pass
class Post(models.Model):
post_user = models.ForeignKey(User, on_delete=models.CASCADE)
post_content = models.TextField()
post_date = models.DateTimeField(default=timezone.now)
class Follow(models.Model):
follow_user = models.ForeignKey(User, on_delete=models.CASCADE)
follow_target = models.ForeignKey(User, on_delete=models.CASCADE, related_name='follow_target')
I'm trying to create a queryset of all posts that were created by users I follow (me being the currently logged in user). My queryset currently looks like this but I keep getting NameError: field not found:
postresult = Post.objects.all().filter(post_user=follow__follow_target, follow__follow_user=request.user)
Any help will be greatly appreciated!
You filter with:
postresult = Post.objects.filter(
post_user__follow_target__follow_user=request.user
)
The post_user will follow the ForeignKey from Post to User. Then we use the related_name='follow_target' to follow the relation to the Follow object, and then we use follow_user to retrieve the following user.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: The related_name=… parameter [Django-doc]
is the name of the relation in reverse, so from the User model to the Follow
model in this case. Therefore it (often) makes not much sense to name it the
same as the forward relation. You thus might want to consider renaming the follow_target relation to followers.
In case you rename the related name, the query is thus:
postresult = Post.objects.filter(
post_user__followers__follow_user=request.user
)

Django subscriber feature

I am creating a django site/platform where the main concept is users can create shops and other users can subscribe to those who have shops open (think Etsy). Trying to implement the subscriber feature and this is a model I have so far for it:
class Subscriber(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
sub_shop = models.ForeignKey(Shop, on_delete=models.CASCADE)
It works perfect for giving users the ability to subscribe and have the subscribtions listed in their profile and vice versa for shop owners, but for now a user can subscribe to a shop as many times as they want and I would like to prevent this. Idk if there is a constraint to allow multiple subscriber model instances by the same user but not allow for the same exact 'user' and 'sub_shop' instance OR if I am just going on about this in a very bad way!
You can use a UniqueConstraint [Django-doc] to specify that the combination of user and sub_shop should be unique:
class Subscriber(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
sub_shop = models.ForeignKey(Shop, on_delete=models.CASCADE)
class Meta:
constraints = [
models.UniqueConstraint(
fields=['user', 'sub_shop'],
name='subscribe_once'
)
]
prior to django-2.2, you can work with unique_together [Django-doc]:
# prior to Django-2.2
class Subscriber(models.Model):
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
sub_shop = models.ForeignKey(Shop, on_delete=models.CASCADE)
class Meta:
unique_together = [['user', 'sub_shop']]
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.

Django models: Parent model accessing child model field

I have the following models
from django.contrib.auth.models import User
User = settings.AUTH_USER_MODEL
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
class Tutor(UserProfile):
# tutor_fields here
From User object how can I access Tutor? is it with user.profile?
user.profile.select_related('tutor') does not seem to work.
OneToOneField work like ForeignKey except unique=True and you don't need to specify the related_name (you can specify if you want to change it).
For you example:
from django.contrib.auth.models import User
User = settings.AUTH_USER_MODEL
class UserProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
class Tutor(UserProfile):
user = models.ForeignKey(UserProfile, on_delete=models.CASCADE, related_name='tutor')
details = models.CharField(max_length=100)
NB: Use a ForeignKey if you want/need one Tutor for many UserProfile, else if you want one (and only one) Tutor for one UserProfile
And you can access to the Tutor bu UserProfile by UserProfile.tutor.details.
As described in the django docs, you should be able to access it with user.user_profile.

How to get collection from both models in a bi-directional many-to-many relationship in django

I have two objects, Company and Account, in different packages.
They have a many-to-many relation through employee, which has an additional field is_admin.
In Company I define the relation, and reading online, it seems that I don't have to redifine the relation in Account (this will result in a circular import).
Retrieving all the Accounts from the CompanySerializer is no problem,
but I need to be able to get all the Companies registered to an account as well.
This is my thinking:
Account Model:
class Account(AbstractBaseUser):
current_jobs = models.ManyToManyField(
Company, through='Employee') // I need to define current_jobs in some way
//,but this results in circular import
Company Model:
class Company(models.Model):
employees = models.ManyToManyField(
settings.AUTH_USER_MODEL, through='Employee')
Employee Model:
class Employee(models.Model):
class Meta:
unique_together = ('user', 'company')
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
company = models.ForeignKey(Company, on_delete=models.CASCADE)
is_admin = models.BooleanField()
The problem is, how will I now define how to get each of the lists in the serializers of the two.
So the Company Serializer, and the Account Serializer...
If i do not define current_jobs, I get an error stating that current_jobs is not defined, which it ofcourse is not.
I don't understand why you think you need to define current_jobs on Account. That is automatically provided for you via the reverse relationship as company_set; if you need it to be current_jobs you can set the related_name attribute.