How to model many-to-many database with 3 tables - python

I'm working on a django backend and I'm trying to model a database and want to do it the best practice way.
I need a "User" table, a "Portfolios" table and a "Stocks" table. A user can have multiple portfolios which consist of multiple stocks.
This is my code so far:
class User(models.Model):
user_id = models.AutoField(primary_key=True)
username = models.CharField(max_length=25)
in_cash = models.DecimalField(max_digits=15, decimal_places=2)
class Portfolios(models.Model):
portfolio_id = models.AutoField(primary_key=True)
user_id = models.ForeignKey("User", on_delete=models.CASCADE)
stock_id = models.ForeignKey("Stocks", on_delete=models.CASCADE)
buy_datetime = models.DateTimeField(default=datetime.now, blank=True)
number_of_shares = models.IntegerField()
class Stocks(models.Model):
stock_id = models.AutoField(primary_key=True)
stock_symbol = models.CharField(max_length=12)
In my head I would have an entry in the "Portfolios" table for each stock of a portfolio.
So "Portfolios" would look like
portfolioid 1, userid: 1, stockid: 1, buydate: 12.01.2019, shares: 20
portfolioid 1, userid: 1, stockid: 2, buydate: 19.02.2020, shares: 41
So there is one portfolio, which contains two stocks. But overall that doesn't seem right. If used like in my example above I can't have the portfolioid as a primary key, how can I improve this?
Thanks for your time

What confused me is the name portfolio, which I would call position. Your initial code was correct, although I changed it a bit, removing AutoField which is probably not needed, using a OneToOneField to connect a Customer to a User, removing the s at the end of class names, which are templates, and therefore should be singular, nor plural, adding price to the Stock. And finally changing Portfolio, which should be the sum of all the Positions.
from django.conf import settings
class Customer(models.Model):
customer = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE,)
in_cash = models.DecimalField(max_digits=15, decimal_places=2)
def __str__(self):
return self.customer.username
class Position(models.Model):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
stock = models.ForeignKey('Stock', on_delete=models.CASCADE)
number_of_shares = models.IntegerField()
buy_datetime = models.DateTimeField(default=datetime.now, blank=True)
def __str__(self):
return self.customer.customer.username + ": " + str(self.number_of_shares) + " shares of " + self.stock.stock_symbol
class Stock(models.Model):
stock_symbol = models.CharField(max_length=12)
price = models.DecimalField(max_digits=10, decimal_places=2)
def __str__(self):
return self.stock_symbol
In my head I would have an entry in the "Portfolios" table for each
stock of a portfolio. So "Portfolios" would look like
portfolioid 1, userid: 1, stockid: 1, buydate: 12.01.2019, shares: 20
portfolioid 1, userid: 1, stockid: 2, buydate: 19.02.2020, shares: 41
So there is one portfolio, which contains two stocks. But overall that
doesn't seem right. If used like in my example above I can't have the
portfolioid as a primary key, how can I improve this?
You are correct, except that should be applied to a Position, each of which is unique, not the Portfolio, which is all the Positions the Customer has.

As in usual many-to-many cases, you will need to create an intermediary table which is also called junction table/association table. Associative Entity
Your users are going to have several portfolios:
class UserPortfolio(models.Model):
user_id = models.ForeignKey("User")
portfolio_id = models.ForeignKey("Portfolio")
The portfolios will have multiple stocks in them:
class PortfolioStock(models.Model):
portfolio_id = models.ForeignKey("Portfolio")
stock_id = models.ForeignKey("Stock")
Now a user can have several portfolios, and those portfolios will include several stocks. In order to get access to the corresponding stocks for a user, you will need to join the tables.

Related

How can I fetch data by joining two tables in django?

