Database: new table or new field for order attributes - django

Current table schema:
User
- id
- default_handling_fee(integer, null=True)
Order
- id
- buyer(user_id)
- order_handling_fee(integer, null=True)
We now want to add a second type of fee that can apply to orders. Some rules:
Fees apply to < 10% of orders.
An order can have 0, either, or both fees.
Every time order info is displayed to users, we need to
highlight fees and show the types of fees.
Fees must be editable on an order-by-order basis.
We may add additional fees going forward.
The db is currently pretty small (a few thousand rows) and unlikely
to grow beyond 100k in the next few years.
I can see two options:
A: Add a new field on order and one on user:
User
- id
- default_handling_fee(integer, null=True)
- default_processing_fee(integer, null=True)
Order
- id
- buyer(user_id)
- order_handling_fee(integer, null=True)
- order_processing_fee(integer, null=True)
or B, add a new 'fees table':
User
- id
Order
- id
- buyer(user_id)
- order_fees(many_to_many_field to OrderFees)
OrderFees
- id
- buyer(user_id)
- price(integer)
- fee_type(choices=['handling', 'processing'])
- is_default_value(boolean)
If a user creates an order and applies one or both of the fees, in option B (new table), we would first look for existing fees that match the user and the price. If that combination exists, we would add it to the order_fees field. If it did not exist (a new price), we would create a new row and add that row to the order_fees field.
I recognize that option A is a lot simpler: no joins when looking up fees, no creating new rows, no stale rows that get created once and never used again.
I see two downsides of A. For one, it's not extensible. If we add a gift_wrapping_fee in a few months, that will mean adding a third field with null on nearly every order, and so on for additional types of order fees. The second disadvantage is that we have to remember to add checks in-app in every place that order info is displayed.
if order.order_processing_fee:
show fee
if order.order_handling_fee:
show fee
With option B, it's just
for fee in order.order_fees:
show fee
There is much less chance of errors in option B at the cost of at least one additional query per order shown to users.
One additional point: since this is all being done with Django as a backend, we can define methods on the model such that all the price fields are defined in one place.
Which option is better? Is there a third option I haven't considered?
(edited for clarity)

i think that if, in a near future, you will need to add new fee types, than the B option is better.
Have you considered a hybrid option of A and B? If i've understand the problem, you don't have only an order fee but a default fee for the users too.
You can have a Fee table:
Fees
-id
-type(choices=[your fee type list])
-price
...(other attributes)
And then you can have two many to many relation, one for the Order
Order
-id
-buyer(user)
-order_fees(many to many)
And a second for the user
User
-id
-default_fees(many to many)

Related

how do i save transaction log to database

good day guys,
I need your opinion on this problem. although am using Django for my project but am sure this problem is not tie to django alone. So, I am working on these services booking system. In my database I have 3 tables listed below:
User_Table with field
• Id
• Username
• Fullname
Services_Table with field
• Id
• name
• Price
Transaction_Table with field
• Id
• User_id
• Services_id (many to many relationship)
When this services get booked, I send it to the transaction table using the user_id and services_id as foreign key for User Table and Services Table meaning it’s the id values that are saved.
When a client want to view his or her transaction history, I provide it by running the query:
price = transaction.service.price
service_name = transaction.service.name
total_cost = sum of all services selected
as not to present the user with id values for price and service_name.
now here is my problem, in future, if the admin decide to change the name and price of a service and the client goes back to view his old transaction log, the new value get populated cus I referenced them by ids which is not what I want, I want the client to see the old value as a receipt would be even when I updated the services table.
What do you suggest I do in this case?
You should record every transaction made and record the price and amount it totalled up to at the moment the txn was made. Transaction model should have fields to record every detail about the transaction.
This means:
You would have a txn_service table, where all services in a transaction are saved and linked to the transaction table.

Inventory design management - FIFO / Weighted Average design which also needs historical inventory value

I have a database with the following details:
Product
Name
SKU
UOM (There is a UOM master, so all purchase and sales are converted to base uom and stored in the db)
Some other details
has_attribute
has_batch
Attributes
Name
Details/Remarks
Product-Attribute
Product (FK)
Attribute(FK)
Value of attribute
Inventory Details
#This is added for every product lot bought & quantity available is updated after every sale
Product (FK)
Warehouse (FK to warehoue model)
Purchase Date
Purchase Price
MRP
Tentative sales price
Quantity_bought
Quantity_available
Other batch details if applicable(batch id, manufactured_date, expiry_date)
Inventory Ledger
#This table records all in & out movement of inventory
Product
Warehouse (FK to warehoue model)
Transaction Type (Purchase/Sales)
Quantity_transacted(i.e. quantity purchased/sold)
Inventory_Purchase_cost(So as to calculate inventory valuation)
Now, my problem is:
I need to find out the historical inventory cost. For example, let's say I need to find out the value of inventory on 10th Feb 2017, what I'll be doing with the current table is not very efficient: I'll find out current inventory and go back through the ledger for all 1000-1500 SKU and about 100 transactions daily (for each sku) for more than 120 days and come to a value. taht's about 1500*100*120. It's Huge. Is there a better DB design to handle this case?
Firstly, have you tested it? 1500*100*120 is not that huge. It may be acceptable performance and there is no problem to be solved!
I'm not 100% clear how you compute the value. Do you sum up the InventoryLedger rows for each Product in each Warehouse? Is so, it's easy to put a value on the purchases, but how do you value the sales? I'm going to assume that you value the sales using the Inventory_Purchase_Cost (so it should maybe be called TransactionValue instead).
If you must optimise it, I suggest you could populate a record each day for the valuation of the product in each warehouse. I suggest the following StockValution table could be populated daily and this would allow quick computation of the valuations for any historical day.
Diagram made using QuickDBD, where I work.

