Checking if any object in a queryset is in a ManyToMany relation - python

What I'd like to be able to do is similar to this pseudo-code - I'm just completely unaware of how to do this in python:
user_groups = request.user.participant_groups.all()
if group in user_groups not in self.object.settings.groups.all():
Basically, I'd like to check if any of the objects in user_groups are in self.object.settings.groups.all(). Is there a simple way to do this?
Models:
class Group(models.Model):
participants = models.ManyToManyField('auth.User', null=True, blank=True, related_name='participant_groups')
title = models.CharField(max_length=180)
date = models.DateTimeField(null=True, blank=True, editable=False)
modified = models.DateTimeField(null=True, blank=True, editable=False)
class Settings(models.Model):
user = models.ForeignKey('auth.User', related_name='settings_objects')
groups = models.ManyToManyField('groups.Group', null=True, blank=True)
participants = models.ManyToManyField('auth.User', null=True, blank=True, related_name='accessible_objects')
private = models.BooleanField(default=True)
What I'm trying to do is check if any of a user's participant_groups (reverse relation to user on group model) are in a settings objects groups manytomany relation.

Try this -
common_groups = user.participant_groups.annotate(
num_settings=Count('settings_objects')
).filter(num_settings__gt=0)
# You can get a count like this
count_of_above = common_groups.count()
I'm assuming self.object.settings is an instance of Settings for the current user. You should make it clear.

Related

Django Model Instance as Template for Another Model that is populated by Models

I'm trying to create a workout tracking application where a user can:
Create an instance of an ExerciseTemplate model from a list of available Exercise models. I've created these as models so that the user can create custom Exercises in the future. There is also an ExerciseInstance which is to be used to track and modify the ExerciseTemplate created by the user, or someone else. I'm stripping the models of several unimportant fields for simplicity, but each contains the following:
class Exercise(models.Model):
# Basic Variables
name = models.CharField(max_length=128)
description = models.TextField(null=True, blank=True)
def __str__(self):
return self.name
class ExerciseTemplate(models.Model):
# Foreign Models
workout = models.ForeignKey(
'Workout',
on_delete=models.SET_NULL,
null=True,
blank=True
)
exercise = models.ForeignKey(
Exercise,
on_delete=models.SET_NULL,
null=True,
blank=True
)
recommended_sets = models.PositiveIntegerField(null=True, blank=True)
class ExerciseInstance(models.Model):
""" Foreign Models """
exercise_template = models.ForeignKey(
ExerciseTemplate,
on_delete=models.SET_NULL,
null=True,
blank=True
)
workout = models.ForeignKey(
'Workout',
on_delete=models.SET_NULL,
null=True,
blank=True
)
""" Fields """
weight = models.PositiveIntegerField(null=True, blank=True)
reps = models.PositiveIntegerField(null=True, blank=True)
Create a WorkoutInstance from a WorkoutTemplate. The WorkoutTemplate is made up of ExerciseTemplates. But the WorkoutInstance should be able to take the WorkoutTemplate and populate it with ExerciseInstances based on the ExerciseTemplates in the WorkoutTemplate. Here are the models that I have so far:
class WorkoutTemplate(models.Model):
name = models.CharField(max_length=128)
description = models.TextField(null=True, blank=True)
date = models.DateTimeField(null=True, blank=True)
#category...
exercises = models.ManyToManyField(
Exercise,
through=ExerciseTemplate
)
def __str__(self):
return self.name
class WorkoutInstance(models.Model):
# Foreign Models
workout_template = models.ForeignKey(
'WorkoutTemplate',
on_delete=models.SET_NULL,
null=True,
blank=True
)
But this is where I get stuck. I'm not sure how to proceed. My intuition is one of the following:
I need to create a more simple architecture to do this. I'll take any suggestions.
I need to create a method within the model that solves this issue. If this is the case, I'm not sure what this would actually look like.
When you create a new WorkoutInstance object which references a given WorkoutTemplate object you get all its related ExerciseTemplate objects.
Then you just create a new object (row) for each ExerciseInstance in another model (table)
If you link your ExerciseInstance to WorkoutInstance via 'workout' you could do something like:
wt = WorkoutTemplate.get(id=1)
wi = WorkoutInstance.create(workout_template=wt)
for e in wt.exercisetemplate_set.all:
ExerciseInstance.create(exercise_template=e, workout=wi)
You can implent this in the method that creates the new WorkoutInstance or take a look at signals
https://docs.djangoproject.com/en/4.0/topics/db/optimization/#create-in-bulk

