Get the values from one app to another in Django - python

I am working on creating shopping cart. I am still in learning phase. I need to know how I can pass/use values from shop's models.py to cart's cart.py.
shop/models.py
class Product(models.Model):
delivery_price = models.DecimalField(max_digits=10, decimal_places=0,default=0)
support_price = models.DecimalField(max_digits=10, decimal_places=0,default=0)
cart/cart.py : I think this is the file where I need to get delivery_price and support_price. I dont know how I can get these two values. I want to add these prices and multiply it by quantity (something like Product.delivery_price + Product.support_price * item.quantity -> not sure about the way this is being done) How is this flow working? If anyone help me understand, it would be great.
class Cart(object):
def __init__(self, request):
def add(self, product, quantity=1,update_quantity=False, support_req=False):
"""
Add a product to the cart or update its quantity.
"""
product_id = str(product.id)
if product_id not in self.cart:
self.cart[product_id] = {'quantity': 0,
'price': str(product.price)}
if update_quantity:
self.cart[product_id]['quantity'] = quantity
else:
self.cart[product_id]['quantity'] += quantity
self.save()
def __iter__(self):
"""
Iterate over the items in the cart and get the products
from the database.
"""
product_ids = self.cart.keys()
# get the product objects and add them to the cart
products = Product.objects.filter(id__in=product_ids)
for product in products:
self.cart[str(product.id)]['product'] = product
for item in self.cart.values():
item['price'] = Decimal(item['price'])
item['total_price'] = item['price'] * item['quantity']
yield item
def __len__(self):
"""
Count all items in the cart.
"""
return sum(item['quantity'] for item in self.cart.values())
def get_total_price(self):
return sum(Decimal(item['price']) * item['quantity'] for item in self.cart.values())
I used code from https://github.com/twtrubiks/django-shop-tutorial

First you need to create an instance of your Product model. This is done by instantiate it like any other Python class (see documentation)
product = Product(100,10) #this is an example
Then you can add a product to a cart by using the built-in add method:
cart.add(product)
Note
You'll also need to instantiate a cart the same way as you did for your product.Then, you'll have access to other methods decalred in the Cart which handles calculating the total price.
And just for your understanding, you were asking how to Get the values from one app to another in Django. Well, in this case, since your passing a product object to cart via product parameter, you have acess to its attributes.

Related

Django and python, how to get a annotate from two different model?