Looking for help got stuck at a point, I am new to python and django. There ARE two payments corresponding to one order, one COLLECTION and multiple TRANSFER and i need the payment corresponding to an order whose direction is COLLECTION only NOT transfered yet so that i can initiate TRANSFER against that order
models.py
class Orders(models.Model):
id= models.AutoField(primary_key=True)
payment_gateway_code = models.CharField(max_length=20,choices=[('PAYTM','PAYTM')])
is_active = models.BooleanField(default=True)
class Payments(models.Model):
id = models.AutoField(primary_key=True)
orders = models.ForeignKey(Orders, on_delete=models.CASCADE)
direction = models.CharField(max_length=20,choices=[('COLLECTION','COLLECTION'),
('TRANSFER','TRANSFER')])
settlement_status = models.CharField(max_length=50,blank=True, null=True,choices=[('YES','YES'),
('NO','NO')])
is_active = models.BooleanField(default=True)
qualified_orders = Orders.objects.filter(payment_gateway_code='CASHFREE',
Exists(Payments.objects.filter(order=OuterRef('pk'), direction='COLLECTION',
settlement_status='YES')), ~Exists(Payments.objects.filter(order=OuterRef('pk'),
direction='TRANSFER')))
But above query is not working
What is OuterRef('pk')?
First, I'd suggest changing orders to order.
Then, the query you're trying to achieve will be something like this (Assuming order_id contains the ID of the order):
Paymen.objects.filter(order_id=order_id, direction="COLLECTION")
You can use views.py for that as follows
Models.py
class Orders(models.Model):
id= models.AutoField(primary_key=True)
payment_gateway_code = models.CharField(max_length=20,choices=[('PAYTM','PAYTM')])
is_active = models.BooleanField(default=True)
class Payments(models.Model):
id = models.AutoField(primary_key=True)
orders = models.ForeignKey(Orders, on_delete=models.CASCADE)
direction = models.CharField(max_length=20,related_name="direction",choices=[('COLLECTION','COLLECTION'),
('TRANSFER','TRANSFER')])
settlement_status = models.CharField(max_length=50,blank=True, null=True,choices=[('YES','YES'),
('NO','NO')])
is_active = models.BooleanField(default=True)
views.py
from App.models import orders, payments
#in case if you need objects of order this is for that
def orderfunc():
order = Orders.objects.all()
def paymentfunc():
payment = Payment.objects.all()
# from here you can check for what record you want using conditional operator
#if direction == COLLECTION:
#then do what you need

Django sum on an external attribut

I have an issue with database and sum in Django.
I have 3 tables: customer, order and orderLine.
For a report, I would like to calculate the sum of all line price for every order of a customer.
class Customer(models.Model):
firstName = models.CharField(max_length=200)
lastName = models.CharField(max_length=200)
mail = models.EmailField(max_length=100)
etc...
def get_count_of_orders(self):
return self.orders.count()
def get_sum_line_prince_of_all_orders(self):
???????????
return (sum_of_all_line_prince_all_orders)
class Order(models.Model):
orderNum = models.CharField(max_length=200)
customer = models.ForeignKey(Customer, related_name="orders")
globalAmount = models.DecimalField(max_digits=20, decimal_places=4)
...
class OrderLine(models.Model):
order = models.ForeignKey(Order, related_name="ordersLines")
linePrice = models.DecimalField(max_digits=20, decimal_places=4)
...
I don't know what to set in get_sum_of_orders to get the right result.
I've trayed different things like annotate or aggregate.
But without success at the moment.
I didn't understand this process at the moment.
Could you help me?
You can access all orders with:
self.orders.all()
and you can iterate them with:
sum = 0
for each_order in self.orders.all():
sum += each_order.globalAmount
return sum
Here I presume that globalAmount is the amount you need to calculate.
If you need to reach OrderLine -> linePrice through each customer and calculate the sum of the linePrice(s) of each customer, try the following:
for each_order in self.orders.all(): #each order
for each_OrderLine in each_order.ordersLines.all()
sum += each_OrderLine.linePrice
Or you could use list comprehension:
sum([myorder.linePrice for myorder in order.ordersLines.all() for order in self.orders.all()])

Combining django queryset objects having similar values