Django multiple foreign key to a same table

I need to log the transaction of the item movement in a warehouse. I've 3 tables as shown in the below image. However Django response error:
ERRORS:
chemstore.ItemTransaction: (models.E007) Field 'outbin' has column name 'bin_code_id' that is used by another field.
which is complaining of multiple uses of the same foreign key. Is my table design problem? or is it not allowed under Django? How can I achieve this under Django? thankyou
DB design
[Models]
class BinLocation(models.Model):
bin_code = models.CharField(max_length=10, unique=True)
desc = models.CharField(max_length=50)
def __str__(self):
return f"{self.bin_code}"
class Meta:
indexes = [models.Index(fields=['bin_code'])]
class ItemMaster(models.Model):
item_code = models.CharField(max_length=20, unique=True)
desc = models.CharField(max_length=50)
long_desc = models.CharField(max_length=150, blank=True)
helper_qty = models.DecimalField(max_digits=10, decimal_places=4)
unit = models.CharField(max_length=10, blank=False)
def __str__(self):
return f"{self.item_code}"
class Meta:
verbose_name = "Item"
verbose_name_plural = "Items"
indexes = [models.Index(fields=['item_code'])]
class ItemTransaction(models.Model):
trace_code = models.CharField(max_length=20, unique=False)
item_code = models.ForeignKey(
ItemMaster, related_name='trans', on_delete=models.CASCADE, null=False)
datetime = models.DateTimeField(auto_now=False, auto_now_add=False)
qty = models.DecimalField(max_digits=10, decimal_places=4)
unit = models.CharField(max_length=10, blank=False)
action = models.CharField(
max_length=1, choices=ACTION, blank=False, null=False)
in_bin = models.ForeignKey(
BinLocation, related_name='in_logs', db_column='bin_code_id', on_delete=models.CASCADE, null=False)
out_bin = models.ForeignKey(
BinLocation, related_name='out_logs', db_column='bin_code_id', on_delete=models.CASCADE, null=False)
remarks = models.TextField(blank=True)
def __str__(self):
return f"{self.trace_code} {self.datetime} {self.item_code} {dict(ACTION)[self.action]} {self.qty} {self.unit} {self.in_bin} {self.out_bin}"
you have same db_column in two fields so change it
in_bin = models.ForeignKey(
BinLocation, related_name='in_logs', db_column='bin_code_id', on_delete=models.CASCADE, null=False)
out_bin = models.ForeignKey(
BinLocation, related_name='out_logs', db_column='other_bin_code', on_delete=models.CASCADE, null=False) /*change db_column whatever you want but it should be unique*/
If are linked to the same model name, You should use different related_name for each foreign_key filed . here is the exemple :
address1 = models.ForeignKey(Address, verbose_name=_("Address1"),related_name="Address1", null=True, blank=True,on_delete=models.SET_NULL)
address2 = models.ForeignKey(Address, verbose_name=_("Address2"),related_name="Address2", null=True, blank=True,on_delete=models.SET_NULL)
thank you for everyone helped. According to Aleksei and Tabaane, it is my DB design issue (broken the RDBMS rule) rather than Django issue. I searched online and find something similar: ONE-TO-MANY DB design pattern
In my case, I should store in bin and out bin as separated transaction instead of both in and out in a single transaction. This is my solution. thankyou.
p.s. alternative solution: I keep in bin and out bin as single transaction, but I don't use foreign key for bins, query both in bin and out bin for the bin selection by client application.

Django - limit choices to foreign key