How can I select records n-at-a-time for multiple users to edit?

I am using a Django backend with postgresql.
Let's say I have a database with a table called Employees with about 20,000 records.
I need to allow multiple users to edit and verify the Area Code field for every record in Employees.
I'd prefer to allow a user to view the records, say, 30 at a time (to reduce burnout).
How can I select 30 records at a time from Employees to send to the front end UI for editing, without letting multiple users edit the same records, or re-selecting a record that has already been verified?
I don't need comments on the content of the database (these are example table and field names).
One way to do this would be to add 2 more fields to your table, say for example assigned_to and verified. You can update assigned_to, which can be a foreign key to the verifying user, when you allow the user to view that Employee. This will create a record preventing the Employee from being chosen twice. assigned_to can also double as a record of who verified this Employee for future reference.
verified could be simply a Boolean field which keeps track if the Employee has already been verified and can be updated when the user confirms the verification
The actual selects can be done like this:
employees = Employee.objects.filter(assigned_to=None, verified=False)[:30]
Then
for emp in employees:
emp.assigned_to = user
emp.save()
Note: This can still potentially cause a race condition if 2 users make this request at exactly the same time. To avoid this, another possibility could be to partition the employee tables into groups for each user with no overlap. This would ensure that no 2 users would ever have the same employees

Filtering on Dates for Availability in Django

Imagine a hostel keeping track of whether or not a room is available on any given night. In addition, if a party of more than 1 guest is looking for a room, they will only want a room with at least that many beds available.
Given a date range, I would like to find rooms that are available and have at least the number of beds as there are guests (along with other filtering).
How can I go about that without effectively chaining ANDs with .filters? (Which is how it works now - and is making my database very sad.)
I'm certainly open to a different scheme for storing the availability data if needed too.
Thanks! (Hypothetical classes below to give a better sense of the problem.)
class Room(models.Model):
name = models.CharField(max_length=100)
class RoomAvailability(models.Model):
room = models.ForeignKey(Rooms)
date = models.DateField()
beds = models.IntegerField(default=1)
available_rooms = (Room.objects
.filter(roomavailability__date__range=(start_date, end_date))
.values('roomavailability__date', 'pk')
.annotate(sum=Sum('roomavailability__beds'))
.filter(sum__gte=min_beds))
Update: forgot that we need room availability per day. This query will return sets of dates available and their room PK.

Query for a ManytoMany Field with Through in Django

I have a models in Django that are something like this:
class Classification(models.Model):
name = models.CharField(choices=class_choices)
...
class Activity(models.Model):
name = models.CharField(max_length=300)
fee = models.ManyToManyField(Classification, through='Fee')
...
class Fee(models.Model):
activity = models.ForeignKey(Activity)
class = models.ForeignKey(Classification)
early_fee = models.IntegerField(decimal_places=2, max_digits=10)
regular_fee = models.IntegerField(decimal_places=2, max_digits=10)
The idea being that there will be a set of fees associated with each Activity and Classification pair. Classification is like Student, Staff, etc.
I know that part works right.
Then in my application, I query for a set of Activities with:
activities = Activity.objects.filter(...)
Which returns a list of activities. I need to display in my template that list of Activities with their Fees. Something like this:
Activity Name
Student Early Price - $4
Student Regular Price - $5
Staff Early Price - $6
Staff Regular Price - $8
But I don't know of an easy way to get this info without a specific get query of the Fees object for each activity/class pair.
I hoped this would work:
activity.fee.all()
But that just returns the Classification Object. Is there a way to get the Fee Object Data for the Pair via the Activities I already queried?
Or am I doing this completely wrong?
Considering michuk's tip to rename "fee" to "classification":
Default name for Fee objects on Activity model will be fee_set. So in order to get your prices, do this:
for a in Activity.objects.all():
a.fee_set.all() #gets you all fees for activity
There's one thing though, as you can see you'll end up doing 1 SELECT on each activity object for fees, there are some apps that can help with that, for example, django-batch-select does only 2 queries in this case.
First of all I think you named your field wrong. This:
fee = models.ManyToManyField(Classification, through='Fee')
should be rather that:
classifications = models.ManyToManyField(Classification, through='Fee')
as ManyToManyField refers to a list of related objects.
In general ManyToManyField, AFAIK, is only a django shortcut to enable easy fetching of all related objects (Classification in your case), with the association table being transparent to the model. What you want is the association table (Fee in your case) not being transparent.
So what I would do is to remove the ManyToManyField field from Activity and simply get all the fees related with the activity. And thenm if you need a Classification for each fee, get the Classification separately.