Django model relations and dividing models into few tables in larger project - django

I have few problems with Django models (relations mostly), but I'll start from code than make desciption and put questions...
Django models and relations in short (very simplified) version:
# Shorten version of services
SERVICES = [
("TC", "Tires Cleaning"),
("EC", "Exterior Cleaning"),
("IC", "Interior Cleaning"),
("CW", "Cleaning & Washing - full service"),
("WX", "Application of wax"),
]
class Client(models.Model):
name = models.CharField(max_length=35)
surname = models.CharField(max_length=35)
car = models.CharField(max_length=25)
class SingleService(models.Model):
service_name = models.CharField(max_length=35)
service_type = models.CharField(max_length=1, choices=SERVICES, default="CW")
price = models.IntegerField()
class SetOfServices(models.Model):
set_of_services = ????
price_of_set = ???
class Purchase(models.Model):
client = OneToOneField(client)
order = models.ForeignKey(SetOfServices, on_delete=models.CASCADE, blank=True, null=True)
price = ???
I want to make small project (Car Wash) with 8+ django models. Client is coming to car wash and I want to store its name/ surname and car make/model/type etc in DB
for future refference (clients coming back will get discount and free coffee).
Then I have SingleService model with about 20-30 services - they have different name and price because of diferernt tasks / time to make and options / extra features.
SetOfServices model must consist of few single services (custom, but customized from Django admin panel, probably only once, at setup / configuration time).
So it must be set of few services (I don't want client to buy only one / single service!), like:
Example SET 1:
Tires cleaning
+
Interior cleaning
Example SET 2:
Exterior cleaning
+
Application of wax
....and so on. I want to join few services in one set of services client can buy (with some discount - for example).
The last model (from above code, I plan to expand this project) is Purchase where Client orders one SetOfServices and pay some money.
The problems I encountered is choosing right relation for set_of_services field and how to calculate overall price of this set (price_of_set field).
Should I use ForeignKey (One to Many) relation in set_of_service field?
How to divide Django models (when project expands) into few tables in database (I am using PostgreSQL)? Could you give me refference or example code for it? I know we use db_table in Meta description of models, but how the relations change??

The question you want to ask yourself is:
Does one SetOfServices contain multiple SingleServices --> YES
Does one SingleService show up in multiple different SetOfServices? --> YES
If your result to these questions is two times YES the relationship is a many-to-many.
class SetOfServices(models.Model):
set_of_services = models.ManyToManyField(SingleService)
# price_of_set --> does this really need to be stored in database?
#property
def price_of_set(self):
return sum([service.price for service in self.set_of_services.all()])
Now you want to ask yourself:
Does a Purchase allow buying multiple SetOfServices (this is dependent on your choice)? --> NO
Is a SetOfServices bought by multiple Purchases? --> YES
YES YES --> ManyToMany
NO YES --> OneToMany --> ForeignKey
class Purchase(models.Model):
client = OneToOneField(client)
order = models.ForeignKey(SetOfServices, on_delete=models.CASCADE, blank=True, null=True)
# same advice as above for price = ???
#property
def price(self):
return self.order.price_of_set
To be honest I feel like your setup of models is already kinda stable. I can't tell you how to divide models at runtime of a project. I've never done it.
Let me know how it goes!
Edit after comment of OP
Depending on your "staring point" you can access the price inside of your templates. Here some examples:
<h1>Object = Purchase</h1>
{{ object.price }}
<h1>Object = SetOfServices</h1>
{{ object.price_of_set }}
<h1>Object = SingleService</h1>
{{ object.price }}

Related

Multiple payments related to multiple customer implementation in django

I'm writing a customer management system for my business and got stuck on the payments entry system. This will run on a local dedicated server and should have only one user so code performance is not really an issue.
Every adult customer who enters the store is given a numbered card (Card, for the rest of this question) and his/her ID ( from Customer model ) is attached to it by a foreign key relation. There is an "entrance fee subtotal", which is the result of a choice field on Card model (there's only two choices and those won't change for a long time) plus kids 'fees'.
This, along with other two kind of models ( Product and Service), will compose the customer's bill. I have it working just fine, except on the payments registration.
As many Customers may be part of a family, and they may split their total bill quite often, I do believe Payment should be a model with an ManyToManyField related to Card so it could cover multiple payments methods ( treated as another choice field, since it will be either money, credit or debit cards ) but I can't figure it out how to model it neither how to handle it in my view/template.
I'm using django 1.9 & postgres 9.5 & python 2.7.
Bootstrap 3 along with some JS for styling (probably irrelevant).
Enough said, here's some code:
models.py
class Customer(models.Model):
id = models.AutoField(primary_key=True) #unnecessary but I had already written it
name = models.CharField(max_length=40)
last = models.CharField(max_length=80)
class Card(models.Model):
entrance_type1 = 1
entrance_type2 = 2
entrance_choices = (
(entrance_type1, 'Fun'),
(entrance_type2, 'Really Fun, kinda expensive'),
)
entrance_types = {
1:"Fun",
2:"Really Fun",
}
entrance_fee= {
'kid':5.0,
entrance_type1:15.0,
entrance_type1:35.0,
}
id = models.AutoField(primary_key=True) #Yeah, I do that
date = models.DateTimeField(auto_now=True, auto_now_add=False)
card_number = models.IntegerField()
entrance_type = models.PositiveIntegerField(choices=entrance_choices)
kids_number = models.PositiveIntegerField()
id_costumer = models.ForeignKey(Customer)
entrances_value = models.DecimalField(max_digits=6, decimal_places=2)
#will be entrance_fee[entrance_type] + entrance_fee['kid'] * kids_number
status = models.BooleanField(default=1) #should be 0 after payment(s)
Anyway, I really need help modelling payments for those. It should contain payment method, date and to which Cards it's related to.
I'm already getting ideas on the views/template step so I won't be strict about those on answers.
I do believe my question is kinda fuzzy and confuse, but can't figure how to make it better ( and maybe this is why I can't solve it by myself ) so please comment in your doubts and I'll edit it ( including removing this part when it does improve) after lunch.
Thanks in advance