I have the following model in Django
class Transfer(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT, limit_choices_to={'is_accepted':True})
amount = models.IntegerField(default=0)
transfer_date = models.DateTimeField(default=timezone.now)
company = models.ForeignKey(Company, on_delete=models.PROTECT)
I would like to filter the users based on is_accepted field. The problem is, that this field is declared in a model called Employee, which is in onetoone relationship with user.
Is there any possibility to reach Employee fields and filter them in this manner?
You can normally define a filter like:
class Transfer(models.Model):
user = models.ForeignKey(
User,
on_delete=models.PROTECT,
limit_choices_to={'employee__is_accepted': True}
)
amount = models.IntegerField(default=0)
transfer_date = models.DateTimeField(default=timezone.now)
company = models.ForeignKey(Company, on_delete=models.PROTECT)

Django Field Error cannot resolve keyword 'is_staff

Cannot resolve keyword 'is_staff' into field. Choices are: dob, experience, id,user, user_id
I get the above error when adding trainer as a Foreign Key to the Subscription model and then accessing any record for Subscription model from admin panel
class Subscription(models.Model):
client = models.OneToOneField(ClientProfile, on_delete=models.CASCADE)
trainer = models.ForeignKey(TrainerProfile, null=True, blank=True,
on_delete=models.SET_NULL, limit_choices_to={'is_staff': True})
plan = models.ForeignKey(Plan, on_delete=models.CASCADE)
transaction = models.OneToOneField(PaymentHistory, on_delete=models.CASCADE)
start_date = models.DateTimeField()
end_date = models.DateTimeField()
class TrainerProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dob = models.DateField(null=True)
experience = models.PositiveIntegerField(default=0)
You're trying to access an attribute is_staff, which does not exist on the TrainerProfile model. is_staff is an attribute of User, which you reference in your TrainerProfile model's user field.
In order to access this property, you need to "traverse" the relationship from Subscription -> TrainerProfile -> User. Django allows you to do this by using double-underscore notation, like this: some_fk_field__fk_field_attribute.
In your example, you need to change your limit_choices_to option on trainer to traverse the relationship to the user, like so:
class Subscription(models.Model):
client = models.OneToOneField(ClientProfile, on_delete=models.CASCADE)
trainer = models.ForeignKey(TrainerProfile, null=True, blank=True,
on_delete=models.SET_NULL, limit_choices_to={'user__is_staff': True})
plan = models.ForeignKey(Plan, on_delete=models.CASCADE)
transaction = models.OneToOneField(PaymentHistory, on_delete=models.CASCADE)
start_date = models.DateTimeField()
end_date = models.DateTimeField()
class TrainerProfile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
dob = models.DateField(null=True)
experience = models.PositiveIntegerField(default=0)
You are referencing the nested relationship in wrong way
class Subscription(models.Model):
# other fields
trainer = models.ForeignKey(TrainerProfile, null=True, blank=True,
on_delete=models.SET_NULL,
limit_choices_to={'user__is_staff': True})
That is, it should be user__is_staff instead of is_staff

Django query with ManyToMany relationship through

I am implementing the connect function like Linkedin.
My models are
class AFSUser(models.Model):
connects = models.ManyToManyField("self", through='UserConnect', symmetrical=False)
class UserConnect(models.Model):
date_followed = models.DateTimeField(auto_now_add=True)
date_approved = models.DateTimeField(null=True, blank=True)
date_rejected = models.DateTimeField(null=True, blank=True)
requester = models.ForeignKey(AFSUser, on_delete=models.CASCADE, related_name='request_user')
acceptor = models.ForeignKey(AFSUser, on_delete=models.CASCADE, related_name='accept_user')
I want to list connections of a user.
I tried
approved_connections = UserConnect.objects.filter(Q(requester=afsUser) | Q(acceptor=afsUser), date_aprroved__isnull=False,
date_rejected__isnull=False)
Then get the list from there.
Any other way so i can do
users = userA.connects.filter(...)
Yes, you can use through
userA.connects.through.objects.filter(Q(requester= userA)|Q(acceptor=userA))

Categories

Resources