Reputation: 1660
I'm stuck in a design problem.
Imagine an order model and material model,
class Material(models.Model):
material_code = models.CharField(max_length=40)
standard_price = models.DecimalField()
class Order(models.Model):
customer = models.ForeignKey(Customer)
class OrderItems(models.Model):
order = models.ForeignKey(Order)
material = models.ForeignKey(Material)
price = models.DecimalField()
In my order detail view, I populate orderitems using inlineformset_factory(Order, OrderItems)
The behaviour I want is below:
If user wants to add more orderitems, I want to redirect user to material listing page, user chooses one or more materials and confirms, then I redirect him to order detail view again with newly added items.
I'm thinking of these to work, like django admin actions with intermediate page.
Before adding new orderitems to order, I also want to set orderitems price from material standard_price, this means I want to modify formsets values before adding it to orderitems.
The Question I want to ask here is, is there an easier way to achieve this, it seems, storing formset data to session and repopulating formset with new items wont be easy. I would consider another solutions to this problem.
I dont want the js solutions by the way, imagine a field on my order model, grandtotal, which I may calculate total item amount, with js solutions I may have to do calculation logic with js, user adds an item and change prices then I do the grandtotal calculation with js. This is what I want to avoid.
Upvotes: 4
Views: 1423
Reputation: 145
You can simplify this by "Adding dynamic fields" to your Order detail form.
When user click an "Add material" button, a new field (say a <select>
field with list of materials) will be added to form.
This link will help you to achieve your task.
Upvotes: 0
Reputation: 4516
From the description you've provided, I don't find the usecase for formsets.
You simply require a material catalogue page, which lists materials. Each material has the ability to be added to the current cart, which is done through Ajax calls to a url that passes along the PK of the material, so the view for that Ajax call can create the cart and add that material to the cart.
Finally you need an OrderDetail, you need a forloop on all the materials, currently in the users cart.
You can first implement it completely in the database, with relational models defined for Cart, which is actually the Order class you've already implemented.
Then you can add a caching layer to limit writes/deletes/updates to the DB, using django session (this has a performance gain, considering you don't store sessions in your DB!)
So all in all, what you probably need is (I'm using the notion of a cart, to imply that the user can have only one active order, if your requirements are different, it's easily changeable to what you've provided with your Order model):
Data model:
class Cart:
user = 1to1(User, related_name='cart') # Can be a FK if you want several active orders
is_approved = Boolean # To tell if the order is considered Over or is still open
created_at = DateTimeField
...
class Material:
orders = M2M(Cart, through=CartItem)
...
class CartItem:
cart = FK(Cart)
material = FK(Material)
...
Views:
def list_materials(request):
returns_rendered_template_with(materials=Material.objects.all())
#Ajax method, called via Post, you probably need a way to also delete, or edit (number of materials to order ?)
def add_material_to_cart(request, material_id):
cart = Cart.objects.get_or_create(user=request.user)
material = Material.objects.get(pk=material_id)
CartItem(material=material, cart=cart).save()
def order_detail_view(request):
cart = user.cart
cart_items = CartItems.objects.filter(cart=cart)
returns_rendered_template_with(cart_items=cart_items)
Templates:
material_list.html
....
{% for item in materials %}
{% show material info, such as price, availability, ... %}
<button data-pk={{item.pk}}, ...> to call add_material_to_cart </button>
{% endfor %}
order_detail.html
....
{% for item in cart_items %}
{% show each cart_item.material and its information + whatever you need %}
{% endfor %}
It's the most basic form you can implement the thing, I understood, you want.
You can merge several of this views together, you can implement a "fully in session" solution with no need for a Cart and CartItem model, just finalized orders get persisted into I don't know, Bill models ? OrderHistory ? whatever you want.
Upvotes: 5
Reputation: 29967
If I understand your intentions correctly, you want to create some kind of shopping cart and you do not want to create a database entry for orders that have not been placed yet.
My suggestions would be to create a Cart
model and an CartItem
model that stores the information about what items are currently in the cart.
When the customer places the order, you create an new Order
instance with the corresponding OrderItem
instances from the cart and its items, and persist that.
Cart
and CartItem
can either be Django models stored in the DB, or plain Python classes and serialize them for storage in the session.
Storing them in the DB is probably easier, but will most likely generate more DB queries than a session-based solution. Whether that is an issue depends on your server capacities and expected load. This approach is implemented in Oscar, an e-commerce framework for Django.
Upvotes: 1