Embed product-variance logic into Django models

I wonder how I would model my Products model to auto-create (and that the admin-App would also understand it) variants of a Product based on it's variant-parts.
My Products have;
Colors
Sizes
and can probably get more features in the future.
How would I model my Product class to generate all variants of the Product?
Say I would create a new Product in Colors Red Blue Green and in Sizes XS S M L XL.
class Product(models.Model):
name = models.CharField(max_length=200)
class Color(models.Model):
product = models.ForeignKey(Product)
name = models.CharField(max_length=200)
class Size(models.Model):
product = models.ForeignKey(Product)
name = models.CharField(max_length=200)
class FutureVariant(models.Model):
product = models.ForeignKey(Product)
name = models.CharField(max_length=200)
# etc.
Now when I would need a smart method that when I would auto-create all color-size-[FUTURE VARIANT] for that product.
So I would tell Django;
Create new Product
In the colors Red Blue Green
In the sizes XS S M L XL
And the Product class would go and produce Products with all possible combinations in the products_product table.
I'm almost sure that this has design flaws. But I'm just curious how to put this logic in the ORM, and not to write weird procedural code, which would probably go against the DRY principal.
In Database logic I would think of something like this;
PRODUCTS
- id
- name
PRODUCTS_VARIANTS_COLORS
- id
- name
- html_code
PRODUCTS_VARIANTS_SIZES
- id
- name
PRODUCTS_VARIANTS_TABLES
- table_name
- table_id
PRODUCTS_VARIANTS
- product_id
- variant_table
- variant_id
This way I could make endless variant tables, as long as I would register them in my PRODUCTS_VARIANTS_TABLES and store their name as relevant. PRODUCTS_VARIANTS would hold all the the variants of the product, including combinations of them all. I am also aiming to have a selection-phase where the user can chose (in a HTML checkbox-list) which variants it does and doesn't want.
The problem (I think) is that this would not really comply with a logic in the ORM.
I don't know if you are asking about alternatives or just looking to make your way work, but what about splitting a product from it's attributes?
So instead of having separate models for attributes, you just have an Attribute model. This way you are future-proofing your database so you can easily add more attributes (like if you have products with a height and width instead of just color or size).
class AttributeBase(models.Model):
label = models.CharField(max_length=255) # e.g. color, size, shape, etc.
...
class Attribute(models.Model):
base = models.ForeignKey('AttributeBase', related_name='attributes')
value = models.CharField(max_length=255) # e.g. red, L, round, etc.
internal_value = models.CharField(max_length=255, null=True, blank=True) # other values you may need e.g. #ff0000, etc.
...
class ProductAttribute(Attribute):
product = models.ForeignKey('Product', related_name='attributes')
It now becomes very easy to create all attributes for a product...
class Product(models.Model):
...
def add_all_attributes(self):
for attribute in Attribute.objects.all():
self.attributes.add(attribute)
now when you use product.add_all_attributes() that product will contain every attribute. AND you can even make it add attributes of a certain AttributeBase
def add_all_attributes_for_base(self, label):
base = AttributeBase.objects.get(label=label)
for attribute in base.attributes.all():
self.attributes.add(attribute)
You could write something as:
class Product(models.Model):
#classmethod
def create_variants(cls):
# compute all possible combinations
combinations = ...
for combination in combinations:
Product.objects.create(**combination)
Creating all the combinations would indeed happen through registering the possible variants and their possible values.
Note that ORM is there to help you map Django objects to database records, it doesn't help you with producing the database records (read: Django models) that you wish to save.

