Django ORM query by related name - django

I am trying to query all the customer of a particular seller/user
This is my sell model
class Sell(models.Model):
entry_for = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='sell_entry_for'
)
paid_by = models.ForeignKey(
User,
on_delete=models.CASCADE,
related_name='sell_customer',
null=True,
blank=True
)
and this is my query
user = User.objects.filter(
sell_entry_for__id=<user id>
)
and return empty but I have many entries for the user
Can anyone help me to fix this issue?

I believe you want something like:
user = User.objects.get(id=user_id_here)
# now you want to get all related Sell objects with that user
sells = user.sell_entry_for.all()
# now you can iterate over sells to get paid_by users
for sell in sells:
print(sell.paid_by)

Related

How to annotate a define a query in django with authenticated

I want to do a django filter that is ordered by authenticated user objects.
Something like this, but the authenticated users objects first in the returned queryset:
user = self.request.user
Company.objects.filter(status="approved")
The Company model has a foreign key user as below:
Company(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
null=True,
default=1,
on_delete=models.CASCADE,
related_name="companies",
)
Note: I am NOT trying to get just the user's objects.

Django admin - Allow inline entires once selected item

I am trying to populate a dropdown in the Django admin panel based on a selected item.
I have a customer model
class Customer(BaseModel):
name = models.CharField(max_length=128)
company = models.ForeignKey("Company", models.PROTECT)
def __str__(self) -> str:
return f"{self.name}"
def save(self, **kwargs):
return super().save(**kwargs)
An invite model
class Invite(BaseModel):
full_name = models.CharField(max_length=128, blank=True)
email = WIEmailField(unique=True)
customer = models.ForeignKey(
to="Customer",
on_delete=models.PROTECT,
related_name="invites",
)
Customer Invite model that defines the invite and customer
class CustomerLocationInvite(BaseModel):
location = models.ForeignKey(
to=Location
)
invite = models.ForeignKey(
to=Invite,
blank=True,
)
Location Model
class Location(BaseModel):
name = models.CharField(max_length=128)
company = models.ForeignKey(
to= Company,
on_delete=models.PROTECT
)
address = models.CharField(max_length=128)
Inline for invite
class CustomerInviteInline(admin.TabularInline):
model = CustomerLocationInvite
fields = ("invite", "location", "created_at", "modified_at")
readonly_fields = ("created_at", "modified_at")
extra = 0
When creating a new Invite, Is it possible to:
Display the inline once a company has been selected?
When selecting a location from the inline, Filter out the locations based on the company they selected?
I assume you want to get the location from CustomerLocationInvite, and get the company from the location? If that is so, maybe you can try
CustomerLocationInvite.objects.filter(location__id=locationid)
Where the location's id can be received by
locationid = Location.objects.filter(company__id=companyid).id
But then since you said that the company is already selected, you should have its ID -- that is the value you will replace companyid with. You can then retrieve the inline admin from the CustomerLocationInvite object that you received in the first line.
SO reference
Doc reference
I'm not sure what you mean. Are you getting the list of companies, or are you getting the list of locations? That being said, please paste your Company model as well.

How to go 2 layers deep reverse relations inside django models?

I'm using a single User model for authentication and creating multiple "type" accounts using the User model. Every type has a different dashboard so different things to show.
Organization
-> Teacher
-> Student
Q - I want to list the teachers and their corresponding students when inside a organization account ? It is a listView so I want to know how would I use revere relations to list all the students under specific teachers from an Organization account ?
class User(AbstractUser):
...
class Organization(models.Model):
user_id = models.OneToOneField(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='superAdmin')
...
class Teacher(models.Model):
user_id = models.OneToOneField(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='faculty')
super_admin_id = models.ForeignKey(
SuperAdmin, on_delete=models.CASCADE, related_name='faculty')
...
class Student(models.Model):
user_id = models.OneToOneField(
settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='user')
faculty_id = models.ForeignKey(
Teacher, on_delete=models.CASCADE, related_name='student')
...
If there's any advice on how I can improve the existing model schema, I'd like to know that as well.
You can fetch the teachers along with their students (in two db hits) like this:
teachers = Teacher.objects.filter(
user_id__superAdmin=request.user.superAdmin
).prefetch_related('student')
for teacher in teachers:
print(f'Teacher: {teacher.pk}')
for student in teacher.student.all():
print(f'Student: {student.pk}')
EDIT:
You can also annotate per teacher the number of students assigned to them so:
teachers = Teacher.objects.filter(
user_id__superAdmin=request.user.superAdmin
).annotate(num_students=Count('student'))
for teacher in teachers:
print(f'Teacher {teacher.pk} has {teacher.num_students} students')

How to store multiple userids in same model?