I have the following model framework:
class Subcategory(models.Model):
nome=models.CharField()
class Order(models.Model):
order=models.CharField()
class Quantity(models.Model):
order=models.ForeignKey(Order)
subcategory=models.ForeignKey(Subcategory)
quantity=models.DecimalField()
class Price(models.Model):
order=models.ForeignKey(Order)
subcategory=models.ForeignKey(Subcategory)
price=models.DecimalField()
Now I want to obtain a new value that give me the possibility to filter for subcategory and order both price and quantity queryset and give me the moltiplication of them.
this is the code that I have set, but I don't know how obtain the price*quantity operation.
cod='1234'
price=dict()
defaults=list(0 for m in range(1))
filter_quantity = list(Quantity.objects.values_list('subcategory__id', flat=True).distinct()).filter(order__order=cod)
for subcategory__id, totals in(Price.objects.filter(
subcategoty__in=filter_quantity ).values_list('subcategory__id').annotate(totals=ExpressionWrapper(Sum(F('price')),
output_field=FloatField())).values_list('subcategory__id', 'totals'):
if subcategory__id not in price.keys():
price[subcategory__id ]=list(defaults)
index=0
price[subcategory__id][index]=totals
total_costs={'Costs': [sum(t) for t in zip(*price.values())]}
You can also make changes to this method according to your need.
def get_order_details(order_code):
order_details = []
quantities = Quantity.objects.filter(order__order=order_code)
prices_queryset = Price.objects.filter(order__order=order_code)
for quantity in quantities:
price = prices_queryset.filter(order__order=order_code, subcategory=quantity.subcategory).first()
if price:
order_details.append({
'subcategory_name': quantity.subcategory.nome,
'quantity': quantity.quantity,
'unit_price': price.price,
'total_price': quantity.quantity * price.price
})
return {
'order_code': order_code,
'details': order_details
}

how to get value from odoo purchase lines order to stock.picking lines?

i'am added analytic account field on stock move model, I need as the stock move lines get quantity form PO lines to get analytic account field form lines when I confirm the order,
how can I do that
class StockMove(models.Model):
_inherit = "stock.move"
analytic_account_id = fields.Many2one(string='Analytic Account',comodel_name='account.analytic.account',)
any help will be appreciated
Override _prepare_stock_moves method which prepares the stock moves data for one order line and returns a list of dictionary ready to be used in stock.move's create().
class PurchaseOrderLine(models.Model):
_inherit = 'purchase.order.line'
#api.multi
def _prepare_stock_moves(self, picking):
res = super(PurchaseOrderLine, self)._prepare_stock_moves(picking)
res[0]['analytic_account_id'] = self.account_analytic_id.id
return res
To get field values from purchase order use the inverse_name order_id.
res[0]['analytic_account_id'] = self.order_id.account_analytic_id.id
Edit:
To use the same logic on production orders, you can set the account when you mark the order as done:
class ManufacturingOrder(models.Model):
_inherit = 'mrp.production'
analytic_account_id = fields.Many2one(string='Analytic Account', comodel_name='account.analytic.account')
#api.multi
def button_mark_done(self):
for order in self:
for move in order.move_finished_ids:
move.analytic_account_id = order.analytic_account_id
res = super(ManufacturingOrder, self).button_mark_done()
return res

Implementing "Save For Later" functionality in a Django Shopping Cart App?

I'm trying to teach myself Django by using it to make an e-commerce site. I'm working on the shopping cart right now; it's implemented using Django sessions and it's currently working fine, but I'm having trouble implementing the "save for later" functionality that you can find on lots of online stores (i.e. Amazon or whatever) that allows users to remove items from their shopping cart and instead put them on a list that allows them to easily see it from their shopping cart page. Before I go on, here's the views.py and cart.py for my current shopping cart:
cart.py:
from decimal import Decimal
from django.conf import settings
from bookDetails.models import Book
# This is the cart class.
class Cart(object):
# Constructor method for the class - includes a request parameter
def __init__(self, request):
# Start by creating a session for the new cart
self.session = request.session
userCart = self.session.get(settings.CART_SESSION_ID)
if not userCart:
userCart = self.session[settings.CART_SESSION_ID] = {}
self.userCart = userCart
def save(self):
self.session.modified = True
def add(self, book, amount=1, change_amount=False):
book_id = str(book.id)
if book_id not in self.userCart:
self.userCart[book_id] = {'amount': 0,
'author': book.book_author,
'author_bio': book.author_bio,
'description': book.book_description,
'genre': book.book_genre,
'publishing_info': book.publishing_info,
'avg_rating': str(book.avg_rating),
'price': str(book.price)}
if change_amount:
self.userCart[book_id]['amount'] = amount
else:
self.userCart[book_id]['amount'] += amount
self.save()
def remove(self, book):
book_id = str(book.id)
if book_id in self.userCart:
del self.userCart[book_id]
self.save()
def __iter__(self):
book_ids = self.userCart.keys()
books = Book.objects.filter(id__in=book_ids)
cart = self.userCart.copy()
for book in books:
cart[str(book.id)]['book'] = book
for book in cart.values():
book['price'] = Decimal(book['price'])
book['total_price'] = book['price'] * book['amount']
yield book
def __len__(self):
return sum(book['amount'] for book in self.userCart.values())
def get_total_price(self):
return sum((book['price'] * book['amount']) for book in self.userCart.values())
def clear(self):
del self.session[settings.CART_SESSION_ID]
self.save()
Views.py:
from django.shortcuts import render, redirect, get_object_or_404
from django.views.decorators.http import require_POST
# This is the Book model from the bookDetails package I made.
from bookDetails.models import Book
# These are the cart and cart forms.
from .cart import Cart
from .forms import AddToCartForm
#require_POST
def addToCart(request, book_id):
userCart = Cart(request)
book = get_object_or_404(Book, id=book_id)
form = AddToCartForm(request.POST)
if form.is_valid():
data = form.cleaned_data
userCart.add(book=book,
amount=data['amount'],
change_amount=data['change_amount'])
return redirect('cart:cart_info')
def removeFromCart(request, book_id):
userCart = Cart(request)
book = get_object_or_404(Book, id=book_id)
userCart.remove(book)
return redirect('cart:cart_info')
def cart_info(request):
userCart = Cart(request)
for current in userCart:
current['update_amount_form'] = AddToCartForm(
initial={'amount': current['amount'],
'change_amount': True}
)
return render(request, 'cart/info.html', {'userCart': userCart})
# This view displays the checkout page
def checkout(request):
userCart = Cart(request)
userCart.clear()
return render(request, 'cart/checkout.html', {'userCart': userCart})
So given the way I have the cart set up, what would be the simplest/most efficient way to set up a "save for later" functionality? What I originally tried to do is create another class just like the Cart class, except it was called SFLList (Saved For Later List), then just copy-pasted most of the code from the cart class right onto it and adjusted them for a simple list, something like this
class SFLList(object):
def __init__(self, request):
self.session = request.session
SFL = self.session.get(settings.SFL_SESSION_ID)
if not SFL:
SFL = self.session[settings.SFL_SESSION_ID] = {}
self.SFL = SFL
# Below this would go functions like addSFL, removeSFL,
# and the __iter__ function, all redefined to work with SFLList
...But this ended up giving me a TypeError because "the Decimal type is not JSON serializable" or something to that effect, which may look like it has something to do with the way I converted the price attribute to str so it could be serializeable (in the add function of the cart class), but the code and site work perfectly fine as they are. It only broke down and gave me that error when I added the SFLList code and tried to integrate it.
I spent all day yesterday trying to get my new SFLList class to work, but to no avail. I ended up just discarding the changes and reverting to my latest commit (before I made SFLList and the changes related to it). As expected, no TypeError because of Decimal when it's just the Cart class, defined exactly as I've got it here.
I feel like there has to be an easier way to do this. All "saved for later" has to do is just the exact same thing my cart already does, but without having the book inside the cart. Ordinarily what I'd do in a language like Java or C++ or whatever is just create an array and move the "Book" instances into it, then just iterate through the array and print all of each book's attributes in order. This is my first time working with something like Django where things are seemingly mostly done through database queries. It doesn't seem I like I can really use an array or list to store things inside models - the closest thing I found was something called an ArrayField, but apparently it requires that your database be "Postgres", which I don't think my project is using (settings.py has the DB set up as sqlite3).
What's the best way to do this?
I think you can simplify the problem just setting a boolean variable in your cart item dictionary like this
self.userCart[book_id] = {
'amount': 0,
'author': book.book_author,
'author_bio': book.author_bio,
'description': book.book_description,
'genre': book.book_genre,
'publishing_info': book.publishing_info,
'avg_rating': str(book.avg_rating),
'price': str(book.price),
'for_later': False # saved for later if True
}
If you want only the books in the cart you can:
def get_books_ids(self):
books_ids = []
for key, val in self.userCart.items():
if not val['for_later']:
books_ids.append(key)
return books_ids
def __iter__(self):
book_ids = self.get_books_ids()
books = Book.objects.filter(id__in=book_ids)
cart = self.userCart.copy()
for book in books:
cart[str(book.id)]['book'] = book
for book in cart.values():
book['price'] = Decimal(book['price'])
book['total_price'] = book['price'] * book['amount']
yield book

How to filter django model based on other non related model

Please refer to the code below
Transaction models
class Transaction(models.Model)
current_product_code = models.CharField(....)
previous_product_code = models.CharField(....)
#property
def status(self):
c_price = Product.objects.get(code=self.current_product_code).price
p_price = Product.objects.get(code=self.previous_product_code).price
if c_price == p_price:
return "Due"
elif c_price > p_price:
return "Upgrade"
else:
return "Downgrade"
Product model
class Product(models.Model):
code = models.CharField(....)
price = models.DecimalField(....)
My question: How can i obtain/filter transactions with upgrade/downgrade/due status. I am trying to create a custom admin filter which filter transaction based on their status but i fail what to put inside .filter() , check the method below
def queryset(self, request, queryset):
value = self.value()
if value == 'Upgrade':
return queryset.filter(***** HERE *****)
elif value == 'Downgrade':
return queryset.filter(***** HERE *****)
elif value == 'Unknown':
return queryset.filter(***** HERE *****)
return queryset
You really should use ForeignKey between Product and Transaction (for both: current_product_code and previous_product_code). This will allow you to use those relations in your querysets with ease.
My proposed models structure looks like this:
class Product(models.Model):
code = models.CharField(....)
price = models.DecimalField(....)
class Transaction(models.Model)
# You have to define related_name for at least one of relations below.
# Without that, automatically generated ones will clash.
# Also don't foget to change `on_delete` to suit your needs.
current_product = models.ForeignKey(Product, related_name="current_transactions", on_delete=models.CASCADE)
previous_product = models.ForeignKey(Product, related_name="previous_transactions", on_delete=models.CASCADE)
#property
def status(self):
# also, no need to do additional queries here manually. You can improve
# it further by using `select_related` when querying for transactions.
c_price = self.current_product.price
p_price = self.previous_product.price
if c_price == p_price:
return "Due"
elif c_price > p_price:
return "Upgrade"
else:
return "Downgrade"
With that model structure, finding specific types of transactions will be easier:
upgrade_transactions = Transaction.objects.filter(current_product__price__gt=F('previous_product__price'))
downgrade_transactions = Transaction.objects.filter(current_product__price__lt=F('previous_product__price'))
due_transactions = Transaction.objects.filter(current_product__price=F('previous_product__price'))
I think you could try to use Subquery, OuterRef and .annotate():
if value == 'Upgrade':
return queryset.annotate(
previous_price=Subquery(
Product.objects.filter(
code=OuterRef("previous_product_code")
)[:1]
),
current_price=Subquery(
Product.objects.filter(
code=OuterRef("current_product_code")
)[:1]
),
).filter(current_price__gt=F("previous_price"))
...
Remember that filter() operation, in the end, is a SQL operation and we should take care on the performance issues.
So my advice is: if you need to filter by status, update the status on Product model everytime a transaction is saved. Your application will be faster and will have a cleaner code.

Python: adding multiple products and prices to a shopping cart

I am completing the Python course on CodeAcademy (I am running the code on my computer not CodeAcademy) and wrote this piece of code to add items to a shopping cart. The shopping cart is a dictionary.
class ShoppingCart(object):
"""Creates shopping cart objects
for users of our fine website."""
items_in_cart = {}
def __init__(self, customer_name):
self.customer_name = customer_name
def add_item(self, product, price):
"""Add product to the cart."""
if not product in self.items_in_cart:
self.items_in_cart[product] = price
print product + " added."
else:
print product + " is already in the cart."
my_cart = ShoppingCart("Amy")
my_cart.add_item("Nishiki Modulus", "$400")
print my_cart.items_in_cart
This code works and returns:
Nishiki Modulus added.
{'Nishiki Modulus': '$400'}
But I would like to figure out how to add several items (and prices) at the same time. I have been experimenting with no luck.
I ran
class ShoppingCart(object):
items_in_cart = {}
def __init__(self, customer_name):
self.customer_name = customer_name
def add_item(self, product, price):
"""Add product to the cart."""
for products in product:
if not products in self.items_in_cart:
self.items_in_cart[products] = price
print "added."
else:
print "Product is already in the cart."
my_cart = ShoppingCart("Amy")
my_cart.add_item(["Nishiki Modulus", "Trek 700", "Fuji Sportif"], ["$400", "$450", "$700"])
print my_cart.items_in_cart
As I predicted, the keys are right but not the values. This returns:
added.
added.
added.
{'Trek 700': ['$400', '$450', '$700'], 'Fuji Sportif': ['$400', '$450', '$700'], 'Nishiki Modulus': ['$400', '$450', '$700']}
I'm think something along the lines of:
for products, price in product.items():
but then I can't figure out how to correctly add the list to items_in_cart
Can anyone point me in the right direction? Please let me know if anything is unclear.
You can zip the products and their prices like this
for prod, money in zip(product, price):
if not prod in self.items_in_cart:
self.items_in_cart[prod] = money
print "added."
else:
print "Product is already in the cart."
This will zip the product and price list and give one value at a time from both the lists. So we will get the product and its corresponding price.
Or you can iterate the product list, and get the corresponding value from the price with the index, like this
for index, prod in enumerate(product):
if not prod in self.items_in_cart:
self.items_in_cart[prod] = price[index]
print "added."
else:
print "Product is already in the cart."
You can try with simple-indexed for loop:
def add_item(self, product, price):
"""Add product to the cart."""
for index in range(len(product):
if not product[index] in self.items_in_cart:
self.items_in_cart[product[index]] = price[index]
print "added."
else:
print "Product is already in the cart."
Or use zip:
for products, prices in zip(product, price):
You are setting the value of items_in_cart[product] to price, a list of strings. You should be setting it to one of the strings in price.
Here's the ShoppingCart class fixed:
class ShoppingCart(object):
items_in_cart = {}
def __init__(self, customer_name):
self.customer_name = customer_name
def add_item(self, product, price):
"""Add product to the cart."""
priceIndex = 0
for products in product:
if not products in self.items_in_cart:
self.items_in_cart[products] = price[priceIndex]
print "added."
else:
print "Product is already in the cart."
priceIndex += 1
I would add a new method add_items that calls your original add_item in a loop. This will keep your code cleaner and easier to work with.
class ShoppingCart(object):
items_in_cart = {}
def __init__(self, customer_name):
self.customer_name = customer_name
def add_item(self, product, price):
"""Add product to the cart."""
if not product in self.items_in_cart:
self.items_in_cart[product] = price
print product + " added."
else:
print product + " is already in the cart."
def add_items(self, products, prices):
for product, price in zip(products, prices):
self.add_item(product, price)
my_cart = ShoppingCart("Amy")
my_cart.add_items(["Nishiki Modulus", "Trek 700", "Fuji Sportif"], ["$400", "$450", "$700"])
print my_cart.items_in_cart

Categories

Resources