Chaining Foreign Keys

I have a few models that look like this:
class System(models.Model):
'''Defines a system'''
system_desc = models.CharField('System Name', max_length=50)
class SystemGroup(models.Model):
'''Groups within a 'System' (ie. Customers, regions, divisions, etc. '''
group_desc = models.CharField('Group Name',max_length=25)
system = models.ForeignKey(System)
class QueryData(models.Model):
'''Queries related to a 'System' (ie. Active users against System1, Orders today in system2, etc. '''
qry_desc = models.CharField('Query Description', max_length=50)
system = models.ForeignKey(System)
class UserDashboard(models.Model):
'''User specific Dashboard '''
user = models.ForeignKey(User)
board_name = models.CharField('Dashboard Name', max_length=50)
class UserDashboardGroup(models.Model):
'''Groups on the dashboard (ie. 'System 1's Key Indicators', 'System 2's Hot Sheet', etc. '''
group_desc = models.CharField('Group Display Title', max_length=50)
user = models.ForeignKey(User)
dashboard = models.ForeignKey(UserDashboard)
system = models.ForeignKey(System)
system_group = models.ForeignKey(SystemGroup)
class UserGroupQuery(models.Model):
'''Queries that run in a specific group on the dashboard (ie. Within 'System 1's Key Indicators, we run queries for active users today, orders in the last hour, etc. '''
user = models.ForeignKey(User)
dashboard = ChainedForeignKey(UserDashboard, chained_field='user', chained_model_field='user', show_all=False, auto_choose=True)
user_dashboard_group = ChainedForeignKey(UserDashboardGroup, chained_field='dashboard', chained_model_field='dashboard')
query = models.ForeignKey(QueryData)
I am having problems with the very last part of this. I want to restrict the 'query' that appears in a admin page based on the selected user_dashboard_group. I'm not sure how I can do this, based on my current models though. query and user_dashboard_group both have a foreign key to System, but not to each other. To get this though, I'd have to get the user_dashboard_group's system and then use that to filter the QueryData. How can I do this?
Edit
I'm adding in a picture to (hopefully) describe a little better what I want to do.
In step 1, the user inputs a name for this group of queries. This group is associated with a system (#2) and a predefined group within the system (#3) (think of #3 as a 'customer' or a 'region', etc and #1 and #3 are NOT the same thing, despite the similar naming). They then select 'Save and Continue editing' on this inline form and the drop down at step 4 becomes populated with information from the above form. Once step #4 has a selection made, I want #5 to populate with data only from the associated system. Since #2 contains this information, I am hoping it is fairly easy to do this, but I can't figure out the chaining.
I also realized that I didn't mention I was using django-smart-selects
I have never worked with django-smart-selects, but following the docs, I would expect
query = models.ChainedForeignKey(QueryData,**kwargs)
instead of
query = models.ForeignKey(QueryData)
since you want the options in query depend on the other selections. Is that understanding correct? If so, it was only about defining the right **kwargs.
For **kwargs, I would propose something like this
Update
chained_field='query', chained_model_field='user_dashboard_group__system__query_set__query'
assuming that the fields name is 'system in both cases.
I am pretty sure that this describes the relationship correctly. I am just not sure, if django-smart-selects supports that syntax.
Same way you would do it across one relation, except across two.
field__relation1__relation2

What's the best way to ensure balanced transactions in a double-entry accounting app?

What's the best way to ensure that transactions are always balanced in double-entry accounting?
I'm creating a double-entry accounting app in Django. I have these models:
class Account(models.Model):
TYPE_CHOICES = (
('asset', 'Asset'),
('liability', 'Liability'),
('equity', 'Equity'),
('revenue', 'Revenue'),
('expense', 'Expense'),
)
num = models.IntegerField()
type = models.CharField(max_length=20, choices=TYPE_CHOICES, blank=False)
description = models.CharField(max_length=1000)
class Transaction(models.Model):
date = models.DateField()
description = models.CharField(max_length=1000)
notes = models.CharField(max_length=1000, blank=True)
class Entry(models.Model):
TYPE_CHOICES = (
('debit', 'Debit'),
('credit', 'Credit'),
)
transaction = models.ForeignKey(Transaction, related_name='entries')
type = models.CharField(max_length=10, choices=TYPE_CHOICES, blank=False)
account = models.ForeignKey(Account, related_name='entries')
amount = models.DecimalField(max_digits=11, decimal_places=2)
I'd like to enforce balanced transactions at the model level but there doesn't seem to be hooks in the right place. For example, Transaction.clean won't work because transactions get saved first, then entries are added due to the Entry.transaction ForeignKey.
I'd like balance checking to work within admin also. Currently, I use an EntryInlineFormSet with a clean method that checks balance in admin but this doesn't help when adding transactions from a script. I'm open to changing my models to make this easier.
(Hi Ryan! -- Steve Traugott)
It's been a while since you posted this, so I'm sure you're way past this puzzle. For others and posterity, I have to say yes, you need to be able to split transactions, and no, you don't want to take the naive approach and assume that transaction legs will always be in pairs, because they won't. You need to be able to do N-way splits, where N is any positive integer greater than 1. Ryan has the right structure here.
What Ryan calls Entry I usually call Leg, as in transaction leg, and I'm usually working with bare Python on top of some SQL database. I haven't used Django yet, but I'd be surprised (shocked) if Django doesn't support something like the following: Rather than use the native db row ID for transaction ID, I instead usually generate a unique transaction ID from some other source, store that in both the Transaction and Leg objects, do my final check to ensure debits and credits balance, and then commit both Transaction and Legs to the db in one SQL transaction.
Ryan, is that more or less what you wound up doing?
This may sound terribly naive, but why not just record each transaction in a single record containing "to account" and "from account" foreign keys that link to an accounts table instead of trying to create two records for each transaction? From my point of view, it seems that the essence of "double-entry" is that transactions always move money from one account to another. There is no advantage using two records to store such transactions and many disadvantages.

Custom Satchmo Store Configuration / Process

I want to create an order form to buy multiple things in this sort of structure:
Business Data 1
---Product A
---Product B
Business Data 2
---Product A
That is, I want to sell products A,B, but before this is done I need additional aggregate data to be in the 'Business Data' object. That is:
Business 1: Joe's Plumbing, located at ... ... ...
---Product A, standard ad appearing in category 3, with text "awesome plumbing"
---Product B, cooler ad appearing in category 9, with text "cheap plumbing"
Business 2: Joe's Burgers, located at ... ... ...
---Product A, standard ad appearing in category 4, with text "zzz"
or, from a model level, more like:
class Business(models.Model):
name = models.CharField(max_length=255)
address = models.MagicAddressField()
class Ad(models.Model):
category = models.ForeignKey(Category)
text = models.CharField(max_length=255)
business = models.ForeignKey(Business)
Now, instead of reimplementing an entire shopping cart backend, I'm pretty sure I want to use Satchmo. However, i'm having trouble wrapping my head around this sort of thing.
Satchmo appears to have multiple options for Products, but they're all "flat". That is, while I could easily get Satchmo to allow the end user to buy Product A and Product B, the db shows no connection to Business 1, and things like the business name would have to be repeated in both product A and B.
I think I can probably get around this with my own views / templates, if only I can get the final "product instance" that satchmo is selling during an order to have a foreign key to the Business table i'll create myself. In other words, I want to make the Ad model a satchmo custom product model - I think...
But If I just change Ad to:
class Ad(Product):
objects = ProductManager()
category = models.ForeignKey(Category)
text = models.CharField(max_length=255)
business = models.ForeignKey(Business)
Isn't this the wrong semantics? Doesn't that mean that "this product type is associated with business x", not "when you buy this, the product's instance will be pointing to business x"?
I'm pretty confused here :-/
If I understood this right, I would make something like this.
Make a ManyToMany relationship between business and the products:
class Business(models.Model):
name = models.CharField(max_length=255)
address = models.MagicAddressField()
products = models.ManyToManyField(Product)
Then, on your custom-form and template/view, you can add for each business a fieldset with the Products/Ads for each business. The only complicatation I see by now, would be, that after filling up the form and sending it, you would also need customized "checkout-process" to save the information of which product got bought through which business... maybe this could be done through product-options(?) but any way it'll probably mean a lot of customization/working with signals ;-) But i think this is the right direction.