I'm fairly new to Django and attempting to store a model which will hold transaction information for a purchase between 2 users; a buyer and seller. I therefore want to store 2 UserIDs:
class Transaction(models.Model):
transactionid = models.AutoField(primary_key=True)
# USERID OF SELLER
# USER ID OF BUYER
orderid = models.ForeignKey('Order', db_column='orderid', on_delete=models.SET_NULL, blank=True, null=True)
status = models.CharField(choices=PAYMENT_STATUS, default='pending', max_length=50)
timestamp = models.DateTimeField(auto_now_add=True)
I want to use the following foreign key:
seller = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
)
however, to my understanding, this will only store the current logged in user which means I wouldn't be able to set both? Is there a better way to do this rather than modifying the model twice (esentially from both accounts)?
Any reccomendations or advice appreciated, thank you!
I hope, in the Transaction model you are going to add the entry if someone made any purchase. Buyer Id you can get from the current user. Seller ID yo can get from the product information.
For adding more than 1 foreign key with the same Model, you can use the related_name parameter.
class Transaction(models.Model):
transactionid = models.AutoField(primary_key=True)
seller = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='seller_name')
buyer = models.ForeignKey(CustomUser, on_delete=models.CASCADE, related_name='buyer_name')

In django how to model an invoice which can have multiple items?

I am using Django Rest Framework in the backend and Angular for frontend.
If the customer order multiple items, it should be be in a single invoice. For example, if the customer order apple, orange and banana, all these should be in a single invoice.
When the customer order again and it will be a new invoice.
class InvoiceItem(models.Model):
product = models.ForeignKey(
Product, on_delete=models.CASCADE, related_name='invoiceitems')
customer = models.ForeignKey(
Customer, on_delete=models.CASCADE, related_name='invoiceitems')
quantity = models.PositiveIntegerField()
date = models.DateTimeField(auto_now_add=True)
class Invoice(models.Model):
invoice_item = models.OneToOneField(
InvoiceItem, on_delete=models.CASCADE, related_name='invoice')
Now I have to link the InvoiceItem with Invoice. I thought about using post_save signals with InvoiceItem as a sender to create Invoice object and link it with the InvoiceItem.
#receiver(signals.post_save, sender=InvoiceItem)
def create_account(sender, instance, created, **kwargs):
Invoice.objects.update_or_create(invoice_item=instance)
How can I do it for multiple items?
Or there is a better way to implement my requirements?
There are multiple problems with your approach.
First of all, your
invoice_item = models.OneToOneField(
InvoiceItem, on_delete=models.CASCADE, related_name='invoice')
implies that there is only one type of Product per Invoice, i.e. having bought apples Customer can't buy also some oranges in the same Invoice.
If you try to fix that by creating ForeignKey on InvoiceItem, as others have already pointed out, you find yourself struggling with the second problem:
You attach InvoiceItem to a customer. Meaning that there's nothing stopping the system from creating a single Invoice that has 5 Oranges bought by Alice as an InvoiceItem and 7 Apples bought by Bob as another InvoiceItem, which seems wrong.
I would also move the date field from InvoiceItem to Invoice and rename it to timestamp, as I assume an Invoice is a set of Products bought together at one time.
You would end up with something like this:
class InvoiceItem(models.Model):
invoice = models.ForeignKey(
Invoice, on_delete=models.CASCADE, related_name='invoice_items')
product = models.ForeignKey(
Product, on_delete=models.CASCADE, related_name='invoice_items')
quantity = models.PositiveIntegerField()
class Invoice(models.Model):
customer = models.ForeignKey(
Customer, on_delete=models.CASCADE, related_name='invoices')
timestamp = models.DateTimeField(auto_now_add=True)
Now if you examine related_name arguments on the fields you'll see that you can do very useful things with your objects, like
customer = Customer.objects.get(id=<some_id>)
customer.invoices
will give you a QuerySet of all the Invoices belonging to a specific Customer. And having an Invoice object you can
invoice.invoice_items
to get all the items on an invoice
Or, for example, having a Product, say, 'apple', you can find all customers that ever bought an apple:
# Find all invoice_items for apple
inv_items = apple_product.invoice_items.all()
# Filter Customers
Customer.objects.filter(invoices__invoice_items__in=inv_items)
If you squint a little, you'll see that this whole structure is just a django M2M through relation (https://docs.djangoproject.com/en/3.0/topics/db/models/#extra-fields-on-many-to-many-relationships) and can be rewritten like this:
class InvoiceItem(models.Model):
invoice = models.ForeignKey(
Invoice, on_delete=models.CASCADE, related_name='invoice_items')
product = models.ForeignKey(
Product, on_delete=models.CASCADE, related_name='invoice_items')
quantity = models.PositiveIntegerField()
class Invoice(models.Model):
customer = models.ForeignKey(
Customer, on_delete=models.CASCADE, related_name='invoices')
timestamp = models.DateTimeField(auto_now_add=True)
products = models.ManyToManyField(Product, through='InvoiceItem')
So now you have Invoice directly connected to Products, and this connection also contains information about the quantity of this Product on this Invoice. I will leave the exploration of the benefits of this approach to the reader.
You should use a ForeignKey from the InvoiceItem to Invoice:
class InvoiceItem(models.Model):
...
invoice = models.ForeignKey(
Invoice, on_delete=models.CASCADE
)
...
You should be able to then remove the post_save. You'll create items like this:
>>> invoice = Invoice.objects.create(<insert arguments here>)
>>> invoice_item = InvoiceItem.objects.create(invoice=invoice, <insert other arguments>)
>>> invoice_item.invoice.pk == invoice.pk
[out] True (i.e., the invoice item is attached to the invoice)