Django query with ManyToMany relationship through - python

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))

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.

How to make an inner join query with same table using Django's ORM

The query to be implemented with ORM is as follows,
SELECT t2.*
FROM sub_menu AS t1
INNER JOIN sub_menu AS t2 ON (t1.sub_menu_id = t2.parent_sub_menu_id)
WHERE t1.sub_menu_id = 1;
The model is as follows,
class SubMenu(models.Model):
sub_menu_id = models.AutoField(primary_key=True)
menu = models.ForeignKey('commons.MainMenu', related_name='sub_menus', on_delete=models.CASCADE)
parent_sub_menu_id = models.IntegerField(blank=True, null=True)
name = models.CharField(max_length=50)
en_name = models.CharField(max_length=50, blank=True)
ord = models.IntegerField()
api = models.CharField(max_length=255, blank=True)
api_method = models.CharField(max_length=7, blank=True)
api_detail = models.CharField(max_length=255, blank=True)
menu_type_cd = models.CharField(max_length=5, blank=True)
menu_auth_type_cd = models.CharField(max_length=5)
is_common = models.BooleanField(default=False)
is_ns = models.BooleanField(default=False)
spc_auth = models.BooleanField(default=False)
spc_auth_cd = models.CharField(max_length=5, blank=True)
create_dt = models.DateTimeField(auto_now_add=True)
update_dt = models.DateTimeField(auto_now=True)
class Meta:
db_table = 'sub_menu'
unique_together = ('api', 'api_method',)
Not using a raw method, Is it possible to implement with Django's ORM?
Thank you.
You should do the relationship correctly on your model: https://docs.djangoproject.com/en/3.0/ref/models/fields/#module-django.db.models.fields.related. Then the parent_sub_menu should be:
class Submenu:
parent_sub_menu = models.ForeignKey('self', null=True, blank=True, on_delete=models.CASCADE)
Then run generate & DB migration. The query below should work.
And never declare relationship like you are doing right now, use Model instead via the documentation I sent.
Django does it for you already. You can just filter the related field.
https://docs.djangoproject.com/en/3.0/topics/db/queries/#lookups-that-span-relationships
SubMenu.objects.filter(parent_sub_menu__sub_menu_id=1)

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

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

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.

Categories

Resources