It's an eCom site that I am working on. I need to filter results based on email, as in obtain all those orders placed by this email account. On executing the query OrderItem.objects.filter(order__email='john#john.com').select_related('order') I get queryset object for each product. So if an order had multiple products then each product is a set on it's own. What I need is all items combined in the one set for an order ID so that I can iterate through them in the template.
OrderItem.objects.filter(order__email='john#john.com').select_related('order').values('order_id').annotate(ct=Count('pk')) - This query gets all those IDs and shows how many sets are there per ID. Likewise I just need the results but with other fields like created_date, order_id, transaction_id, product.
I have explored the django documentation but don't seem to find an appropriate solution. Another option is to manually loop through each item in the queryset and segregate based on the order_id having multiple items but then again that doesn't seem efficient. So any thoughts on how to proceed would be great.
Models are below:
class Order(models.Model):
payment_date = models.DateTimeField(blank=True, null=True)
created_date = models.DateTimeField(auto_now_add=True)
transaction_id = models.CharField(max_length=256, blank=True, null=True)
email = models.EmailField(max_length=128, blank=True, null=True)
def __str__(self):
return "Order #{},Date:{}".format(self.id,self.created_date)
class OrderItem(models.Model):
product = models.ForeignKey(PRODUCT_VARIANT_MODEL, on_delete=models.DO_NOTHING)
quantity = models.IntegerField(default=1)
order = models.ForeignKey(Order, related_name='items', on_delete=models.CASCADE)
def __str__(self):
return "Quantity:{},Item:{},{}".format(self.quantity, self.product.get_product_title(),self.order)
Result of query 1:(There are 2 orders placed by this email ID) OrderItem.objects.filter(order__email='john#john.com').select_related('order')
<QuerySet [<OrderItem: Quantity:3,Item:sampleitem 1,Order #21,Date:2019-06-07 06:14:22.929052+00:00>, <OrderItem: Quantity:1,Item:sampleitem 91,Order #22,Date:2019-06-24 05:33:16.509479+00:00>, <OrderItem: Quantity:2,Item:sampleitem 44,Order
#22,Date:2019-06-24 05:33:16.509479+00:00>, <OrderItem: Quantity:1,Item:sampleitem 8,Order #22,Date:2019-06-24 05:33:16.509479+00:00>]>
Result of query 2:
OrderItem.objects.filter(order__email='john#john.com').select_related('order').values('order_id').annotate(ct=Count('pk'))
<QuerySet [{'order_id': 21, 'ct': 1}, {'order_id': 22, 'ct': 3}]>

Django Cannot assign "'Pizza'": "Order.Food_Name" must be a "Foods" instance

Hello Guys I am working on a restaurant project which allow user to select food item and book an order but i am getting this error as i try to book an order
"Django Cannot assign "'Pizza'": "Order.Food_Name" must be a "Foods" instance."
I am using drop down menu to select food items i am using django version 2.1.5 . Please Help
views.py
def place_order(request):
name = request.POST["user"]
food_items = request.POST['food_item']
qty = request.POST['qty']
rating = request.POST['ratings']
price = Foods.Food_Price
order = Order(Date=datetime.date, Name_of_Person=name,Food_Name=food_items, Qty=qty, Total=price, Ratings=rating)
order.save()
return render(request, "index.html")
model.py
from django.db import models
class Foods(models.Model):
Food_Number = models.IntegerField(null=False,)
Food_Name = models.CharField(max_length=30, primary_key=True, null=False)
Food_Qty = models.CharField(max_length=10)
Food_Price = models.IntegerField()
def __str__(self):
return f"{self.Food_Number} - {self.Food_Name} {self.Food_Price}"
class Order(models.Model):
Order_id = models.AutoField(null=False, primary_key=True)
Date = models.DateField()
Name_of_Person = models.CharField(null=False, max_length=40)
Food_Name = models.ForeignKey(Foods, on_delete=models.CASCADE)
Qty = models.CharField(max_length=10)
Total = models.IntegerField()
Ratings = models.IntegerField()
def __str__(self):
return f"{self.Order_id} - {self.Name_of_Person} |{self.Food_Name} |{self.Total}"
What can i do solve this error
Problem is in your Order model Food_Name is foreign-key field. So you need to assign model-instance which is Food in this case to this field. But you are assigning food_items = request.POST['food_item'] which is suppose to be food_name string i guess. That is why this error raise. I don't think your model is properly design. Food_Name is not an unique id field in Food model rather in your Order table you would like to have Food not Food_name.

SQLite Django Model for Inventory of Seeds

I'm trying to build an Inventory Model for a Django App that handles the sale of seeds. Seeds are stored and sold in packs of 3, 5, or 10 seeds of a single variety (for example: 3 pack of mustard seeds).
I want to add x amount of products to inventory with a price for each entry, and sell that product at that price for as long as that entry has items left(quantity field > 0) even if later entries have been made for the same product and presentation but at a different price, so i have the following model:
class Product(models.Model):
name = models.CharField(max_length=100)
class Presentation(models.Model):
seed_qty = models.IntegerField()
class Stock(models.Model):
date = models.DateField(auto_now=True)
quantity = models.IntegerField()
product = models.ForeignKey(Product, on_delete=models.CASCADE)
presentation = models.ForeignKey(Presentation, on_delete=models.CASCADE)
cost = models.FloatField(null=True, blank=True)
sell_price = models.FloatField(null=True, blank=True)
I'm wondering if I should actually relate Product and Stock with a ManyToMany field through a GeneralEntry intermediate model in which I'd store date_added, presentation and cost/price.
My issue is that when I add multiple Stock entries for the same product and presentation, I can't seem to query the earliest prices for each available (quantity>0) stock entry for each product.
What I've tried so far has been:
stock = Stock.objects.filter(quantity__gt=0).order_by('-date')
stock = stock.annotate(min_date=Min('date')).filter(date=min_date)
But that returns that max_date isn't defined.
Any ideas on how to query or rearrange this model ?
Thanks!
*** UPDATE : I wasn't using F() function from django.db.models.
Doing it like this works:
stock = Stock.objects.filter(quantity__gt=0).order_by('-date')
stock = stock.annotate(min_date=Min('date')).filter(date=F('min_date'))
Turns out I wasn't using F() function from django.db.models.
Doing it like this works:
stock = Stock.objects.filter(quantity__gt=0).order_by('-date')
stock = stock.annotate(min_date=Min('date')).filter(date=F('min_date'))

Categories